packages feed

futhark 0.25.10 → 0.25.11

raw patch · 75 files changed

+2284/−2109 lines, 75 filesdep ~futhark-manifestPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: futhark-manifest

API changes (from Hackage documentation)

- Futhark.CodeGen.ImpCode: SizeGroup :: SizeClass
- Futhark.CodeGen.ImpCode: SizeLocalMemory :: SizeClass
- Futhark.CodeGen.ImpCode: SizeNumGroups :: SizeClass
- Futhark.CodeGen.ImpCode.GPU: GetGroupId :: VName -> Int -> KernelOp
- Futhark.CodeGen.ImpCode.GPU: [kernelCheckLocalMemory] :: Kernel -> Bool
- Futhark.CodeGen.ImpCode.GPU: [kernelGroupSize] :: Kernel -> [GroupDim]
- Futhark.CodeGen.ImpCode.GPU: [kernelNumGroups] :: Kernel -> [Exp]
- Futhark.CodeGen.ImpCode.GPU: type GroupDim = Either Exp KernelConst
- Futhark.CodeGen.ImpCode.OpenCL: type GroupDim = Either Exp KernelConst
- Futhark.CodeGen.ImpGen.GPU.Base: [kAttrCheckLocalMemory] :: KernelAttrs -> Bool
- Futhark.CodeGen.ImpGen.GPU.Base: [kAttrGroupSize] :: KernelAttrs -> Count GroupSize SubExp
- Futhark.CodeGen.ImpGen.GPU.Base: [kAttrNumGroups] :: KernelAttrs -> Count NumGroups SubExp
- Futhark.CodeGen.ImpGen.GPU.Base: [kernelGroupIdVar] :: KernelConstants -> VName
- Futhark.CodeGen.ImpGen.GPU.Base: [kernelGroupId] :: KernelConstants -> TExp Int32
- Futhark.CodeGen.ImpGen.GPU.Base: [kernelGroupSizeCount] :: KernelConstants -> Count GroupSize SubExp
- Futhark.CodeGen.ImpGen.GPU.Base: [kernelGroupSize] :: KernelConstants -> TExp Int64
- Futhark.CodeGen.ImpGen.GPU.Base: [kernelNumGroupsCount] :: KernelConstants -> Count NumGroups SubExp
- Futhark.CodeGen.ImpGen.GPU.Base: [kernelNumGroups] :: KernelConstants -> TExp Int64
- Futhark.CodeGen.ImpGen.GPU.Base: groupCoverSpace :: IntExp t => [TExp t] -> ([TExp t] -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Base: groupLoop :: IntExp t => TExp t -> (TExp t -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Base: groupReduce :: TExp Int32 -> Lambda GPUMem -> [VName] -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Base: groupScan :: Maybe (TExp Int32 -> TExp Int32 -> TExp Bool) -> TExp Int64 -> TExp Int64 -> Lambda GPUMem -> [VName] -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Base: virtualiseGroups :: SegVirt -> TExp Int32 -> (TExp Int32 -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Group: atomicUpdateLocking :: AtomicBinOp -> Lambda GPUMem -> AtomicUpdate GPUMem KernelEnv
- Futhark.CodeGen.ImpGen.GPU.Group: compileGroupResult :: SegSpace -> PatElem LetDecMem -> KernelResult -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Group: data Precomputed
- Futhark.CodeGen.ImpGen.GPU.Group: groupOperations :: Operations GPUMem KernelEnv KernelOp
- Futhark.CodeGen.ImpGen.GPU.Group: precomputeConstants :: Count GroupSize (TExp Int64) -> Stms GPUMem -> CallKernelGen Precomputed
- Futhark.CodeGen.ImpGen.GPU.Group: precomputedConstants :: Precomputed -> InKernelGen a -> InKernelGen a
- Futhark.CodeGen.ImpGen.GPU.Group: sKernelGroup :: String -> VName -> KernelAttrs -> InKernelGen () -> CallKernelGen ()
- Futhark.CodeGen.OpenCL.Heuristics: GroupSize :: WhichSize
- Futhark.CodeGen.OpenCL.Heuristics: NumGroups :: WhichSize
- Futhark.IR.GPU: scremaLambda :: ScremaForm rep -> Lambda rep
- Futhark.IR.GPU.Op: CalcNumGroups :: SubExp -> Name -> SubExp -> SizeOp
- Futhark.IR.GPU.Op: SegGroup :: SegVirt -> Maybe KernelGrid -> SegLevel
- Futhark.IR.GPU.Op: SegThreadInGroup :: SegVirt -> SegLevel
- Futhark.IR.GPU.Op: [gridGroupSize] :: KernelGrid -> Count GroupSize SubExp
- Futhark.IR.GPU.Op: [gridNumGroups] :: KernelGrid -> Count NumGroups SubExp
- Futhark.IR.GPU.Sizes: SizeGroup :: SizeClass
- Futhark.IR.GPU.Sizes: SizeLocalMemory :: SizeClass
- Futhark.IR.GPU.Sizes: SizeNumGroups :: SizeClass
- Futhark.IR.GPU.Sizes: data GroupSize
- Futhark.IR.GPU.Sizes: data NumGroups
- Futhark.IR.MC: scremaLambda :: ScremaForm rep -> Lambda rep
- Futhark.IR.Mem: instance (GHC.Classes.Eq d, GHC.Classes.Eq u, GHC.Classes.Eq ret) => GHC.Classes.Eq (Futhark.IR.Mem.MemInfo d u ret)
- Futhark.IR.Mem: instance (GHC.Classes.Ord d, GHC.Classes.Ord u, GHC.Classes.Ord ret) => GHC.Classes.Ord (Futhark.IR.Mem.MemInfo d u ret)
- Futhark.IR.Mem: instance (GHC.Show.Show d, GHC.Show.Show u, GHC.Show.Show ret) => GHC.Show.Show (Futhark.IR.Mem.MemInfo d u ret)
- Futhark.IR.SOACS.SOAC: scremaLambda :: ScremaForm rep -> Lambda rep
- Futhark.IR.Syntax.Core: instance (GHC.Classes.Eq u, GHC.Classes.Eq shape) => GHC.Classes.Eq (Futhark.IR.Syntax.Core.TypeBase shape u)
- Futhark.IR.Syntax.Core: instance (GHC.Classes.Ord u, GHC.Classes.Ord shape) => GHC.Classes.Ord (Futhark.IR.Syntax.Core.TypeBase shape u)
- Futhark.IR.Syntax.Core: instance (GHC.Show.Show u, GHC.Show.Show shape) => GHC.Show.Show (Futhark.IR.Syntax.Core.TypeBase shape u)
- Language.Futhark.Syntax: instance (GHC.Classes.Eq u, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.ScalarTypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Classes.Eq u, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.TypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Classes.Ord u, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.ScalarTypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Classes.Ord u, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.TypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Show.Show u, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.ScalarTypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Show.Show u, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.TypeBase dim u)
+ Futhark.Analysis.HORep.SOAC: [scremaLambda] :: ScremaForm rep -> Lambda rep
+ Futhark.Analysis.HORep.SOAC: [scremaReduces] :: ScremaForm rep -> [Reduce rep]
+ Futhark.Analysis.HORep.SOAC: [scremaScans] :: ScremaForm rep -> [Scan rep]
+ Futhark.CodeGen.ImpCode: SizeGrid :: SizeClass
+ Futhark.CodeGen.ImpCode: SizeSharedMemory :: SizeClass
+ Futhark.CodeGen.ImpCode: SizeThreadBlock :: SizeClass
+ Futhark.CodeGen.ImpCode.GPU: GetBlockId :: VName -> Int -> KernelOp
+ Futhark.CodeGen.ImpCode.GPU: [kernelBlockSize] :: Kernel -> [BlockDim]
+ Futhark.CodeGen.ImpCode.GPU: [kernelCheckSharedMemory] :: Kernel -> Bool
+ Futhark.CodeGen.ImpCode.GPU: [kernelNumBlocks] :: Kernel -> [Exp]
+ Futhark.CodeGen.ImpCode.GPU: type BlockDim = Either Exp KernelConst
+ Futhark.CodeGen.ImpCode.OpenCL: type BlockDim = Either Exp KernelConst
+ Futhark.CodeGen.ImpGen.GPU.Base: [kAttrBlockSize] :: KernelAttrs -> Count BlockSize SubExp
+ Futhark.CodeGen.ImpGen.GPU.Base: [kAttrCheckSharedMemory] :: KernelAttrs -> Bool
+ Futhark.CodeGen.ImpGen.GPU.Base: [kAttrNumBlocks] :: KernelAttrs -> Count NumBlocks SubExp
+ Futhark.CodeGen.ImpGen.GPU.Base: [kernelBlockIdVar] :: KernelConstants -> VName
+ Futhark.CodeGen.ImpGen.GPU.Base: [kernelBlockId] :: KernelConstants -> TExp Int32
+ Futhark.CodeGen.ImpGen.GPU.Base: [kernelBlockSizeCount] :: KernelConstants -> Count BlockSize SubExp
+ Futhark.CodeGen.ImpGen.GPU.Base: [kernelBlockSize] :: KernelConstants -> TExp Int64
+ Futhark.CodeGen.ImpGen.GPU.Base: [kernelNumBlocksCount] :: KernelConstants -> Count NumBlocks SubExp
+ Futhark.CodeGen.ImpGen.GPU.Base: [kernelNumBlocks] :: KernelConstants -> TExp Int64
+ Futhark.CodeGen.ImpGen.GPU.Base: blockCoverSpace :: IntExp t => [TExp t] -> ([TExp t] -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: blockLoop :: IntExp t => TExp t -> (TExp t -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: blockReduce :: TExp Int32 -> Lambda GPUMem -> [VName] -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: blockScan :: Maybe (TExp Int32 -> TExp Int32 -> TExp Bool) -> TExp Int64 -> TExp Int64 -> Lambda GPUMem -> [VName] -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: virtualiseBlocks :: SegVirt -> TExp Int32 -> (TExp Int32 -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Block: atomicUpdateLocking :: AtomicBinOp -> Lambda GPUMem -> AtomicUpdate GPUMem KernelEnv
+ Futhark.CodeGen.ImpGen.GPU.Block: blockOperations :: Operations GPUMem KernelEnv KernelOp
+ Futhark.CodeGen.ImpGen.GPU.Block: compileBlockResult :: SegSpace -> PatElem LetDecMem -> KernelResult -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Block: data Precomputed
+ Futhark.CodeGen.ImpGen.GPU.Block: precomputeConstants :: Count BlockSize (TExp Int64) -> Stms GPUMem -> CallKernelGen Precomputed
+ Futhark.CodeGen.ImpGen.GPU.Block: precomputedConstants :: Precomputed -> InKernelGen a -> InKernelGen a
+ Futhark.CodeGen.ImpGen.GPU.Block: sKernelBlock :: String -> VName -> KernelAttrs -> InKernelGen () -> CallKernelGen ()
+ Futhark.CodeGen.OpenCL.Heuristics: BlockSize :: WhichSize
+ Futhark.CodeGen.OpenCL.Heuristics: NumBlocks :: WhichSize
+ Futhark.IR.GPU: [scremaLambda] :: ScremaForm rep -> Lambda rep
+ Futhark.IR.GPU: [scremaReduces] :: ScremaForm rep -> [Reduce rep]
+ Futhark.IR.GPU: [scremaScans] :: ScremaForm rep -> [Scan rep]
+ Futhark.IR.GPU.Op: CalcNumBlocks :: SubExp -> Name -> SubExp -> SizeOp
+ Futhark.IR.GPU.Op: SegBlock :: SegVirt -> Maybe KernelGrid -> SegLevel
+ Futhark.IR.GPU.Op: SegThreadInBlock :: SegVirt -> SegLevel
+ Futhark.IR.GPU.Op: [gridBlockSize] :: KernelGrid -> Count BlockSize SubExp
+ Futhark.IR.GPU.Op: [gridNumBlocks] :: KernelGrid -> Count NumBlocks SubExp
+ Futhark.IR.GPU.Sizes: SizeGrid :: SizeClass
+ Futhark.IR.GPU.Sizes: SizeSharedMemory :: SizeClass
+ Futhark.IR.GPU.Sizes: SizeThreadBlock :: SizeClass
+ Futhark.IR.GPU.Sizes: data BlockSize
+ Futhark.IR.GPU.Sizes: data NumBlocks
+ Futhark.IR.MC: [scremaLambda] :: ScremaForm rep -> Lambda rep
+ Futhark.IR.MC: [scremaReduces] :: ScremaForm rep -> [Reduce rep]
+ Futhark.IR.MC: [scremaScans] :: ScremaForm rep -> [Scan rep]
+ Futhark.IR.Mem: instance (GHC.Classes.Eq d, GHC.Classes.Eq ret, GHC.Classes.Eq u) => GHC.Classes.Eq (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem: instance (GHC.Classes.Ord d, GHC.Classes.Ord ret, GHC.Classes.Ord u) => GHC.Classes.Ord (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem: instance (GHC.Show.Show d, GHC.Show.Show ret, GHC.Show.Show u) => GHC.Show.Show (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem.IxFun: existentialized :: IxFun a -> [a]
+ Futhark.IR.SOACS.SOAC: [scremaLambda] :: ScremaForm rep -> Lambda rep
+ Futhark.IR.SOACS.SOAC: [scremaReduces] :: ScremaForm rep -> [Reduce rep]
+ Futhark.IR.SOACS.SOAC: [scremaScans] :: ScremaForm rep -> [Scan rep]
+ Futhark.IR.Syntax.Core: instance (GHC.Classes.Eq shape, GHC.Classes.Eq u) => GHC.Classes.Eq (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.IR.Syntax.Core: instance (GHC.Classes.Ord shape, GHC.Classes.Ord u) => GHC.Classes.Ord (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.IR.Syntax.Core: instance (GHC.Show.Show shape, GHC.Show.Show u) => GHC.Show.Show (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Eq dim, GHC.Classes.Eq u) => GHC.Classes.Eq (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Eq dim, GHC.Classes.Eq u) => GHC.Classes.Eq (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Ord dim, GHC.Classes.Ord u) => GHC.Classes.Ord (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Ord dim, GHC.Classes.Ord u) => GHC.Classes.Ord (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Show.Show dim, GHC.Show.Show u) => GHC.Show.Show (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Show.Show dim, GHC.Show.Show u) => GHC.Show.Show (Language.Futhark.Syntax.TypeBase dim u)
- Futhark.CodeGen.ImpCode.GPU: Kernel :: Code KernelOp -> [KernelUse] -> [Exp] -> [GroupDim] -> Name -> Bool -> Bool -> Kernel
+ Futhark.CodeGen.ImpCode.GPU: Kernel :: Code KernelOp -> [KernelUse] -> [Exp] -> [BlockDim] -> Name -> Bool -> Bool -> Kernel
- Futhark.CodeGen.ImpCode.OpenCL: LaunchKernel :: KernelSafety -> KernelName -> Count Bytes (TExp Int64) -> [KernelArg] -> [Exp] -> [GroupDim] -> OpenCL
+ Futhark.CodeGen.ImpCode.OpenCL: LaunchKernel :: KernelSafety -> KernelName -> Count Bytes (TExp Int64) -> [KernelArg] -> [Exp] -> [BlockDim] -> OpenCL
- Futhark.CodeGen.ImpGen.GPU.Base: KernelAttrs :: Bool -> Bool -> Count NumGroups SubExp -> Count GroupSize SubExp -> Map VName KernelConstExp -> KernelAttrs
+ Futhark.CodeGen.ImpGen.GPU.Base: KernelAttrs :: Bool -> Bool -> Count NumBlocks SubExp -> Count BlockSize SubExp -> Map VName KernelConstExp -> KernelAttrs
- Futhark.CodeGen.ImpGen.GPU.Base: KernelConstants :: TExp Int32 -> TExp Int32 -> TExp Int32 -> VName -> VName -> VName -> Count NumGroups SubExp -> Count GroupSize SubExp -> TExp Int64 -> TExp Int64 -> TExp Int32 -> TExp Int32 -> Map [SubExp] [TExp Int32] -> Map [SubExp] (TExp Int32) -> KernelConstants
+ Futhark.CodeGen.ImpGen.GPU.Base: KernelConstants :: TExp Int32 -> TExp Int32 -> TExp Int32 -> VName -> VName -> VName -> Count NumBlocks SubExp -> Count BlockSize SubExp -> TExp Int64 -> TExp Int64 -> TExp Int32 -> TExp Int32 -> Map [SubExp] [TExp Int32] -> Map [SubExp] (TExp Int32) -> KernelConstants
- Futhark.CodeGen.ImpGen.GPU.Base: defKernelAttrs :: Count NumGroups SubExp -> Count GroupSize SubExp -> KernelAttrs
+ Futhark.CodeGen.ImpGen.GPU.Base: defKernelAttrs :: Count NumBlocks SubExp -> Count BlockSize SubExp -> KernelAttrs
- Futhark.IR.GPU.Op: KernelGrid :: Count NumGroups SubExp -> Count GroupSize SubExp -> KernelGrid
+ Futhark.IR.GPU.Op: KernelGrid :: Count NumBlocks SubExp -> Count BlockSize SubExp -> KernelGrid
- Futhark.IR.Mem.IxFun: existentialize :: IxFun (TPrimExp Int64 a) -> IxFun (TPrimExp Int64 (Ext b))
+ Futhark.IR.Mem.IxFun: existentialize :: Int -> IxFun (TPrimExp Int64 a) -> IxFun (TPrimExp Int64 (Ext a))
- Futhark.IR.Mem.IxFun: mkExistential :: Int -> Int -> Int -> IxFun (Ext a)
+ Futhark.IR.Mem.IxFun: mkExistential :: Int -> Shape (Ext a) -> Int -> IxFun (Ext a)
- Futhark.IR.Mem.LMAD: mkExistential :: Int -> Int -> LMAD (Ext a)
+ Futhark.IR.Mem.LMAD: mkExistential :: Shape (Ext a) -> Int -> LMAD (Ext a)
- Futhark.IR.Syntax: class Pretty a
+ Futhark.IR.Syntax: class () => Pretty a
- Futhark.Pkg.Types: data Chunk
+ Futhark.Pkg.Types: data () => Chunk
- Futhark.Pkg.Types: data SemVer
+ Futhark.Pkg.Types: data () => SemVer
- Futhark.Pkg.Types: newtype Release
+ Futhark.Pkg.Types: newtype () => Release
- Futhark.Test: data Mismatch
+ Futhark.Test: data () => Mismatch
- Futhark.Test: data Value
+ Futhark.Test: data () => Value
- Futhark.Util.Pretty: data AnsiStyle
+ Futhark.Util.Pretty: data () => AnsiStyle
- Futhark.Util.Pretty: data Color
+ Futhark.Util.Pretty: data () => Color
- Futhark.Util.Table: data AnsiStyle
+ Futhark.Util.Table: data () => AnsiStyle
- Futhark.Util.Table: data Color
+ Futhark.Util.Table: data () => Color
- Language.Futhark.Core: class Located a
+ Language.Futhark.Core: class () => Located a
- Language.Futhark.Core: data Half
+ Language.Futhark.Core: data () => Half
- Language.Futhark.Core: data Int16
+ Language.Futhark.Core: data () => Int16
- Language.Futhark.Core: data Int32
+ Language.Futhark.Core: data () => Int32
- Language.Futhark.Core: data Int64
+ Language.Futhark.Core: data () => Int64
- Language.Futhark.Core: data Int8
+ Language.Futhark.Core: data () => Int8
- Language.Futhark.Core: data Loc
+ Language.Futhark.Core: data () => Loc
- Language.Futhark.Core: data SrcLoc
+ Language.Futhark.Core: data () => SrcLoc
- Language.Futhark.Core: data Word16
+ Language.Futhark.Core: data () => Word16
- Language.Futhark.Core: data Word32
+ Language.Futhark.Core: data () => Word32
- Language.Futhark.Core: data Word64
+ Language.Futhark.Core: data () => Word64
- Language.Futhark.Core: data Word8
+ Language.Futhark.Core: data () => Word8
- Language.Futhark.Parser.Monad: data L a
+ Language.Futhark.Parser.Monad: data () => L a
- Language.Futhark.Primitive: data Half
+ Language.Futhark.Primitive: data () => Half
- Language.Futhark.Primitive: data Int16
+ Language.Futhark.Primitive: data () => Int16
- Language.Futhark.Primitive: data Int32
+ Language.Futhark.Primitive: data () => Int32
- Language.Futhark.Primitive: data Int64
+ Language.Futhark.Primitive: data () => Int64
- Language.Futhark.Primitive: data Int8
+ Language.Futhark.Primitive: data () => Int8
- Language.Futhark.Primitive: data Word16
+ Language.Futhark.Primitive: data () => Word16
- Language.Futhark.Primitive: data Word32
+ Language.Futhark.Primitive: data () => Word32
- Language.Futhark.Primitive: data Word64
+ Language.Futhark.Primitive: data () => Word64
- Language.Futhark.Primitive: data Word8
+ Language.Futhark.Primitive: data () => Word8
- Language.Futhark.Query: data Pos
+ Language.Futhark.Query: data () => Pos

Files

CHANGELOG.md view
@@ -5,6 +5,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.25.11]++### Added++* New prelude function: `manifest`.  For doing subtle things to memory.++* The GPU backends now handle up to 20 operators in a single fused+  reduction.++* CUDA/HIP terminology for GPU concepts (e.g. "thread block") is now+  used in all public interfaces. The OpenCL names are still supported+  for backwards compatibility.++* More fusion across array slicing.++### Fixed++* Compatibility with CUDA versions prior than 12.+ ## [0.25.10]  ### Added
docs/c-api.rst view
@@ -461,13 +461,25 @@  The following functions are not interesting to most users. +.. c:function:: void futhark_context_config_set_default_thread_block_size(struct futhark_context_config *cfg, int size)++   Set the default number of work-items in a thread block.+ .. c:function:: void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size) -   Set the default number of work-items in a work-group.+   Identical to+   :c:func:`futhark_context_config_set_default_thread_block_size`;+   provided for backwards compatibility. +.. c:function:: void futhark_context_config_set_default_grid_size(struct futhark_context_config *cfg, int num)++   Set the default number of thread blocks used for kernels.+ .. c:function:: void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) -   Set the default number of work-groups used for kernels.+   Identical to+   :c:func:`futhark_context_config_set_default_grid_size`;+   provided for backwards compatibility.  .. c:function:: void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int num) 
docs/language-reference.rst view
@@ -1745,13 +1745,13 @@ ``incremental_flattening(no_intra)`` .................................... -When using incremental flattening, do not generate the "intra-group+When using incremental flattening, do not generate the "intra-block parallelism" version for the attributed SOACs.  ``incremental_flattening(only_intra)`` ...................................... -When using incremental flattening, *only* generate the "intra-group+When using incremental flattening, *only* generate the "intra-block parallelism" version of the attributed SOACs.  **Beware**: the resulting program will fail to run if the inner parallelism does not fit on the device.
docs/man/futhark-cuda.rst view
@@ -51,9 +51,8 @@ ==================  Generated executables accept the same options as those generated by-:ref:`futhark-c(1)`.  The ``-t`` option behaves as with-:ref:`futhark-opencl(1)`.  For commonality, the options use OpenCL-nomenclature ("group" instead of "thread block").+:ref:`futhark-c(1)`. The ``-t`` option behaves as with+:ref:`futhark-opencl(1)`.  The following additional options are accepted. @@ -61,12 +60,12 @@    Print help text to standard output and exit. ---default-group-size=INT+--default-thread-block-size=INT    The default size of thread blocks that are launched.  Capped to the   hardware limit if necessary. ---default-num-groups=INT+--default-num-thread-blocks=INT    The default number of thread blocks that are launched. 
docs/man/futhark-opencl.rst view
@@ -25,6 +25,10 @@ macOS).  If using ``--library``, you will need to do the same when linking the final binary. +The GPU terminology used is derived from CUDA nomenclature (e.g.+"thread block" instead of "workgroup"), but OpenCL nomenclature is+also supported for compatibility.+ OPTIONS ======= @@ -64,14 +68,14 @@   options are supported.  Be careful - some options can easily   result in invalid results. ---default-group-size=INT+--default-thread-block-size=INT, --default-group-size=INT -  The default size of OpenCL workgroups that are launched.  Capped-  to the hardware limit if necessary.+  The default size of thread blocks that are launched. Capped to the+  hardware limit if necessary. ---default-num-groups=INT+--default-num-thread-blocks, --default-num-groups=INT -  The default number of OpenCL workgroups that are launched.+  The default number of thread blocks that are launched.  --default-threshold=INT 
docs/usage.rst view
@@ -166,6 +166,15 @@     special string ``#k``, where ``k`` is an integer, can be used to     pick the *k*-th device, numbered from zero. +  ``--default-thread-block-size INT``++    The default size of GPU thread blocks that are launched. Capped to+    the hardware limit if necessary.++  ``--default-num-thread-blocks INT``++    The default number of GPU thread blocks that are launched.+   ``-P/--profile``      Measure the time taken by various GPU operations (such as kernels)@@ -305,9 +314,7 @@ While compiling a Futhark program to an executable is useful for testing, it is not suitable for production use.  Instead, a Futhark program should be compiled into a reusable library in some target-language, enabling integration into a larger program.  Five of the-Futhark compilers support this: ``futhark c``, ``futhark opencl``,-``futhark cuda``, ``futhark py``, and ``futhark pyopencl``.+language, enabling integration into a larger program.  General Concerns ^^^^^^^^^^^^^^^^@@ -321,7 +328,8 @@ *out*-parameters for writing the result, for target languages that do not support multiple return values from functions. -The entry point should have a name that is also a valid C identifier.+The entry point should have a name that is also a valid identifier in+the target language (usually C).  Not all Futhark types can be mapped cleanly to the target language. Arrays of tuples, for example, are a common issue.  In such cases,
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:           futhark-version:        0.25.10+version:        0.25.11 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to@@ -215,7 +215,7 @@       Futhark.CodeGen.ImpGen.CUDA       Futhark.CodeGen.ImpGen.GPU       Futhark.CodeGen.ImpGen.GPU.Base-      Futhark.CodeGen.ImpGen.GPU.Group+      Futhark.CodeGen.ImpGen.GPU.Block       Futhark.CodeGen.ImpGen.GPU.SegHist       Futhark.CodeGen.ImpGen.GPU.SegMap       Futhark.CodeGen.ImpGen.GPU.SegRed
prelude/array.fut view
@@ -137,6 +137,18 @@ def copy 't (a: t): *t =   ([a])[0] +-- | Copy a value. The result will not alias anything. Additionally,+-- there is a guarantee that the result will be laid out in row-major+-- order in memory. This can be used for locality optimisations in+-- cases where the compiler does not otherwise do the right thing.+--+-- **Work:** O(n).+--+-- **Span:** O(1).+#[inline]+def manifest 't (a: t): *t =+  intrinsics.manifest a+ -- | Combines the outer two dimensions of an array. -- -- **Complexity:** O(1).
rts/c/atomics.h view
@@ -1,29 +1,29 @@ // Start of atomics.h  SCALAR_FUN_ATTR int32_t atomic_xchg_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_xchg_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_xchg_i32_shared(volatile __local int32_t *p, int32_t x); SCALAR_FUN_ATTR int32_t atomic_cmpxchg_i32_global(volatile __global int32_t *p,                                                          int32_t cmp, int32_t val);-SCALAR_FUN_ATTR int32_t atomic_cmpxchg_i32_local(volatile __local int32_t *p,+SCALAR_FUN_ATTR int32_t atomic_cmpxchg_i32_shared(volatile __local int32_t *p,                                                         int32_t cmp, int32_t val); SCALAR_FUN_ATTR int32_t atomic_add_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_add_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_add_i32_shared(volatile __local int32_t *p, int32_t x); SCALAR_FUN_ATTR float atomic_fadd_f32_global(volatile __global float *p, float x);-SCALAR_FUN_ATTR float atomic_fadd_f32_local(volatile __local float *p, float x);+SCALAR_FUN_ATTR float atomic_fadd_f32_shared(volatile __local float *p, float x); SCALAR_FUN_ATTR int32_t atomic_smax_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_smax_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_smax_i32_shared(volatile __local int32_t *p, int32_t x); SCALAR_FUN_ATTR int32_t atomic_smin_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_smin_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_smin_i32_shared(volatile __local int32_t *p, int32_t x); SCALAR_FUN_ATTR uint32_t atomic_umax_i32_global(volatile __global uint32_t *p, uint32_t x);-SCALAR_FUN_ATTR uint32_t atomic_umax_i32_local(volatile __local uint32_t *p, uint32_t x);+SCALAR_FUN_ATTR uint32_t atomic_umax_i32_shared(volatile __local uint32_t *p, uint32_t x); SCALAR_FUN_ATTR uint32_t atomic_umin_i32_global(volatile __global uint32_t *p, uint32_t x);-SCALAR_FUN_ATTR uint32_t atomic_umin_i32_local(volatile __local uint32_t *p, uint32_t x);+SCALAR_FUN_ATTR uint32_t atomic_umin_i32_shared(volatile __local uint32_t *p, uint32_t x); SCALAR_FUN_ATTR int32_t atomic_and_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_and_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_and_i32_shared(volatile __local int32_t *p, int32_t x); SCALAR_FUN_ATTR int32_t atomic_or_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_or_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_or_i32_shared(volatile __local int32_t *p, int32_t x); SCALAR_FUN_ATTR int32_t atomic_xor_i32_global(volatile __global int32_t *p, int32_t x);-SCALAR_FUN_ATTR int32_t atomic_xor_i32_local(volatile __local int32_t *p, int32_t x);+SCALAR_FUN_ATTR int32_t atomic_xor_i32_shared(volatile __local int32_t *p, int32_t x);  SCALAR_FUN_ATTR int32_t atomic_xchg_i32_global(volatile __global int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)@@ -33,7 +33,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_xchg_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_xchg_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicExch((int32_t*)p, x); #else@@ -50,7 +50,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_cmpxchg_i32_local(volatile __local int32_t *p,+SCALAR_FUN_ATTR int32_t atomic_cmpxchg_i32_shared(volatile __local int32_t *p,                                                         int32_t cmp, int32_t val) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicCAS((int32_t*)p, cmp, val);@@ -67,7 +67,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_add_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_add_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicAdd((int32_t*)p, x); #else@@ -91,7 +91,7 @@ #endif } -SCALAR_FUN_ATTR float atomic_fadd_f32_local(volatile __local float *p, float x) {+SCALAR_FUN_ATTR float atomic_fadd_f32_shared(volatile __local float *p, float x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicAdd((float*)p, x); #else@@ -101,7 +101,7 @@   do {     assumed.f = old.f;     old.f = old.f + x;-    old.i = atomic_cmpxchg_i32_local((volatile __local int32_t*)p, assumed.i, old.i);+    old.i = atomic_cmpxchg_i32_shared((volatile __local int32_t*)p, assumed.i, old.i);   } while (assumed.i != old.i);   return old.f; #endif@@ -115,7 +115,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_smax_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_smax_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicMax((int32_t*)p, x); #else@@ -131,7 +131,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_smin_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_smin_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicMin((int32_t*)p, x); #else@@ -147,7 +147,7 @@ #endif } -SCALAR_FUN_ATTR uint32_t atomic_umax_i32_local(volatile __local uint32_t *p, uint32_t x) {+SCALAR_FUN_ATTR uint32_t atomic_umax_i32_shared(volatile __local uint32_t *p, uint32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicMax((uint32_t*)p, x); #else@@ -163,7 +163,7 @@ #endif } -SCALAR_FUN_ATTR uint32_t atomic_umin_i32_local(volatile __local uint32_t *p, uint32_t x) {+SCALAR_FUN_ATTR uint32_t atomic_umin_i32_shared(volatile __local uint32_t *p, uint32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicMin((uint32_t*)p, x); #else@@ -179,7 +179,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_and_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_and_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicAnd((int32_t*)p, x); #else@@ -195,7 +195,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_or_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_or_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicOr((int32_t*)p, x); #else@@ -211,7 +211,7 @@ #endif } -SCALAR_FUN_ATTR int32_t atomic_xor_i32_local(volatile __local int32_t *p, int32_t x) {+SCALAR_FUN_ATTR int32_t atomic_xor_i32_shared(volatile __local int32_t *p, int32_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicXor((int32_t*)p, x); #else@@ -224,31 +224,31 @@ #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP) || defined(cl_khr_int64_base_atomics) && defined(cl_khr_int64_extended_atomics)  SCALAR_FUN_ATTR int64_t atomic_xchg_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_xchg_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_xchg_i64_shared(volatile __local int64_t *p, int64_t x); SCALAR_FUN_ATTR int64_t atomic_cmpxchg_i64_global(volatile __global int64_t *p,                                                          int64_t cmp, int64_t val);-SCALAR_FUN_ATTR int64_t atomic_cmpxchg_i64_local(volatile __local int64_t *p,+SCALAR_FUN_ATTR int64_t atomic_cmpxchg_i64_shared(volatile __local int64_t *p,                                                         int64_t cmp, int64_t val); SCALAR_FUN_ATTR int64_t atomic_add_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_add_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_add_i64_shared(volatile __local int64_t *p, int64_t x); SCALAR_FUN_ATTR int64_t atomic_smax_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_smax_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_smax_i64_shared(volatile __local int64_t *p, int64_t x); SCALAR_FUN_ATTR int64_t atomic_smin_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_smin_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_smin_i64_shared(volatile __local int64_t *p, int64_t x); SCALAR_FUN_ATTR uint64_t atomic_umax_i64_global(volatile __global uint64_t *p, uint64_t x);-SCALAR_FUN_ATTR uint64_t atomic_umax_i64_local(volatile __local uint64_t *p, uint64_t x);+SCALAR_FUN_ATTR uint64_t atomic_umax_i64_shared(volatile __local uint64_t *p, uint64_t x); SCALAR_FUN_ATTR uint64_t atomic_umin_i64_global(volatile __global uint64_t *p, uint64_t x);-SCALAR_FUN_ATTR uint64_t atomic_umin_i64_local(volatile __local uint64_t *p, uint64_t x);+SCALAR_FUN_ATTR uint64_t atomic_umin_i64_shared(volatile __local uint64_t *p, uint64_t x); SCALAR_FUN_ATTR int64_t atomic_and_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_and_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_and_i64_shared(volatile __local int64_t *p, int64_t x); SCALAR_FUN_ATTR int64_t atomic_or_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_or_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_or_i64_shared(volatile __local int64_t *p, int64_t x); SCALAR_FUN_ATTR int64_t atomic_xor_i64_global(volatile __global int64_t *p, int64_t x);-SCALAR_FUN_ATTR int64_t atomic_xor_i64_local(volatile __local int64_t *p, int64_t x);+SCALAR_FUN_ATTR int64_t atomic_xor_i64_shared(volatile __local int64_t *p, int64_t x);  #ifdef FUTHARK_F64_ENABLED SCALAR_FUN_ATTR double atomic_fadd_f64_global(volatile __global double *p, double x);-SCALAR_FUN_ATTR double atomic_fadd_f64_local(volatile __local double *p, double x);+SCALAR_FUN_ATTR double atomic_fadd_f64_shared(volatile __local double *p, double x); #endif  SCALAR_FUN_ATTR int64_t atomic_xchg_i64_global(volatile __global int64_t *p, int64_t x) {@@ -259,7 +259,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_xchg_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_xchg_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicExch((uint64_t*)p, x); #else@@ -276,7 +276,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_cmpxchg_i64_local(volatile __local int64_t *p,+SCALAR_FUN_ATTR int64_t atomic_cmpxchg_i64_shared(volatile __local int64_t *p,                                                         int64_t cmp, int64_t val) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicCAS((uint64_t*)p, cmp, val);@@ -293,7 +293,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_add_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_add_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicAdd((uint64_t*)p, x); #else@@ -319,7 +319,7 @@ #endif } -SCALAR_FUN_ATTR double atomic_fadd_f64_local(volatile __local double *p, double x) {+SCALAR_FUN_ATTR double atomic_fadd_f64_shared(volatile __local double *p, double x) { #if defined(FUTHARK_CUDA) && __CUDA_ARCH__ >= 600 || defined(FUTHARK_HIP)   return atomicAdd((double*)p, x); #else@@ -329,7 +329,7 @@   do {     assumed.f = old.f;     old.f = old.f + x;-    old.i = atomic_cmpxchg_i64_local((volatile __local int64_t*)p, assumed.i, old.i);+    old.i = atomic_cmpxchg_i64_shared((volatile __local int64_t*)p, assumed.i, old.i);   } while (assumed.i != old.i);   return old.f; #endif@@ -354,7 +354,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_smax_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_smax_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA)   return atomicMax((int64_t*)p, x); #elif defined(FUTHARK_HIP)@@ -363,7 +363,7 @@   do {     assumed = old;     old = smax64(old, x);-    old = atomic_cmpxchg_i64_local((volatile __local int64_t*)p, assumed, old);+    old = atomic_cmpxchg_i64_shared((volatile __local int64_t*)p, assumed, old);   } while (assumed != old);   return old; #else@@ -388,7 +388,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_smin_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_smin_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA)   return atomicMin((int64_t*)p, x); #elif defined(FUTHARK_HIP)@@ -397,7 +397,7 @@   do {     assumed = old;     old = smin64(old, x);-    old = atomic_cmpxchg_i64_local((volatile __local int64_t*)p, assumed, old);+    old = atomic_cmpxchg_i64_shared((volatile __local int64_t*)p, assumed, old);   } while (assumed != old);   return old; #else@@ -413,7 +413,7 @@ #endif } -SCALAR_FUN_ATTR uint64_t atomic_umax_i64_local(volatile __local uint64_t *p, uint64_t x) {+SCALAR_FUN_ATTR uint64_t atomic_umax_i64_shared(volatile __local uint64_t *p, uint64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicMax((uint64_t*)p, x); #else@@ -429,7 +429,7 @@ #endif } -SCALAR_FUN_ATTR uint64_t atomic_umin_i64_local(volatile __local uint64_t *p, uint64_t x) {+SCALAR_FUN_ATTR uint64_t atomic_umin_i64_shared(volatile __local uint64_t *p, uint64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicMin((uint64_t*)p, x); #else@@ -445,7 +445,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_and_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_and_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicAnd((uint64_t*)p, x); #else@@ -461,7 +461,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_or_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_or_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicOr((uint64_t*)p, x); #else@@ -477,7 +477,7 @@ #endif } -SCALAR_FUN_ATTR int64_t atomic_xor_i64_local(volatile __local int64_t *p, int64_t x) {+SCALAR_FUN_ATTR int64_t atomic_xor_i64_shared(volatile __local int64_t *p, int64_t x) { #if defined(FUTHARK_CUDA) || defined(FUTHARK_HIP)   return atomicXor((uint64_t*)p, x); #else
rts/c/backends/cuda.h view
@@ -186,16 +186,24 @@   cfg->load_ptx_from = strdup(path); } -void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size) {+void futhark_context_config_set_default_thread_block_size(struct futhark_context_config *cfg, int size) {   cfg->default_block_size = size;   cfg->default_block_size_changed = 1; } -void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) {+void futhark_context_config_set_default_grid_size(struct futhark_context_config *cfg, int num) {   cfg->default_grid_size = num;   cfg->default_grid_size_changed = 1; } +void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size) {+  futhark_context_config_set_default_thread_block_size(cfg, size);+}++void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) {+  futhark_context_config_set_default_grid_size(cfg, num);+}+ void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size) {   cfg->default_tile_size = size;   cfg->default_tile_size_changed = 1;@@ -218,11 +226,13 @@       return 0;     }   }-  if (strcmp(param_name, "default_group_size") == 0) {+  if (strcmp(param_name, "default_thread_block_size") == 0+      || strcmp(param_name, "default_group_size") == 0) {     cfg->default_block_size = new_value;     return 0;   }-  if (strcmp(param_name, "default_num_groups") == 0) {+  if (strcmp(param_name, "default_num_groups") == 0 ||+      strcmp(param_name, "default_grid_size") == 0) {     cfg->default_grid_size = new_value;     return 0;   }@@ -283,11 +293,11 @@    struct free_list gpu_free_list; -  size_t max_group_size;+  size_t max_thread_block_size;   size_t max_grid_size;   size_t max_tile_size;   size_t max_threshold;-  size_t max_local_memory;+  size_t max_shared_memory;   size_t max_bespoke;   size_t max_registers;   size_t max_cache;@@ -461,11 +471,11 @@     opts[i++] = strdup("--disable-warnings");   }   opts[i++] = msgprintf("-D%s=%d",-                        "max_group_size",-                        (int)ctx->max_group_size);+                        "max_thread_block_size",+                        (int)ctx->max_thread_block_size);   opts[i++] = msgprintf("-D%s=%d",-                        "max_local_memory",-                        (int)ctx->max_local_memory);+                        "max_shared_memory",+                        (int)ctx->max_shared_memory);   opts[i++] = msgprintf("-D%s=%d",                         "max_registers",                         (int)ctx->max_registers);@@ -479,7 +489,7 @@                           cfg->tuning_params[j]);   }   opts[i++] = msgprintf("-DLOCKSTEP_WIDTH=%zu", ctx->lockstep_width);-  opts[i++] = msgprintf("-DMAX_THREADS_PER_BLOCK=%zu", ctx->max_group_size);+  opts[i++] = msgprintf("-DMAX_THREADS_PER_BLOCK=%zu", ctx->max_thread_block_size);    // Time for the best lines of the code in the entire compiler.   if (getenv("CUDA_HOME") != NULL) {@@ -570,13 +580,13 @@ static void cuda_size_setup(struct futhark_context *ctx) {   struct futhark_context_config *cfg = ctx->cfg;-  if (cfg->default_block_size > ctx->max_group_size) {+  if (cfg->default_block_size > ctx->max_thread_block_size) {     if (cfg->default_block_size_changed) {       fprintf(stderr,               "Note: Device limits default block size to %zu (down from %zu).\n",-              ctx->max_group_size, cfg->default_block_size);+              ctx->max_thread_block_size, cfg->default_block_size);     }-    cfg->default_block_size = ctx->max_group_size;+    cfg->default_block_size = ctx->max_thread_block_size;   }   if (cfg->default_grid_size > ctx->max_grid_size) {     if (cfg->default_grid_size_changed) {@@ -608,10 +618,10 @@     const char* size_name = cfg->tuning_param_names[i];     int64_t max_value = 0, default_value = 0; -    if (strstr(size_class, "group_size") == size_class) {-      max_value = ctx->max_group_size;+    if (strstr(size_class, "thread_block_size") == size_class) {+      max_value = ctx->max_thread_block_size;       default_value = cfg->default_block_size;-    } else if (strstr(size_class, "num_groups") == size_class) {+    } else if (strstr(size_class, "grid_size") == size_class) {       max_value = ctx->max_grid_size;       default_value = cfg->default_grid_size;       // XXX: as a quick and dirty hack, use twice as many threads for@@ -821,12 +831,13 @@   // MAX_SHARED_MEMORY_PER_BLOCK gives bogus numbers (48KiB); probably   // for backwards compatibility.  Add _OPTIN and you seem to get the   // right number.-  ctx->max_local_memory =-    device_query(ctx->dev, MAX_SHARED_MEMORY_PER_BLOCK_OPTIN) --    device_query(ctx->dev, RESERVED_SHARED_MEMORY_PER_BLOCK);-  ctx->max_group_size = device_query(ctx->dev, MAX_THREADS_PER_BLOCK);+  ctx->max_shared_memory = device_query(ctx->dev, MAX_SHARED_MEMORY_PER_BLOCK_OPTIN);+#if CUDART_VERSION >= 12000+  ctx->max_shared_memory -= device_query(ctx->dev, RESERVED_SHARED_MEMORY_PER_BLOCK);+#endif+  ctx->max_thread_block_size = device_query(ctx->dev, MAX_THREADS_PER_BLOCK);   ctx->max_grid_size = device_query(ctx->dev, MAX_GRID_DIM_X);-  ctx->max_tile_size = sqrt(ctx->max_group_size);+  ctx->max_tile_size = sqrt(ctx->max_thread_block_size);   ctx->max_threshold = 0;   ctx->max_bespoke = 0;   ctx->max_registers = device_query(ctx->dev, MAX_REGISTERS_PER_BLOCK);@@ -883,7 +894,7 @@   // Unless the below is set, the kernel is limited to 48KiB of memory.   CUDA_SUCCEED_FATAL(cuFuncSetAttribute(*kernel,                                         cudaFuncAttributeMaxDynamicSharedMemorySize,-                                        ctx->max_local_memory));+                                        ctx->max_shared_memory)); }  static void gpu_free_kernel(struct futhark_context *ctx,@@ -1015,7 +1026,7 @@                              gpu_kernel kernel, const char *name,                              const int32_t grid[3],                              const int32_t block[3],-                             unsigned int local_mem_bytes,+                             unsigned int shared_mem_bytes,                              int num_args,                              void* args[num_args],                              size_t args_sizes[num_args]) {@@ -1039,7 +1050,7 @@                         name,                         grid[0], grid[1], grid[2],                         block[0], block[1], block[2],-                        local_mem_bytes),+                        shared_mem_bytes),               event,               (event_report_fn)cuda_event_report);   }@@ -1048,7 +1059,7 @@     (cuLaunchKernel(kernel,                     grid[0], grid[1], grid[2],                     block[0], block[1], block[2],-                    local_mem_bytes, ctx->stream,+                    shared_mem_bytes, ctx->stream,                     args, NULL));    if (event != NULL) {
rts/c/backends/hip.h view
@@ -166,16 +166,25 @@   cfg->program = strdup(s); } -void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size) {+void futhark_context_config_set_default_thread_block_size(struct futhark_context_config *cfg, int size) {   cfg->default_block_size = size;   cfg->default_block_size_changed = 1; } -void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) {+void futhark_context_config_set_default_grid_size(struct futhark_context_config *cfg, int num) {   cfg->default_grid_size = num;   cfg->default_grid_size_changed = 1; } +void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int num) {+  futhark_context_config_set_default_thread_block_size(cfg, num);+}+++void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) {+  futhark_context_config_set_default_grid_size(cfg, num);+}+ void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size) {   cfg->default_tile_size = size;   cfg->default_tile_size_changed = 1;@@ -198,11 +207,13 @@       return 0;     }   }-  if (strcmp(param_name, "default_group_size") == 0) {+  if (strcmp(param_name, "default_thread_block_size") == 0 ||+      strcmp(param_name, "default_group_size") == 0) {     cfg->default_block_size = new_value;     return 0;   }-  if (strcmp(param_name, "default_num_groups") == 0) {+  if (strcmp(param_name, "default_grid_size") == 0 ||+      strcmp(param_name, "default_num_groups") == 0) {     cfg->default_grid_size = new_value;     return 0;   }@@ -257,11 +268,11 @@    struct free_list gpu_free_list; -  size_t max_group_size;+  size_t max_thread_block_size;   size_t max_grid_size;   size_t max_tile_size;   size_t max_threshold;-  size_t max_local_memory;+  size_t max_shared_memory;   size_t max_bespoke;   size_t max_registers;   size_t max_cache;@@ -342,13 +353,13 @@  static void hip_size_setup(struct futhark_context *ctx) {   struct futhark_context_config *cfg = ctx->cfg;-  if (cfg->default_block_size > ctx->max_group_size) {+  if (cfg->default_block_size > ctx->max_thread_block_size) {     if (cfg->default_block_size_changed) {       fprintf(stderr,               "Note: Device limits default block size to %zu (down from %zu).\n",-              ctx->max_group_size, cfg->default_block_size);+              ctx->max_thread_block_size, cfg->default_block_size);     }-    cfg->default_block_size = ctx->max_group_size;+    cfg->default_block_size = ctx->max_thread_block_size;   }   if (cfg->default_grid_size > ctx->max_grid_size) {     if (cfg->default_grid_size_changed) {@@ -380,10 +391,10 @@     const char* size_name = cfg->tuning_param_names[i];     int64_t max_value = 0, default_value = 0; -    if (strstr(size_class, "group_size") == size_class) {-      max_value = ctx->max_group_size;+    if (strstr(size_class, "thread_block_size") == size_class) {+      max_value = ctx->max_thread_block_size;       default_value = cfg->default_block_size;-    } else if (strstr(size_class, "num_groups") == size_class) {+    } else if (strstr(size_class, "grid_size") == size_class) {       max_value = ctx->max_grid_size;       default_value = cfg->default_grid_size;       // XXX: as a quick and dirty hack, use twice as many threads for@@ -477,11 +488,11 @@     opts[i++] = strdup("-lineinfo");   }   opts[i++] = msgprintf("-D%s=%d",-                        "max_group_size",-                        (int)ctx->max_group_size);+                        "max_thread_block_size",+                        (int)ctx->max_thread_block_size);   opts[i++] = msgprintf("-D%s=%d",-                        "max_local_memory",-                        (int)ctx->max_local_memory);+                        "max_shared_memory",+                        (int)ctx->max_shared_memory);   opts[i++] = msgprintf("-D%s=%d",                         "max_registers",                         (int)ctx->max_registers);@@ -495,7 +506,7 @@                           cfg->tuning_params[j]);   }   opts[i++] = msgprintf("-DLOCKSTEP_WIDTH=%zu", ctx->lockstep_width);-  opts[i++] = msgprintf("-DMAX_THREADS_PER_BLOCK=%zu", ctx->max_group_size);+  opts[i++] = msgprintf("-DMAX_THREADS_PER_BLOCK=%zu", ctx->max_thread_block_size);    for (int j = 0; extra_opts[j] != NULL; j++) {     opts[i++] = strdup(extra_opts[j]);@@ -673,10 +684,10 @@    free_list_init(&ctx->gpu_free_list); -  ctx->max_local_memory = device_query(ctx->dev, hipDeviceAttributeMaxSharedMemoryPerBlock);-  ctx->max_group_size = device_query(ctx->dev, hipDeviceAttributeMaxThreadsPerBlock);+  ctx->max_shared_memory = device_query(ctx->dev, hipDeviceAttributeMaxSharedMemoryPerBlock);+  ctx->max_thread_block_size = device_query(ctx->dev, hipDeviceAttributeMaxThreadsPerBlock);   ctx->max_grid_size = device_query(ctx->dev, hipDeviceAttributeMaxGridDimX);-  ctx->max_tile_size = sqrt(ctx->max_group_size);+  ctx->max_tile_size = sqrt(ctx->max_thread_block_size);   ctx->max_threshold = 0;   ctx->max_bespoke = 0;   ctx->max_registers = device_query(ctx->dev, hipDeviceAttributeMaxRegistersPerBlock);@@ -872,7 +883,7 @@                              gpu_kernel kernel, const char *name,                              const int32_t grid[3],                              const int32_t block[3],-                             unsigned int local_mem_bytes,+                             unsigned int shared_mem_bytes,                              int num_args,                              void* args[num_args],                              size_t args_sizes[num_args]) {@@ -895,7 +906,7 @@                         name,                         grid[0], grid[1], grid[2],                         block[0], block[1], block[2],-                        local_mem_bytes),+                        shared_mem_bytes),               event,               (event_report_fn)hip_event_report);   }@@ -904,7 +915,7 @@     (hipModuleLaunchKernel(kernel,                            grid[0], grid[1], grid[2],                            block[0], block[1], block[2],-                           local_mem_bytes, ctx->stream,+                           shared_mem_bytes, ctx->stream,                            args, NULL));    if (event != NULL) {
rts/c/backends/opencl.h view
@@ -432,15 +432,23 @@   cfg->load_binary_from = strdup(path); } -void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size) {+void futhark_context_config_set_default_thread_block_size(struct futhark_context_config *cfg, int size) {   cfg->default_group_size = size;   cfg->default_group_size_changed = 1; } -void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) {+void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size) {+  futhark_context_config_set_default_thread_block_size(cfg, size);+}++void futhark_context_config_set_default_grid_size(struct futhark_context_config *cfg, int num) {   cfg->default_num_groups = num; } +void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int num) {+  futhark_context_config_set_default_grid_size(cfg, num);+}+ void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size) {   cfg->default_tile_size = size;   cfg->default_tile_size_changed = 1;@@ -463,11 +471,13 @@       return 0;     }   }-  if (strcmp(param_name, "default_group_size") == 0) {+  if (strcmp(param_name, "default_thread_block_size") == 0 ||+      strcmp(param_name, "default_group_size") == 0) {     cfg->default_group_size = new_value;     return 0;   }-  if (strcmp(param_name, "default_num_groups") == 0) {+  if (strcmp(param_name, "default_grid_size") == 0 ||+      strcmp(param_name, "default_num_groups") == 0) {     cfg->default_num_groups = new_value;     return 0;   }@@ -523,11 +533,11 @@    struct free_list gpu_free_list; -  size_t max_group_size;+  size_t max_thread_block_size;   size_t max_num_groups;   size_t max_tile_size;   size_t max_threshold;-  size_t max_local_memory;+  size_t max_shared_memory;   size_t max_registers;   size_t max_cache; @@ -601,13 +611,13 @@    w += snprintf(compile_opts+w, compile_opts_size-w,                 "-D%s=%d ",-                "max_group_size",-                (int)ctx->max_group_size);+                "max_thread_block_size",+                (int)ctx->max_thread_block_size);    w += snprintf(compile_opts+w, compile_opts_size-w,                 "-D%s=%d ",-                "max_local_memory",-                (int)ctx->max_local_memory);+                "max_shared_memory",+                (int)ctx->max_shared_memory);    w += snprintf(compile_opts+w, compile_opts_size-w,                 "-D%s=%d ",@@ -767,18 +777,18 @@     }   } -  size_t max_group_size;+  size_t max_thread_block_size;   OPENCL_SUCCEED_FATAL(clGetDeviceInfo(device_option.device, CL_DEVICE_MAX_WORK_GROUP_SIZE,-                                       sizeof(size_t), &max_group_size, NULL));+                                       sizeof(size_t), &max_thread_block_size, NULL)); -  size_t max_tile_size = sqrt(max_group_size);+  size_t max_tile_size = sqrt(max_thread_block_size); -  cl_ulong max_local_memory;+  cl_ulong max_shared_memory;   OPENCL_SUCCEED_FATAL(clGetDeviceInfo(device_option.device, CL_DEVICE_LOCAL_MEM_SIZE,-                                       sizeof(size_t), &max_local_memory, NULL));+                                       sizeof(size_t), &max_shared_memory, NULL));    // Futhark reserves 4 bytes for bookkeeping information.-  max_local_memory -= 4;+  max_shared_memory -= 4;    // The OpenCL implementation may reserve some local memory bytes for   // various purposes.  In principle, we should use@@ -789,20 +799,20 @@   // (but which might be arbitrarily wrong).  Fortunately, we rarely   // try to really push the local memory usage.   if (strstr(device_option.platform_name, "NVIDIA CUDA") != NULL) {-    max_local_memory -= 12;+    max_shared_memory -= 12;   } else if (strstr(device_option.platform_name, "AMD") != NULL) {-    max_local_memory -= 16;+    max_shared_memory -= 16;   }    // Make sure this function is defined.   post_opencl_setup(ctx, &device_option); -  if (max_group_size < ctx->cfg->default_group_size) {+  if (max_thread_block_size < ctx->cfg->default_group_size) {     if (ctx->cfg->default_group_size_changed) {       fprintf(stderr, "Note: Device limits default group size to %zu (down from %zu).\n",-              max_group_size, ctx->cfg->default_group_size);+              max_thread_block_size, ctx->cfg->default_group_size);     }-    ctx->cfg->default_group_size = max_group_size;+    ctx->cfg->default_group_size = max_thread_block_size;   }    if (max_tile_size < ctx->cfg->default_tile_size) {@@ -827,10 +837,10 @@    ctx->max_registers = 1<<16; // I cannot find a way to query for this. -  ctx->max_group_size = max_group_size;+  ctx->max_thread_block_size = max_thread_block_size;   ctx->max_tile_size = max_tile_size; // No limit.   ctx->max_threshold = ctx->max_num_groups = 0; // No limit.-  ctx->max_local_memory = max_local_memory;+  ctx->max_shared_memory = max_shared_memory;    // Now we go through all the sizes, clamp them to the valid range,   // or set them to the default.@@ -840,11 +850,11 @@     const char* size_name = ctx->cfg->tuning_param_names[i];     int64_t max_value = 0, default_value = 0; -    if (strstr(size_class, "group_size") == size_class) {-      max_value = max_group_size;+    if (strstr(size_class, "thread_block_size") == size_class) {+      max_value = max_thread_block_size;       default_value = ctx->cfg->default_group_size;-    } else if (strstr(size_class, "num_groups") == size_class) {-      max_value = max_group_size; // Futhark assumes this constraint.+    } else if (strstr(size_class, "grid_size") == size_class) {+      max_value = max_thread_block_size; // Futhark assumes this constraint.       default_value = ctx->cfg->default_num_groups;       // XXX: as a quick and dirty hack, use twice as many threads for       // histograms by default.  We really should just be smarter@@ -853,7 +863,7 @@         default_value *= 2;       }     } else if (strstr(size_class, "tile_size") == size_class) {-      max_value = sqrt(max_group_size);+      max_value = sqrt(max_thread_block_size);       default_value = ctx->cfg->default_tile_size;     } else if (strstr(size_class, "reg_tile_size") == size_class) {       max_value = 0; // No limit.@@ -879,8 +889,8 @@    if (ctx->cfg->logging) {     fprintf(stderr, "Lockstep width: %d\n", (int)ctx->lockstep_width);-    fprintf(stderr, "Default group size: %d\n", (int)ctx->cfg->default_group_size);-    fprintf(stderr, "Default number of groups: %d\n", (int)ctx->cfg->default_num_groups);+    fprintf(stderr, "Default thread block size: %d\n", (int)ctx->cfg->default_group_size);+    fprintf(stderr, "Default number of thread blocks: %d\n", (int)ctx->cfg->default_num_groups);   }    char *compile_opts = mk_compile_opts(ctx, extra_build_opts, device_option);@@ -1265,7 +1275,7 @@                              gpu_kernel kernel, const char *name,                              const int32_t grid[3],                              const int32_t block[3],-                             unsigned int local_mem_bytes,+                             unsigned int shared_mem_bytes,                              int num_args,                              void* args[num_args],                              size_t args_sizes[num_args]) {@@ -1282,7 +1292,7 @@                         name,                         grid[0], grid[1], grid[2],                         block[0], block[1], block[2],-                        local_mem_bytes),+                        shared_mem_bytes),               event,               (event_report_fn)opencl_event_report);   }@@ -1292,12 +1302,12 @@   }    // Some implementations do not work with 0-byte local memory.-  if (local_mem_bytes == 0) {-    local_mem_bytes = 4;+  if (shared_mem_bytes == 0) {+    shared_mem_bytes = 4;   }    OPENCL_SUCCEED_OR_RETURN-    (clSetKernelArg(kernel, 0, local_mem_bytes, NULL));+    (clSetKernelArg(kernel, 0, shared_mem_bytes, NULL));   for (int i = 0; i < num_args; i++) {     OPENCL_SUCCEED_OR_RETURN       (clSetKernelArg(kernel, i+1, args_sizes[i], args[i]));@@ -1325,7 +1335,7 @@     fprintf(ctx->log, "  runtime: %ldus\n", time_diff);   }   if (ctx->logging) {-    printf("\n");+    fprintf(ctx->log, "\n");   }    return FUTHARK_SUCCESS;
rts/c/context.h view
@@ -123,9 +123,9 @@   ctx->peak_mem_usage_default = 0;   ctx->cur_mem_usage_default = 0;   ctx->constants = malloc(sizeof(struct constants));-  ctx->detail_memory = cfg->debugging;   ctx->debugging = cfg->debugging;   ctx->logging = cfg->logging;+  ctx->detail_memory = cfg->logging;   ctx->profiling = cfg->profiling;   ctx->profiling_paused = 0;   ctx->error = NULL;
rts/c/gpu.h view
@@ -10,7 +10,7 @@                       gpu_kernel kernel, const char *name,                       const int32_t grid[3],                       const int32_t block[3],-                      unsigned int local_mem_bytes,+                      unsigned int shared_mem_bytes,                       int num_args,                       void* args[num_args],                       size_t args_sizes[num_args]);@@ -28,9 +28,9 @@                        gpu_kernel* kernel,                        const char* name); -// Max number of groups we allow along the second or third dimension-// for transpositions.-#define MAX_TR_GROUPS 65535+// Max number of thead blocks we allow along the second or third+// dimension for transpositions.+#define MAX_TR_THREAD_BLOCKS 65535  struct builtin_kernels {   // We have a lot of ways to transpose arrays.@@ -327,12 +327,12 @@     args_sizes[8] = sizeof(int64_t);   } -  // Cap the number of groups we launch and figure out how many+  // Cap the number of thead blocks we launch and figure out how many   // repeats we need alongside each dimension.-  int32_t repeat_1 = grid[1] / MAX_TR_GROUPS;-  int32_t repeat_2 = grid[2] / MAX_TR_GROUPS;-  grid[1] = repeat_1 > 0 ? MAX_TR_GROUPS : grid[1];-  grid[2] = repeat_2 > 0 ? MAX_TR_GROUPS : grid[2];+  int32_t repeat_1 = grid[1] / MAX_TR_THREAD_BLOCKS;+  int32_t repeat_2 = grid[2] / MAX_TR_THREAD_BLOCKS;+  grid[1] = repeat_1 > 0 ? MAX_TR_THREAD_BLOCKS : grid[1];+  grid[2] = repeat_2 > 0 ? MAX_TR_THREAD_BLOCKS : grid[2];   args[9] = &repeat_1;   args[10] = &repeat_2;   args_sizes[9] = sizeof(repeat_1);@@ -412,7 +412,7 @@       args[6+i*3+2] = &zero;     }   }-  const size_t w = 256; // XXX: hardcoded workgroup size.+  const size_t w = 256; // XXX: hardcoded thread block size.    return gpu_launch_kernel(ctx, kernel, "copy_lmad_dev_to_dev",                            (const int32_t[3]) {(n+w-1)/w,1,1},
rts/c/scheduler.h view
@@ -140,21 +140,20 @@ }  /* returns the number of logical cores */-static int num_processors()-{+static int num_processors(void) { #if  defined(_WIN32)-/* https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info */-    SYSTEM_INFO sysinfo;-    GetSystemInfo(&sysinfo);-    int ncores = sysinfo.dwNumberOfProcessors;-    fprintf(stderr, "Found %d cores on your Windows machine\n Is that correct?\n", ncores);-    return ncores;+  /* https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info */+  SYSTEM_INFO sysinfo;+  GetSystemInfo(&sysinfo);+  int ncores = sysinfo.dwNumberOfProcessors;+  fprintf(stderr, "Found %d cores on your Windows machine\n Is that correct?\n", ncores);+  return ncores; #elif defined(__APPLE__)-    int ncores;-    size_t ncores_size = sizeof(ncores);-    CHECK_ERRNO(sysctlbyname("hw.logicalcpu", &ncores, &ncores_size, NULL, 0),-                "sysctlbyname (hw.logicalcpu)");-    return ncores;+  int ncores;+  size_t ncores_size = sizeof(ncores);+  CHECK_ERRNO(sysctlbyname("hw.logicalcpu", &ncores, &ncores_size, NULL, 0),+              "sysctlbyname (hw.logicalcpu)");+  return ncores; #elif defined(__linux__)   return get_nprocs(); #elif __EMSCRIPTEN__
rts/c/server.h view
@@ -282,14 +282,14 @@  // Print the command-done marker, indicating that we are ready for // more input.-void ok() {+void ok(void) {   printf("%%%%%% OK\n");   fflush(stdout); }  // Print the failure marker.  Output is now an error message until the // next ok().-void failure() {+void failure(void) {   printf("%%%%%% FAILURE\n"); } 
rts/cuda/prelude.cu view
@@ -20,7 +20,7 @@ #define __write_only #define __read_only -static inline __device__ int get_group_id(int d) {+static inline __device__ int get_tblock_id(int d) {   switch (d) {   case 0: return blockIdx.x;   case 1: return blockIdx.y;@@ -29,7 +29,7 @@   } } -static inline __device__ int get_num_groups(int d) {+static inline __device__ int get_num_tblocks(int d) {   switch(d) {   case 0: return gridDim.x;   case 1: return gridDim.y;@@ -93,9 +93,9 @@  #define NAN (0.0/0.0) #define INFINITY (1.0/0.0)-extern volatile __shared__ unsigned char local_mem[];+extern volatile __shared__ unsigned char shared_mem[]; -#define LOCAL_MEM_PARAM+#define SHARED_MEM_PARAM #define FUTHARK_KERNEL extern "C" __global__ __launch_bounds__(MAX_THREADS_PER_BLOCK) #define FUTHARK_KERNEL_SIZED(a,b,c) extern "C" __global__ __launch_bounds__(a*b*c) 
rts/opencl/copy.cl view
@@ -1,7 +1,7 @@ // Start of copy.cl  #define GEN_COPY_KERNEL(NAME, ELEM_TYPE) \-FUTHARK_KERNEL void lmad_copy_##NAME(LOCAL_MEM_PARAM                    \+FUTHARK_KERNEL void lmad_copy_##NAME(SHARED_MEM_PARAM                   \                                __global ELEM_TYPE *dst_mem,             \                                int64_t dst_offset,                      \                                __global ELEM_TYPE *src_mem,             \
rts/opencl/prelude.cl view
@@ -13,6 +13,8 @@ typedef uint uint32_t; typedef ulong uint64_t; +#define get_tblock_id(d) get_group_id(d)+#define get_num_tblocks(d) get_num_groups(d)  // Clang-based OpenCL implementations need this for 'static' to work. #ifdef cl_clang_storage_class_specifiers@@ -47,7 +49,7 @@ }  // Important for this to be int64_t so it has proper alignment for any type.-#define LOCAL_MEM_PARAM __local uint64_t* local_mem,+#define SHARED_MEM_PARAM __local uint64_t* shared_mem, #define FUTHARK_KERNEL __kernel #define FUTHARK_KERNEL_SIZED(a,b,c) __attribute__((reqd_work_group_size(a, b, c))) __kernel 
rts/opencl/transpose.cl view
@@ -2,7 +2,7 @@  #define GEN_TRANSPOSE_KERNELS(NAME, ELEM_TYPE)                          \ FUTHARK_KERNEL_SIZED(TR_BLOCK_DIM*2, TR_TILE_DIM/TR_ELEMS_PER_THREAD, 1)\-void map_transpose_##NAME(LOCAL_MEM_PARAM                               \+void map_transpose_##NAME(SHARED_MEM_PARAM                              \                           __global ELEM_TYPE *dst_mem,                  \                           int64_t dst_offset,                           \                           __global ELEM_TYPE *src_mem,                  \@@ -15,20 +15,20 @@                           int32_t repeat_1,                             \                           int32_t repeat_2) {                           \   (void)mulx; (void)muly;                                               \-  __local ELEM_TYPE* block = (__local ELEM_TYPE*)local_mem;             \-  int group_id_0 = get_group_id(0);                                     \+  __local ELEM_TYPE* block = (__local ELEM_TYPE*)shared_mem;            \+  int tblock_id_0 = get_tblock_id(0);                                   \   int global_id_0 = get_global_id(0);                                   \-  int group_id_1 = get_group_id(1);                                     \+  int tblock_id_1 = get_tblock_id(1);                                   \   int global_id_1 = get_global_id(1);                                   \   for (int i1 = 0; i1 <= repeat_1; i1++) {                              \-    int group_id_2 = get_group_id(2);                                   \+    int tblock_id_2 = get_tblock_id(2);                                 \     int global_id_2 = get_global_id(2);                                 \     for (int i2 = 0; i2 <= repeat_2; i2++) {                            \-      int32_t our_array_offset = group_id_2 * x_elems * y_elems;        \+      int32_t our_array_offset = tblock_id_2 * x_elems * y_elems;       \       int32_t odata_offset = dst_offset + our_array_offset;             \       int32_t idata_offset = src_offset + our_array_offset;             \       int32_t x_index = global_id_0;                                    \-      int32_t y_index = group_id_1 * TR_TILE_DIM + get_local_id(1);     \+      int32_t y_index = tblock_id_1 * TR_TILE_DIM + get_local_id(1);    \       if (x_index < x_elems) {                                          \         for (int32_t j = 0; j < TR_ELEMS_PER_THREAD; j++) {             \           int32_t index_i = (y_index + j * (TR_TILE_DIM/TR_ELEMS_PER_THREAD)) * x_elems + x_index; \@@ -40,8 +40,8 @@         }                                                               \       }                                                                 \       barrier_local();                                                  \-      x_index = group_id_1 * TR_TILE_DIM + get_local_id(0);             \-      y_index = group_id_0 * TR_TILE_DIM + get_local_id(1);             \+      x_index = tblock_id_1 * TR_TILE_DIM + get_local_id(0);            \+      y_index = tblock_id_0 * TR_TILE_DIM + get_local_id(1);            \       if (x_index < y_elems) {                                          \         for (int32_t j = 0; j < TR_ELEMS_PER_THREAD; j++) {             \           int32_t index_out = (y_index + j * (TR_TILE_DIM/TR_ELEMS_PER_THREAD)) * y_elems + x_index; \@@ -52,16 +52,16 @@           }                                                             \         }                                                               \       }                                                                 \-      group_id_2 += get_num_groups(2);                                  \+      tblock_id_2 += get_num_tblocks(2);                                \       global_id_2 += get_global_size(2);                                \     }                                                                   \-    group_id_1 += get_num_groups(1);                                    \+    tblock_id_1 += get_num_tblocks(1);                                  \     global_id_1 += get_global_size(1);                                  \   }                                                                     \ }                                                                       \                                                                         \ FUTHARK_KERNEL_SIZED(TR_BLOCK_DIM, TR_BLOCK_DIM, 1)                     \-void map_transpose_##NAME##_low_height(LOCAL_MEM_PARAM                  \+void map_transpose_##NAME##_low_height(SHARED_MEM_PARAM                 \                                                 __global ELEM_TYPE *dst_mem, \                                                 int64_t dst_offset,     \                                                 __global ELEM_TYPE *src_mem, \@@ -73,32 +73,32 @@                                                 int32_t muly,           \                                                 int32_t repeat_1,       \                                                 int32_t repeat_2) {     \-  __local ELEM_TYPE* block = (__local ELEM_TYPE*)local_mem; \-  int group_id_0 = get_group_id(0);                                     \+  __local ELEM_TYPE* block = (__local ELEM_TYPE*)shared_mem;            \+  int tblock_id_0 = get_tblock_id(0);                                   \   int global_id_0 = get_global_id(0);                                   \-  int group_id_1 = get_group_id(1);                                     \+  int tblock_id_1 = get_tblock_id(1);                                   \   int global_id_1 = get_global_id(1);                                   \   for (int i1 = 0; i1 <= repeat_1; i1++) {                              \-    int group_id_2 = get_group_id(2);                                   \+    int tblock_id_2 = get_tblock_id(2);                                 \     int global_id_2 = get_global_id(2);                                 \     for (int i2 = 0; i2 <= repeat_2; i2++) {                            \-      int32_t our_array_offset = group_id_2 * x_elems * y_elems;        \+      int32_t our_array_offset = tblock_id_2 * x_elems * y_elems;       \       int32_t odata_offset = dst_offset + our_array_offset;             \       int32_t idata_offset = src_offset + our_array_offset;             \       int32_t x_index =                                                 \-        group_id_0 * TR_BLOCK_DIM * mulx +                              \+        tblock_id_0 * TR_BLOCK_DIM * mulx +                             \         get_local_id(0) +                                               \         get_local_id(1)%mulx * TR_BLOCK_DIM;                            \-      int32_t y_index = group_id_1 * TR_BLOCK_DIM + get_local_id(1)/mulx; \+      int32_t y_index = tblock_id_1 * TR_BLOCK_DIM + get_local_id(1)/mulx; \       int32_t index_in = y_index * x_elems + x_index;                   \       if (x_index < x_elems && y_index < y_elems) {                     \         block[get_local_id(1) * (TR_BLOCK_DIM+1) + get_local_id(0)] =   \           src_mem[idata_offset + index_in];                             \       }                                                                 \       barrier_local();                                                  \-      x_index = group_id_1 * TR_BLOCK_DIM + get_local_id(0)/mulx;       \+      x_index = tblock_id_1 * TR_BLOCK_DIM + get_local_id(0)/mulx;      \       y_index =                                                         \-        group_id_0 * TR_BLOCK_DIM * mulx +                              \+        tblock_id_0 * TR_BLOCK_DIM * mulx +                             \         get_local_id(1) +                                               \         (get_local_id(0)%mulx) * TR_BLOCK_DIM;                          \       int32_t index_out = y_index * y_elems + x_index;                  \@@ -106,16 +106,16 @@         dst_mem[odata_offset + index_out] =                             \           block[get_local_id(0) * (TR_BLOCK_DIM+1) + get_local_id(1)];  \       }                                                                 \-      group_id_2 += get_num_groups(2);                                  \+      tblock_id_2 += get_num_tblocks(2);                                \       global_id_2 += get_global_size(2);                                \     }                                                                   \-    group_id_1 += get_num_groups(1);                                    \+    tblock_id_1 += get_num_tblocks(1);                                  \     global_id_1 += get_global_size(1);                                  \   }                                                                     \ }                                                                       \                                                                         \ FUTHARK_KERNEL_SIZED(TR_BLOCK_DIM, TR_BLOCK_DIM, 1)                     \-void map_transpose_##NAME##_low_width(LOCAL_MEM_PARAM                   \+void map_transpose_##NAME##_low_width(SHARED_MEM_PARAM                  \                                       __global ELEM_TYPE *dst_mem,      \                                       int64_t dst_offset,               \                                       __global ELEM_TYPE *src_mem,      \@@ -127,21 +127,21 @@                                       int32_t muly,                     \                                       int32_t repeat_1,                 \                                       int32_t repeat_2) {               \-  __local ELEM_TYPE* block = (__local ELEM_TYPE*)local_mem;             \-  int group_id_0 = get_group_id(0);                                     \+  __local ELEM_TYPE* block = (__local ELEM_TYPE*)shared_mem;            \+  int tblock_id_0 = get_tblock_id(0);                                   \   int global_id_0 = get_global_id(0);                                   \-  int group_id_1 = get_group_id(1);                                     \+  int tblock_id_1 = get_tblock_id(1);                                   \   int global_id_1 = get_global_id(1);                                   \   for (int i1 = 0; i1 <= repeat_1; i1++) {                              \-    int group_id_2 = get_group_id(2);                                   \+    int tblock_id_2 = get_tblock_id(2);                                 \     int global_id_2 = get_global_id(2);                                 \     for (int i2 = 0; i2 <= repeat_2; i2++) {                            \-      int32_t our_array_offset = group_id_2 * x_elems * y_elems;        \+      int32_t our_array_offset = tblock_id_2 * x_elems * y_elems;       \       int32_t odata_offset = dst_offset + our_array_offset;             \       int32_t idata_offset = src_offset + our_array_offset;             \-      int32_t x_index = group_id_0 * TR_BLOCK_DIM + get_local_id(0)/muly; \+      int32_t x_index = tblock_id_0 * TR_BLOCK_DIM + get_local_id(0)/muly; \       int32_t y_index =                                                 \-        group_id_1 * TR_BLOCK_DIM * muly +                              \+        tblock_id_1 * TR_BLOCK_DIM * muly +                             \         get_local_id(1) + (get_local_id(0)%muly) * TR_BLOCK_DIM;        \       int32_t index_in = y_index * x_elems + x_index;                   \       if (x_index < x_elems && y_index < y_elems) {                     \@@ -149,24 +149,24 @@           src_mem[idata_offset + index_in];                             \       }                                                                 \       barrier_local();                                                  \-      x_index = group_id_1 * TR_BLOCK_DIM * muly +                      \+      x_index = tblock_id_1 * TR_BLOCK_DIM * muly +                     \         get_local_id(0) + (get_local_id(1)%muly) * TR_BLOCK_DIM;        \-      y_index = group_id_0 * TR_BLOCK_DIM + get_local_id(1)/muly;       \+      y_index = tblock_id_0 * TR_BLOCK_DIM + get_local_id(1)/muly;      \       int32_t index_out = y_index * y_elems + x_index;                  \       if (x_index < y_elems && y_index < x_elems) {                     \         dst_mem[odata_offset + index_out] =                             \           block[get_local_id(0) * (TR_BLOCK_DIM+1) + get_local_id(1)];  \       }                                                                 \-      group_id_2 += get_num_groups(2);                                  \-      global_id_2 += get_num_groups(2) * get_local_size(2);             \+      tblock_id_2 += get_num_tblocks(2);                                \+      global_id_2 += get_num_tblocks(2) * get_local_size(2);            \     }                                                                   \-    group_id_1 += get_num_groups(1);                                    \-    global_id_1 += get_num_groups(1) * get_local_size(1);               \+    tblock_id_1 += get_num_tblocks(1);                                  \+    global_id_1 += get_num_tblocks(1) * get_local_size(1);              \   }                                                                     \ }                                                                       \                                                                         \ FUTHARK_KERNEL_SIZED(TR_BLOCK_DIM*TR_BLOCK_DIM, 1, 1)                   \-void map_transpose_##NAME##_small(LOCAL_MEM_PARAM                       \+void map_transpose_##NAME##_small(SHARED_MEM_PARAM                       \                                   __global ELEM_TYPE *dst_mem,          \                                   int64_t dst_offset,                   \                                   __global ELEM_TYPE *src_mem,          \@@ -179,13 +179,13 @@                                   int32_t repeat_1,                     \                                   int32_t repeat_2) {                   \   (void)mulx; (void)muly;                                               \-  __local ELEM_TYPE* block = (__local ELEM_TYPE*)local_mem;             \-  int group_id_0 = get_group_id(0);                                     \+  __local ELEM_TYPE* block = (__local ELEM_TYPE*)shared_mem;            \+  int tblock_id_0 = get_tblock_id(0);                                   \   int global_id_0 = get_global_id(0);                                   \-  int group_id_1 = get_group_id(1);                                     \+  int tblock_id_1 = get_tblock_id(1);                                   \   int global_id_1 = get_global_id(1);                                   \   for (int i1 = 0; i1 <= repeat_1; i1++) {                              \-    int group_id_2 = get_group_id(2);                                   \+    int tblock_id_2 = get_tblock_id(2);                                 \     int global_id_2 = get_global_id(2);                                 \     for (int i2 = 0; i2 <= repeat_2; i2++) {                            \       int32_t our_array_offset = global_id_0/(y_elems * x_elems) * y_elems * x_elems; \@@ -198,16 +198,16 @@       if (global_id_0 < x_elems * y_elems * num_arrays) {               \         dst_mem[odata_offset + index_out] = src_mem[idata_offset + index_in]; \       }                                                                 \-      group_id_2 += get_num_groups(2);                                  \+      tblock_id_2 += get_num_tblocks(2);                                \       global_id_2 += get_global_size(2);                                \     }                                                                   \-    group_id_1 += get_num_groups(1);                                    \+    tblock_id_1 += get_num_tblocks(1);                                  \     global_id_1 += get_global_size(1);                                  \   }                                                                     \ }                                                                       \                                                                         \ FUTHARK_KERNEL_SIZED(TR_BLOCK_DIM*2, TR_TILE_DIM/TR_ELEMS_PER_THREAD, 1)\-void map_transpose_##NAME##_large(LOCAL_MEM_PARAM                       \+void map_transpose_##NAME##_large(SHARED_MEM_PARAM                      \                                   __global ELEM_TYPE *dst_mem,          \                                   int64_t dst_offset,                   \                                   __global ELEM_TYPE *src_mem,          \@@ -220,20 +220,20 @@                                   int32_t repeat_1,                     \                                   int32_t repeat_2) {                   \   (void)mulx; (void)muly;                                               \-  __local ELEM_TYPE* block = (__local ELEM_TYPE*)local_mem;             \-  int group_id_0 = get_group_id(0);                                     \+  __local ELEM_TYPE* block = (__local ELEM_TYPE*)shared_mem;             \+  int tblock_id_0 = get_tblock_id(0);                                   \   int global_id_0 = get_global_id(0);                                   \-  int group_id_1 = get_group_id(1);                                     \+  int tblock_id_1 = get_tblock_id(1);                                   \   int global_id_1 = get_global_id(1);                                   \   for (int i1 = 0; i1 <= repeat_1; i1++) {                              \-    int group_id_2 = get_group_id(2);                                   \+    int tblock_id_2 = get_tblock_id(2);                                 \     int global_id_2 = get_global_id(2);                                 \     for (int i2 = 0; i2 <= repeat_2; i2++) {                            \-      int64_t our_array_offset = group_id_2 * x_elems * y_elems;        \+      int64_t our_array_offset = tblock_id_2 * x_elems * y_elems;       \       int64_t odata_offset = dst_offset + our_array_offset;             \       int64_t idata_offset = src_offset + our_array_offset;             \       int64_t x_index = global_id_0;                                    \-      int64_t y_index = group_id_1 * TR_TILE_DIM + get_local_id(1);     \+      int64_t y_index = tblock_id_1 * TR_TILE_DIM + get_local_id(1);    \       if (x_index < x_elems) {                                          \         for (int64_t j = 0; j < TR_ELEMS_PER_THREAD; j++) {             \           int64_t index_i = (y_index + j * (TR_TILE_DIM/TR_ELEMS_PER_THREAD)) * x_elems + x_index; \@@ -245,8 +245,8 @@         }                                                               \       }                                                                 \       barrier_local();                                                  \-      x_index = group_id_1 * TR_TILE_DIM + get_local_id(0);             \-      y_index = group_id_0 * TR_TILE_DIM + get_local_id(1);             \+      x_index = tblock_id_1 * TR_TILE_DIM + get_local_id(0);            \+      y_index = tblock_id_0 * TR_TILE_DIM + get_local_id(1);            \       if (x_index < y_elems) {                                          \         for (int64_t j = 0; j < TR_ELEMS_PER_THREAD; j++) {             \           int64_t index_out = (y_index + j * (TR_TILE_DIM/TR_ELEMS_PER_THREAD)) * y_elems + x_index; \@@ -257,10 +257,10 @@           }                                                             \         }                                                               \       }                                                                 \-      group_id_2 += get_num_groups(2);                                  \+      tblock_id_2 += get_num_tblocks(2);                                \       global_id_2 += get_global_size(2);                                \     }                                                                   \-    group_id_1 += get_num_groups(1);                                    \+    tblock_id_1 += get_num_tblocks(1);                                  \     global_id_1 += get_global_size(1);                                  \   }                                                                     \ }                                                                       \
rts/python/opencl.py view
@@ -120,7 +120,7 @@     interactive=False,     platform_pref=None,     device_pref=None,-    default_group_size=None,+    default_tblock_size=None,     default_num_groups=None,     default_tile_size=None,     default_reg_tile_size=None,@@ -146,10 +146,10 @@      check_types(self, required_types) -    max_group_size = int(self.device.max_work_group_size)-    max_tile_size = int(np.sqrt(self.device.max_work_group_size))+    max_tblock_size = int(self.device.max_work_tblock_size)+    max_tile_size = int(np.sqrt(self.device.max_work_tblock_size)) -    self.max_group_size = max_group_size+    self.max_tblock_size = max_tblock_size     self.max_tile_size = max_tile_size     self.max_threshold = 0     self.max_num_groups = 0@@ -176,9 +176,9 @@     )     self.failure_is_an_option = np.int32(0) -    if "default_group_size" in sizes:-        default_group_size = sizes["default_group_size"]-        del sizes["default_group_size"]+    if "default_tblock_size" in sizes:+        default_tblock_size = sizes["default_tblock_size"]+        del sizes["default_tblock_size"]      if "default_num_groups" in sizes:         default_num_groups = sizes["default_num_groups"]@@ -196,13 +196,13 @@         default_threshold = sizes["default_threshold"]         del sizes["default_threshold"] -    default_group_size_set = default_group_size != None+    default_tblock_size_set = default_tblock_size != None     default_tile_size_set = default_tile_size != None     default_sizes = apply_size_heuristics(         self,         size_heuristics,         {-            "group_size": default_group_size,+            "tblock_size": default_tblock_size,             "tile_size": default_tile_size,             "reg_tile_size": default_reg_tile_size,             "num_groups": default_num_groups,@@ -210,21 +210,21 @@             "threshold": default_threshold,         },     )-    default_group_size = default_sizes["group_size"]+    default_tblock_size = default_sizes["tblock_size"]     default_num_groups = default_sizes["num_groups"]     default_threshold = default_sizes["threshold"]     default_tile_size = default_sizes["tile_size"]     default_reg_tile_size = default_sizes["reg_tile_size"]     lockstep_width = default_sizes["lockstep_width"] -    if default_group_size > max_group_size:-        if default_group_size_set:+    if default_tblock_size > max_tblock_size:+        if default_tblock_size_set:             sys.stderr.write(                 "Note: Device limits group size to {} (down from {})\n".format(-                    max_tile_size, default_group_size+                    max_tile_size, default_tblock_size                 )             )-        default_group_size = max_group_size+        default_tblock_size = max_tblock_size      if default_tile_size > max_tile_size:         if default_tile_size_set:@@ -247,11 +247,11 @@      self.sizes = {}     for k, v in all_sizes.items():-        if v["class"] == "group_size":-            max_value = max_group_size-            default_value = default_group_size+        if v["class"] == "tblock_size":+            max_value = max_tblock_size+            default_value = default_tblock_size         elif v["class"] == "num_groups":-            max_value = max_group_size  # Intentional!+            max_value = max_tblock_size  # Intentional!             default_value = default_num_groups         elif v["class"] == "tile_size":             max_value = max_tile_size@@ -283,7 +283,7 @@     if len(program_src) >= 0:         build_options += ["-DLOCKSTEP_WIDTH={}".format(lockstep_width)] -        build_options += ["-D{}={}".format("max_group_size", max_group_size)]+        build_options += ["-D{}={}".format("max_tblock_size", max_tblock_size)]          build_options += [             "-D{}={}".format(
src/Futhark/CLI/Autotune.hs view
@@ -76,8 +76,8 @@  type Path = [(T.Text, Int)] -regexGroups :: Regex -> T.Text -> Maybe [T.Text]-regexGroups regex s = do+regexBlocks :: Regex -> T.Text -> Maybe [T.Text]+regexBlocks regex s = do   (_, _, _, groups) <-     matchM regex s :: Maybe (T.Text, T.Text, T.Text, [T.Text])   Just groups@@ -87,7 +87,7 @@   where     regex = makeRegex ("Compared ([^ ]+) <= (-?[0-9]+)" :: String)     isComparison l = do-      [thresh, val] <- regexGroups regex l+      [thresh, val] <- regexBlocks regex l       val' <- readMaybe $ T.unpack val       pure (thresh, val') @@ -246,7 +246,7 @@      findThreshold :: (T.Text, T.Text) -> Maybe (T.Text, [(T.Text, Bool)])     findThreshold (name, param_class) = do-      [_, grp] <- regexGroups regex param_class+      [_, grp] <- regexBlocks regex param_class       pure         ( name,           filter (not . T.null . fst)
src/Futhark/CodeGen/Backends/CCUDA.hs view
@@ -49,11 +49,6 @@   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_program(struct futhark_context_config *cfg, const char* s);|]   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_dump_ptx_to(struct futhark_context_config *cfg, const char* s);|]   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_load_ptx_from(struct futhark_context_config *cfg, const char* s);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_reg_tile_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_threshold(struct futhark_context_config *cfg, int size);|]  cliOptions :: [Option] cliOptions =
src/Futhark/CodeGen/Backends/COpenCL.hs view
@@ -44,8 +44,8 @@      which' = case which of       LockstepWidth -> [C.cexp|ctx->lockstep_width|]-      NumGroups -> [C.cexp|ctx->cfg->default_num_groups|]-      GroupSize -> [C.cexp|ctx->cfg->default_group_size|]+      NumBlocks -> [C.cexp|ctx->cfg->default_num_groups|]+      BlockSize -> [C.cexp|ctx->cfg->default_group_size|]       TileSize -> [C.cexp|ctx->cfg->default_tile_size|]       RegTileSize -> [C.cexp|ctx->cfg->default_reg_tile_size|]       Threshold -> [C.cexp|ctx->cfg->default_threshold|]@@ -105,11 +105,6 @@   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_program(struct futhark_context_config *cfg, const char* s);|]   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_dump_binary_to(struct futhark_context_config *cfg, const char* s);|]   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_load_binary_from(struct futhark_context_config *cfg, const char* s);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_reg_tile_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_threshold(struct futhark_context_config *cfg, int size);|]   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_command_queue(struct futhark_context_config *cfg, typename cl_command_queue);|]   GC.headerDecl GC.MiscDecl [C.cedecl|typename cl_command_queue futhark_context_get_command_queue(struct futhark_context* ctx);|] 
src/Futhark/CodeGen/Backends/GPU.hs view
@@ -78,9 +78,9 @@   where     field = "max_" <> prettyString size_class -compileGroupDim :: GroupDim -> GC.CompilerM op s C.Exp-compileGroupDim (Left e) = GC.compileExp e-compileGroupDim (Right kc) = pure $ kernelConstToExp kc+compileBlockDim :: BlockDim -> GC.CompilerM op s C.Exp+compileBlockDim (Left e) = GC.compileExp e+compileBlockDim (Right kc) = pure $ kernelConstToExp kc  genLaunchKernel ::   KernelSafety ->@@ -88,25 +88,25 @@   Count Bytes (TExp Int64) ->   [KernelArg] ->   [Exp] ->-  [GroupDim] ->+  [BlockDim] ->   GC.CompilerM op s ()-genLaunchKernel safety kernel_name local_memory args num_groups group_size = do+genLaunchKernel safety kernel_name shared_memory args num_tblocks tblock_size = do   (arg_params, arg_params_inits, call_args) <-     unzip3 <$> zipWithM mkArgs [(0 :: Int) ..] args -  (grid_x, grid_y, grid_z) <- mkDims <$> mapM GC.compileExp num_groups-  (group_x, group_y, group_z) <- mkDims <$> mapM compileGroupDim group_size+  (grid_x, grid_y, grid_z) <- mkDims <$> mapM GC.compileExp num_tblocks+  (block_x, block_y, block_z) <- mkDims <$> mapM compileBlockDim tblock_size    kernel_fname <- genKernelFunction kernel_name safety arg_params arg_params_inits -  local_memory' <- GC.compileExp $ untyped $ unCount local_memory+  shared_memory' <- GC.compileExp $ untyped $ unCount shared_memory    GC.stm     [C.cstm|{            err = $id:kernel_fname(ctx,                                   $exp:grid_x, $exp:grid_y, $exp:grid_z,-                                  $exp:group_x, $exp:group_y, $exp:group_z,-                                  $exp:local_memory',+                                  $exp:block_x, $exp:block_y, $exp:block_z,+                                  $exp:shared_memory',                                   $args:call_args);            if (err != FUTHARK_SUCCESS) { goto cleanup; }            }|]@@ -152,8 +152,8 @@ callKernel (GetSizeMax v size_class) = do   let e = kernelConstToExp $ SizeMaxConst size_class   GC.stm [C.cstm|$id:v = $exp:e;|]-callKernel (LaunchKernel safety kernel_name local_memory args num_groups group_size) =-  genLaunchKernel safety kernel_name local_memory args num_groups group_size+callKernel (LaunchKernel safety kernel_name shared_memory args num_tblocks tblock_size) =+  genLaunchKernel safety kernel_name shared_memory args num_tblocks tblock_size  copygpu2gpu :: GC.DoLMADCopy op s copygpu2gpu _ t shape dst (dstoffset, dststride) src (srcoffset, srcstride) = do@@ -323,17 +323,31 @@         optionAction = [C.cstm|futhark_context_config_set_device(cfg, optarg);|]       },     Option+      { optionLongName = "default-thread-block-size",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionDescription = "The default size of thread blocks that are launched.",+        optionAction = [C.cstm|futhark_context_config_set_default_thread_block_size(cfg, atoi(optarg));|]+      },+    Option+      { optionLongName = "default-grid-size",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionDescription = "The default number of thread blocks that are launched.",+        optionAction = [C.cstm|futhark_context_config_set_default_grid_size(cfg, atoi(optarg));|]+      },+    Option       { optionLongName = "default-group-size",         optionShortName = Nothing,         optionArgument = RequiredArgument "INT",-        optionDescription = "The default size of workgroups that are launched.",+        optionDescription = "Alias for --default-thread-block-size.",         optionAction = [C.cstm|futhark_context_config_set_default_group_size(cfg, atoi(optarg));|]       },     Option       { optionLongName = "default-num-groups",         optionShortName = Nothing,         optionArgument = RequiredArgument "INT",-        optionDescription = "The default number of workgroups that are launched.",+        optionDescription = "Alias for --default-num-thread-blocks.",         optionAction = [C.cstm|futhark_context_config_set_default_num_groups(cfg, atoi(optarg));|]       },     Option@@ -440,3 +454,11 @@   GC.generateProgramStruct    GC.onClear [C.citem|if (ctx->error == NULL) { gpu_free_all(ctx); }|]++  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_thread_block_size(struct futhark_context_config *cfg, int size);|]+  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_grid_size(struct futhark_context_config *cfg, int size);|]+  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size);|]+  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int size);|]+  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size);|]+  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_reg_tile_size(struct futhark_context_config *cfg, int size);|]+  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_threshold(struct futhark_context_config *cfg, int size);|]
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -573,7 +573,7 @@       size_default_inits = map (intinit . fromMaybe 0 . sizeDefault) param_classes       size_decls = map (\k -> [C.csdecl|typename int64_t *$id:k;|]) param_names       num_params = length params-  earlyDecl [C.cedecl|struct tuning_params { $sdecls:size_decls };|]+  earlyDecl [C.cedecl|struct tuning_params { int dummy; $sdecls:size_decls };|]   earlyDecl [C.cedecl|static const int num_tuning_params = $int:num_params;|]   earlyDecl [C.cedecl|static const char *tuning_param_names[] = { $inits:size_name_inits, NULL };|]   earlyDecl [C.cedecl|static const char *tuning_param_vars[] = { $inits:size_var_inits, NULL };|]
src/Futhark/CodeGen/Backends/HIP.hs view
@@ -47,11 +47,6 @@   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_device(struct futhark_context_config *cfg, const char* s);|]   GC.headerDecl GC.InitDecl [C.cedecl|const char* futhark_context_config_get_program(struct futhark_context_config *cfg);|]   GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_program(struct futhark_context_config *cfg, const char* s);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_group_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_num_groups(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_tile_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_reg_tile_size(struct futhark_context_config *cfg, int size);|]-  GC.headerDecl GC.InitDecl [C.cedecl|void futhark_context_config_set_default_threshold(struct futhark_context_config *cfg, int size);|]  cliOptions :: [Option] cliOptions =
src/Futhark/CodeGen/Backends/MulticoreC.hs view
@@ -395,6 +395,7 @@                                  int subtask_id,                                  int tid) {                        (void)subtask_id;+                       (void)tid;                        int err = 0;                        struct $id:fstruct *$id:fstruct = (struct $id:fstruct*) args;                        struct futhark_context *ctx = $id:fstruct->ctx;
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -59,8 +59,8 @@           Assign (Var "build_options") $ List [],           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_tblock_size") None,+          Assign (Var "default_num_tblocks") None,           Assign (Var "default_tile_size") None,           Assign (Var "default_reg_tile_size") None,           Assign (Var "fut_opencl_src") $ RawStringLiteral $ opencl_prelude <> opencl_code@@ -83,8 +83,8 @@             "interactive=False",             "platform_pref=preferred_platform",             "device_pref=preferred_device",-            "default_group_size=default_group_size",-            "default_num_groups=default_num_groups",+            "default_tblock_size=default_tblock_size",+            "default_num_tblocks=default_num_tblocks",             "default_tile_size=default_tile_size",             "default_reg_tile_size=default_reg_tile_size",             "default_threshold=default_threshold",@@ -128,14 +128,14 @@               optionShortName = Nothing,               optionArgument = RequiredArgument "int",               optionAction =-                [Assign (Var "default_group_size") $ Var "optarg"]+                [Assign (Var "default_tblock_size") $ Var "optarg"]             },           Option             { optionLongName = "default-num-groups",               optionShortName = Nothing,               optionArgument = RequiredArgument "int",               optionAction =-                [Assign (Var "default_num_groups") $ Var "optarg"]+                [Assign (Var "default_num_tblocks") $ Var "optarg"]             },           Option             { optionLongName = "default-tile-size",@@ -212,9 +212,9 @@ kernelConstToExp (Imp.SizeMaxConst size_class) =   Var $ "self.max_" <> prettyString size_class -compileGroupDim :: Imp.GroupDim -> CompilerM op s PyExp-compileGroupDim (Left e) = asLong <$> compileExp e-compileGroupDim (Right kc) = pure $ kernelConstToExp kc+compileBlockDim :: Imp.BlockDim -> CompilerM op s PyExp+compileBlockDim (Left e) = asLong <$> compileExp e+compileBlockDim (Right kc) = pure $ kernelConstToExp kc  callKernel :: OpCompiler Imp.OpenCL () callKernel (Imp.GetSize v key) = do@@ -227,14 +227,14 @@ callKernel (Imp.GetSizeMax v size_class) = do   v' <- compileVar v   stm $ Assign v' $ kernelConstToExp $ Imp.SizeMaxConst size_class-callKernel (Imp.LaunchKernel safety name local_memory args num_workgroups workgroup_size) = do-  num_workgroups' <- mapM (fmap asLong . compileExp) num_workgroups-  workgroup_size' <- mapM compileGroupDim workgroup_size-  let kernel_size = zipWith mult_exp num_workgroups' workgroup_size'+callKernel (Imp.LaunchKernel safety name shared_memory args num_threadblocks worktblock_size) = do+  num_threadblocks' <- mapM (fmap asLong . compileExp) num_threadblocks+  worktblock_size' <- mapM compileBlockDim worktblock_size+  let kernel_size = zipWith mult_exp num_threadblocks' worktblock_size'       total_elements = foldl mult_exp (Integer 1) kernel_size       cond = BinOp "!=" total_elements (Integer 0)-  local_memory' <- compileExp $ Imp.untyped $ Imp.unCount local_memory-  body <- collect $ launchKernel name safety kernel_size workgroup_size' local_memory' args+  shared_memory' <- compileExp $ Imp.untyped $ Imp.unCount shared_memory+  body <- collect $ launchKernel name safety kernel_size worktblock_size' shared_memory' args   stm $ If cond body []    when (safety >= Imp.SafetyFull) $@@ -252,9 +252,9 @@   PyExp ->   [Imp.KernelArg] ->   CompilerM op s ()-launchKernel kernel_name safety kernel_dims workgroup_dims local_memory args = do+launchKernel kernel_name safety kernel_dims threadblock_dims shared_memory args = do   let kernel_dims' = Tuple kernel_dims-      workgroup_dims' = Tuple workgroup_dims+      threadblock_dims' = Tuple threadblock_dims       kernel_name' = "self." <> zEncodeText (nameToText kernel_name) <> "_var"   args' <- mapM processKernelArg args   let failure_args =@@ -266,13 +266,13 @@           ]   stm . Exp $     simpleCall (T.unpack $ kernel_name' <> ".set_args") $-      [simpleCall "cl.LocalMemory" [simpleCall "max" [local_memory, Integer 1]]]+      [simpleCall "cl.SharedMemory" [simpleCall "max" [shared_memory, Integer 1]]]         ++ failure_args         ++ args'   stm . Exp $     simpleCall       "cl.enqueue_nd_range_kernel"-      [Var "self.queue", Var (T.unpack kernel_name'), kernel_dims', workgroup_dims']+      [Var "self.queue", Var (T.unpack kernel_name'), kernel_dims', threadblock_dims']   finishIfSynchronous   where     processKernelArg :: Imp.KernelArg -> CompilerM op s PyExp
src/Futhark/CodeGen/Backends/PyOpenCL/Boilerplate.hs view
@@ -59,8 +59,8 @@                                    interactive=interactive,                                    platform_pref=platform_pref,                                    device_pref=device_pref,-                                   default_group_size=default_group_size,-                                   default_num_groups=default_num_groups,+                                   default_tblock_size=default_tblock_size,+                                   default_num_tblocks=default_num_tblocks,                                    default_tile_size=default_tile_size,                                    default_reg_tile_size=default_reg_tile_size,                                    default_threshold=default_threshold,@@ -128,8 +128,8 @@          which' = case which of           LockstepWidth -> String "lockstep_width"-          NumGroups -> String "num_groups"-          GroupSize -> String "group_size"+          NumBlocks -> String "num_tblocks"+          BlockSize -> String "tblock_size"           TileSize -> String "tile_size"           RegTileSize -> String "reg_tile_size"           Threshold -> String "threshold"
src/Futhark/CodeGen/ImpCode/GPU.hs view
@@ -10,7 +10,7 @@     KernelOp (..),     Fence (..),     AtomicOp (..),-    GroupDim,+    BlockDim,     Kernel (..),     KernelUse (..),     module Futhark.CodeGen.ImpCode,@@ -49,16 +49,16 @@   | GetSizeMax VName SizeClass   deriving (Show) --- | The size of one dimension of a group.-type GroupDim = Either Exp KernelConst+-- | The size of one dimension of a block.+type BlockDim = Either Exp KernelConst  -- | A generic kernel containing arbitrary kernel code. data Kernel = Kernel   { kernelBody :: Code KernelOp,     -- | The host variables referenced by the kernel.     kernelUses :: [KernelUse],-    kernelNumGroups :: [Exp],-    kernelGroupSize :: [GroupDim],+    kernelNumBlocks :: [Exp],+    kernelBlockSize :: [BlockDim],     -- | A short descriptive and _unique_ name - should be     -- alphanumeric and without spaces.     kernelName :: Name,@@ -71,7 +71,7 @@     -- | If true, multi-versioning branches will consider this kernel     -- when considering the local memory requirements. Set this to     -- false for kernels that do their own checking.-    kernelCheckLocalMemory :: Bool+    kernelCheckSharedMemory :: Bool   }   deriving (Show) @@ -134,24 +134,24 @@   freeIn' kernel =     freeIn'       ( kernelBody kernel,-        kernelNumGroups kernel,-        kernelGroupSize kernel+        kernelNumBlocks kernel,+        kernelBlockSize kernel       )  instance Pretty Kernel where   pretty kernel =     "kernel"       <+> brace-        ( "groups"-            <+> brace (pretty $ kernelNumGroups kernel)-            </> "group_size"-            <+> brace (list $ map (either pretty pretty) $ kernelGroupSize kernel)+        ( "blocks"+            <+> brace (pretty $ kernelNumBlocks kernel)+            </> "tblock_size"+            <+> brace (list $ map (either pretty pretty) $ kernelBlockSize kernel)             </> "uses"             <+> brace (commasep $ map pretty $ kernelUses kernel)             </> "failure_tolerant"             <+> brace (pretty $ kernelFailureTolerant kernel)-            </> "check_local_memory"-            <+> brace (pretty $ kernelCheckLocalMemory kernel)+            </> "check_shared_memory"+            <+> brace (pretty $ kernelCheckSharedMemory kernel)             </> "body"             <+> brace (pretty $ kernelBody kernel)         )@@ -163,7 +163,7 @@  -- | An operation that occurs within a kernel body. data KernelOp-  = GetGroupId VName Int+  = GetBlockId VName Int   | GetLocalId VName Int   | GetLocalSize VName Int   | GetLockstepWidth VName@@ -211,10 +211,10 @@   freeIn' (AtomicXchg _ _ arr i x) = freeIn' arr <> freeIn' i <> freeIn' x  instance Pretty KernelOp where-  pretty (GetGroupId dest i) =+  pretty (GetBlockId dest i) =     pretty dest       <+> "<-"-      <+> "get_group_id"+      <+> "get_tblock_id"       <> parens (pretty i)   pretty (GetLocalId dest i) =     pretty dest
src/Futhark/CodeGen/ImpCode/OpenCL.hs view
@@ -16,7 +16,7 @@     numFailureParams,     KernelTarget (..),     FailureMsg (..),-    GroupDim,+    BlockDim,     KernelConst (..),     KernelConstExp,     module Futhark.CodeGen.ImpCode,@@ -27,7 +27,7 @@ import Data.Map qualified as M import Data.Text qualified as T import Futhark.CodeGen.ImpCode-import Futhark.CodeGen.ImpCode.GPU (GroupDim, KernelConst (..), KernelConstExp)+import Futhark.CodeGen.ImpCode.GPU (BlockDim, KernelConst (..), KernelConstExp) import Futhark.IR.GPU.Sizes import Futhark.Util.Pretty @@ -97,7 +97,7 @@  -- | Host-level OpenCL operation. data OpenCL-  = LaunchKernel KernelSafety KernelName (Count Bytes (TExp Int64)) [KernelArg] [Exp] [GroupDim]+  = LaunchKernel KernelSafety KernelName (Count Bytes (TExp Int64)) [KernelArg] [Exp] [BlockDim]   | GetSize VName Name   | CmpSizeLe VName Name Exp   | GetSizeMax VName SizeClass
src/Futhark/CodeGen/ImpGen/GPU.hs view
@@ -108,24 +108,24 @@     =<< toExp x opCompiler (Pat [pe]) (Inner (SizeOp (GetSizeMax size_class))) =   sOp $ Imp.GetSizeMax (patElemName pe) size_class-opCompiler (Pat [pe]) (Inner (SizeOp (CalcNumGroups w64 max_num_groups_key group_size))) = do+opCompiler (Pat [pe]) (Inner (SizeOp (CalcNumBlocks w64 max_num_tblocks_key tblock_size))) = do   fname <- askFunction-  max_num_groups :: TV Int32 <- dPrim "max_num_groups" int32+  max_num_tblocks :: TV Int32 <- dPrim "max_num_tblocks" int32   sOp $-    Imp.GetSize (tvVar max_num_groups) (keyWithEntryPoint fname max_num_groups_key) $-      sizeClassWithEntryPoint fname SizeNumGroups+    Imp.GetSize (tvVar max_num_tblocks) (keyWithEntryPoint fname max_num_tblocks_key) $+      sizeClassWithEntryPoint fname SizeGrid -  -- If 'w' is small, we launch fewer groups than we normally would.-  -- We don't want any idle groups.+  -- If 'w' is small, we launch fewer blocks than we normally would.+  -- We don't want any idle blocks.   --   -- The calculations are done with 64-bit integers to avoid overflow   -- issues.-  let num_groups_maybe_zero =-        sMin64 (pe64 w64 `divUp` pe64 group_size) $-          sExt64 (tvExp max_num_groups)-  -- We also don't want zero groups.-  let num_groups = sMax64 1 num_groups_maybe_zero-  mkTV (patElemName pe) int32 <-- sExt32 num_groups+  let num_tblocks_maybe_zero =+        sMin64 (pe64 w64 `divUp` pe64 tblock_size) $+          sExt64 (tvExp max_num_tblocks)+  -- We also don't want zero blocks.+  let num_tblocks = sMax64 1 num_tblocks_maybe_zero+  mkTV (patElemName pe) int32 <-- sExt32 num_tblocks opCompiler dest (Inner (SegOp op)) =   segOpCompiler dest op opCompiler (Pat pes) (Inner (GPUBody _ (Body _ stms res))) = do@@ -170,8 +170,8 @@ -- otherwise protected by their own multi-versioning branches deeper -- down.  Currently the compiler will not generate multi-versioning -- that makes this a problem, but it might in the future.-checkLocalMemoryReqs :: (VName -> Bool) -> Imp.HostCode -> CallKernelGen (Maybe (Imp.TExp Bool))-checkLocalMemoryReqs in_scope code = do+checkSharedMemoryReqs :: (VName -> Bool) -> Imp.HostCode -> CallKernelGen (Maybe (Imp.TExp Bool))+checkSharedMemoryReqs in_scope code = do   let alloc_sizes = map (sum . map alignedSize . localAllocSizes . Imp.kernelBody) $ getGPU code    -- If any of the sizes involve a variable that is not known at this@@ -179,17 +179,17 @@   if not $ all in_scope $ namesToList $ freeIn alloc_sizes     then pure Nothing     else do-      local_memory_capacity :: TV Int32 <- dPrim "local_memory_capacity" int32-      sOp $ Imp.GetSizeMax (tvVar local_memory_capacity) SizeLocalMemory+      shared_memory_capacity :: TV Int32 <- dPrim "shared_memory_capacity" int32+      sOp $ Imp.GetSizeMax (tvVar shared_memory_capacity) SizeSharedMemory -      let local_memory_capacity_64 =-            sExt64 $ tvExp local_memory_capacity+      let shared_memory_capacity_64 =+            sExt64 $ tvExp shared_memory_capacity           fits size =-            unCount size .<=. local_memory_capacity_64+            unCount size .<=. shared_memory_capacity_64       pure $ Just $ foldl' (.&&.) true (map fits alloc_sizes)   where     getGPU = foldMap getKernel-    getKernel (Imp.CallKernel k) | Imp.kernelCheckLocalMemory k = [k]+    getKernel (Imp.CallKernel k) | Imp.kernelCheckSharedMemory k = [k]     getKernel _ = []      localAllocSizes = foldMap localAllocSize@@ -237,8 +237,8 @@       if shapeRank shape == 0         then copyDWIM (patElemName pe) [] se []         else sReplicate (patElemName pe) se--- Allocation in the "local" space is just a placeholder.-expCompiler _ (Op (Alloc _ (Space "local"))) =+-- Allocation in the "shared" space is just a placeholder.+expCompiler _ (Op (Alloc _ (Space "shared"))) =   pure () expCompiler pat (WithAcc inputs lam) =   withAcc pat inputs lam@@ -252,7 +252,7 @@   scope <- askScope   tcode <- collect $ compileBody dest $ caseBody first_case   fcode <- collect $ expCompiler dest $ Match cond cases defbranch sort-  check <- checkLocalMemoryReqs (`M.member` scope) tcode+  check <- checkSharedMemoryReqs (`M.member` scope) tcode   let matches = caseMatch cond (casePat first_case)   emit $ case check of     Nothing -> fcode
src/Futhark/CodeGen/ImpGen/GPU/Base.hs view
@@ -11,9 +11,9 @@     HostEnv (..),     Target (..),     KernelEnv (..),-    groupReduce,-    groupScan,-    groupLoop,+    blockReduce,+    blockScan,+    blockLoop,     isActive,     sKernel,     sKernelThread,@@ -23,9 +23,9 @@     allocLocal,     kernelAlloc,     compileThreadResult,-    virtualiseGroups,+    virtualiseBlocks,     kernelLoop,-    groupCoverSpace,+    blockCoverSpace,     fenceForArrays,     updateAcc,     genZeroes,@@ -91,14 +91,14 @@ data KernelConstants = KernelConstants   { kernelGlobalThreadId :: Imp.TExp Int32,     kernelLocalThreadId :: Imp.TExp Int32,-    kernelGroupId :: Imp.TExp Int32,+    kernelBlockId :: Imp.TExp Int32,     kernelGlobalThreadIdVar :: VName,     kernelLocalThreadIdVar :: VName,-    kernelGroupIdVar :: VName,-    kernelNumGroupsCount :: Count NumGroups SubExp,-    kernelGroupSizeCount :: Count GroupSize SubExp,-    kernelNumGroups :: Imp.TExp Int64,-    kernelGroupSize :: Imp.TExp Int64,+    kernelBlockIdVar :: VName,+    kernelNumBlocksCount :: Count NumBlocks SubExp,+    kernelBlockSizeCount :: Count BlockSize SubExp,+    kernelNumBlocks :: Imp.TExp Int64,+    kernelBlockSize :: Imp.TExp Int64,     kernelNumThreads :: Imp.TExp Int32,     kernelWaveSize :: Imp.TExp Int32,     -- | A mapping from dimensions of nested SegOps to already@@ -126,7 +126,7 @@   -- Handled by the declaration of the memory block, which is then   -- translated to an actual scalar variable during C code generation.   pure ()-kernelAlloc (Pat [mem]) size (Space "local") =+kernelAlloc (Pat [mem]) size (Space "shared") =   allocLocal (patElemName mem) $ Imp.bytes $ pe64 size kernelAlloc (Pat [mem]) _ _ =   compilerLimitationS $ "Cannot allocate memory block " ++ prettyString mem ++ " in kernel."@@ -187,7 +187,7 @@ -- | Assign iterations of a for-loop to all threads in the kernel. -- The passed-in function is invoked with the (symbolic) iteration. -- The body must contain thread-level code.  For multidimensional--- loops, use 'groupCoverSpace'.+-- loops, use 'blockCoverSpace'. kernelLoop ::   (IntExp t) =>   Imp.TExp t ->@@ -205,47 +205,47 @@           i <- dPrimVE "i" $ chunk_i * num_threads + tid           sWhen (i .<. n) $ f i --- | Assign iterations of a for-loop to threads in the workgroup.  The+-- | Assign iterations of a for-loop to threads in the threadblock.  The -- passed-in function is invoked with the (symbolic) iteration.  For--- multidimensional loops, use 'groupCoverSpace'.-groupLoop ::+-- multidimensional loops, use 'blockCoverSpace'.+blockLoop ::   (IntExp t) =>   Imp.TExp t ->   (Imp.TExp t -> InKernelGen ()) ->   InKernelGen ()-groupLoop n f = do+blockLoop n f = do   constants <- kernelConstants <$> askEnv   kernelLoop     (kernelLocalThreadId constants `sExtAs` n)-    (kernelGroupSize constants `sExtAs` n)+    (kernelBlockSize constants `sExtAs` n)     n     f  -- | Iterate collectively though a multidimensional space, such that--- all threads in the group participate.  The passed-in function is+-- all threads in the block participate.  The passed-in function is -- invoked with a (symbolic) point in the index space.-groupCoverSpace ::+blockCoverSpace ::   (IntExp t) =>   [Imp.TExp t] ->   ([Imp.TExp t] -> InKernelGen ()) ->   InKernelGen ()-groupCoverSpace ds f = do+blockCoverSpace ds f = do   constants <- kernelConstants <$> askEnv-  let group_size = kernelGroupSize constants+  let tblock_size = kernelBlockSize constants   case splitFromEnd 1 ds of     -- Optimise the case where the inner dimension of the space is-    -- equal to the group size.+    -- equal to the block size.     (ds', [last_d])-      | last_d == (group_size `sExtAs` last_d) -> do+      | last_d == (tblock_size `sExtAs` last_d) -> do           let ltid = kernelLocalThreadId constants `sExtAs` last_d           sLoopSpace ds' $ \ds_is ->             f $ ds_is ++ [ltid]     _ ->-      groupLoop (product ds) $ f . unflattenIndex ds+      blockLoop (product ds) $ f . unflattenIndex ds  -- Which fence do we need to protect shared access to this memory space? fenceForSpace :: Space -> Imp.Fence-fenceForSpace (Space "local") = Imp.FenceLocal+fenceForSpace (Space "shared") = Imp.FenceLocal fenceForSpace _ = Imp.FenceGlobal  -- | If we are touching these arrays, which kind of fence do we need?@@ -281,11 +281,11 @@ -- (with primitive non-commutative operators only). getChunkSize :: [Type] -> Imp.KernelConstExp getChunkSize types = do-  let max_group_size = Imp.SizeMaxConst SizeGroup-      max_group_mem = Imp.SizeMaxConst SizeLocalMemory-      max_group_reg = Imp.SizeMaxConst SizeRegisters-      k_mem = le64 max_group_mem `quot` le64 max_group_size-      k_reg = le64 max_group_reg `quot` le64 max_group_size+  let max_tblock_size = Imp.SizeMaxConst SizeThreadBlock+      max_block_mem = Imp.SizeMaxConst SizeSharedMemory+      max_block_reg = Imp.SizeMaxConst SizeRegisters+      k_mem = le64 max_block_mem `quot` le64 max_tblock_size+      k_reg = le64 max_block_reg `quot` le64 max_tblock_size       types' = map elemType $ filter primType types       sizes = map primByteSize types' @@ -297,7 +297,7 @@       reg_constraint = (k_reg - 1 - sum_sizes') `quot` (2 * sum_sizes')   untyped $ sMax64 1 $ sMin64 mem_constraint reg_constraint -inBlockScan ::+inChunkScan ::   KernelConstants ->   Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->   Imp.TExp Int64 ->@@ -308,7 +308,7 @@   InKernelGen () ->   Lambda GPUMem ->   InKernelGen ()-inBlockScan constants seg_flag arrs_full_size lockstep_width block_size active arrs barrier scan_lam = everythingVolatile $ do+inChunkScan constants seg_flag arrs_full_size lockstep_width block_size active arrs barrier scan_lam = everythingVolatile $ do   skip_threads <- dPrim "skip_threads" int32   let actual_params = lambdaParams scan_lam       (x_params, y_params) =@@ -398,14 +398,14 @@         copyDWIMFix arr [ltid] (Var $ paramName x) []       copyDWIM (paramName y) [] (Var $ paramName x) [] -groupScan ::+blockScan ::   Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->   Imp.TExp Int64 ->   Imp.TExp Int64 ->   Lambda GPUMem ->   [VName] ->   InKernelGen ()-groupScan seg_flag arrs_full_size w lam arrs = do+blockScan seg_flag arrs_full_size w lam arrs = do   constants <- kernelConstants <$> askEnv   renamed_lam <- renameLambda lam @@ -419,27 +419,26 @@    fence <- fenceForArrays arrs -  -- The scan works by splitting the group into blocks, which are-  -- scanned separately.  Typically, these blocks are smaller than-  -- the lockstep width, which enables barrier-free execution inside-  -- them.+  -- The scan works by splitting the block into chunks, which are+  -- scanned separately. Typically, these chunks are at most the+  -- lockstep width, which enables barrier-free execution inside them.   ---  -- We hardcode the block size here.  The only requirement is that-  -- it should not be less than the square root of the group size.-  -- With 32, we will work on groups of size 1024 or smaller, which-  -- fits every device Troels has seen.  Still, it would be nicer if-  -- it were a runtime parameter.  Some day.-  let block_size = 32+  -- We hardcode the chunk size here. The only requirement is that it+  -- should not be less than the square root of the block size. With+  -- 32, we will work on blocks of size 1024 or smaller, which fits+  -- every device Troels has seen. Still, it would be nicer if it were+  -- a runtime parameter. Some day.+  let chunk_size = 32       simd_width = kernelWaveSize constants-      block_id = ltid32 `quot` block_size-      in_block_id = ltid32 - block_id * block_size-      doInBlockScan seg_flag' active =-        inBlockScan+      chunk_id = ltid32 `quot` chunk_size+      in_chunk_id = ltid32 - chunk_id * chunk_size+      doInChunkScan seg_flag' active =+        inChunkScan           constants           seg_flag'           arrs_full_size           simd_width-          block_size+          chunk_size           active           arrs           barrier@@ -456,34 +455,34 @@         | otherwise =             sOp $ Imp.ErrorSync Imp.FenceLocal -      group_offset = sExt64 (kernelGroupId constants) * kernelGroupSize constants+      block_offset = sExt64 (kernelBlockId constants) * kernelBlockSize constants        writeBlockResult p arr         | isPrimParam p =-            copyDWIMFix arr [sExt64 block_id] (Var $ paramName p) []+            copyDWIMFix arr [sExt64 chunk_id] (Var $ paramName p) []         | otherwise =-            copyDWIMFix arr [group_offset + sExt64 block_id] (Var $ paramName p) []+            copyDWIMFix arr [block_offset + sExt64 chunk_id] (Var $ paramName p) []        readPrevBlockResult p arr         | isPrimParam p =-            copyDWIMFix (paramName p) [] (Var arr) [sExt64 block_id - 1]+            copyDWIMFix (paramName p) [] (Var arr) [sExt64 chunk_id - 1]         | otherwise =-            copyDWIMFix (paramName p) [] (Var arr) [group_offset + sExt64 block_id - 1]+            copyDWIMFix (paramName p) [] (Var arr) [block_offset + sExt64 chunk_id - 1] -  doInBlockScan seg_flag ltid_in_bounds lam+  doInChunkScan seg_flag ltid_in_bounds lam   barrier -  let is_first_block = block_id .==. 0+  let is_first_block = chunk_id .==. 0   when array_scan $ do     sComment "save correct values for first block" $       sWhen is_first_block $         forM_ (zip x_params arrs) $ \(x, arr) ->           unless (isPrimParam x) $-            copyDWIMFix arr [arrs_full_size + group_offset + sExt64 block_size + ltid] (Var $ paramName x) []+            copyDWIMFix arr [arrs_full_size + block_offset + sExt64 chunk_size + ltid] (Var $ paramName x) []      barrier -  let last_in_block = in_block_id .==. block_size - 1+  let last_in_block = in_chunk_id .==. chunk_size - 1   sComment "last thread of block 'i' writes its result to offset 'i'" $     sWhen (last_in_block .&&. ltid_in_bounds) $       everythingVolatile $@@ -494,10 +493,10 @@   let first_block_seg_flag = do         flag_true <- seg_flag         Just $ \from to ->-          flag_true (from * block_size + block_size - 1) (to * block_size + block_size - 1)+          flag_true (from * chunk_size + chunk_size - 1) (to * chunk_size + chunk_size - 1)   sComment     "scan the first block, after which offset 'i' contains carry-in for block 'i+1'"-    $ doInBlockScan first_block_seg_flag (is_first_block .&&. ltid_in_bounds) renamed_lam+    $ doInChunkScan first_block_seg_flag (is_first_block .&&. ltid_in_bounds) renamed_lam    errorsync @@ -508,9 +507,9 @@           unless (isPrimParam x) $             copyDWIMFix               arr-              [arrs_full_size + group_offset + ltid]+              [arrs_full_size + block_offset + ltid]               (Var arr)-              [arrs_full_size + group_offset + sExt64 block_size + ltid]+              [arrs_full_size + block_offset + sExt64 chunk_size + ltid]      barrier @@ -526,7 +525,7 @@             sUnless no_carry_in $ compileBody' x_params $ lambdaBody lam         | Just flag_true <- seg_flag = do             inactive <--              dPrimVE "inactive" $ flag_true (block_id * block_size - 1) ltid32+              dPrimVE "inactive" $ flag_true (chunk_id * chunk_size - 1) ltid32             sUnless no_carry_in . sWhen inactive . forM_ (zip x_params y_params) $ \(x, y) ->               copyDWIM (paramName x) [] (Var (paramName y)) []             -- The convoluted control flow is to ensure all threads@@ -552,26 +551,26 @@       forM_ (zip3 x_params y_params arrs) $ \(x, y, arr) ->         if isPrimParam y           then copyDWIMFix arr [ltid] (Var $ paramName y) []-          else copyDWIMFix (paramName x) [] (Var arr) [arrs_full_size + group_offset + ltid]+          else copyDWIMFix (paramName x) [] (Var arr) [arrs_full_size + block_offset + ltid]    barrier -groupReduce ::+blockReduce ::   Imp.TExp Int32 ->   Lambda GPUMem ->   [VName] ->   InKernelGen ()-groupReduce w lam arrs = do+blockReduce w lam arrs = do   offset <- dPrim "offset" int32-  groupReduceWithOffset offset w lam arrs+  blockReduceWithOffset offset w lam arrs -groupReduceWithOffset ::+blockReduceWithOffset ::   TV Int32 ->   Imp.TExp Int32 ->   Lambda GPUMem ->   [VName] ->   InKernelGen ()-groupReduceWithOffset offset w lam arrs = do+blockReduceWithOffset offset w lam arrs = do   constants <- kernelConstants <$> askEnv    let local_tid = kernelLocalThreadId constants@@ -619,10 +618,10 @@       in_wave_reduce = everythingVolatile do_reduce        wave_size = kernelWaveSize constants-      group_size = kernelGroupSize constants+      tblock_size = kernelBlockSize constants       wave_id = local_tid `quot` wave_size       in_wave_id = local_tid - wave_id * wave_size-      num_waves = (sExt32 group_size + wave_size - 1) `quot` wave_size+      num_waves = (sExt32 tblock_size + wave_size - 1) `quot` wave_size       arg_in_bounds = local_tid + tvExp offset .<. w        doing_in_wave_reductions =@@ -919,7 +918,7 @@       case t of         Array {} -> pure Nothing         Acc {} -> pure Nothing-        Mem (Space "local") -> pure Nothing+        Mem (Space "shared") -> pure Nothing         Mem {} -> pure $ Just $ Imp.MemoryUse var         Prim bt ->           isConstExp vtable (Imp.var var bt) >>= \case@@ -948,30 +947,30 @@     hasExp (MemVar e _) = e  kernelInitialisationSimple ::-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   CallKernelGen (KernelConstants, InKernelGen ())-kernelInitialisationSimple num_groups group_size = do+kernelInitialisationSimple num_tblocks tblock_size = do   global_tid <- newVName "global_tid"   local_tid <- newVName "local_tid"-  group_id <- newVName "group_tid"+  tblock_id <- newVName "block_id"   wave_size <- newVName "wave_size"-  inner_group_size <- newVName "group_size"-  let num_groups' = Imp.pe64 (unCount num_groups)-      group_size' = Imp.pe64 (unCount group_size)+  inner_tblock_size <- newVName "tblock_size"+  let num_tblocks' = Imp.pe64 (unCount num_tblocks)+      tblock_size' = Imp.pe64 (unCount tblock_size)       constants =         KernelConstants           { kernelGlobalThreadId = Imp.le32 global_tid,             kernelLocalThreadId = Imp.le32 local_tid,-            kernelGroupId = Imp.le32 group_id,+            kernelBlockId = Imp.le32 tblock_id,             kernelGlobalThreadIdVar = global_tid,             kernelLocalThreadIdVar = local_tid,-            kernelNumGroupsCount = num_groups,-            kernelGroupSizeCount = group_size,-            kernelGroupIdVar = group_id,-            kernelNumGroups = num_groups',-            kernelGroupSize = group_size',-            kernelNumThreads = sExt32 (group_size' * num_groups'),+            kernelNumBlocksCount = num_tblocks,+            kernelBlockSizeCount = tblock_size,+            kernelBlockIdVar = tblock_id,+            kernelNumBlocks = num_tblocks',+            kernelBlockSize = tblock_size',+            kernelNumThreads = sExt32 (tblock_size' * num_tblocks'),             kernelWaveSize = Imp.le32 wave_size,             kernelLocalIdMap = mempty,             kernelChunkItersMap = mempty@@ -979,15 +978,15 @@    let set_constants = do         dPrim_ local_tid int32-        dPrim_ inner_group_size int64+        dPrim_ inner_tblock_size int64         dPrim_ wave_size int32-        dPrim_ group_id int32+        dPrim_ tblock_id int32          sOp (Imp.GetLocalId local_tid 0)-        sOp (Imp.GetLocalSize inner_group_size 0)+        sOp (Imp.GetLocalSize inner_tblock_size 0)         sOp (Imp.GetLockstepWidth wave_size)-        sOp (Imp.GetGroupId group_id 0)-        dPrimV_ global_tid $ le32 group_id * le32 inner_group_size + le32 local_tid+        sOp (Imp.GetBlockId tblock_id 0)+        dPrimV_ global_tid $ le32 tblock_id * le32 inner_tblock_size + le32 local_tid    pure (constants, set_constants) @@ -1010,23 +1009,23 @@   localDefaultSpace (Imp.Space "global") . localVTable (M.map globalMemory)   where     globalMemory (MemVar _ entry)-      | entryMemSpace entry /= Space "local" =+      | entryMemSpace entry /= Space "shared" =           MemVar Nothing entry {entryMemSpace = Imp.Space "global"}     globalMemory entry =       entry -simpleKernelGroups ::+simpleKernelBlocks ::   Imp.TExp Int64 ->   Imp.TExp Int64 ->-  CallKernelGen (Imp.TExp Int32, Count NumGroups SubExp, Count GroupSize SubExp)-simpleKernelGroups max_num_groups kernel_size = do-  group_size <- dPrim "group_size" int64+  CallKernelGen (Imp.TExp Int32, Count NumBlocks SubExp, Count BlockSize SubExp)+simpleKernelBlocks max_num_tblocks kernel_size = do+  tblock_size <- dPrim "tblock_size" int64   fname <- askFunction-  let group_size_key = keyWithEntryPoint fname $ nameFromString $ prettyString $ tvVar group_size-  sOp $ Imp.GetSize (tvVar group_size) group_size_key Imp.SizeGroup-  virt_num_groups <- dPrimVE "virt_num_groups" $ kernel_size `divUp` tvExp group_size-  num_groups <- dPrimV "num_groups" $ virt_num_groups `sMin64` max_num_groups-  pure (sExt32 virt_num_groups, Count $ tvSize num_groups, Count $ tvSize group_size)+  let tblock_size_key = keyWithEntryPoint fname $ nameFromString $ prettyString $ tvVar tblock_size+  sOp $ Imp.GetSize (tvVar tblock_size) tblock_size_key Imp.SizeThreadBlock+  virt_num_tblocks <- dPrimVE "virt_num_tblocks" $ kernel_size `divUp` tvExp tblock_size+  num_tblocks <- dPrimV "num_tblocks" $ virt_num_tblocks `sMin64` max_num_tblocks+  pure (sExt32 virt_num_tblocks, Count $ tvSize num_tblocks, Count $ tvSize tblock_size)  simpleKernelConstants ::   Imp.TExp Int64 ->@@ -1038,32 +1037,32 @@ simpleKernelConstants kernel_size desc = do   -- For performance reasons, codegen assumes that the thread count is   -- never more than will fit in an i32.  This means we need to cap-  -- the number of groups here.  The cap is set much higher than any+  -- the number of blocks here.  The cap is set much higher than any   -- GPU will possibly need.  Feel free to come back and laugh at me   -- in the future.-  let max_num_groups = 1024 * 1024+  let max_num_tblocks = 1024 * 1024   thread_gtid <- newVName $ desc ++ "_gtid"   thread_ltid <- newVName $ desc ++ "_ltid"-  group_id <- newVName $ desc ++ "_gid"-  inner_group_size <- newVName "group_size"-  (virt_num_groups, num_groups, group_size) <--    simpleKernelGroups max_num_groups kernel_size-  let group_size' = Imp.pe64 $ unCount group_size-      num_groups' = Imp.pe64 $ unCount num_groups+  tblock_id <- newVName $ desc ++ "_gid"+  inner_tblock_size <- newVName "tblock_size"+  (virt_num_tblocks, num_tblocks, tblock_size) <-+    simpleKernelBlocks max_num_tblocks kernel_size+  let tblock_size' = Imp.pe64 $ unCount tblock_size+      num_tblocks' = Imp.pe64 $ unCount num_tblocks        constants =         KernelConstants           { kernelGlobalThreadId = Imp.le32 thread_gtid,             kernelLocalThreadId = Imp.le32 thread_ltid,-            kernelGroupId = Imp.le32 group_id,+            kernelBlockId = Imp.le32 tblock_id,             kernelGlobalThreadIdVar = thread_gtid,             kernelLocalThreadIdVar = thread_ltid,-            kernelGroupIdVar = group_id,-            kernelNumGroupsCount = num_groups,-            kernelGroupSizeCount = group_size,-            kernelNumGroups = num_groups',-            kernelGroupSize = group_size',-            kernelNumThreads = sExt32 (group_size' * num_groups'),+            kernelBlockIdVar = tblock_id,+            kernelNumBlocksCount = num_tblocks,+            kernelBlockSizeCount = tblock_size,+            kernelNumBlocks = num_tblocks',+            kernelBlockSize = tblock_size',+            kernelNumThreads = sExt32 (tblock_size' * num_tblocks'),             kernelWaveSize = 0,             kernelLocalIdMap = mempty,             kernelChunkItersMap = mempty@@ -1071,51 +1070,51 @@        wrapKernel m = do         dPrim_ thread_ltid int32-        dPrim_ inner_group_size int64-        dPrim_ group_id int32+        dPrim_ inner_tblock_size int64+        dPrim_ tblock_id int32         sOp (Imp.GetLocalId thread_ltid 0)-        sOp (Imp.GetLocalSize inner_group_size 0)-        sOp (Imp.GetGroupId group_id 0)-        dPrimV_ thread_gtid $ le32 group_id * le32 inner_group_size + le32 thread_ltid-        virtualiseGroups SegVirt virt_num_groups $ \virt_group_id -> do+        sOp (Imp.GetLocalSize inner_tblock_size 0)+        sOp (Imp.GetBlockId tblock_id 0)+        dPrimV_ thread_gtid $ le32 tblock_id * le32 inner_tblock_size + le32 thread_ltid+        virtualiseBlocks SegVirt virt_num_tblocks $ \virt_tblock_id -> do           global_tid <-             dPrimVE "global_tid" $-              sExt64 virt_group_id * sExt64 (le32 inner_group_size)+              sExt64 virt_tblock_id * sExt64 (le32 inner_tblock_size)                 + sExt64 (kernelLocalThreadId constants)           m global_tid    pure (wrapKernel, constants) --- | For many kernels, we may not have enough physical groups to cover--- the logical iteration space.  Some groups thus have to perform+-- | For many kernels, we may not have enough physical blocks to cover+-- the logical iteration space.  Some blocks thus have to perform -- double duty; we put an outer loop to accomplish this.  The -- advantage over just launching a bazillion threads is that the cost -- of memory expansion should be proportional to the number of -- *physical* threads (hardware parallelism), not the amount of -- application parallelism.-virtualiseGroups ::+virtualiseBlocks ::   SegVirt ->   Imp.TExp Int32 ->   (Imp.TExp Int32 -> InKernelGen ()) ->   InKernelGen ()-virtualiseGroups SegVirt required_groups m = do+virtualiseBlocks SegVirt required_blocks m = do   constants <- kernelConstants <$> askEnv-  phys_group_id <- dPrim "phys_group_id" int32-  sOp $ Imp.GetGroupId (tvVar phys_group_id) 0+  phys_tblock_id <- dPrim "phys_tblock_id" int32+  sOp $ Imp.GetBlockId (tvVar phys_tblock_id) 0   iterations <-     dPrimVE "iterations" $-      (required_groups - tvExp phys_group_id) `divUp` sExt32 (kernelNumGroups constants)+      (required_blocks - tvExp phys_tblock_id) `divUp` sExt32 (kernelNumBlocks constants)    sFor "i" iterations $ \i -> do     m . tvExp       =<< dPrimV-        "virt_group_id"-        (tvExp phys_group_id + i * sExt32 (kernelNumGroups constants))-    -- Make sure the virtual group is actually done before we let-    -- another virtual group have its way with it.+        "virt_tblock_id"+        (tvExp phys_tblock_id + i * sExt32 (kernelNumBlocks constants))+    -- Make sure the virtual block is actually done before we let+    -- another virtual block have its way with it.     sOp $ Imp.ErrorSync Imp.FenceGlobal-virtualiseGroups _ _ m = do-  gid <- kernelGroupIdVar . kernelConstants <$> askEnv+virtualiseBlocks _ _ m = do+  gid <- kernelBlockIdVar . kernelConstants <$> askEnv   m $ Imp.le32 gid  -- | Various extra configuration of the kernel being generated.@@ -1123,11 +1122,11 @@   { -- | Can this kernel execute correctly even if previous kernels failed?     kAttrFailureTolerant :: Bool,     -- | Does whatever launch this kernel check for local memory capacity itself?-    kAttrCheckLocalMemory :: Bool,-    -- | Number of groups.-    kAttrNumGroups :: Count NumGroups SubExp,-    -- | Group size.-    kAttrGroupSize :: Count GroupSize SubExp,+    kAttrCheckSharedMemory :: Bool,+    -- | Number of blocks.+    kAttrNumBlocks :: Count NumBlocks SubExp,+    -- | Block size.+    kAttrBlockSize :: Count BlockSize SubExp,     -- | Variables that are specially in scope inside the kernel.     -- Operationally, these will be available at kernel compile time     -- (which happens at run-time, with access to machine-specific@@ -1137,15 +1136,15 @@  -- | The default kernel attributes. defKernelAttrs ::-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   KernelAttrs-defKernelAttrs num_groups group_size =+defKernelAttrs num_tblocks tblock_size =   KernelAttrs     { kAttrFailureTolerant = False,-      kAttrCheckLocalMemory = True,-      kAttrNumGroups = num_groups,-      kAttrGroupSize = group_size,+      kAttrCheckSharedMemory = True,+      kAttrNumBlocks = num_tblocks,+      kAttrBlockSize = tblock_size,       kAttrConstExps = mempty     } @@ -1158,23 +1157,23 @@   pure v  -- | Compute kernel attributes from 'SegLevel'; including synthesising--- group-size and thread count if no grid is provided.+-- block-size and thread count if no grid is provided. lvlKernelAttrs :: SegLevel -> CallKernelGen KernelAttrs lvlKernelAttrs lvl =   case lvl of     SegThread _ Nothing -> mkGrid-    SegThread _ (Just (KernelGrid num_groups group_size)) ->-      pure $ defKernelAttrs num_groups group_size-    SegGroup _ Nothing -> mkGrid-    SegGroup _ (Just (KernelGrid num_groups group_size)) ->-      pure $ defKernelAttrs num_groups group_size-    SegThreadInGroup {} ->-      error "lvlKernelAttrs: SegThreadInGroup"+    SegThread _ (Just (KernelGrid num_tblocks tblock_size)) ->+      pure $ defKernelAttrs num_tblocks tblock_size+    SegBlock _ Nothing -> mkGrid+    SegBlock _ (Just (KernelGrid num_tblocks tblock_size)) ->+      pure $ defKernelAttrs num_tblocks tblock_size+    SegThreadInBlock {} ->+      error "lvlKernelAttrs: SegThreadInBlock"   where     mkGrid = do-      group_size <- getSize "group_size" Imp.SizeGroup-      num_groups <- getSize "num_groups" Imp.SizeNumGroups-      pure $ defKernelAttrs (Count $ tvSize num_groups) (Count $ tvSize group_size)+      tblock_size <- getSize "tblock_size" Imp.SizeThreadBlock+      num_tblocks <- getSize "num_tblocks" Imp.SizeGrid+      pure $ defKernelAttrs (Count $ tvSize num_tblocks) (Count $ tvSize tblock_size)  sKernel ::   Operations GPUMem KernelEnv Imp.KernelOp ->@@ -1186,7 +1185,7 @@   CallKernelGen () sKernel ops flatf name v attrs f = do   (constants, set_constants) <--    kernelInitialisationSimple (kAttrNumGroups attrs) (kAttrGroupSize attrs)+    kernelInitialisationSimple (kAttrNumBlocks attrs) (kAttrBlockSize attrs)   name' <- nameForFun $ name ++ "_" ++ show (baseTag v)   sKernelOp attrs constants ops name' $ do     set_constants@@ -1212,21 +1211,21 @@   HostEnv atomics _ locks <- askEnv   body <- makeAllMemoryGlobal $ subImpM_ (KernelEnv atomics constants locks) ops m   uses <- computeKernelUses body $ M.keys $ kAttrConstExps attrs-  group_size <- onGroupSize $ kernelGroupSize constants+  tblock_size <- onBlockSize $ kernelBlockSize constants   emit . Imp.Op . Imp.CallKernel $     Imp.Kernel       { Imp.kernelBody = body,         Imp.kernelUses = uses <> map constToUse (M.toList (kAttrConstExps attrs)),-        Imp.kernelNumGroups = [untyped $ kernelNumGroups constants],-        Imp.kernelGroupSize = [group_size],+        Imp.kernelNumBlocks = [untyped $ kernelNumBlocks constants],+        Imp.kernelBlockSize = [tblock_size],         Imp.kernelName = name,         Imp.kernelFailureTolerant = kAttrFailureTolerant attrs,-        Imp.kernelCheckLocalMemory = kAttrCheckLocalMemory attrs+        Imp.kernelCheckSharedMemory = kAttrCheckSharedMemory attrs       }   where     -- Figure out if this expression actually corresponds to a     -- KernelConst.-    onGroupSize e = do+    onBlockSize e = do       vtable <- getVTable       x <- isConstExp vtable $ untyped e       pure $@@ -1248,8 +1247,8 @@   where     attrs =       ( defKernelAttrs-          (kernelNumGroupsCount constants)-          (kernelGroupSizeCount constants)+          (kernelNumBlocksCount constants)+          (kernelBlockSizeCount constants)       )         { kAttrFailureTolerant = tol         }@@ -1261,7 +1260,7 @@       opsExpCompiler = compileThreadExp,       opsStmsCompiler = \_ -> defCompileStms mempty,       opsAllocCompilers =-        M.fromList [(Space "local", allocLocal)]+        M.fromList [(Space "shared", allocLocal)]     }  -- | Perform a Replicate with a kernel.
+ src/Futhark/CodeGen/ImpGen/GPU/Block.hs view
@@ -0,0 +1,743 @@+{-# LANGUAGE TypeFamilies #-}++-- | Generation of kernels with block-level bodies.+module Futhark.CodeGen.ImpGen.GPU.Block+  ( sKernelBlock,+    compileBlockResult,+    blockOperations,++    -- * Precomputation+    Precomputed,+    precomputeConstants,+    precomputedConstants,+    atomicUpdateLocking,+  )+where++import Control.Monad+import Data.Bifunctor+import Data.List (partition, zip4)+import Data.Map.Strict qualified as M+import Data.Maybe+import Data.Set qualified as S+import Futhark.CodeGen.ImpCode.GPU qualified as Imp+import Futhark.CodeGen.ImpGen+import Futhark.CodeGen.ImpGen.GPU.Base+import Futhark.Construct (fullSliceNum)+import Futhark.Error+import Futhark.IR.GPUMem+import Futhark.IR.Mem.LMAD qualified as LMAD+import Futhark.Transform.Rename+import Futhark.Util (chunks, mapAccumLM, takeLast)+import Futhark.Util.IntegralExp (divUp, rem)+import Prelude hiding (quot, rem)++-- | @flattenArray k flat arr@ flattens the outer @k@ dimensions of+-- @arr@ to @flat@.  (Make sure @flat@ is the sum of those dimensions+-- or you'll have a bad time.)+flattenArray :: Int -> TV Int64 -> VName -> ImpM rep r op VName+flattenArray k flat arr = do+  ArrayEntry arr_loc pt <- lookupArray arr+  let flat_shape = Shape $ Var (tvVar flat) : drop k (memLocShape arr_loc)+  sArray (baseString arr ++ "_flat") pt flat_shape (memLocName arr_loc) $+    fromMaybe (error "flattenArray") $+      LMAD.reshape (memLocLMAD arr_loc) (map pe64 $ shapeDims flat_shape)++sliceArray :: Imp.TExp Int64 -> TV Int64 -> VName -> ImpM rep r op VName+sliceArray start size arr = do+  MemLoc mem _ ixfun <- entryArrayLoc <$> lookupArray arr+  arr_t <- lookupType arr+  let slice =+        fullSliceNum+          (map Imp.pe64 (arrayDims arr_t))+          [DimSlice start (tvExp size) 1]+  sArray+    (baseString arr ++ "_chunk")+    (elemType arr_t)+    (arrayShape arr_t `setOuterDim` Var (tvVar size))+    mem+    $ LMAD.slice ixfun slice++-- | @applyLambda lam dests args@ emits code that:+--+-- 1. Binds each parameter of @lam@ to the corresponding element of+--    @args@, interpreted as a (name,slice) pair (as in 'copyDWIM').+--    Use an empty list for a scalar.+--+-- 2. Executes the body of @lam@.+--+-- 3. Binds the t'SubExp's that are the 'Result' of @lam@ to the+-- provided @dest@s, again interpreted as the destination for a+-- 'copyDWIM'.+applyLambda ::+  (Mem rep inner) =>+  Lambda rep ->+  [(VName, [DimIndex (Imp.TExp Int64)])] ->+  [(SubExp, [DimIndex (Imp.TExp Int64)])] ->+  ImpM rep r op ()+applyLambda lam dests args = do+  dLParams $ lambdaParams lam+  forM_ (zip (lambdaParams lam) args) $ \(p, (arg, arg_slice)) ->+    copyDWIM (paramName p) [] arg arg_slice+  compileStms mempty (bodyStms $ lambdaBody lam) $ do+    let res = map resSubExp $ bodyResult $ lambdaBody lam+    forM_ (zip dests res) $ \((dest, dest_slice), se) ->+      copyDWIM dest dest_slice se []++-- | As applyLambda, but first rename the names in the lambda.  This+-- makes it safe to apply it in multiple places.  (It might be safe+-- anyway, but you have to be more careful - use this if you are in+-- doubt.)+applyRenamedLambda ::+  (Mem rep inner) =>+  Lambda rep ->+  [(VName, [DimIndex (Imp.TExp Int64)])] ->+  [(SubExp, [DimIndex (Imp.TExp Int64)])] ->+  ImpM rep r op ()+applyRenamedLambda lam dests args = do+  lam_renamed <- renameLambda lam+  applyLambda lam_renamed dests args++blockChunkLoop ::+  Imp.TExp Int32 ->+  (Imp.TExp Int32 -> TV Int64 -> InKernelGen ()) ->+  InKernelGen ()+blockChunkLoop w m = do+  constants <- kernelConstants <$> askEnv+  let max_chunk_size = sExt32 $ kernelBlockSize constants+  num_chunks <- dPrimVE "num_chunks" $ w `divUp` max_chunk_size+  sFor "chunk_i" num_chunks $ \chunk_i -> do+    chunk_start <-+      dPrimVE "chunk_start" $ chunk_i * max_chunk_size+    chunk_end <-+      dPrimVE "chunk_end" $ sMin32 w (chunk_start + max_chunk_size)+    chunk_size <-+      dPrimV "chunk_size" $ sExt64 $ chunk_end - chunk_start+    m chunk_start chunk_size++virtualisedBlockScan ::+  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->+  Imp.TExp Int32 ->+  Lambda GPUMem ->+  [VName] ->+  InKernelGen ()+virtualisedBlockScan seg_flag w lam arrs = do+  blockChunkLoop w $ \chunk_start chunk_size -> do+    constants <- kernelConstants <$> askEnv+    let ltid = kernelLocalThreadId constants+        crosses_segment =+          case seg_flag of+            Nothing -> false+            Just flag_true ->+              flag_true (sExt32 (chunk_start - 1)) (sExt32 chunk_start)+    sComment "possibly incorporate carry" $+      sWhen (chunk_start .>. 0 .&&. ltid .==. 0 .&&. bNot crosses_segment) $ do+        carry_idx <- dPrimVE "carry_idx" $ sExt64 chunk_start - 1+        applyRenamedLambda+          lam+          (map (,[DimFix $ sExt64 chunk_start]) arrs)+          ( map ((,[DimFix carry_idx]) . Var) arrs+              ++ map ((,[DimFix $ sExt64 chunk_start]) . Var) arrs+          )++    arrs_chunks <- mapM (sliceArray (sExt64 chunk_start) chunk_size) arrs++    sOp $ Imp.ErrorSync Imp.FenceLocal++    blockScan seg_flag (sExt64 w) (tvExp chunk_size) lam arrs_chunks++copyInBlock :: CopyCompiler GPUMem KernelEnv Imp.KernelOp+copyInBlock pt destloc srcloc = do+  dest_space <- entryMemSpace <$> lookupMemory (memLocName destloc)+  src_space <- entryMemSpace <$> lookupMemory (memLocName srcloc)++  let src_lmad = memLocLMAD srcloc+      dims = LMAD.shape src_lmad+      rank = length dims++  case (dest_space, src_space) of+    (ScalarSpace destds _, ScalarSpace srcds _) -> do+      let fullDim d = DimSlice 0 d 1+          destslice' =+            Slice $+              replicate (rank - length destds) (DimFix 0)+                ++ takeLast (length destds) (map fullDim dims)+          srcslice' =+            Slice $+              replicate (rank - length srcds) (DimFix 0)+                ++ takeLast (length srcds) (map fullDim dims)+      lmadCopy+        pt+        (sliceMemLoc destloc destslice')+        (sliceMemLoc srcloc srcslice')+    _ -> do+      blockCoverSpace (map sExt32 dims) $ \is ->+        lmadCopy+          pt+          (sliceMemLoc destloc (Slice $ map (DimFix . sExt64) is))+          (sliceMemLoc srcloc (Slice $ map (DimFix . sExt64) is))+      sOp $ Imp.Barrier Imp.FenceLocal++localThreadIDs :: [SubExp] -> InKernelGen [Imp.TExp Int64]+localThreadIDs dims = do+  ltid <- sExt64 . kernelLocalThreadId . kernelConstants <$> askEnv+  let dims' = map pe64 dims+  maybe (dIndexSpace' "ltid" dims' ltid) (pure . map sExt64)+    . M.lookup dims+    . kernelLocalIdMap+    . kernelConstants+    =<< askEnv++partitionSeqDims :: SegSeqDims -> SegSpace -> ([(VName, SubExp)], [(VName, SubExp)])+partitionSeqDims (SegSeqDims seq_is) space =+  bimap (map fst) (map fst) $+    partition ((`elem` seq_is) . snd) (zip (unSegSpace space) [0 ..])++compileFlatId :: SegSpace -> InKernelGen ()+compileFlatId space = do+  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+  dPrimV_ (segFlat space) ltid++-- Construct the necessary lock arrays for an intra-block histogram.+prepareIntraBlockSegHist ::+  Shape ->+  Count BlockSize SubExp ->+  [HistOp GPUMem] ->+  InKernelGen [[Imp.TExp Int64] -> InKernelGen ()]+prepareIntraBlockSegHist segments tblock_size =+  fmap snd . mapAccumLM onOp Nothing+  where+    onOp l op = do+      constants <- kernelConstants <$> askEnv+      atomicBinOp <- kernelAtomics <$> askEnv++      let local_subhistos = histDest op++      case (l, atomicUpdateLocking atomicBinOp $ histOp op) of+        (_, AtomicPrim f) -> pure (l, f (Space "shared") local_subhistos)+        (_, AtomicCAS f) -> pure (l, f (Space "shared") local_subhistos)+        (Just l', AtomicLocking f) -> pure (l, f l' (Space "shared") local_subhistos)+        (Nothing, AtomicLocking f) -> do+          locks <- newVName "locks"++          let num_locks = pe64 $ unCount tblock_size+              dims = map pe64 $ shapeDims (segments <> histOpShape op <> histShape op)+              l' = Locking locks 0 1 0 (pure . (`rem` num_locks) . flattenIndex dims)+              locks_t = Array int32 (Shape [unCount tblock_size]) NoUniqueness++          locks_mem <- sAlloc "locks_mem" (typeSize locks_t) $ Space "shared"+          dArray locks int32 (arrayShape locks_t) locks_mem $+            LMAD.iota 0 . map pe64 . arrayDims $+              locks_t++          sComment "All locks start out unlocked" $+            blockCoverSpace [kernelBlockSize constants] $ \is ->+              copyDWIMFix locks is (intConst Int32 0) []++          pure (Just l', f l' (Space "shared") local_subhistos)++blockCoverSegSpace :: SegVirt -> SegSpace -> InKernelGen () -> InKernelGen ()+blockCoverSegSpace virt space m = do+  let (ltids, dims) = unzip $ unSegSpace space+      dims' = map pe64 dims++  constants <- kernelConstants <$> askEnv+  let tblock_size = kernelBlockSize constants+  -- Maybe we can statically detect that this is actually a+  -- SegNoVirtFull and generate ever-so-slightly simpler code.+  let virt' = if dims' == [tblock_size] then SegNoVirtFull (SegSeqDims []) else virt+  case virt' of+    SegVirt -> do+      iters <- M.lookup dims . kernelChunkItersMap . kernelConstants <$> askEnv+      case iters of+        Nothing -> do+          iterations <- dPrimVE "iterations" $ product $ map sExt32 dims'+          blockLoop iterations $ \i -> do+            dIndexSpace (zip ltids dims') $ sExt64 i+            m+        Just num_chunks -> localOps threadOperations $ do+          let ltid = kernelLocalThreadId constants+          sFor "chunk_i" num_chunks $ \chunk_i -> do+            i <- dPrimVE "i" $ chunk_i * sExt32 tblock_size + ltid+            dIndexSpace (zip ltids dims') $ sExt64 i+            sWhen (inBounds (Slice (map (DimFix . le64) ltids)) dims') m+    SegNoVirt -> localOps threadOperations $ do+      zipWithM_ dPrimV_ ltids =<< localThreadIDs dims+      sWhen (isActive $ zip ltids dims) m+    SegNoVirtFull seq_dims -> do+      let ((ltids_seq, dims_seq), (ltids_par, dims_par)) =+            bimap unzip unzip $ partitionSeqDims seq_dims space+      sLoopNest (Shape dims_seq) $ \is_seq -> do+        zipWithM_ dPrimV_ ltids_seq is_seq+        localOps threadOperations $ do+          zipWithM_ dPrimV_ ltids_par =<< localThreadIDs dims_par+          m++compileBlockExp :: ExpCompiler GPUMem KernelEnv Imp.KernelOp+compileBlockExp (Pat [pe]) (BasicOp (Opaque _ se)) =+  -- Cannot print in GPU code.+  copyDWIM (patElemName pe) [] se []+-- The static arrays stuff does not work inside kernels.+compileBlockExp (Pat [dest]) (BasicOp (ArrayLit es _)) =+  forM_ (zip [0 ..] es) $ \(i, e) ->+    copyDWIMFix (patElemName dest) [fromIntegral (i :: Int64)] e []+compileBlockExp _ (BasicOp (UpdateAcc acc is vs)) = do+  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+  sWhen (ltid .==. 0) $ updateAcc acc is vs+  sOp $ Imp.Barrier Imp.FenceLocal+compileBlockExp (Pat [dest]) (BasicOp (Replicate ds se)) | ds /= mempty = do+  flat <- newVName "rep_flat"+  is <- replicateM (arrayRank dest_t) (newVName "rep_i")+  let is' = map le64 is+  blockCoverSegSpace SegVirt (SegSpace flat $ zip is $ arrayDims dest_t) $+    copyDWIMFix (patElemName dest) is' se (drop (shapeRank ds) is')+  sOp $ Imp.Barrier Imp.FenceLocal+  where+    dest_t = patElemType dest+compileBlockExp (Pat [dest]) (BasicOp (Iota n e s it)) = do+  n' <- toExp n+  e' <- toExp e+  s' <- toExp s+  blockLoop (TPrimExp n') $ \i' -> do+    x <-+      dPrimV "x" $+        TPrimExp $+          BinOpExp (Add it OverflowUndef) e' $+            BinOpExp (Mul it OverflowUndef) (untyped i') s'+    copyDWIMFix (patElemName dest) [i'] (Var (tvVar x)) []+  sOp $ Imp.Barrier Imp.FenceLocal++-- When generating code for a scalar in-place update, we must make+-- sure that only one thread performs the write.  When writing an+-- array, the block-level copy code will take care of doing the right+-- thing.+compileBlockExp (Pat [pe]) (BasicOp (Update safety _ slice se))+  | null $ sliceDims slice = do+      sOp $ Imp.Barrier Imp.FenceLocal+      ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+      sWhen (ltid .==. 0) $+        case safety of+          Unsafe -> write+          Safe -> sWhen (inBounds slice' dims) write+      sOp $ Imp.Barrier Imp.FenceLocal+  where+    slice' = fmap pe64 slice+    dims = map pe64 $ arrayDims $ patElemType pe+    write = copyDWIM (patElemName pe) (unSlice slice') se []+compileBlockExp dest e = do+  -- It is a messy to jump into control flow for error handling.+  -- Avoid that by always doing an error sync here.  Potential+  -- improvement: only do this if any errors are pending (this could+  -- also be handled in later codegen).+  when (doSync e) $ sOp $ Imp.ErrorSync Imp.FenceLocal+  defCompileExp dest e+  where+    doSync Loop {} = True+    doSync Match {} = True+    doSync _ = False++compileBlockOp :: OpCompiler GPUMem KernelEnv Imp.KernelOp+compileBlockOp pat (Alloc size space) =+  kernelAlloc pat size space+compileBlockOp pat (Inner (SegOp (SegMap lvl space _ body))) = do+  compileFlatId space++  blockCoverSegSpace (segVirt lvl) space $+    compileStms mempty (kernelBodyStms body) $+      zipWithM_ (compileThreadResult space) (patElems pat) $+        kernelBodyResult body+  sOp $ Imp.ErrorSync Imp.FenceLocal+compileBlockOp pat (Inner (SegOp (SegScan lvl space scans _ body))) = do+  compileFlatId space++  let (ltids, dims) = unzip $ unSegSpace space+      dims' = map pe64 dims++  blockCoverSegSpace (segVirt lvl) space $+    compileStms mempty (kernelBodyStms body) $+      forM_ (zip (patNames pat) $ kernelBodyResult body) $ \(dest, res) ->+        copyDWIMFix+          dest+          (map Imp.le64 ltids)+          (kernelResultSubExp res)+          []++  fence <- fenceForArrays $ patNames pat+  sOp $ Imp.ErrorSync fence++  let segment_size = last dims'+      crossesSegment from to =+        (sExt64 to - sExt64 from) .>. (sExt64 to `rem` segment_size)++  -- blockScan needs to treat the scan output as a one-dimensional+  -- array of scan elements, so we invent some new flattened arrays+  -- here.+  dims_flat <- dPrimV "dims_flat" $ product dims'+  let scan = head scans+      num_scan_results = length $ segBinOpNeutral scan+  arrs_flat <-+    mapM (flattenArray (length dims') dims_flat) $+      take num_scan_results $+        patNames pat++  case segVirt lvl of+    SegVirt ->+      virtualisedBlockScan+        (Just crossesSegment)+        (sExt32 $ tvExp dims_flat)+        (segBinOpLambda scan)+        arrs_flat+    _ ->+      blockScan+        (Just crossesSegment)+        (product dims')+        (product dims')+        (segBinOpLambda scan)+        arrs_flat+compileBlockOp pat (Inner (SegOp (SegRed lvl space ops _ body))) = do+  compileFlatId space++  let dims' = map pe64 dims+      mkTempArr t =+        sAllocArray "red_arr" (elemType t) (Shape dims <> arrayShape t) $ Space "shared"++  tmp_arrs <- mapM mkTempArr $ concatMap (lambdaReturnType . segBinOpLambda) ops+  blockCoverSegSpace (segVirt lvl) space $+    compileStms mempty (kernelBodyStms body) $ do+      let (red_res, map_res) =+            splitAt (segBinOpResults ops) $ kernelBodyResult body+      forM_ (zip tmp_arrs red_res) $ \(dest, res) ->+        copyDWIMFix dest (map Imp.le64 ltids) (kernelResultSubExp res) []+      zipWithM_ (compileThreadResult space) map_pes map_res++  sOp $ Imp.ErrorSync Imp.FenceLocal++  let tmps_for_ops = chunks (map (length . segBinOpNeutral) ops) tmp_arrs+  case segVirt lvl of+    SegVirt -> virtCase dims' tmps_for_ops+    _ -> nonvirtCase dims' tmps_for_ops+  where+    (ltids, dims) = unzip $ unSegSpace space+    (red_pes, map_pes) = splitAt (segBinOpResults ops) $ patElems pat++    virtCase [dim'] tmps_for_ops = do+      ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+      blockChunkLoop (sExt32 dim') $ \chunk_start chunk_size -> do+        sComment "possibly incorporate carry" $+          sWhen (chunk_start .>. 0 .&&. ltid .==. 0) $+            forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->+              applyRenamedLambda+                (segBinOpLambda op)+                (map (,[DimFix $ sExt64 chunk_start]) tmps)+                ( map ((,[]) . Var . patElemName) red_pes+                    ++ map ((,[DimFix $ sExt64 chunk_start]) . Var) tmps+                )++        sOp $ Imp.ErrorSync Imp.FenceLocal++        forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do+          tmps_chunks <- mapM (sliceArray (sExt64 chunk_start) chunk_size) tmps+          blockReduce (sExt32 (tvExp chunk_size)) (segBinOpLambda op) tmps_chunks++        sOp $ Imp.ErrorSync Imp.FenceLocal++        sComment "Save result of reduction." $+          forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->+            copyDWIMFix (patElemName pe) [] (Var arr) [sExt64 chunk_start]++    --+    virtCase dims' tmps_for_ops = do+      dims_flat <- dPrimV "dims_flat" $ product dims'+      let segment_size = last dims'+          crossesSegment from to =+            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` sExt64 segment_size)++      forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do+        tmps_flat <- mapM (flattenArray (length dims') dims_flat) tmps+        virtualisedBlockScan+          (Just crossesSegment)+          (sExt32 $ tvExp dims_flat)+          (segBinOpLambda op)+          tmps_flat++      sOp $ Imp.ErrorSync Imp.FenceLocal++      sComment "Save result of reduction." $+        forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->+          copyDWIM+            (patElemName pe)+            []+            (Var arr)+            (map (unitSlice 0) (init dims') ++ [DimFix $ last dims' - 1])++      sOp $ Imp.Barrier Imp.FenceLocal++    -- Nonsegmented case (or rather, a single segment) - this we can+    -- handle directly with a block-level reduction.+    nonvirtCase [dim'] tmps_for_ops = do+      forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->+        blockReduce (sExt32 dim') (segBinOpLambda op) tmps+      sOp $ Imp.ErrorSync Imp.FenceLocal+      sComment "Save result of reduction." $+        forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->+          copyDWIMFix (patElemName pe) [] (Var arr) [0]+      sOp $ Imp.ErrorSync Imp.FenceLocal++    -- Segmented intra-block reductions are turned into (regular)+    -- segmented scans.  It is possible that this can be done+    -- better, but at least this approach is simple.+    nonvirtCase dims' tmps_for_ops = do+      -- blockScan operates on flattened arrays.  This does not+      -- involve copying anything; merely playing with the index+      -- function.+      dims_flat <- dPrimV "dims_flat" $ product dims'+      let segment_size = last dims'+          crossesSegment from to =+            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` sExt64 segment_size)++      forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do+        tmps_flat <- mapM (flattenArray (length dims') dims_flat) tmps+        blockScan+          (Just crossesSegment)+          (product dims')+          (product dims')+          (segBinOpLambda op)+          tmps_flat++      sOp $ Imp.ErrorSync Imp.FenceLocal++      sComment "Save result of reduction." $+        forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->+          copyDWIM+            (patElemName pe)+            []+            (Var arr)+            (map (unitSlice 0) (init dims') ++ [DimFix $ last dims' - 1])++      sOp $ Imp.Barrier Imp.FenceLocal+compileBlockOp pat (Inner (SegOp (SegHist lvl space ops _ kbody))) = do+  compileFlatId space+  let (ltids, dims) = unzip $ unSegSpace space++  -- We don't need the red_pes, because it is guaranteed by our type+  -- rules that they occupy the same memory as the destinations for+  -- the ops.+  let num_red_res = length ops + sum (map (length . histNeutral) ops)+      (_red_pes, map_pes) =+        splitAt num_red_res $ patElems pat++  tblock_size <- kernelBlockSizeCount . kernelConstants <$> askEnv+  ops' <- prepareIntraBlockSegHist (Shape $ init dims) tblock_size ops++  -- Ensure that all locks have been initialised.+  sOp $ Imp.Barrier Imp.FenceLocal++  blockCoverSegSpace (segVirt lvl) space $+    compileStms mempty (kernelBodyStms kbody) $ do+      let (red_res, map_res) = splitAt num_red_res $ kernelBodyResult kbody+          (red_is, red_vs) = splitAt (length ops) $ map kernelResultSubExp red_res+      zipWithM_ (compileThreadResult space) map_pes map_res++      let vs_per_op = chunks (map (length . histDest) ops) red_vs++      forM_ (zip4 red_is vs_per_op ops' ops) $+        \(bin, op_vs, do_op, HistOp dest_shape _ _ _ shape lam) -> do+          let bin' = pe64 bin+              dest_shape' = map pe64 $ shapeDims dest_shape+              bin_in_bounds = inBounds (Slice [DimFix bin']) dest_shape'+              bin_is = map Imp.le64 (init ltids) ++ [bin']+              vs_params = takeLast (length op_vs) $ lambdaParams lam++          sComment "perform atomic updates" $+            sWhen bin_in_bounds $ do+              dLParams $ lambdaParams lam+              sLoopNest shape $ \is -> do+                forM_ (zip vs_params op_vs) $ \(p, v) ->+                  copyDWIMFix (paramName p) [] v is+                do_op (bin_is ++ is)++  sOp $ Imp.ErrorSync Imp.FenceLocal+compileBlockOp pat _ =+  compilerBugS $ "compileBlockOp: cannot compile rhs of binding " ++ prettyString pat++blockOperations :: Operations GPUMem KernelEnv Imp.KernelOp+blockOperations =+  (defaultOperations compileBlockOp)+    { opsCopyCompiler = copyInBlock,+      opsExpCompiler = compileBlockExp,+      opsStmsCompiler = \_ -> defCompileStms mempty,+      opsAllocCompilers =+        M.fromList [(Space "shared", allocLocal)]+    }++arrayInSharedMemory :: SubExp -> InKernelGen Bool+arrayInSharedMemory (Var name) = do+  res <- lookupVar name+  case res of+    ArrayVar _ entry ->+      (Space "shared" ==) . entryMemSpace+        <$> lookupMemory (memLocName (entryArrayLoc entry))+    _ -> pure False+arrayInSharedMemory Constant {} = pure False++sKernelBlock ::+  String ->+  VName ->+  KernelAttrs ->+  InKernelGen () ->+  CallKernelGen ()+sKernelBlock = sKernel blockOperations kernelBlockId++compileBlockResult ::+  SegSpace ->+  PatElem LetDecMem ->+  KernelResult ->+  InKernelGen ()+compileBlockResult _ pe (TileReturns _ [(w, per_block_elems)] what) = do+  n <- pe64 . arraySize 0 <$> lookupType what++  constants <- kernelConstants <$> askEnv+  let ltid = sExt64 $ kernelLocalThreadId constants+      offset =+        pe64 per_block_elems+          * sExt64 (kernelBlockId constants)++  -- Avoid loop for the common case where each thread is statically+  -- known to write at most one element.+  localOps threadOperations $+    if pe64 per_block_elems == kernelBlockSize constants+      then+        sWhen (ltid + offset .<. pe64 w) $+          copyDWIMFix (patElemName pe) [ltid + offset] (Var what) [ltid]+      else sFor "i" (n `divUp` kernelBlockSize constants) $ \i -> do+        j <- dPrimVE "j" $ kernelBlockSize constants * i + ltid+        sWhen (j + offset .<. pe64 w) $+          copyDWIMFix (patElemName pe) [j + offset] (Var what) [j]+compileBlockResult space pe (TileReturns _ dims what) = do+  let gids = map fst $ unSegSpace space+      out_tile_sizes = map (pe64 . snd) dims+      block_is = zipWith (*) (map Imp.le64 gids) out_tile_sizes+  local_is <- localThreadIDs $ map snd dims+  is_for_thread <-+    mapM (dPrimV "thread_out_index") $+      zipWith (+) block_is local_is++  localOps threadOperations $+    sWhen (isActive $ zip (map tvVar is_for_thread) $ map fst dims) $+      copyDWIMFix (patElemName pe) (map tvExp is_for_thread) (Var what) local_is+compileBlockResult space pe (RegTileReturns _ dims_n_tiles what) = do+  constants <- kernelConstants <$> askEnv++  let gids = map fst $ unSegSpace space+      (dims, block_tiles, reg_tiles) = unzip3 dims_n_tiles+      block_tiles' = map pe64 block_tiles+      reg_tiles' = map pe64 reg_tiles++  -- Which block tile is this block responsible for?+  let block_tile_is = map Imp.le64 gids++  -- Within the block tile, which register tile is this thread+  -- responsible for?+  reg_tile_is <-+    dIndexSpace' "reg_tile_i" block_tiles' $ sExt64 $ kernelLocalThreadId constants++  -- Compute output array slice for the register tile belonging to+  -- this thread.+  let regTileSliceDim (block_tile, block_tile_i) (reg_tile, reg_tile_i) = do+        tile_dim_start <-+          dPrimVE "tile_dim_start" $+            reg_tile * (block_tile * block_tile_i + reg_tile_i)+        pure $ DimSlice tile_dim_start reg_tile 1+  reg_tile_slices <-+    Slice+      <$> zipWithM+        regTileSliceDim+        (zip block_tiles' block_tile_is)+        (zip reg_tiles' reg_tile_is)++  localOps threadOperations $+    sLoopNest (Shape reg_tiles) $ \is_in_reg_tile -> do+      let dest_is = fixSlice reg_tile_slices is_in_reg_tile+          src_is = reg_tile_is ++ is_in_reg_tile+      sWhen (foldl1 (.&&.) $ zipWith (.<.) dest_is $ map pe64 dims) $+        copyDWIMFix (patElemName pe) dest_is (Var what) src_is+compileBlockResult space pe (Returns _ _ what) = do+  constants <- kernelConstants <$> askEnv+  in_shared_memory <- arrayInSharedMemory what+  let gids = map (Imp.le64 . fst) $ unSegSpace space++  if not in_shared_memory+    then+      localOps threadOperations $+        sWhen (kernelLocalThreadId constants .==. 0) $+          copyDWIMFix (patElemName pe) gids what []+    else -- If the result of the block is an array in local memory, we+    -- store it by collective copying among all the threads of the+    -- block.  TODO: also do this if the array is in global memory+    -- (but this is a bit more tricky, synchronisation-wise).+      copyDWIMFix (patElemName pe) gids what []+compileBlockResult _ _ WriteReturns {} =+  compilerLimitationS "compileBlockResult: WriteReturns not handled yet."++-- | The sizes of nested iteration spaces in the kernel.+type SegOpSizes = S.Set [SubExp]++-- | Various useful precomputed information for block-level SegOps.+data Precomputed = Precomputed+  { pcSegOpSizes :: SegOpSizes,+    pcChunkItersMap :: M.Map [SubExp] (Imp.TExp Int32)+  }++-- | Find the sizes of nested parallelism in a t'SegOp' body.+segOpSizes :: Stms GPUMem -> SegOpSizes+segOpSizes = onStms+  where+    onStms = foldMap onStm+    onStm (Let _ _ (Op (Inner (SegOp op)))) =+      case segVirt $ segLevel op of+        SegNoVirtFull seq_dims ->+          S.singleton $ map snd $ snd $ partitionSeqDims seq_dims $ segSpace op+        _ -> S.singleton $ map snd $ unSegSpace $ segSpace op+    onStm (Let (Pat [pe]) _ (BasicOp (Replicate {}))) =+      S.singleton $ arrayDims $ patElemType pe+    onStm (Let (Pat [pe]) _ (BasicOp (Iota {}))) =+      S.singleton $ arrayDims $ patElemType pe+    onStm (Let (Pat [pe]) _ (BasicOp (Manifest {}))) =+      S.singleton $ arrayDims $ patElemType pe+    onStm (Let _ _ (Match _ cases defbody _)) =+      foldMap (onStms . bodyStms . caseBody) cases <> onStms (bodyStms defbody)+    onStm (Let _ _ (Loop _ _ body)) =+      onStms (bodyStms body)+    onStm _ = mempty++-- | Precompute various constants and useful information.+precomputeConstants :: Count BlockSize (Imp.TExp Int64) -> Stms GPUMem -> CallKernelGen Precomputed+precomputeConstants tblock_size stms = do+  let sizes = segOpSizes stms+  iters_map <- M.fromList <$> mapM mkMap (S.toList sizes)+  pure $ Precomputed sizes iters_map+  where+    mkMap dims = do+      let n = product $ map Imp.pe64 dims+      num_chunks <- dPrimVE "num_chunks" $ sExt32 $ n `divUp` unCount tblock_size+      pure (dims, num_chunks)++-- | Make use of various precomputed constants.+precomputedConstants :: Precomputed -> InKernelGen a -> InKernelGen a+precomputedConstants pre m = do+  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+  new_ids <- M.fromList <$> mapM (mkMap ltid) (S.toList (pcSegOpSizes pre))+  let f env =+        env+          { kernelConstants =+              (kernelConstants env)+                { kernelLocalIdMap = new_ids,+                  kernelChunkItersMap = pcChunkItersMap pre+                }+          }+  localEnv f m+  where+    mkMap ltid dims = do+      let dims' = map pe64 dims+      ids' <- dIndexSpace' "ltid_pre" dims' (sExt64 ltid)+      pure (dims, map sExt32 ids')
− src/Futhark/CodeGen/ImpGen/GPU/Group.hs
@@ -1,743 +0,0 @@-{-# LANGUAGE TypeFamilies #-}---- | Generation of kernels with group-level bodies.-module Futhark.CodeGen.ImpGen.GPU.Group-  ( sKernelGroup,-    compileGroupResult,-    groupOperations,--    -- * Precomputation-    Precomputed,-    precomputeConstants,-    precomputedConstants,-    atomicUpdateLocking,-  )-where--import Control.Monad-import Data.Bifunctor-import Data.List (partition, zip4)-import Data.Map.Strict qualified as M-import Data.Maybe-import Data.Set qualified as S-import Futhark.CodeGen.ImpCode.GPU qualified as Imp-import Futhark.CodeGen.ImpGen-import Futhark.CodeGen.ImpGen.GPU.Base-import Futhark.Construct (fullSliceNum)-import Futhark.Error-import Futhark.IR.GPUMem-import Futhark.IR.Mem.LMAD qualified as LMAD-import Futhark.Transform.Rename-import Futhark.Util (chunks, mapAccumLM, takeLast)-import Futhark.Util.IntegralExp (divUp, rem)-import Prelude hiding (quot, rem)---- | @flattenArray k flat arr@ flattens the outer @k@ dimensions of--- @arr@ to @flat@.  (Make sure @flat@ is the sum of those dimensions--- or you'll have a bad time.)-flattenArray :: Int -> TV Int64 -> VName -> ImpM rep r op VName-flattenArray k flat arr = do-  ArrayEntry arr_loc pt <- lookupArray arr-  let flat_shape = Shape $ Var (tvVar flat) : drop k (memLocShape arr_loc)-  sArray (baseString arr ++ "_flat") pt flat_shape (memLocName arr_loc) $-    fromMaybe (error "flattenArray") $-      LMAD.reshape (memLocLMAD arr_loc) (map pe64 $ shapeDims flat_shape)--sliceArray :: Imp.TExp Int64 -> TV Int64 -> VName -> ImpM rep r op VName-sliceArray start size arr = do-  MemLoc mem _ ixfun <- entryArrayLoc <$> lookupArray arr-  arr_t <- lookupType arr-  let slice =-        fullSliceNum-          (map Imp.pe64 (arrayDims arr_t))-          [DimSlice start (tvExp size) 1]-  sArray-    (baseString arr ++ "_chunk")-    (elemType arr_t)-    (arrayShape arr_t `setOuterDim` Var (tvVar size))-    mem-    $ LMAD.slice ixfun slice---- | @applyLambda lam dests args@ emits code that:------ 1. Binds each parameter of @lam@ to the corresponding element of---    @args@, interpreted as a (name,slice) pair (as in 'copyDWIM').---    Use an empty list for a scalar.------ 2. Executes the body of @lam@.------ 3. Binds the t'SubExp's that are the 'Result' of @lam@ to the--- provided @dest@s, again interpreted as the destination for a--- 'copyDWIM'.-applyLambda ::-  (Mem rep inner) =>-  Lambda rep ->-  [(VName, [DimIndex (Imp.TExp Int64)])] ->-  [(SubExp, [DimIndex (Imp.TExp Int64)])] ->-  ImpM rep r op ()-applyLambda lam dests args = do-  dLParams $ lambdaParams lam-  forM_ (zip (lambdaParams lam) args) $ \(p, (arg, arg_slice)) ->-    copyDWIM (paramName p) [] arg arg_slice-  compileStms mempty (bodyStms $ lambdaBody lam) $ do-    let res = map resSubExp $ bodyResult $ lambdaBody lam-    forM_ (zip dests res) $ \((dest, dest_slice), se) ->-      copyDWIM dest dest_slice se []---- | As applyLambda, but first rename the names in the lambda.  This--- makes it safe to apply it in multiple places.  (It might be safe--- anyway, but you have to be more careful - use this if you are in--- doubt.)-applyRenamedLambda ::-  (Mem rep inner) =>-  Lambda rep ->-  [(VName, [DimIndex (Imp.TExp Int64)])] ->-  [(SubExp, [DimIndex (Imp.TExp Int64)])] ->-  ImpM rep r op ()-applyRenamedLambda lam dests args = do-  lam_renamed <- renameLambda lam-  applyLambda lam_renamed dests args--groupChunkLoop ::-  Imp.TExp Int32 ->-  (Imp.TExp Int32 -> TV Int64 -> InKernelGen ()) ->-  InKernelGen ()-groupChunkLoop w m = do-  constants <- kernelConstants <$> askEnv-  let max_chunk_size = sExt32 $ kernelGroupSize constants-  num_chunks <- dPrimVE "num_chunks" $ w `divUp` max_chunk_size-  sFor "chunk_i" num_chunks $ \chunk_i -> do-    chunk_start <--      dPrimVE "chunk_start" $ chunk_i * max_chunk_size-    chunk_end <--      dPrimVE "chunk_end" $ sMin32 w (chunk_start + max_chunk_size)-    chunk_size <--      dPrimV "chunk_size" $ sExt64 $ chunk_end - chunk_start-    m chunk_start chunk_size--virtualisedGroupScan ::-  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->-  Imp.TExp Int32 ->-  Lambda GPUMem ->-  [VName] ->-  InKernelGen ()-virtualisedGroupScan seg_flag w lam arrs = do-  groupChunkLoop w $ \chunk_start chunk_size -> do-    constants <- kernelConstants <$> askEnv-    let ltid = kernelLocalThreadId constants-        crosses_segment =-          case seg_flag of-            Nothing -> false-            Just flag_true ->-              flag_true (sExt32 (chunk_start - 1)) (sExt32 chunk_start)-    sComment "possibly incorporate carry" $-      sWhen (chunk_start .>. 0 .&&. ltid .==. 0 .&&. bNot crosses_segment) $ do-        carry_idx <- dPrimVE "carry_idx" $ sExt64 chunk_start - 1-        applyRenamedLambda-          lam-          (map (,[DimFix $ sExt64 chunk_start]) arrs)-          ( map ((,[DimFix carry_idx]) . Var) arrs-              ++ map ((,[DimFix $ sExt64 chunk_start]) . Var) arrs-          )--    arrs_chunks <- mapM (sliceArray (sExt64 chunk_start) chunk_size) arrs--    sOp $ Imp.ErrorSync Imp.FenceLocal--    groupScan seg_flag (sExt64 w) (tvExp chunk_size) lam arrs_chunks--copyInGroup :: CopyCompiler GPUMem KernelEnv Imp.KernelOp-copyInGroup pt destloc srcloc = do-  dest_space <- entryMemSpace <$> lookupMemory (memLocName destloc)-  src_space <- entryMemSpace <$> lookupMemory (memLocName srcloc)--  let src_lmad = memLocLMAD srcloc-      dims = LMAD.shape src_lmad-      rank = length dims--  case (dest_space, src_space) of-    (ScalarSpace destds _, ScalarSpace srcds _) -> do-      let fullDim d = DimSlice 0 d 1-          destslice' =-            Slice $-              replicate (rank - length destds) (DimFix 0)-                ++ takeLast (length destds) (map fullDim dims)-          srcslice' =-            Slice $-              replicate (rank - length srcds) (DimFix 0)-                ++ takeLast (length srcds) (map fullDim dims)-      lmadCopy-        pt-        (sliceMemLoc destloc destslice')-        (sliceMemLoc srcloc srcslice')-    _ -> do-      groupCoverSpace (map sExt32 dims) $ \is ->-        lmadCopy-          pt-          (sliceMemLoc destloc (Slice $ map (DimFix . sExt64) is))-          (sliceMemLoc srcloc (Slice $ map (DimFix . sExt64) is))-      sOp $ Imp.Barrier Imp.FenceLocal--localThreadIDs :: [SubExp] -> InKernelGen [Imp.TExp Int64]-localThreadIDs dims = do-  ltid <- sExt64 . kernelLocalThreadId . kernelConstants <$> askEnv-  let dims' = map pe64 dims-  maybe (dIndexSpace' "ltid" dims' ltid) (pure . map sExt64)-    . M.lookup dims-    . kernelLocalIdMap-    . kernelConstants-    =<< askEnv--partitionSeqDims :: SegSeqDims -> SegSpace -> ([(VName, SubExp)], [(VName, SubExp)])-partitionSeqDims (SegSeqDims seq_is) space =-  bimap (map fst) (map fst) $-    partition ((`elem` seq_is) . snd) (zip (unSegSpace space) [0 ..])--compileFlatId :: SegSpace -> InKernelGen ()-compileFlatId space = do-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  dPrimV_ (segFlat space) ltid---- Construct the necessary lock arrays for an intra-group histogram.-prepareIntraGroupSegHist ::-  Shape ->-  Count GroupSize SubExp ->-  [HistOp GPUMem] ->-  InKernelGen [[Imp.TExp Int64] -> InKernelGen ()]-prepareIntraGroupSegHist segments group_size =-  fmap snd . mapAccumLM onOp Nothing-  where-    onOp l op = do-      constants <- kernelConstants <$> askEnv-      atomicBinOp <- kernelAtomics <$> askEnv--      let local_subhistos = histDest op--      case (l, atomicUpdateLocking atomicBinOp $ histOp op) of-        (_, AtomicPrim f) -> pure (l, f (Space "local") local_subhistos)-        (_, AtomicCAS f) -> pure (l, f (Space "local") local_subhistos)-        (Just l', AtomicLocking f) -> pure (l, f l' (Space "local") local_subhistos)-        (Nothing, AtomicLocking f) -> do-          locks <- newVName "locks"--          let num_locks = pe64 $ unCount group_size-              dims = map pe64 $ shapeDims (segments <> histOpShape op <> histShape op)-              l' = Locking locks 0 1 0 (pure . (`rem` num_locks) . flattenIndex dims)-              locks_t = Array int32 (Shape [unCount group_size]) NoUniqueness--          locks_mem <- sAlloc "locks_mem" (typeSize locks_t) $ Space "local"-          dArray locks int32 (arrayShape locks_t) locks_mem $-            LMAD.iota 0 . map pe64 . arrayDims $-              locks_t--          sComment "All locks start out unlocked" $-            groupCoverSpace [kernelGroupSize constants] $ \is ->-              copyDWIMFix locks is (intConst Int32 0) []--          pure (Just l', f l' (Space "local") local_subhistos)--groupCoverSegSpace :: SegVirt -> SegSpace -> InKernelGen () -> InKernelGen ()-groupCoverSegSpace virt space m = do-  let (ltids, dims) = unzip $ unSegSpace space-      dims' = map pe64 dims--  constants <- kernelConstants <$> askEnv-  let group_size = kernelGroupSize constants-  -- Maybe we can statically detect that this is actually a-  -- SegNoVirtFull and generate ever-so-slightly simpler code.-  let virt' = if dims' == [group_size] then SegNoVirtFull (SegSeqDims []) else virt-  case virt' of-    SegVirt -> do-      iters <- M.lookup dims . kernelChunkItersMap . kernelConstants <$> askEnv-      case iters of-        Nothing -> do-          iterations <- dPrimVE "iterations" $ product $ map sExt32 dims'-          groupLoop iterations $ \i -> do-            dIndexSpace (zip ltids dims') $ sExt64 i-            m-        Just num_chunks -> localOps threadOperations $ do-          let ltid = kernelLocalThreadId constants-          sFor "chunk_i" num_chunks $ \chunk_i -> do-            i <- dPrimVE "i" $ chunk_i * sExt32 group_size + ltid-            dIndexSpace (zip ltids dims') $ sExt64 i-            sWhen (inBounds (Slice (map (DimFix . le64) ltids)) dims') m-    SegNoVirt -> localOps threadOperations $ do-      zipWithM_ dPrimV_ ltids =<< localThreadIDs dims-      sWhen (isActive $ zip ltids dims) m-    SegNoVirtFull seq_dims -> do-      let ((ltids_seq, dims_seq), (ltids_par, dims_par)) =-            bimap unzip unzip $ partitionSeqDims seq_dims space-      sLoopNest (Shape dims_seq) $ \is_seq -> do-        zipWithM_ dPrimV_ ltids_seq is_seq-        localOps threadOperations $ do-          zipWithM_ dPrimV_ ltids_par =<< localThreadIDs dims_par-          m--compileGroupExp :: ExpCompiler GPUMem KernelEnv Imp.KernelOp-compileGroupExp (Pat [pe]) (BasicOp (Opaque _ se)) =-  -- Cannot print in GPU code.-  copyDWIM (patElemName pe) [] se []--- The static arrays stuff does not work inside kernels.-compileGroupExp (Pat [dest]) (BasicOp (ArrayLit es _)) =-  forM_ (zip [0 ..] es) $ \(i, e) ->-    copyDWIMFix (patElemName dest) [fromIntegral (i :: Int64)] e []-compileGroupExp _ (BasicOp (UpdateAcc acc is vs)) = do-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  sWhen (ltid .==. 0) $ updateAcc acc is vs-  sOp $ Imp.Barrier Imp.FenceLocal-compileGroupExp (Pat [dest]) (BasicOp (Replicate ds se)) | ds /= mempty = do-  flat <- newVName "rep_flat"-  is <- replicateM (arrayRank dest_t) (newVName "rep_i")-  let is' = map le64 is-  groupCoverSegSpace SegVirt (SegSpace flat $ zip is $ arrayDims dest_t) $-    copyDWIMFix (patElemName dest) is' se (drop (shapeRank ds) is')-  sOp $ Imp.Barrier Imp.FenceLocal-  where-    dest_t = patElemType dest-compileGroupExp (Pat [dest]) (BasicOp (Iota n e s it)) = do-  n' <- toExp n-  e' <- toExp e-  s' <- toExp s-  groupLoop (TPrimExp n') $ \i' -> do-    x <--      dPrimV "x" $-        TPrimExp $-          BinOpExp (Add it OverflowUndef) e' $-            BinOpExp (Mul it OverflowUndef) (untyped i') s'-    copyDWIMFix (patElemName dest) [i'] (Var (tvVar x)) []-  sOp $ Imp.Barrier Imp.FenceLocal---- When generating code for a scalar in-place update, we must make--- sure that only one thread performs the write.  When writing an--- array, the group-level copy code will take care of doing the right--- thing.-compileGroupExp (Pat [pe]) (BasicOp (Update safety _ slice se))-  | null $ sliceDims slice = do-      sOp $ Imp.Barrier Imp.FenceLocal-      ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-      sWhen (ltid .==. 0) $-        case safety of-          Unsafe -> write-          Safe -> sWhen (inBounds slice' dims) write-      sOp $ Imp.Barrier Imp.FenceLocal-  where-    slice' = fmap pe64 slice-    dims = map pe64 $ arrayDims $ patElemType pe-    write = copyDWIM (patElemName pe) (unSlice slice') se []-compileGroupExp dest e = do-  -- It is a messy to jump into control flow for error handling.-  -- Avoid that by always doing an error sync here.  Potential-  -- improvement: only do this if any errors are pending (this could-  -- also be handled in later codegen).-  when (doSync e) $ sOp $ Imp.ErrorSync Imp.FenceLocal-  defCompileExp dest e-  where-    doSync Loop {} = True-    doSync Match {} = True-    doSync _ = False--compileGroupOp :: OpCompiler GPUMem KernelEnv Imp.KernelOp-compileGroupOp pat (Alloc size space) =-  kernelAlloc pat size space-compileGroupOp pat (Inner (SegOp (SegMap lvl space _ body))) = do-  compileFlatId space--  groupCoverSegSpace (segVirt lvl) space $-    compileStms mempty (kernelBodyStms body) $-      zipWithM_ (compileThreadResult space) (patElems pat) $-        kernelBodyResult body-  sOp $ Imp.ErrorSync Imp.FenceLocal-compileGroupOp pat (Inner (SegOp (SegScan lvl space scans _ body))) = do-  compileFlatId space--  let (ltids, dims) = unzip $ unSegSpace space-      dims' = map pe64 dims--  groupCoverSegSpace (segVirt lvl) space $-    compileStms mempty (kernelBodyStms body) $-      forM_ (zip (patNames pat) $ kernelBodyResult body) $ \(dest, res) ->-        copyDWIMFix-          dest-          (map Imp.le64 ltids)-          (kernelResultSubExp res)-          []--  fence <- fenceForArrays $ patNames pat-  sOp $ Imp.ErrorSync fence--  let segment_size = last dims'-      crossesSegment from to =-        (sExt64 to - sExt64 from) .>. (sExt64 to `rem` segment_size)--  -- groupScan needs to treat the scan output as a one-dimensional-  -- array of scan elements, so we invent some new flattened arrays-  -- here.-  dims_flat <- dPrimV "dims_flat" $ product dims'-  let scan = head scans-      num_scan_results = length $ segBinOpNeutral scan-  arrs_flat <--    mapM (flattenArray (length dims') dims_flat) $-      take num_scan_results $-        patNames pat--  case segVirt lvl of-    SegVirt ->-      virtualisedGroupScan-        (Just crossesSegment)-        (sExt32 $ tvExp dims_flat)-        (segBinOpLambda scan)-        arrs_flat-    _ ->-      groupScan-        (Just crossesSegment)-        (product dims')-        (product dims')-        (segBinOpLambda scan)-        arrs_flat-compileGroupOp pat (Inner (SegOp (SegRed lvl space ops _ body))) = do-  compileFlatId space--  let dims' = map pe64 dims-      mkTempArr t =-        sAllocArray "red_arr" (elemType t) (Shape dims <> arrayShape t) $ Space "local"--  tmp_arrs <- mapM mkTempArr $ concatMap (lambdaReturnType . segBinOpLambda) ops-  groupCoverSegSpace (segVirt lvl) space $-    compileStms mempty (kernelBodyStms body) $ do-      let (red_res, map_res) =-            splitAt (segBinOpResults ops) $ kernelBodyResult body-      forM_ (zip tmp_arrs red_res) $ \(dest, res) ->-        copyDWIMFix dest (map Imp.le64 ltids) (kernelResultSubExp res) []-      zipWithM_ (compileThreadResult space) map_pes map_res--  sOp $ Imp.ErrorSync Imp.FenceLocal--  let tmps_for_ops = chunks (map (length . segBinOpNeutral) ops) tmp_arrs-  case segVirt lvl of-    SegVirt -> virtCase dims' tmps_for_ops-    _ -> nonvirtCase dims' tmps_for_ops-  where-    (ltids, dims) = unzip $ unSegSpace space-    (red_pes, map_pes) = splitAt (segBinOpResults ops) $ patElems pat--    virtCase [dim'] tmps_for_ops = do-      ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-      groupChunkLoop (sExt32 dim') $ \chunk_start chunk_size -> do-        sComment "possibly incorporate carry" $-          sWhen (chunk_start .>. 0 .&&. ltid .==. 0) $-            forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->-              applyRenamedLambda-                (segBinOpLambda op)-                (map (,[DimFix $ sExt64 chunk_start]) tmps)-                ( map ((,[]) . Var . patElemName) red_pes-                    ++ map ((,[DimFix $ sExt64 chunk_start]) . Var) tmps-                )--        sOp $ Imp.ErrorSync Imp.FenceLocal--        forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do-          tmps_chunks <- mapM (sliceArray (sExt64 chunk_start) chunk_size) tmps-          groupReduce (sExt32 (tvExp chunk_size)) (segBinOpLambda op) tmps_chunks--        sOp $ Imp.ErrorSync Imp.FenceLocal--        sComment "Save result of reduction." $-          forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->-            copyDWIMFix (patElemName pe) [] (Var arr) [sExt64 chunk_start]--    ---    virtCase dims' tmps_for_ops = do-      dims_flat <- dPrimV "dims_flat" $ product dims'-      let segment_size = last dims'-          crossesSegment from to =-            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` sExt64 segment_size)--      forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do-        tmps_flat <- mapM (flattenArray (length dims') dims_flat) tmps-        virtualisedGroupScan-          (Just crossesSegment)-          (sExt32 $ tvExp dims_flat)-          (segBinOpLambda op)-          tmps_flat--      sOp $ Imp.ErrorSync Imp.FenceLocal--      sComment "Save result of reduction." $-        forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->-          copyDWIM-            (patElemName pe)-            []-            (Var arr)-            (map (unitSlice 0) (init dims') ++ [DimFix $ last dims' - 1])--      sOp $ Imp.Barrier Imp.FenceLocal--    -- Nonsegmented case (or rather, a single segment) - this we can-    -- handle directly with a group-level reduction.-    nonvirtCase [dim'] tmps_for_ops = do-      forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->-        groupReduce (sExt32 dim') (segBinOpLambda op) tmps-      sOp $ Imp.ErrorSync Imp.FenceLocal-      sComment "Save result of reduction." $-        forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->-          copyDWIMFix (patElemName pe) [] (Var arr) [0]-      sOp $ Imp.ErrorSync Imp.FenceLocal--    -- Segmented intra-group reductions are turned into (regular)-    -- segmented scans.  It is possible that this can be done-    -- better, but at least this approach is simple.-    nonvirtCase dims' tmps_for_ops = do-      -- groupScan operates on flattened arrays.  This does not-      -- involve copying anything; merely playing with the index-      -- function.-      dims_flat <- dPrimV "dims_flat" $ product dims'-      let segment_size = last dims'-          crossesSegment from to =-            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` sExt64 segment_size)--      forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do-        tmps_flat <- mapM (flattenArray (length dims') dims_flat) tmps-        groupScan-          (Just crossesSegment)-          (product dims')-          (product dims')-          (segBinOpLambda op)-          tmps_flat--      sOp $ Imp.ErrorSync Imp.FenceLocal--      sComment "Save result of reduction." $-        forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->-          copyDWIM-            (patElemName pe)-            []-            (Var arr)-            (map (unitSlice 0) (init dims') ++ [DimFix $ last dims' - 1])--      sOp $ Imp.Barrier Imp.FenceLocal-compileGroupOp pat (Inner (SegOp (SegHist lvl space ops _ kbody))) = do-  compileFlatId space-  let (ltids, dims) = unzip $ unSegSpace space--  -- We don't need the red_pes, because it is guaranteed by our type-  -- rules that they occupy the same memory as the destinations for-  -- the ops.-  let num_red_res = length ops + sum (map (length . histNeutral) ops)-      (_red_pes, map_pes) =-        splitAt num_red_res $ patElems pat--  group_size <- kernelGroupSizeCount . kernelConstants <$> askEnv-  ops' <- prepareIntraGroupSegHist (Shape $ init dims) group_size ops--  -- Ensure that all locks have been initialised.-  sOp $ Imp.Barrier Imp.FenceLocal--  groupCoverSegSpace (segVirt lvl) space $-    compileStms mempty (kernelBodyStms kbody) $ do-      let (red_res, map_res) = splitAt num_red_res $ kernelBodyResult kbody-          (red_is, red_vs) = splitAt (length ops) $ map kernelResultSubExp red_res-      zipWithM_ (compileThreadResult space) map_pes map_res--      let vs_per_op = chunks (map (length . histDest) ops) red_vs--      forM_ (zip4 red_is vs_per_op ops' ops) $-        \(bin, op_vs, do_op, HistOp dest_shape _ _ _ shape lam) -> do-          let bin' = pe64 bin-              dest_shape' = map pe64 $ shapeDims dest_shape-              bin_in_bounds = inBounds (Slice [DimFix bin']) dest_shape'-              bin_is = map Imp.le64 (init ltids) ++ [bin']-              vs_params = takeLast (length op_vs) $ lambdaParams lam--          sComment "perform atomic updates" $-            sWhen bin_in_bounds $ do-              dLParams $ lambdaParams lam-              sLoopNest shape $ \is -> do-                forM_ (zip vs_params op_vs) $ \(p, v) ->-                  copyDWIMFix (paramName p) [] v is-                do_op (bin_is ++ is)--  sOp $ Imp.ErrorSync Imp.FenceLocal-compileGroupOp pat _ =-  compilerBugS $ "compileGroupOp: cannot compile rhs of binding " ++ prettyString pat--groupOperations :: Operations GPUMem KernelEnv Imp.KernelOp-groupOperations =-  (defaultOperations compileGroupOp)-    { opsCopyCompiler = copyInGroup,-      opsExpCompiler = compileGroupExp,-      opsStmsCompiler = \_ -> defCompileStms mempty,-      opsAllocCompilers =-        M.fromList [(Space "local", allocLocal)]-    }--arrayInLocalMemory :: SubExp -> InKernelGen Bool-arrayInLocalMemory (Var name) = do-  res <- lookupVar name-  case res of-    ArrayVar _ entry ->-      (Space "local" ==) . entryMemSpace-        <$> lookupMemory (memLocName (entryArrayLoc entry))-    _ -> pure False-arrayInLocalMemory Constant {} = pure False--sKernelGroup ::-  String ->-  VName ->-  KernelAttrs ->-  InKernelGen () ->-  CallKernelGen ()-sKernelGroup = sKernel groupOperations kernelGroupId--compileGroupResult ::-  SegSpace ->-  PatElem LetDecMem ->-  KernelResult ->-  InKernelGen ()-compileGroupResult _ pe (TileReturns _ [(w, per_group_elems)] what) = do-  n <- pe64 . arraySize 0 <$> lookupType what--  constants <- kernelConstants <$> askEnv-  let ltid = sExt64 $ kernelLocalThreadId constants-      offset =-        pe64 per_group_elems-          * sExt64 (kernelGroupId constants)--  -- Avoid loop for the common case where each thread is statically-  -- known to write at most one element.-  localOps threadOperations $-    if pe64 per_group_elems == kernelGroupSize constants-      then-        sWhen (ltid + offset .<. pe64 w) $-          copyDWIMFix (patElemName pe) [ltid + offset] (Var what) [ltid]-      else sFor "i" (n `divUp` kernelGroupSize constants) $ \i -> do-        j <- dPrimVE "j" $ kernelGroupSize constants * i + ltid-        sWhen (j + offset .<. pe64 w) $-          copyDWIMFix (patElemName pe) [j + offset] (Var what) [j]-compileGroupResult space pe (TileReturns _ dims what) = do-  let gids = map fst $ unSegSpace space-      out_tile_sizes = map (pe64 . snd) dims-      group_is = zipWith (*) (map Imp.le64 gids) out_tile_sizes-  local_is <- localThreadIDs $ map snd dims-  is_for_thread <--    mapM (dPrimV "thread_out_index") $-      zipWith (+) group_is local_is--  localOps threadOperations $-    sWhen (isActive $ zip (map tvVar is_for_thread) $ map fst dims) $-      copyDWIMFix (patElemName pe) (map tvExp is_for_thread) (Var what) local_is-compileGroupResult space pe (RegTileReturns _ dims_n_tiles what) = do-  constants <- kernelConstants <$> askEnv--  let gids = map fst $ unSegSpace space-      (dims, group_tiles, reg_tiles) = unzip3 dims_n_tiles-      group_tiles' = map pe64 group_tiles-      reg_tiles' = map pe64 reg_tiles--  -- Which group tile is this group responsible for?-  let group_tile_is = map Imp.le64 gids--  -- Within the group tile, which register tile is this thread-  -- responsible for?-  reg_tile_is <--    dIndexSpace' "reg_tile_i" group_tiles' $ sExt64 $ kernelLocalThreadId constants--  -- Compute output array slice for the register tile belonging to-  -- this thread.-  let regTileSliceDim (group_tile, group_tile_i) (reg_tile, reg_tile_i) = do-        tile_dim_start <--          dPrimVE "tile_dim_start" $-            reg_tile * (group_tile * group_tile_i + reg_tile_i)-        pure $ DimSlice tile_dim_start reg_tile 1-  reg_tile_slices <--    Slice-      <$> zipWithM-        regTileSliceDim-        (zip group_tiles' group_tile_is)-        (zip reg_tiles' reg_tile_is)--  localOps threadOperations $-    sLoopNest (Shape reg_tiles) $ \is_in_reg_tile -> do-      let dest_is = fixSlice reg_tile_slices is_in_reg_tile-          src_is = reg_tile_is ++ is_in_reg_tile-      sWhen (foldl1 (.&&.) $ zipWith (.<.) dest_is $ map pe64 dims) $-        copyDWIMFix (patElemName pe) dest_is (Var what) src_is-compileGroupResult space pe (Returns _ _ what) = do-  constants <- kernelConstants <$> askEnv-  in_local_memory <- arrayInLocalMemory what-  let gids = map (Imp.le64 . fst) $ unSegSpace space--  if not in_local_memory-    then-      localOps threadOperations $-        sWhen (kernelLocalThreadId constants .==. 0) $-          copyDWIMFix (patElemName pe) gids what []-    else -- If the result of the group is an array in local memory, we-    -- store it by collective copying among all the threads of the-    -- group.  TODO: also do this if the array is in global memory-    -- (but this is a bit more tricky, synchronisation-wise).-      copyDWIMFix (patElemName pe) gids what []-compileGroupResult _ _ WriteReturns {} =-  compilerLimitationS "compileGroupResult: WriteReturns not handled yet."---- | The sizes of nested iteration spaces in the kernel.-type SegOpSizes = S.Set [SubExp]---- | Various useful precomputed information for group-level SegOps.-data Precomputed = Precomputed-  { pcSegOpSizes :: SegOpSizes,-    pcChunkItersMap :: M.Map [SubExp] (Imp.TExp Int32)-  }---- | Find the sizes of nested parallelism in a t'SegOp' body.-segOpSizes :: Stms GPUMem -> SegOpSizes-segOpSizes = onStms-  where-    onStms = foldMap onStm-    onStm (Let _ _ (Op (Inner (SegOp op)))) =-      case segVirt $ segLevel op of-        SegNoVirtFull seq_dims ->-          S.singleton $ map snd $ snd $ partitionSeqDims seq_dims $ segSpace op-        _ -> S.singleton $ map snd $ unSegSpace $ segSpace op-    onStm (Let (Pat [pe]) _ (BasicOp (Replicate {}))) =-      S.singleton $ arrayDims $ patElemType pe-    onStm (Let (Pat [pe]) _ (BasicOp (Iota {}))) =-      S.singleton $ arrayDims $ patElemType pe-    onStm (Let (Pat [pe]) _ (BasicOp (Manifest {}))) =-      S.singleton $ arrayDims $ patElemType pe-    onStm (Let _ _ (Match _ cases defbody _)) =-      foldMap (onStms . bodyStms . caseBody) cases <> onStms (bodyStms defbody)-    onStm (Let _ _ (Loop _ _ body)) =-      onStms (bodyStms body)-    onStm _ = mempty---- | Precompute various constants and useful information.-precomputeConstants :: Count GroupSize (Imp.TExp Int64) -> Stms GPUMem -> CallKernelGen Precomputed-precomputeConstants group_size stms = do-  let sizes = segOpSizes stms-  iters_map <- M.fromList <$> mapM mkMap (S.toList sizes)-  pure $ Precomputed sizes iters_map-  where-    mkMap dims = do-      let n = product $ map Imp.pe64 dims-      num_chunks <- dPrimVE "num_chunks" $ sExt32 $ n `divUp` unCount group_size-      pure (dims, num_chunks)---- | Make use of various precomputed constants.-precomputedConstants :: Precomputed -> InKernelGen a -> InKernelGen a-precomputedConstants pre m = do-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  new_ids <- M.fromList <$> mapM (mkMap ltid) (S.toList (pcSegOpSizes pre))-  let f env =-        env-          { kernelConstants =-              (kernelConstants env)-                { kernelLocalIdMap = new_ids,-                  kernelChunkItersMap = pcChunkItersMap pre-                }-          }-  localEnv f m-  where-    mkMap ltid dims = do-      let dims' = map pe64 dims-      ids' <- dIndexSpace' "ltid_pre" dims' (sExt64 ltid)-      pure (dims, map sExt32 ids')
src/Futhark/CodeGen/ImpGen/GPU/SegHist.hs view
@@ -14,12 +14,12 @@ -- -- H: total size of histograms in bytes, including any lock arrays. ----- G: group size+-- G: block size -- -- T: number of bytes of local memory each thread can be given without -- impacting occupancy (determined experimentally, e.g. 32). ----- LMAX: maximum amount of local memory per workgroup (hard limit).+-- LMAX: maximum amount of local memory per threadblock (hard limit). -- -- We wish to compute: --@@ -380,8 +380,8 @@  histKernelGlobalPass ::   [PatElem LetDecMem] ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   SegSpace ->   [SegHistSlug] ->   KernelBody GPUMem ->@@ -389,7 +389,7 @@   Imp.TExp Int32 ->   Imp.TExp Int32 ->   CallKernelGen ()-histKernelGlobalPass map_pes num_groups group_size space slugs kbody histograms hist_S chk_i = do+histKernelGlobalPass map_pes num_tblocks tblock_size space slugs kbody histograms hist_S chk_i = do   let (space_is, space_sizes) = unzip $ unSegSpace space       space_sizes_64 = map (sExt64 . pe64) space_sizes       total_w_64 = product space_sizes_64@@ -397,7 +397,7 @@   hist_H_chks <- forM (map (histSize . slugOp) slugs) $ \w ->     dPrimVE "hist_H_chk" $ w `divUp` sExt64 hist_S -  sKernelThread "seghist_global" (segFlat space) (defKernelAttrs num_groups group_size) $ do+  sKernelThread "seghist_global" (segFlat space) (defKernelAttrs num_tblocks tblock_size) $ do     constants <- kernelConstants <$> askEnv      -- Compute subhistogram index for each thread, per histogram.@@ -472,17 +472,17 @@  histKernelGlobal ::   [PatElem LetDecMem] ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   SegSpace ->   [SegHistSlug] ->   KernelBody GPUMem ->   CallKernelGen ()-histKernelGlobal map_pes num_groups group_size space slugs kbody = do-  let num_groups' = fmap pe64 num_groups-      group_size' = fmap pe64 group_size+histKernelGlobal map_pes num_tblocks tblock_size space slugs kbody = do+  let num_tblocks' = fmap pe64 num_tblocks+      tblock_size' = fmap pe64 tblock_size   let (_space_is, space_sizes) = unzip $ unSegSpace space-      num_threads = sExt32 $ unCount num_groups' * unCount group_size'+      num_threads = sExt32 $ unCount num_tblocks' * unCount tblock_size'    emit $ Imp.DebugPrint "## Using global memory" Nothing @@ -497,8 +497,8 @@   sFor "chk_i" hist_S $ \chk_i ->     histKernelGlobalPass       map_pes-      num_groups-      group_size+      num_tblocks+      tblock_size       space       slugs       kbody@@ -518,14 +518,14 @@  prepareIntermediateArraysLocal ::   TV Int32 ->-  Count NumGroups (Imp.TExp Int64) ->+  Count NumBlocks (Imp.TExp Int64) ->   [SegHistSlug] ->   CallKernelGen InitLocalHistograms-prepareIntermediateArraysLocal num_subhistos_per_group groups_per_segment =+prepareIntermediateArraysLocal num_subhistos_per_block blocks_per_segment =   mapM onOp   where     onOp (SegHistSlug op num_subhistos subhisto_info do_op) = do-      num_subhistos <-- sExt64 (unCount groups_per_segment)+      num_subhistos <-- sExt64 (unCount blocks_per_segment)        emit $         Imp.DebugPrint "Number of subhistograms in global memory per segment" $@@ -539,14 +539,14 @@           AtomicCAS f -> pure $ const $ pure f           AtomicLocking f -> pure $ \hist_H_chk -> do             let lock_shape =-                  Shape [tvSize num_subhistos_per_group, hist_H_chk]+                  Shape [tvSize num_subhistos_per_block, hist_H_chk]              let dims = map pe64 $ shapeDims lock_shape -            locks <- sAllocArray "locks" int32 lock_shape $ Space "local"+            locks <- sAllocArray "locks" int32 lock_shape $ Space "shared"              sComment "All locks start out unlocked" $-              groupCoverSpace dims $ \is ->+              blockCoverSpace dims $ \is ->                 copyDWIMFix locks is (intConst Int32 0) []              pure $ f $ Locking locks 0 1 0 id@@ -557,17 +557,17 @@             local_subhistos <-               forM (histType op) $ \t -> do                 let sub_local_shape =-                      Shape [tvSize num_subhistos_per_group]+                      Shape [tvSize num_subhistos_per_block]                         <> setOuterDims (arrayShape t) (histRank op) (Shape [hist_H_chk])                 sAllocArray                   "subhistogram_local"                   (elemType t)                   sub_local_shape-                  (Space "local")+                  (Space "shared")              do_op' <- mk_op hist_H_chk -            pure (local_subhistos, do_op' (Space "local") local_subhistos)+            pure (local_subhistos, do_op' (Space "shared") local_subhistos)        -- Initialise global-memory sub-histograms.       glob_subhistos <- forM subhisto_info $ \info -> do@@ -578,10 +578,10 @@  histKernelLocalPass ::   TV Int32 ->-  Count NumGroups (Imp.TExp Int64) ->+  Count NumBlocks (Imp.TExp Int64) ->   [PatElem LetDecMem] ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   SegSpace ->   [SegHistSlug] ->   KernelBody GPUMem ->@@ -590,11 +590,11 @@   Imp.TExp Int32 ->   CallKernelGen () histKernelLocalPass-  num_subhistos_per_group_var-  groups_per_segment+  num_subhistos_per_block_var+  blocks_per_segment   map_pes-  num_groups-  group_size+  num_tblocks+  tblock_size   space   slugs   kbody@@ -605,7 +605,7 @@         segment_is = init space_is         segment_dims = init space_sizes         (i_in_segment, segment_size) = last $ unSegSpace space-        num_subhistos_per_group = tvExp num_subhistos_per_group_var+        num_subhistos_per_block = tvExp num_subhistos_per_block_var         segment_size' = pe64 segment_size      num_segments <-@@ -622,29 +622,29 @@               : map pe64 (shapeDims (histOpShape (slugOp slug)))       histo_size <-         dPrimVE "histo_size" $ product histo_dims-      let group_hists_size =-            sExt64 num_subhistos_per_group * histo_size+      let block_hists_size =+            sExt64 num_subhistos_per_block * histo_size       init_per_thread <--        dPrimVE "init_per_thread" $ sExt32 $ group_hists_size `divUp` pe64 (unCount group_size)+        dPrimVE "init_per_thread" $ sExt32 $ block_hists_size `divUp` pe64 (unCount tblock_size)       pure (histo_dims, histo_size, init_per_thread) -    let attrs = (defKernelAttrs num_groups group_size) {kAttrCheckLocalMemory = False}+    let attrs = (defKernelAttrs num_tblocks tblock_size) {kAttrCheckSharedMemory = False}     sKernelThread "seghist_local" (segFlat space) attrs $-      virtualiseGroups SegVirt (sExt32 $ unCount groups_per_segment * num_segments) $ \group_id -> do+      virtualiseBlocks SegVirt (sExt32 $ unCount blocks_per_segment * num_segments) $ \tblock_id -> do         constants <- kernelConstants <$> askEnv -        flat_segment_id <- dPrimVE "flat_segment_id" $ group_id `quot` sExt32 (unCount groups_per_segment)-        gid_in_segment <- dPrimVE "gid_in_segment" $ group_id `rem` sExt32 (unCount groups_per_segment)+        flat_segment_id <- dPrimVE "flat_segment_id" $ tblock_id `quot` sExt32 (unCount blocks_per_segment)+        gid_in_segment <- dPrimVE "gid_in_segment" $ tblock_id `rem` sExt32 (unCount blocks_per_segment)         -- This pgtid is kind of a "virtualised physical" gtid - not the         -- same thing as the gtid used for the SegHist itself.         pgtid_in_segment <-           dPrimVE "pgtid_in_segment" $-            gid_in_segment * sExt32 (kernelGroupSize constants)+            gid_in_segment * sExt32 (kernelBlockSize constants)               + kernelLocalThreadId constants         threads_per_segment <-           dPrimVE "threads_per_segment" $             sExt32 $-              unCount groups_per_segment * kernelGroupSize constants+              unCount blocks_per_segment * kernelBlockSize constants          -- Set segment indices.         zipWithM_ dPrimV_ segment_is $@@ -661,7 +661,7 @@         -- warp use different subhistograms, to avoid conflicts.         thread_local_subhisto_i <-           dPrimVE "thread_local_subhisto_i" $-            kernelLocalThreadId constants `rem` num_subhistos_per_group+            kernelLocalThreadId constants `rem` num_subhistos_per_block          let onSlugs f =               forM_ (zip3 slugs histograms histo_sizes) $@@ -670,18 +670,18 @@          let onAllHistograms f =               onSlugs $ \slug dests hist_H_chk histo_dims histo_size init_per_thread -> do-                let group_hists_size = num_subhistos_per_group * sExt32 histo_size+                let block_hists_size = num_subhistos_per_block * sExt32 histo_size                  forM_ (zip dests (histNeutral $ slugOp slug)) $                   \((dest_global, dest_local), ne) ->                     sFor "local_i" init_per_thread $ \i -> do                       j <-                         dPrimVE "j" $-                          i * sExt32 (kernelGroupSize constants)+                          i * sExt32 (kernelBlockSize constants)                             + kernelLocalThreadId constants                       j_offset <-                         dPrimVE "j_offset" $-                          num_subhistos_per_group * sExt32 histo_size * gid_in_segment + j+                          num_subhistos_per_block * sExt32 histo_size * gid_in_segment + j                        local_subhisto_i <- dPrimVE "local_subhisto_i" $ j `quot` sExt32 histo_size                       let local_bucket_is = unflattenIndex histo_dims $ sExt64 $ j `rem` sExt32 histo_size@@ -695,7 +695,7 @@                               ++ tail local_bucket_is                       global_subhisto_i <- dPrimVE "global_subhisto_i" $ j_offset `quot` sExt32 histo_size -                      sWhen (j .<. group_hists_size) $+                      sWhen (j .<. block_hists_size) $                         f                           dest_local                           dest_global@@ -785,7 +785,7 @@             sFor "local_i" bins_per_thread $ \i -> do               j <-                 dPrimVE "j" $-                  i * sExt32 (kernelGroupSize constants)+                  i * sExt32 (kernelBlockSize constants)                     + kernelLocalThreadId constants               sWhen (j .<. trunc_histo_size) $ do                 -- We are responsible for compacting the flat bin 'j', which@@ -815,7 +815,7 @@                       (0 : local_bucket_is)                  sComment "Accumulate based on values in other subhistograms." $-                  sFor "subhisto_id" (num_subhistos_per_group - 1) $ \subhisto_id -> do+                  sFor "subhisto_id" (num_subhistos_per_block - 1) $ \subhisto_id -> do                     forM_ (zip yparams local_dests) $ \(yp, subhisto) ->                       copyDWIMFix                         (paramName yp)@@ -827,40 +827,40 @@                 sComment "Put final bucket value in global memory." $ do                   let global_is =                         map Imp.le64 segment_is-                          ++ [sExt64 group_id `rem` unCount groups_per_segment]+                          ++ [sExt64 tblock_id `rem` unCount blocks_per_segment]                           ++ global_bucket_is                   forM_ (zip xparams global_dests) $ \(xp, global_dest) ->                     copyDWIMFix global_dest global_is (Var $ paramName xp) []  histKernelLocal ::   TV Int32 ->-  Count NumGroups (Imp.TExp Int64) ->+  Count NumBlocks (Imp.TExp Int64) ->   [PatElem LetDecMem] ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   SegSpace ->   Imp.TExp Int32 ->   [SegHistSlug] ->   KernelBody GPUMem ->   CallKernelGen ()-histKernelLocal num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space hist_S slugs kbody = do-  let num_subhistos_per_group = tvExp num_subhistos_per_group_var+histKernelLocal num_subhistos_per_block_var blocks_per_segment map_pes num_tblocks tblock_size space hist_S slugs kbody = do+  let num_subhistos_per_block = tvExp num_subhistos_per_block_var    emit $-    Imp.DebugPrint "Number of local subhistograms per group" $+    Imp.DebugPrint "Number of local subhistograms per block" $       Just $-        untyped num_subhistos_per_group+        untyped num_subhistos_per_block    init_histograms <--    prepareIntermediateArraysLocal num_subhistos_per_group_var groups_per_segment slugs+    prepareIntermediateArraysLocal num_subhistos_per_block_var blocks_per_segment slugs    sFor "chk_i" hist_S $ \chk_i ->     histKernelLocalPass-      num_subhistos_per_group_var-      groups_per_segment+      num_subhistos_per_block_var+      blocks_per_segment       map_pes-      num_groups-      group_size+      num_tblocks+      tblock_size       space       slugs       kbody@@ -894,30 +894,30 @@       segmented = not $ null segment_dims    hist_L <- dPrim "hist_L" int32-  sOp $ Imp.GetSizeMax (tvVar hist_L) Imp.SizeLocalMemory+  sOp $ Imp.GetSizeMax (tvVar hist_L) Imp.SizeSharedMemory -  max_group_size <- dPrim "max_group_size" int32-  sOp $ Imp.GetSizeMax (tvVar max_group_size) Imp.SizeGroup+  max_tblock_size <- dPrim "max_tblock_size" int32+  sOp $ Imp.GetSizeMax (tvVar max_tblock_size) Imp.SizeThreadBlock -  -- XXX: we need to record for later use that max_group_size is the+  -- XXX: we need to record for later use that max_tblock_size is the   -- result of GetSizeMax.  This is an ugly hack that reflects our   -- inability to track which variables are actually constants.   let withSizeMax vtable =-        case M.lookup (tvVar max_group_size) vtable of+        case M.lookup (tvVar max_tblock_size) vtable of           Just (ScalarVar _ se) ->             M.insert-              (tvVar max_group_size)-              (ScalarVar (Just (Op (Inner (SizeOp (GetSizeMax SizeGroup))))) se)+              (tvVar max_tblock_size)+              (ScalarVar (Just (Op (Inner (SizeOp (GetSizeMax SizeThreadBlock))))) se)               vtable           _ -> vtable -  let group_size = Imp.Count $ Var $ tvVar max_group_size-  num_groups <-+  let tblock_size = Imp.Count $ Var $ tvVar max_tblock_size+  num_tblocks <-     fmap (Imp.Count . tvSize) $-      dPrimV "num_groups" $-        sExt64 hist_T `divUp` pe64 (unCount group_size)-  let num_groups' = pe64 <$> num_groups-      group_size' = pe64 <$> group_size+      dPrimV "num_tblocks" $+        sExt64 hist_T `divUp` pe64 (unCount tblock_size)+  let num_tblocks' = pe64 <$> num_tblocks+      tblock_size' = pe64 <$> tblock_size    let r64 = isF64 . ConvOpExp (SIToFP Int64 Float64) . untyped       t64 = isInt64 . ConvOpExp (FPToSI Float64 Int64) . untyped@@ -928,11 +928,11 @@       r64         ( sMin64             (sExt64 (tvExp hist_L `quot` hist_el_size))-            (hist_N `divUp` sExt64 (unCount num_groups'))+            (hist_N `divUp` sExt64 (unCount num_tblocks'))         )         / r64 hist_H -  let hist_B = unCount group_size'+  let hist_B = unCount tblock_size'    -- M in the paper, but not adjusted for asymptotic efficiency.   hist_M0 <-@@ -958,14 +958,14 @@               sMin64 (sExt64 hist_Nin * sExt64 hist_Nout) (sExt64 hist_T)                 `divUp` sExt64 hist_Nout -        -- Number of groups, rounded up.+        -- Number of blocks, rounded up.         let r = hist_T_hist_min `divUp` sExt32 hist_B          dPrimVE "work_asymp_M_max" $ hist_Nin `quot` (sExt64 r * hist_H)       else         dPrimVE "work_asymp_M_max" $           (hist_Nout * hist_N)-            `quot` ( (q_small * unCount num_groups' * hist_H)+            `quot` ( (q_small * unCount num_tblocks' * hist_H)                        `quot` genericLength slugs                    ) @@ -1008,13 +1008,13 @@         MustBeSinglePass -> 1         MayBeMultiPass -> fromIntegral $ maxinum $ map slugMaxLocalMemPasses slugs -  groups_per_segment <-+  blocks_per_segment <-     if segmented       then         fmap Count $-          dPrimVE "groups_per_segment" $-            unCount num_groups' `divUp` hist_Nout-      else pure num_groups'+          dPrimVE "blocks_per_segment" $+            unCount num_tblocks' `divUp` hist_Nout+      else pure num_tblocks'    -- We only use local memory if the number of updates per histogram   -- at least matches the histogram size, as otherwise it is not@@ -1038,17 +1038,17 @@         emit $ Imp.DebugPrint "Number of chunks (S)" $ Just $ untyped hist_S         when segmented $           emit $-            Imp.DebugPrint "Groups per segment" $+            Imp.DebugPrint "Blocks per segment" $               Just $                 untyped $-                  unCount groups_per_segment+                  unCount blocks_per_segment         localVTable withSizeMax $           histKernelLocal             hist_M-            groups_per_segment+            blocks_per_segment             map_pes-            num_groups-            group_size+            num_tblocks+            tblock_size             space             hist_S             slugs@@ -1065,14 +1065,14 @@   KernelBody GPUMem ->   CallKernelGen () compileSegHist (Pat pes) lvl space ops kbody = do-  KernelAttrs {kAttrNumGroups = num_groups, kAttrGroupSize = group_size} <-+  KernelAttrs {kAttrNumBlocks = num_tblocks, kAttrBlockSize = tblock_size} <-     lvlKernelAttrs lvl   -- Most of this function is not the histogram part itself, but   -- rather figuring out whether to use a local or global memory   -- strategy, as well as collapsing the subhistograms produced (which   -- are always in global memory, but their number may vary).-  let num_groups' = fmap pe64 num_groups-      group_size' = fmap pe64 group_size+  let num_tblocks' = fmap pe64 num_tblocks+      tblock_size' = fmap pe64 tblock_size       dims = map pe64 $ segSpaceDims space        num_red_res = length ops + sum (map (length . histNeutral) ops)@@ -1085,8 +1085,8 @@    -- Check for emptyness to avoid division-by-zero.   sUnless (seg_h .==. 0) $ do-    -- Maximum group size (or actual, in this case).-    let hist_B = unCount group_size'+    -- Maximum block size (or actual, in this case).+    let hist_B = unCount tblock_size'      -- Size of a histogram.     hist_H <- dPrimVE "hist_H" $ sum $ map histSize ops@@ -1112,10 +1112,10 @@           sum (map (pe64 . histRaceFactor . slugOp) slugs)             `quot` genericLength slugs -    let hist_T = sExt32 $ unCount num_groups' * unCount group_size'+    let hist_T = sExt32 $ unCount num_tblocks' * unCount tblock_size'     emit $ Imp.DebugPrint "\n# SegHist" Nothing     emit $ Imp.DebugPrint "Number of threads (T)" $ Just $ untyped hist_T-    emit $ Imp.DebugPrint "Desired group size (B)" $ Just $ untyped hist_B+    emit $ Imp.DebugPrint "Desired block size (B)" $ Just $ untyped hist_B     emit $ Imp.DebugPrint "Histogram size (H)" $ Just $ untyped hist_H     emit $ Imp.DebugPrint "Input elements per histogram (N)" $ Just $ untyped hist_N     emit $@@ -1129,11 +1129,11 @@     emit $ Imp.DebugPrint "Memory per set of subhistograms per segment" $ Just $ untyped h     emit $ Imp.DebugPrint "Memory per set of subhistograms times segments" $ Just $ untyped seg_h -    (use_local_memory, run_in_local_memory) <-+    (use_shared_memory, run_in_shared_memory) <-       localMemoryCase map_pes hist_T space hist_H hist_el_size hist_N hist_RF slugs kbody -    sIf use_local_memory run_in_local_memory $-      histKernelGlobal map_pes num_groups group_size space slugs kbody+    sIf use_shared_memory run_in_shared_memory $+      histKernelGlobal map_pes num_tblocks tblock_size space slugs kbody      let pes_per_op = chunks (map (length . histDest) ops) all_red_pes @@ -1166,7 +1166,7 @@          flat_gtid <- newVName "flat_gtid" -        let grid = KernelGrid num_groups group_size+        let grid = KernelGrid num_tblocks tblock_size             segred_space =               SegSpace flat_gtid $                 segment_dims
src/Futhark/CodeGen/ImpGen/GPU/SegMap.hs view
@@ -3,14 +3,14 @@ -- | Code generation for 'SegMap' is quite straightforward.  The only -- trick is virtualisation in case the physical number of threads is -- not sufficient to cover the logical thread space.  This is handled--- by having actual workgroups run a loop to imitate multiple workgroups.+-- by having actual threadblocks run a loop to imitate multiple threadblocks. module Futhark.CodeGen.ImpGen.GPU.SegMap (compileSegMap) where  import Control.Monad import Futhark.CodeGen.ImpCode.GPU qualified as Imp import Futhark.CodeGen.ImpGen import Futhark.CodeGen.ImpGen.GPU.Base-import Futhark.CodeGen.ImpGen.GPU.Group+import Futhark.CodeGen.ImpGen.GPU.Block import Futhark.IR.GPUMem import Futhark.Util.IntegralExp (divUp) import Prelude hiding (quot, rem)@@ -27,19 +27,19 @@    let (is, dims) = unzip $ unSegSpace space       dims' = map pe64 dims-      group_size' = pe64 <$> kAttrGroupSize attrs+      tblock_size' = pe64 <$> kAttrBlockSize attrs    emit $ Imp.DebugPrint "\n# SegMap" Nothing   case lvl of     SegThread {} -> do-      virt_num_groups <- dPrimVE "virt_num_groups" $ sExt32 $ product dims' `divUp` unCount group_size'+      virt_num_tblocks <- dPrimVE "virt_num_tblocks" $ sExt32 $ product dims' `divUp` unCount tblock_size'       sKernelThread "segmap" (segFlat space) attrs $-        virtualiseGroups (segVirt lvl) virt_num_groups $ \group_id -> do+        virtualiseBlocks (segVirt lvl) virt_num_tblocks $ \tblock_id -> do           local_tid <- kernelLocalThreadId . kernelConstants <$> askEnv            global_tid <-             dPrimVE "global_tid" $-              sExt64 group_id * sExt64 (unCount group_size')+              sExt64 tblock_id * sExt64 (unCount tblock_size')                 + sExt64 local_tid            dIndexSpace (zip is dims') global_tid@@ -48,17 +48,17 @@             compileStms mempty (kernelBodyStms kbody) $               zipWithM_ (compileThreadResult space) (patElems pat) $                 kernelBodyResult kbody-    SegGroup {} -> do-      pc <- precomputeConstants group_size' $ kernelBodyStms kbody-      virt_num_groups <- dPrimVE "virt_num_groups" $ sExt32 $ product dims'-      sKernelGroup "segmap_intragroup" (segFlat space) attrs $ do+    SegBlock {} -> do+      pc <- precomputeConstants tblock_size' $ kernelBodyStms kbody+      virt_num_tblocks <- dPrimVE "virt_num_tblocks" $ sExt32 $ product dims'+      sKernelBlock "segmap_intrablock" (segFlat space) attrs $ do         precomputedConstants pc $-          virtualiseGroups (segVirt lvl) virt_num_groups $ \group_id -> do-            dIndexSpace (zip is dims') $ sExt64 group_id+          virtualiseBlocks (segVirt lvl) virt_num_tblocks $ \tblock_id -> do+            dIndexSpace (zip is dims') $ sExt64 tblock_id              compileStms mempty (kernelBodyStms kbody) $-              zipWithM_ (compileGroupResult space) (patElems pat) $+              zipWithM_ (compileBlockResult space) (patElems pat) $                 kernelBodyResult kbody-    SegThreadInGroup {} ->-      error "compileSegMap: SegThreadInGroup"+    SegThreadInBlock {} ->+      error "compileSegMap: SegThreadInBlock"   emit $ Imp.DebugPrint "" Nothing
src/Futhark/CodeGen/ImpGen/GPU/SegRed.hs view
@@ -6,12 +6,12 @@ -- deviations are: -- -- * While we still use two-phase reduction, we use only a single---   kernel, with the final workgroup to write a result (tracked via+--   kernel, with the final threadblock to write a result (tracked via --   an atomic counter) performing the final reduction as well. -- -- * Instead of depending on storage layout transformations to handle --   non-commutative reductions efficiently, we slide a---   @groupsize@-sized window over the input, and perform a parallel+--   @tblocksize@-sized window over the input, and perform a parallel --   reduction for each window.  This sacrifices the notion of --   efficient sequentialisation, but is sometimes faster and --   definitely simpler and more predictable (and uses less auxiliary@@ -21,32 +21,32 @@ -- Regular Segmented Reductions on GPU" (FHPC '17).  This involves -- having two different strategies, and dynamically deciding which one -- to use based on the number of segments and segment size. We use the--- (static) @group_size@ to decide which of the following two+-- (static) @tblock_size@ to decide which of the following two -- strategies to choose: ----- * Large: uses one or more groups to process a single segment. If---   multiple groups are used per segment, the intermediate reduction+-- * Large: uses one or more blocks to process a single segment. If+--   multiple blocks are used per segment, the intermediate reduction --   results must be recursively reduced, until there is only a single --   value per segment. -- --   Each thread /can/ read multiple elements, which will greatly --   increase performance; however, if the reduction is --   non-commutative we will have to use a less efficient traversal---   (with interim group-wide reductions) to enable coalesced memory+--   (with interim block-wide reductions) to enable coalesced memory --   accesses, just as in the non-segmented case. ----- * Small: is used to let each group process *multiple* segments---   within a group. We will only use this approach when we can---   process at least two segments within a single group. In those---   cases, we would allocate a /whole/ group per segment with the---   large strategy, but at most 50% of the threads in the group would+-- * Small: is used to let each block process *multiple* segments+--   within a block. We will only use this approach when we can+--   process at least two segments within a single block. In those+--   cases, we would allocate a /whole/ block per segment with the+--   large strategy, but at most 50% of the threads in the block would --   have any element to read, which becomes highly inefficient. -- -- An optimization specfically targeted at non-segmented and large-segments -- segmented reductions with non-commutative is made: The stage one main loop is -- essentially stripmined by a factor `chunk`, inserting collective copies via--- local memory of each reduction parameter going into the intra-group (partial)--- reductions. This saves a factor `chunk` number of intra-group reductions at+-- local memory of each reduction parameter going into the intra-block (partial)+-- reductions. This saves a factor `chunk` number of intra-block reductions at -- the cost of some overhead in collective copies. module Futhark.CodeGen.ImpGen.GPU.SegRed   ( compileSegRed,@@ -76,7 +76,7 @@ -- | The maximum number of operators we support in a single SegRed. -- This limit arises out of the static allocation of counters. maxNumOps :: Int-maxNumOps = 10+maxNumOps = 20  -- | Code generation for the body of the SegRed, taking a continuation -- for saving the results of the body.  The results should be@@ -88,11 +88,11 @@ -- intermediate memory we need for the different ReduceKinds. data SegRedIntermediateArrays   = GeneralSegRedInterms-      { groupRedArrs :: [VName]+      { blockRedArrs :: [VName]       }   | NoncommPrimSegRedInterms       { collCopyArrs :: [VName],-        groupRedArrs :: [VName],+        blockRedArrs :: [VName],         privateChunks :: [VName]       } @@ -107,9 +107,9 @@   CallKernelGen () compileSegRed pat lvl space segbinops map_kbody = do   emit $ Imp.DebugPrint "\n# SegRed" Nothing-  KernelAttrs {kAttrNumGroups = num_groups, kAttrGroupSize = group_size} <-+  KernelAttrs {kAttrNumBlocks = num_tblocks, kAttrBlockSize = tblock_size} <-     lvlKernelAttrs lvl-  let grid = KernelGrid num_groups group_size+  let grid = KernelGrid num_tblocks tblock_size    compileSegRed' pat grid space segbinops $ \red_cont ->     sComment "apply map function" $@@ -143,7 +143,8 @@ compileSegRed' pat grid space segbinops map_body_cont   | genericLength segbinops > maxNumOps =       compilerLimitationS $-        "compileSegRed': at most " ++ show maxNumOps ++ " reduction operators are supported."+        ("compileSegRed': at most " <> show maxNumOps <> " reduction operators are supported.\n")+          <> ("Pattern: " <> prettyString pat)   | otherwise = do       chunk_v <- dPrimV "chunk_size" . isInt64 =<< kernelConstToExp chunk_const       case unSegSpace space of@@ -151,19 +152,19 @@           compileReduction (chunk_v, chunk_const) nonsegmentedReduction         _ -> do           let segment_size = pe64 $ last $ segSpaceDims space-              use_small_segments = segment_size * 2 .<. pe64 (unCount group_size) * tvExp chunk_v+              use_small_segments = segment_size * 2 .<. pe64 (unCount tblock_size) * tvExp chunk_v           sIf             use_small_segments             (compileReduction (chunk_v, chunk_const) smallSegmentsReduction)             (compileReduction (chunk_v, chunk_const) largeSegmentsReduction)   where     compileReduction chunk f =-      f pat num_groups group_size chunk space segbinops map_body_cont+      f pat num_tblocks tblock_size chunk space segbinops map_body_cont      param_types = map paramType $ concatMap paramOf segbinops -    num_groups = gridNumGroups grid-    group_size = gridGroupSize grid+    num_tblocks = gridNumBlocks grid+    tblock_size = gridBlockSize grid      chunk_const =       if Noncommutative `elem` map segBinOpComm segbinops@@ -186,34 +187,34 @@   SubExp ->   [SegBinOp GPUMem] ->   InKernelGen [SegRedIntermediateArrays]-makeIntermArrays group_id group_size chunk segbinops+makeIntermArrays tblock_id tblock_size chunk segbinops   | Noncommutative <- mconcat (map segBinOpComm segbinops),     all isPrimSegBinOp segbinops =       noncommPrimSegRedInterms   | otherwise =-      generalSegRedInterms group_id group_size segbinops+      generalSegRedInterms tblock_id tblock_size segbinops   where     params = map paramOf segbinops      noncommPrimSegRedInterms = do-      group_worksize <- tvSize <$> dPrimV "group_worksize" group_worksize_E+      block_worksize <- tvSize <$> dPrimV "block_worksize" block_worksize_E        -- compute total amount of lmem.-      let sum_ x y = nextMul x y + group_size_E * y-          group_reds_lmem_requirement = foldl sum_ 0 $ concat elem_sizes-          collcopy_lmem_requirement = group_worksize_E * max_elem_size+      let sum_ x y = nextMul x y + tblock_size_E * y+          block_reds_lmem_requirement = foldl sum_ 0 $ concat elem_sizes+          collcopy_lmem_requirement = block_worksize_E * max_elem_size           lmem_total_size =             Imp.bytes $-              collcopy_lmem_requirement `sMax64` group_reds_lmem_requirement+              collcopy_lmem_requirement `sMax64` block_reds_lmem_requirement -      -- offsets into the total pool of lmem for each group reduction array.+      -- offsets into the total pool of lmem for each block reduction array.       (_, offsets) <-         forAccumLM2D 0 elem_sizes $ \byte_offs elem_size ->           (,byte_offs `quot` elem_size)             <$> dPrimVE "offset" (sum_ byte_offs elem_size)        -- total pool of local mem.-      lmem <- sAlloc "local_mem" lmem_total_size (Space "local")+      lmem <- sAlloc "local_mem" lmem_total_size (Space "shared")       let arrInLMem ptype name len_se offset =             sArray               (name ++ "_" ++ prettyString ptype)@@ -223,20 +224,20 @@               $ LMAD.iota offset [pe64 len_se]        forM (zipWith zip params offsets) $ \ps_and_offsets -> do-        (coll_copy_arrs, group_red_arrs, priv_chunks) <-+        (coll_copy_arrs, block_red_arrs, priv_chunks) <-           fmap unzip3 $ forM ps_and_offsets $ \(p, offset) -> do             let ptype = elemType $ paramType p             (,,)-              <$> arrInLMem ptype "coll_copy_arr" group_worksize 0-              <*> arrInLMem ptype "group_red_arr" group_size offset+              <$> arrInLMem ptype "coll_copy_arr" block_worksize 0+              <*> arrInLMem ptype "block_red_arr" tblock_size offset               <*> sAllocArray                 ("chunk_" ++ prettyString ptype)                 ptype                 (Shape [chunk])                 (ScalarSpace [chunk] ptype)-        pure $ NoncommPrimSegRedInterms coll_copy_arrs group_red_arrs priv_chunks-    group_size_E = pe64 group_size-    group_worksize_E = group_size_E * pe64 chunk+        pure $ NoncommPrimSegRedInterms coll_copy_arrs block_red_arrs priv_chunks+    tblock_size_E = pe64 tblock_size+    block_worksize_E = tblock_size_E * pe64 chunk      paramSize = primByteSize . elemType . paramType     elem_sizes = map (map paramSize) params@@ -249,50 +250,50 @@   SubExp ->   [SegBinOp GPUMem] ->   InKernelGen [SegRedIntermediateArrays]-generalSegRedInterms group_id group_size segbinops =+generalSegRedInterms tblock_id tblock_size segbinops =   fmap (map GeneralSegRedInterms) $     forM (map paramOf segbinops) $       mapM $ \p ->         case paramDec p of           MemArray pt shape _ (ArrayIn mem _) -> do-            let shape' = Shape [group_size] <> shape+            let shape' = Shape [tblock_size] <> shape             let shape_E = map pe64 $ shapeDims shape'             sArray ("red_arr_" ++ prettyString pt) pt shape' mem $-              LMAD.iota (group_id * product shape_E) shape_E+              LMAD.iota (tblock_id * product shape_E) shape_E           _ -> do             let pt = elemType $ paramType p-                shape = Shape [group_size]-            sAllocArray ("red_arr_" ++ prettyString pt) pt shape $ Space "local"+                shape = Shape [tblock_size]+            sAllocArray ("red_arr_" ++ prettyString pt) pt shape $ Space "shared" --- | Arrays for storing group results.+-- | Arrays for storing block results. ----- The group-result arrays have an extra dimension because they are+-- The block-result arrays have an extra dimension because they are -- also used for keeping vectorised accumulators for first-stage -- reduction, if necessary.  If necessary, this dimension has size--- group_size, and otherwise 1.  When actually storing group results,+-- tblock_size, and otherwise 1.  When actually storing block results, -- the first index is set to 0. groupResultArrays ::   SubExp ->   SubExp ->   [SegBinOp GPUMem] ->   CallKernelGen [[VName]]-groupResultArrays num_virtgroups group_size segbinops =+groupResultArrays num_virtblocks tblock_size segbinops =   forM segbinops $ \(SegBinOp _ lam _ shape) ->     forM (lambdaReturnType lam) $ \t -> do       let pt = elemType t           extra_dim             | primType t, shapeRank shape == 0 = intConst Int64 1-            | otherwise = group_size-          full_shape = Shape [extra_dim, num_virtgroups] <> shape <> arrayShape t-          -- Move the groupsize dimension last to ensure coalesced+            | otherwise = tblock_size+          full_shape = Shape [extra_dim, num_virtblocks] <> shape <> arrayShape t+          -- Move the tblocksize dimension last to ensure coalesced           -- memory access.           perm = [1 .. shapeRank full_shape - 1] ++ [0]       sAllocArrayPerm "segred_tmp" pt full_shape (Space "device") perm  type DoCompileSegRed =   Pat LetDecMem ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   (TV Int64, Imp.KernelConstExp) ->   SegSpace ->   [SegBinOp GPUMem] ->@@ -300,34 +301,34 @@   CallKernelGen ()  nonsegmentedReduction :: DoCompileSegRed-nonsegmentedReduction (Pat segred_pes) num_groups group_size (chunk_v, chunk_const) space segbinops map_body_cont = do+nonsegmentedReduction (Pat segred_pes) num_tblocks tblock_size (chunk_v, chunk_const) space segbinops map_body_cont = do   let (gtids, dims) = unzip $ unSegSpace space       chunk = tvExp chunk_v-      num_groups_se = unCount num_groups-      group_size_se = unCount group_size-      group_size' = pe64 group_size_se+      num_tblocks_se = unCount num_tblocks+      tblock_size_se = unCount tblock_size+      tblock_size' = pe64 tblock_size_se       global_tid = Imp.le64 $ segFlat space       n = pe64 $ last dims    counters <- genZeroes "counters" maxNumOps -  reds_group_res_arrs <- groupResultArrays num_groups_se group_size_se segbinops+  reds_block_res_arrs <- groupResultArrays num_tblocks_se tblock_size_se segbinops    num_threads <--    fmap tvSize $ dPrimV "num_threads" $ pe64 num_groups_se * group_size'+    fmap tvSize $ dPrimV "num_threads" $ pe64 num_tblocks_se * tblock_size'    let attrs =-        (defKernelAttrs num_groups group_size)+        (defKernelAttrs num_tblocks tblock_size)           { kAttrConstExps = M.singleton (tvVar chunk_v) chunk_const           }    sKernelThread "segred_nonseg" (segFlat space) attrs $ do     constants <- kernelConstants <$> askEnv     let ltid = kernelLocalThreadId constants-    let group_id = kernelGroupId constants+    let tblock_id = kernelBlockId constants -    interms <- makeIntermArrays (sExt64 group_id) group_size_se (tvSize chunk_v) segbinops-    sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"+    interms <- makeIntermArrays (sExt64 tblock_id) tblock_size_se (tvSize chunk_v) segbinops+    sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "shared"      -- Since this is the nonsegmented case, all outer segment IDs must     -- necessarily be 0.@@ -336,8 +337,8 @@     q <- dPrimVE "q" $ n `divUp` (sExt64 (kernelNumThreads constants) * chunk)      slugs <--      mapM (segBinOpSlug ltid group_id) $-        zip3 segbinops interms reds_group_res_arrs+      mapM (segBinOpSlug ltid tblock_id) $+        zip3 segbinops interms reds_block_res_arrs     new_lambdas <-       reductionStageOne         gtids@@ -358,10 +359,10 @@       \(pes, slug, new_lambda, i) ->         reductionStageTwo           pes-          group_id+          tblock_id           [0]           0-          (sExt64 $ kernelNumGroups constants)+          (sExt64 $ kernelNumBlocks constants)           slug           new_lambda           counters@@ -369,7 +370,7 @@           (fromInteger i)  smallSegmentsReduction :: DoCompileSegRed-smallSegmentsReduction (Pat segred_pes) num_groups group_size _ space segbinops map_body_cont = do+smallSegmentsReduction (Pat segred_pes) num_tblocks tblock_size _ space segbinops map_body_cont = do   let (gtids, dims) = unzip $ unSegSpace space       dims' = map pe64 dims       segment_size = last dims'@@ -378,39 +379,39 @@   segment_size_nonzero <-     dPrimVE "segment_size_nonzero" $ sMax64 1 segment_size -  let group_size_se = unCount group_size-      num_groups_se = unCount group_size-      num_groups' = pe64 num_groups_se-      group_size' = pe64 group_size_se-  num_threads <- fmap tvSize $ dPrimV "num_threads" $ num_groups' * group_size'+  let tblock_size_se = unCount tblock_size+      num_tblocks_se = unCount tblock_size+      num_tblocks' = pe64 num_tblocks_se+      tblock_size' = pe64 tblock_size_se+  num_threads <- fmap tvSize $ dPrimV "num_threads" $ num_tblocks' * tblock_size'   let num_segments = product $ init dims'-      segments_per_group = group_size' `quot` segment_size_nonzero-      required_groups = sExt32 $ num_segments `divUp` segments_per_group+      segments_per_block = tblock_size' `quot` segment_size_nonzero+      required_blocks = sExt32 $ num_segments `divUp` segments_per_block    emit $ Imp.DebugPrint "# SegRed-small" Nothing   emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments   emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size-  emit $ Imp.DebugPrint "segments_per_group" $ Just $ untyped segments_per_group-  emit $ Imp.DebugPrint "required_groups" $ Just $ untyped required_groups+  emit $ Imp.DebugPrint "segments_per_block" $ Just $ untyped segments_per_block+  emit $ Imp.DebugPrint "required_blocks" $ Just $ untyped required_blocks -  sKernelThread "segred_small" (segFlat space) (defKernelAttrs num_groups group_size) $ do+  sKernelThread "segred_small" (segFlat space) (defKernelAttrs num_tblocks tblock_size) $ do     constants <- kernelConstants <$> askEnv-    let group_id = kernelGroupSize constants+    let tblock_id = kernelBlockSize constants         ltid = sExt64 $ kernelLocalThreadId constants -    interms <- generalSegRedInterms group_id group_size_se segbinops-    let reds_arrs = map groupRedArrs interms+    interms <- generalSegRedInterms tblock_id tblock_size_se segbinops+    let reds_arrs = map blockRedArrs interms -    -- We probably do not have enough actual workgroups to cover the-    -- entire iteration space.  Some groups thus have to perform double+    -- We probably do not have enough actual threadblocks to cover the+    -- entire iteration space.  Some blocks thus have to perform double     -- duty; we put an outer loop to accomplish this.-    virtualiseGroups SegVirt required_groups $ \virtgroup_id -> do+    virtualiseBlocks SegVirt required_blocks $ \virttblock_id -> do       -- Compute the 'n' input indices.  The outer 'n-1' correspond to-      -- the segment ID, and are computed from the group id.  The inner+      -- the segment ID, and are computed from the block id.  The inner       -- is computed from the local thread id, and may be out-of-bounds.       let segment_index =             (ltid `quot` segment_size_nonzero)-              + (sExt64 virtgroup_id * sExt64 segments_per_group)+              + (sExt64 virttblock_id * sExt64 segments_per_block)           index_within_segment = ltid `rem` segment_size        dIndexSpace (zip (init gtids) (init dims')) segment_index@@ -434,7 +435,7 @@               .&&. isActive (init $ zip gtids dims)               .&&. ltid               .<. segment_size-              * segments_per_group+              * segments_per_block           )           in_bounds           out_of_bounds@@ -445,10 +446,10 @@       sWhen (segment_size .>. 0) $         sComment "perform segmented scan to imitate reduction" $           forM2_ segbinops reds_arrs $ \(SegBinOp _ red_op _ _) red_arrs ->-            groupScan+            blockScan               (Just crossesSegment)               (sExt64 $ pe64 num_threads)-              (segment_size * segments_per_group)+              (segment_size * segments_per_block)               red_op               red_arrs @@ -456,18 +457,18 @@        sComment "save final values of segments"         $ sWhen-          ( sExt64 virtgroup_id-              * segments_per_group+          ( sExt64 virttblock_id+              * segments_per_block               + sExt64 ltid                 .<. num_segments                 .&&. ltid-                .<. segments_per_group+                .<. segments_per_block           )         $ forM2_ segred_pes (concat reds_arrs)         $ \pe arr -> do           -- Figure out which segment result this thread should write...           let flat_segment_index =-                sExt64 virtgroup_id * segments_per_group + sExt64 ltid+                sExt64 virttblock_id * segments_per_block + sExt64 ltid               gtids' =                 unflattenIndex (init dims') flat_segment_index           copyDWIMFix@@ -481,91 +482,91 @@       sOp $ Imp.Barrier Imp.FenceLocal  largeSegmentsReduction :: DoCompileSegRed-largeSegmentsReduction (Pat segred_pes) num_groups group_size (chunk_v, chunk_const) space segbinops map_body_cont = do+largeSegmentsReduction (Pat segred_pes) num_tblocks tblock_size (chunk_v, chunk_const) space segbinops map_body_cont = do   let (gtids, dims) = unzip $ unSegSpace space       dims' = map pe64 dims       num_segments = product $ init dims'       segment_size = last dims'-      num_groups' = pe64 $ unCount num_groups-      group_size_se = unCount group_size-      group_size' = pe64 group_size_se+      num_tblocks' = pe64 $ unCount num_tblocks+      tblock_size_se = unCount tblock_size+      tblock_size' = pe64 tblock_size_se       chunk = tvExp chunk_v -  groups_per_segment <--    dPrimVE "groups_per_segment" $-      num_groups' `divUp` sMax64 1 num_segments+  blocks_per_segment <-+    dPrimVE "blocks_per_segment" $+      num_tblocks' `divUp` sMax64 1 num_segments    q <-     dPrimVE "q" $-      segment_size `divUp` (group_size' * groups_per_segment * chunk)+      segment_size `divUp` (tblock_size' * blocks_per_segment * chunk) -  num_virtgroups <--    dPrimV "num_virtgroups" $-      groups_per_segment * num_segments+  num_virtblocks <-+    dPrimV "num_virtblocks" $+      blocks_per_segment * num_segments   threads_per_segment <-     dPrimVE "threads_per_segment" $-      groups_per_segment * group_size'+      blocks_per_segment * tblock_size'    emit $ Imp.DebugPrint "# SegRed-large" Nothing   emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments   emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size-  emit $ Imp.DebugPrint "num_virtgroups" $ Just $ untyped $ tvExp num_virtgroups-  emit $ Imp.DebugPrint "num_groups" $ Just $ untyped num_groups'-  emit $ Imp.DebugPrint "group_size" $ Just $ untyped group_size'+  emit $ Imp.DebugPrint "num_virtblocks" $ Just $ untyped $ tvExp num_virtblocks+  emit $ Imp.DebugPrint "num_tblocks" $ Just $ untyped num_tblocks'+  emit $ Imp.DebugPrint "tblock_size" $ Just $ untyped tblock_size'   emit $ Imp.DebugPrint "q" $ Just $ untyped q-  emit $ Imp.DebugPrint "groups_per_segment" $ Just $ untyped groups_per_segment+  emit $ Imp.DebugPrint "blocks_per_segment" $ Just $ untyped blocks_per_segment -  reds_group_res_arrs <- groupResultArrays (tvSize num_virtgroups) group_size_se segbinops+  reds_block_res_arrs <- groupResultArrays (tvSize num_virtblocks) tblock_size_se segbinops    -- In principle we should have a counter for every segment.  Since   -- the number of segments is a dynamic quantity, we would have to   -- allocate and zero out an array here, which is expensive.   -- However, we exploit the fact that the number of segments being   -- reduced at any point in time is limited by the number of-  -- workgroups. If we bound the number of workgroups, we can get away+  -- threadblocks. If we bound the number of threadblocks, we can get away   -- with using that many counters.  FIXME: Is this limit checked   -- anywhere?  There are other places in the compiler that will fail-  -- if the group count exceeds the maximum group size, which is at+  -- if the block count exceeds the maximum block size, which is at   -- most 1024 anyway.   let num_counters = maxNumOps * 1024   counters <- genZeroes "counters" $ fromIntegral num_counters    let attrs =-        (defKernelAttrs num_groups group_size)+        (defKernelAttrs num_tblocks tblock_size)           { kAttrConstExps = M.singleton (tvVar chunk_v) chunk_const           }    sKernelThread "segred_large" (segFlat space) attrs $ do     constants <- kernelConstants <$> askEnv-    let group_id = sExt64 $ kernelGroupId constants+    let tblock_id = sExt64 $ kernelBlockId constants         ltid = kernelLocalThreadId constants -    interms <- makeIntermArrays group_id group_size_se (tvSize chunk_v) segbinops-    sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"+    interms <- makeIntermArrays tblock_id tblock_size_se (tvSize chunk_v) segbinops+    sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "shared" -    -- We probably do not have enough actual workgroups to cover the-    -- entire iteration space.  Some groups thus have to perform double+    -- We probably do not have enough actual threadblocks to cover the+    -- entire iteration space.  Some blocks thus have to perform double     -- duty; we put an outer loop to accomplish this.-    virtualiseGroups SegVirt (sExt32 (tvExp num_virtgroups)) $ \virtgroup_id -> do+    virtualiseBlocks SegVirt (sExt32 (tvExp num_virtblocks)) $ \virttblock_id -> do       let segment_gtids = init gtids        flat_segment_id <-         dPrimVE "flat_segment_id" $-          sExt64 virtgroup_id `quot` groups_per_segment+          sExt64 virttblock_id `quot` blocks_per_segment        global_tid <-         dPrimVE "global_tid" $-          (sExt64 virtgroup_id * sExt64 group_size' + sExt64 ltid)+          (sExt64 virttblock_id * sExt64 tblock_size' + sExt64 ltid)             `rem` threads_per_segment -      let first_group_for_segment = flat_segment_id * groups_per_segment+      let first_block_for_segment = flat_segment_id * blocks_per_segment       dIndexSpace (zip segment_gtids (init dims')) flat_segment_id       dPrim_ (last gtids) int64       let n = pe64 $ last dims        slugs <--        mapM (segBinOpSlug ltid virtgroup_id) $-          zip3 segbinops interms reds_group_res_arrs+        mapM (segBinOpSlug ltid virttblock_id) $+          zip3 segbinops interms reds_block_res_arrs       new_lambdas <-         reductionStageOne           gtids@@ -582,7 +583,7 @@               (map (length . segBinOpNeutral) segbinops)               segred_pes -          multiple_groups_per_segment =+          multiple_blocks_per_segment =             forM_ (zip4 segred_pess slugs new_lambdas [0 ..]) $               \(pes, slug, new_lambda, i) -> do                 let counter_idx =@@ -591,24 +592,24 @@                           `rem` fromIntegral num_counters                 reductionStageTwo                   pes-                  virtgroup_id+                  virttblock_id                   (map Imp.le64 segment_gtids)-                  first_group_for_segment-                  groups_per_segment+                  first_block_for_segment+                  blocks_per_segment                   slug                   new_lambda                   counters                   sync_arr                   counter_idx -          one_group_per_segment =-            sComment "first thread in group saves final result to memory" $+          one_block_per_segment =+            sComment "first thread in block saves final result to memory" $               forM2_ slugs segred_pess $ \slug pes ->                 sWhen (ltid .==. 0) $                   forM2_ pes (slugAccs slug) $ \v (acc, acc_is) ->                     copyDWIMFix (patElemName v) (map Imp.le64 segment_gtids) (Var acc) acc_is -      sIf (groups_per_segment .==. 1) one_group_per_segment multiple_groups_per_segment+      sIf (blocks_per_segment .==. 1) one_block_per_segment multiple_blocks_per_segment  -- | Auxiliary information for a single reduction. A slug holds the `SegBinOp` -- operator for a single reduction, the different arrays required throughout@@ -618,11 +619,11 @@   { slugOp :: SegBinOp GPUMem,     -- | Intermediate arrays needed for the given reduction.     slugInterms :: SegRedIntermediateArrays,-    -- | Place(s) to store group accumulator(s) in stage 1 reduction.+    -- | Place(s) to store block accumulator(s) in stage 1 reduction.     slugAccs :: [(VName, [Imp.TExp Int64])],     -- | Global memory destination(s) for the final result(s) for this     -- particular reduction.-    groupResArrs :: [VName]+    blockResArrs :: [VName]   }  segBinOpSlug ::@@ -630,19 +631,19 @@   Imp.TExp Int32 ->   (SegBinOp GPUMem, SegRedIntermediateArrays, [VName]) ->   InKernelGen SegBinOpSlug-segBinOpSlug ltid group_id (op, interms, group_res_arrs) = do-  accs <- zipWithM mkAcc (lambdaParams (segBinOpLambda op)) group_res_arrs-  pure $ SegBinOpSlug op interms accs group_res_arrs+segBinOpSlug ltid tblock_id (op, interms, block_res_arrs) = do+  accs <- zipWithM mkAcc (lambdaParams (segBinOpLambda op)) block_res_arrs+  pure $ SegBinOpSlug op interms accs block_res_arrs   where-    mkAcc p group_res_arr+    mkAcc p block_res_arr       | Prim t <- paramType p,         shapeRank (segBinOpShape op) == 0 = do-          group_res_acc <- dPrim (baseString (paramName p) <> "_group_res_acc") t-          pure (tvVar group_res_acc, [])+          block_res_acc <- dPrim (baseString (paramName p) <> "_block_res_acc") t+          pure (tvVar block_res_acc, [])       -- if this is a non-primitive reduction, the global mem result array will       -- double as accumulator.       | otherwise =-          pure (group_res_arr, [sExt64 ltid, sExt64 group_id])+          pure (block_res_arr, [sExt64 ltid, sExt64 tblock_id])  slugLambda :: SegBinOpSlug -> Lambda GPUMem slugLambda = segBinOpLambda . slugOp@@ -665,8 +666,8 @@ slugSplitParams :: SegBinOpSlug -> ([LParam GPUMem], [LParam GPUMem]) slugSplitParams slug = splitAt (length (slugNeutral slug)) $ slugParams slug -slugGroupRedArrs :: SegBinOpSlug -> [VName]-slugGroupRedArrs = groupRedArrs . slugInterms+slugBlockRedArrs :: SegBinOpSlug -> [VName]+slugBlockRedArrs = blockRedArrs . slugInterms  slugPrivChunks :: SegBinOpSlug -> [VName] slugPrivChunks = privateChunks . slugInterms@@ -691,32 +692,32 @@    dScope Nothing $ scopeOfLParams $ concatMap slugParams slugs -  sComment "ne-initialise the outer (per-group) accumulator(s)" $ do+  sComment "ne-initialise the outer (per-block) accumulator(s)" $ do     forM_ slugs $ \slug ->       forM2_ (slugAccs slug) (slugNeutral slug) $ \(acc, acc_is) ne ->         sLoopNest (slugShape slug) $ \vec_is ->           copyDWIMFix acc (acc_is ++ vec_is) ne []    new_lambdas <- mapM (renameLambda . slugLambda) slugs-  let group_size = sExt32 $ kernelGroupSize constants-  let doGroupReduce =+  let tblock_size = sExt32 $ kernelBlockSize constants+  let doBlockReduce =         forM2_ slugs new_lambdas $ \slug new_lambda -> do           let accs = slugAccs slug           let params = slugParams slug           sLoopNest (slugShape slug) $ \vec_is -> do-            let group_red_arrs = slugGroupRedArrs slug+            let block_red_arrs = slugBlockRedArrs slug             sComment "store accs. prims go in lmem; non-prims in params (in global mem)" $-              forM_ (zip3 group_red_arrs accs params) $+              forM_ (zip3 block_red_arrs accs params) $                 \(arr, (acc, acc_is), p) ->                   if isPrimParam p                     then copyDWIMFix arr [ltid] (Var acc) (acc_is ++ vec_is)                     else copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is)              sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.-            groupReduce group_size new_lambda group_red_arrs+            blockReduce tblock_size new_lambda block_red_arrs             sOp $ Imp.Barrier Imp.FenceLocal -            sComment "thread 0 updates per-group acc(s); rest reset to ne" $ do+            sComment "thread 0 updates per-block acc(s); rest reset to ne" $ do               sIf                 (ltid .==. 0)                 ( forM2_ accs (lambdaParams new_lambda) $@@ -738,7 +739,7 @@         q         n         chunk-        doGroupReduce+        doBlockReduce     _ ->       generalStageOneBody         slugs@@ -748,7 +749,7 @@         q         n         threads_per_segment-        doGroupReduce+        doBlockReduce    pure new_lambdas @@ -762,24 +763,24 @@   Imp.TExp Int64 ->   InKernelGen () ->   InKernelGen ()-generalStageOneBody slugs body_cont glb_ind_var global_tid q n threads_per_segment doGroupReduce = do+generalStageOneBody slugs body_cont glb_ind_var global_tid q n threads_per_segment doBlockReduce = do   let is_comm = slugsComm slugs == Commutative    constants <- kernelConstants <$> askEnv-  let group_size = kernelGroupSize constants+  let tblock_size = kernelBlockSize constants       ltid = sExt64 $ kernelLocalThreadId constants -  -- this group's id within its designated segment, and this group's initial+  -- this block's id within its designated segment, and this block's initial   -- global offset.-  group_id_in_segment <- dPrimVE "group_id_in_segment" $ global_tid `quot` group_size-  group_base_offset <- dPrimVE "group_base_offset" $ group_id_in_segment * q * group_size+  tblock_id_in_segment <- dPrimVE "tblock_id_in_segment" $ global_tid `quot` tblock_size+  block_base_offset <- dPrimVE "block_base_offset" $ tblock_id_in_segment * q * tblock_size    sFor "i" q $ \i -> do-    group_offset <- dPrimVE "group_offset" $ group_base_offset + i * group_size+    block_offset <- dPrimVE "block_offset" $ block_base_offset + i * tblock_size     glb_ind_var       <-- if is_comm         then global_tid + threads_per_segment * i-        else group_offset + ltid+        else block_offset + ltid      sWhen (tvExp glb_ind_var .<. n) $       sComment "apply map function(s)" $@@ -804,9 +805,9 @@                 $ \(acc, acc_is) se ->                   copyDWIMFix acc (acc_is ++ vec_is) se [] -    unless is_comm doGroupReduce+    unless is_comm doBlockReduce   sOp $ Imp.ErrorSync Imp.FenceLocal-  when is_comm doGroupReduce+  when is_comm doBlockReduce  noncommPrimParamsStageOneBody ::   [SegBinOpSlug] ->@@ -818,25 +819,25 @@   Imp.TExp Int64 ->   InKernelGen () ->   InKernelGen ()-noncommPrimParamsStageOneBody slugs body_cont glb_ind_var global_tid q n chunk doLMemGroupReduce = do+noncommPrimParamsStageOneBody slugs body_cont glb_ind_var global_tid q n chunk doLMemBlockReduce = do   constants <- kernelConstants <$> askEnv-  let group_size = kernelGroupSize constants+  let tblock_size = kernelBlockSize constants       ltid = sExt64 $ kernelLocalThreadId constants -  -- this group's id within its designated segment; the stride made per group in-  -- the outer `i < q` loop; and this group's initial global offset.-  group_id_in_segment <- dPrimVE "group_offset_in_segment" $ global_tid `quot` group_size-  group_stride <- dPrimVE "group_stride" $ group_size * chunk-  group_base_offset <- dPrimVE "group_base_offset" $ group_id_in_segment * q * group_stride+  -- this block's id within its designated segment; the stride made per block in+  -- the outer `i < q` loop; and this block's initial global offset.+  tblock_id_in_segment <- dPrimVE "block_offset_in_segment" $ global_tid `quot` tblock_size+  block_stride <- dPrimVE "block_stride" $ tblock_size * chunk+  block_base_offset <- dPrimVE "block_base_offset" $ tblock_id_in_segment * q * block_stride    let chunkLoop = sFor "k" chunk    sFor "i" q $ \i -> do-    -- group offset in this iteration.-    group_offset <- dPrimVE "group_offset" $ group_base_offset + i * group_stride+    -- block offset in this iteration.+    block_offset <- dPrimVE "block_offset" $ block_base_offset + i * block_stride     chunkLoop $ \k -> do-      loc_ind <- dPrimVE "loc_ind" $ k * group_size + ltid-      glb_ind_var <-- group_offset + loc_ind+      loc_ind <- dPrimVE "loc_ind" $ k * tblock_size + ltid+      glb_ind_var <-- block_offset + loc_ind        sIf         (tvExp glb_ind_var .<. n)@@ -864,7 +865,7 @@          forM2_ coll_copy_arrs priv_chunks $ \lmem_arr priv_chunk -> do           chunkLoop $ \k -> do-            lmem_idx <- dPrimVE "lmem_idx" $ ltid + k * group_size+            lmem_idx <- dPrimVE "lmem_idx" $ ltid + k * tblock_size             copyDWIMFix lmem_arr [lmem_idx] (Var priv_chunk) [k]            sOp $ Imp.Barrier Imp.FenceLocal@@ -892,7 +893,7 @@             compileStms mempty (bodyStms $ slugBody slug) $               forM2_ accs binop_ress $ \acc binop_res ->                 copyDWIM acc [] binop_res []-    doLMemGroupReduce+    doLMemBlockReduce   sOp $ Imp.ErrorSync Imp.FenceLocal  reductionStageTwo ::@@ -907,27 +908,27 @@   VName ->   Imp.TExp Int64 ->   InKernelGen ()-reductionStageTwo segred_pes group_id segment_gtids first_group_for_segment groups_per_segment slug new_lambda counters sync_arr counter_idx = do+reductionStageTwo segred_pes tblock_id segment_gtids first_block_for_segment blocks_per_segment slug new_lambda counters sync_arr counter_idx = do   constants <- kernelConstants <$> askEnv    let ltid32 = kernelLocalThreadId constants       ltid = sExt64 ltid32-      group_size = kernelGroupSize constants+      tblock_size = kernelBlockSize constants    let (acc_params, next_params) = slugSplitParams slug-  let nes = slugNeutral slug-  let red_arrs = slugGroupRedArrs slug-  let group_res_arrs = groupResArrs slug+      nes = slugNeutral slug+      red_arrs = slugBlockRedArrs slug+      block_res_arrs = blockResArrs slug    old_counter <- dPrim "old_counter" int32   (counter_mem, _, counter_offset) <-     fullyIndexArray       counters       [counter_idx]-  sComment "first thread in group saves group result to global memory" $+  sComment "first thread in block saves block result to global memory" $     sWhen (ltid32 .==. 0) $ do-      forM_ (take (length nes) $ zip group_res_arrs (slugAccs slug)) $ \(v, (acc, acc_is)) ->-        copyDWIMFix v [0, sExt64 group_id] (Var acc) acc_is+      forM_ (take (length nes) $ zip block_res_arrs (slugAccs slug)) $ \(v, (acc, acc_is)) ->+        copyDWIMFix v [0, sExt64 tblock_id] (Var acc) acc_is       sOp $ Imp.MemFence Imp.FenceGlobal       -- Increment the counter, thus stating that our result is       -- available.@@ -939,17 +940,17 @@           counter_mem           counter_offset         $ untyped (1 :: Imp.TExp Int32)-      -- Now check if we were the last group to write our result.  If+      -- Now check if we were the last block to write our result.  If       -- so, it is our responsibility to produce the final result.-      sWrite sync_arr [0] $ untyped $ tvExp old_counter .==. groups_per_segment - 1+      sWrite sync_arr [0] $ untyped $ tvExp old_counter .==. blocks_per_segment - 1    sOp $ Imp.Barrier Imp.FenceGlobal -  is_last_group <- dPrim "is_last_group" Bool-  copyDWIMFix (tvVar is_last_group) [] (Var sync_arr) [0]-  sWhen (tvExp is_last_group) $ do-    -- The final group has written its result (and it was-    -- us!), so read in all the group results and perform the+  is_last_block <- dPrim "is_last_block" Bool+  copyDWIMFix (tvVar is_last_block) [] (Var sync_arr) [0]+  sWhen (tvExp is_last_block) $ do+    -- The final block has written its result (and it was+    -- us!), so read in all the block results and perform the     -- final stage of the reduction.  But first, we reset the     -- counter so it is ready for next time.  This is done     -- with an atomic to avoid warnings about write/write@@ -959,49 +960,49 @@         Imp.Atomic DefaultSpace $           Imp.AtomicAdd Int32 (tvVar old_counter) counter_mem counter_offset $             untyped $-              negate groups_per_segment+              negate blocks_per_segment      sLoopNest (slugShape slug) $ \vec_is -> do       unless (null $ slugShape slug) $         sOp (Imp.Barrier Imp.FenceLocal) -      -- There is no guarantee that the number of workgroups for the-      -- segment is less than the workgroup size, so each thread may+      -- There is no guarantee that the number of threadblocks for the+      -- segment is less than the threadblock size, so each thread may       -- have to read multiple elements.  We do this in a sequential       -- way that may induce non-coalesced accesses, but the total       -- number of accesses should be tiny here.       ---      -- TODO: here we *could* insert a collective copy of the num_groups-      -- per-group results. However, it may not be beneficial, since num_groups-      -- is not necessarily larger than group_size, meaning the number of+      -- TODO: here we *could* insert a collective copy of the num_tblocks+      -- per-block results. However, it may not be beneficial, since num_tblocks+      -- is not necessarily larger than tblock_size, meaning the number of       -- uncoalesced reads here may be insignificant. In fact, if we happen to-      -- have a num_groups < group_size, then the collective copy would add+      -- have a num_tblocks < tblock_size, then the collective copy would add       -- unnecessary overhead. Also, this code is only executed by a single-      -- group.-      sComment "read in the per-group-results" $ do+      -- block.+      sComment "read in the per-block-results" $ do         read_per_thread <-           dPrimVE "read_per_thread" $-            groups_per_segment `divUp` sExt64 group_size+            blocks_per_segment `divUp` sExt64 tblock_size          forM2_ acc_params nes $ \p ne ->           copyDWIM (paramName p) [] ne []          sFor "i" read_per_thread $ \i -> do-          group_res_id <--            dPrimVE "group_res_id" $+          block_res_id <-+            dPrimVE "block_res_id" $               ltid * read_per_thread + i-          index_of_group_res <--            dPrimVE "index_of_group_res" $-              first_group_for_segment + group_res_id+          index_of_block_res <-+            dPrimVE "index_of_block_res" $+              first_block_for_segment + block_res_id -          sWhen (group_res_id .<. groups_per_segment) $ do-            forM2_ next_params group_res_arrs $-              \p group_res_arr ->+          sWhen (block_res_id .<. blocks_per_segment) $ do+            forM2_ next_params block_res_arrs $+              \p block_res_arr ->                 copyDWIMFix                   (paramName p)                   []-                  (Var group_res_arr)-                  ([0, index_of_group_res] ++ vec_is)+                  (Var block_res_arr)+                  ([0, index_of_block_res] ++ vec_is)              compileStms mempty (bodyStms $ slugBody slug) $               forM2_ acc_params (map resSubExp $ bodyResult $ slugBody slug) $ \p se ->@@ -1013,8 +1014,8 @@        sOp $ Imp.ErrorSync Imp.FenceLocal -      sComment "reduce the per-group results" $ do-        groupReduce (sExt32 group_size) new_lambda red_arrs+      sComment "reduce the per-block results" $ do+        blockReduce (sExt32 tblock_size) new_lambda red_arrs          sComment "and back to memory with the final result" $           sWhen (ltid32 .==. 0) $@@ -1031,8 +1032,8 @@ -- reductions with all primitive parameters: -- --   These kernels need local memory for 1) the initial collective copy, 2) the---   (virtualized) group reductions, and (TODO: this one not implemented yet!)---   3) the final single-group collective copy. There are no dependencies+--   (virtualized) block reductions, and (TODO: this one not implemented yet!)+--   3) the final single-block collective copy. There are no dependencies --   between these three stages, so we can reuse the same pool of local mem for --   all three. These intermediates all go into local mem because of the --   assumption of primitive parameter types.@@ -1042,26 +1043,26 @@ --   across the three steps are: -- --   1) The initial collective copy from global to thread-private memory---   requires `group_size * CHUNK * max elem_sizes`, since the collective copies+--   requires `tblock_size * CHUNK * max elem_sizes`, since the collective copies --   are performed in sequence (ie. inputs to different reduction operators need --   not be held in local mem simultaneously).---   2) The intra-group reductions of local memory held per-thread results---   require `group_size * sum elem_sizes` bytes, since per-thread results for---   all fused reductions are group-reduced simultaneously.---   3) If group_size < num_groups, then after the final single-group collective---   copy, a thread-sequential reduction reduces the number of per-group partial---   results from num_groups down to group_size for each reduction array, such---   that they will each fit in the final intra-group reduction. This requires---   `num_groups * max elem_sizes`.+--   2) The intra-block reductions of local memory held per-thread results+--   require `tblock_size * sum elem_sizes` bytes, since per-thread results for+--   all fused reductions are block-reduced simultaneously.+--   3) If tblock_size < num_tblocks, then after the final single-block collective+--   copy, a thread-sequential reduction reduces the number of per-block partial+--   results from num_tblocks down to tblock_size for each reduction array, such+--   that they will each fit in the final intra-block reduction. This requires+--   `num_tblocks * max elem_sizes`. -- --   In summary, the total amount of local mem needed is the maximum between:---   1) initial collective copy: group_size * CHUNK * max elem_sizes---   2) intra-group reductions:  group_size * sum elem_sizes---   3) final collective copy:   num_groups * max elem_sizes+--   1) initial collective copy: tblock_size * CHUNK * max elem_sizes+--   2) intra-block reductions:  tblock_size * sum elem_sizes+--   3) final collective copy:   num_tblocks * max elem_sizes -- --   The amount of local mem will most likely be decided by 1) in most cases, --   unless the number of fused operators is very high *or* if we have a---   `num_groups > group_size * CHUNK`, but this is unlikely, in which case 2)+--   `num_tblocks > tblock_size * CHUNK`, but this is unlikely, in which case 2) --   and 3), respectively, will dominate. -- --   Aside from local memory, these kernels also require a CHUNK-sized array of@@ -1070,6 +1071,6 @@ -- For all other reductions, ie. commutative reductions, reductions with at -- least one non-primitive operator, and small segments reductions: -----   These kernels use local memory only for the intra-group reductions, and---   since they do not use chunking or CHUNK, they all require onlly `group_size+--   These kernels use local memory only for the intra-block reductions, and+--   since they do not use chunking or CHUNK, they all require onlly `tblock_size --   * max elem_sizes` bytes of local memory and no thread-private register mem.
src/Futhark/CodeGen/ImpGen/GPU/SegScan/SinglePass.hs view
@@ -26,31 +26,31 @@   drop (length (segBinOpNeutral scan)) (lambdaParams (segBinOpLambda scan))  createLocalArrays ::-  Count GroupSize SubExp ->+  Count BlockSize SubExp ->   SubExp ->   [PrimType] ->   InKernelGen (VName, [VName], [VName], VName, [VName])-createLocalArrays (Count groupSize) chunk types = do-  let groupSizeE = pe64 groupSize-      workSize = pe64 chunk * groupSizeE+createLocalArrays (Count block_size) chunk types = do+  let block_sizeE = pe64 block_size+      workSize = pe64 chunk * block_sizeE       prefixArraysSize =-        foldl (\acc tySize -> nextMul acc tySize + tySize * groupSizeE) 0 $+        foldl (\acc tySize -> nextMul acc tySize + tySize * block_sizeE) 0 $           map primByteSize types       maxTransposedArraySize =         foldl1 sMax64 $ map (\ty -> workSize * primByteSize ty) types -      warpSize :: (Num a) => a-      warpSize = 32+      warp_size :: (Num a) => a+      warp_size = 32       maxWarpExchangeSize =-        foldl (\acc tySize -> nextMul acc tySize + tySize * fromInteger warpSize) 0 $+        foldl (\acc tySize -> nextMul acc tySize + tySize * fromInteger warp_size) 0 $           map primByteSize types-      maxLookbackSize = maxWarpExchangeSize + warpSize+      maxLookbackSize = maxWarpExchangeSize + warp_size       size = Imp.bytes $ maxLookbackSize `sMax64` prefixArraysSize `sMax64` maxTransposedArraySize    (_, byteOffsets) <-     mapAccumLM       ( \off tySize -> do-          off' <- dPrimVE "byte_offsets" $ nextMul off tySize + pe64 groupSize * tySize+          off' <- dPrimVE "byte_offsets" $ nextMul off tySize + pe64 block_size * tySize           pure (off', off)       )       0@@ -59,15 +59,15 @@   (_, warpByteOffsets) <-     mapAccumLM       ( \off tySize -> do-          off' <- dPrimVE "warp_byte_offset" $ nextMul off tySize + warpSize * tySize+          off' <- dPrimVE "warp_byte_offset" $ nextMul off tySize + warp_size * tySize           pure (off', off)       )-      warpSize+      warp_size       $ map primByteSize types    sComment "Allocate reusable shared memory" $ pure () -  localMem <- sAlloc "local_mem" size (Space "local")+  localMem <- sAlloc "local_mem" size (Space "shared")   transposeArrayLength <- dPrimV "trans_arr_len" workSize    sharedId <- sArrayInMem "shared_id" int32 (Shape [constant (1 :: Int32)]) localMem@@ -86,20 +86,20 @@       sArray         "local_prefix_arr"         ty-        (Shape [groupSize])+        (Shape [block_size])         localMem-        $ LMAD.iota off' [pe64 groupSize]+        $ LMAD.iota off' [pe64 block_size] -  warpscan <- sArrayInMem "warpscan" int8 (Shape [constant (warpSize :: Int64)]) localMem+  warpscan <- sArrayInMem "warpscan" int8 (Shape [constant (warp_size :: Int64)]) localMem   warpExchanges <-     forM (zip warpByteOffsets types) $ \(off, ty) -> do       let off' = off `quot` primByteSize ty       sArray         "warp_exchange"         ty-        (Shape [constant (warpSize :: Int64)])+        (Shape [constant (warp_size :: Int64)])         localMem-        $ LMAD.iota off' [warpSize]+        $ LMAD.iota off' [warp_size]    pure (sharedId, transposedArrays, prefixArrays, warpscan, warpExchanges) @@ -232,19 +232,19 @@        tys = map elemType tys' -      group_size_e = pe64 $ unCount $ kAttrGroupSize attrs-      num_physgroups_e = pe64 $ unCount $ kAttrNumGroups attrs+      tblock_size_e = pe64 $ unCount $ kAttrBlockSize attrs+      num_phys_blocks_e = pe64 $ unCount $ kAttrNumBlocks attrs    let chunk_const = getChunkSize tys'   chunk_v <- dPrimV "chunk_size" . isInt64 =<< kernelConstToExp chunk_const   let chunk = tvExp chunk_v -  num_virtgroups <--    tvSize <$> dPrimV "num_virtgroups" (n `divUp` (group_size_e * chunk))-  let num_virtgroups_e = pe64 num_virtgroups+  num_virt_blocks <-+    tvSize <$> dPrimV "num_virt_blocks" (n `divUp` (tblock_size_e * chunk))+  let num_virt_blocks_e = pe64 num_virt_blocks    num_virt_threads <--    dPrimVE "num_virt_threads" $ num_virtgroups_e * group_size_e+    dPrimVE "num_virt_threads" $ num_virt_blocks_e * tblock_size_e    let (gtids, dims) = unzip $ unSegSpace space       dims' = map pe64 dims@@ -254,15 +254,15 @@    emit $ Imp.DebugPrint "Sequential elements per thread (chunk)" $ Just $ untyped chunk -  statusFlags <- sAllocArray "status_flags" int8 (Shape [num_virtgroups]) (Space "device")+  statusFlags <- sAllocArray "status_flags" int8 (Shape [num_virt_blocks]) (Space "device")   sReplicate statusFlags $ intConst Int8 statusX    (aggregateArrays, incprefixArrays) <-     fmap unzip $       forM tys $ \ty ->         (,)-          <$> sAllocArray "aggregates" ty (Shape [num_virtgroups]) (Space "device")-          <*> sAllocArray "incprefixes" ty (Shape [num_virtgroups]) (Space "device")+          <$> sAllocArray "aggregates" ty (Shape [num_virt_blocks]) (Space "device")+          <*> sAllocArray "incprefixes" ty (Shape [num_virt_blocks]) (Space "device")    global_id <- genZeroes "global_dynid" 1 @@ -277,22 +277,23 @@         ltid = sExt64 ltid32      (sharedId, transposedArrays, prefixArrays, warpscan, exchanges) <--      createLocalArrays (kAttrGroupSize attrs) (tvSize chunk_v) tys+      createLocalArrays (kAttrBlockSize attrs) (tvSize chunk_v) tys -    -- We wrap the entire kernel body in a virtualisation loop to handle the-    -- case where we do not have enough workgroups to cover the iteration space.-    -- Dynamic group indexing has no implication on this, since each group-    -- simply fetches a new dynamic ID upon entry into the virtualisation loop.+    -- We wrap the entire kernel body in a virtualisation loop to+    -- handle the case where we do not have enough thread blocks to+    -- cover the iteration space. Dynamic block indexing has no+    -- implication on this, since each block simply fetches a new+    -- dynamic ID upon entry into the virtualisation loop.     ---    -- We could use virtualiseGroups, but this introduces a barrier which is-    -- redundant in this case, and also we don't need to base virtual group IDs+    -- We could use virtualiseBlocks, but this introduces a barrier which is+    -- redundant in this case, and also we don't need to base virtual block IDs     -- on the loop variable, but rather on the dynamic IDs.-    physgroup_id <- dPrim "physgroup_id" int32-    sOp $ Imp.GetGroupId (tvVar physgroup_id) 0+    phys_block_id <- dPrim "phys_block_id" int32+    sOp $ Imp.GetBlockId (tvVar phys_block_id) 0     iters <-       dPrimVE "virtloop_bound" $-        (num_virtgroups_e - tvExp physgroup_id)-          `divUp` num_physgroups_e+        (num_virt_blocks_e - tvExp phys_block_id)+          `divUp` num_phys_blocks_e      sFor "virtloop_i" iters $ const $ do       dyn_id <- dPrim "dynamic_id" int32@@ -311,7 +312,7 @@             copyDWIMFix sharedId [0] (tvSize dyn_id) []            sComment "First thread in last (virtual) block resets global dynamic_id" $ do-            sWhen (tvExp dyn_id .==. num_virtgroups_e - 1) $+            sWhen (tvExp dyn_id .==. num_virt_blocks_e - 1) $               copyDWIMFix global_id [0] (intConst Int32 0) []        let local_barrier = Imp.Barrier Imp.FenceLocal@@ -324,16 +325,16 @@        block_offset <-         dPrimVE "block_offset" $-          sExt64 (tvExp dyn_id) * chunk * group_size_e+          sExt64 (tvExp dyn_id) * chunk * tblock_size_e       sgm_idx <- dPrimVE "sgm_idx" $ block_offset `mod` segment_size       boundary <-         dPrimVE "boundary" $           sExt32 $-            sMin64 (chunk * group_size_e) (segment_size - sgm_idx)+            sMin64 (chunk * tblock_size_e) (segment_size - sgm_idx)       segsize_compact <-         dPrimVE "segsize_compact" $           sExt32 $-            sMin64 (chunk * group_size_e) segment_size+            sMin64 (chunk * tblock_size_e) segment_size       private_chunks <-         forM tys $ \ty ->           sAllocArray@@ -347,7 +348,7 @@       sComment "Load and map" $         sFor "i" chunk $ \i -> do           -- The map's input index-          virt_tid <- dPrimVE "virt_tid" $ thd_offset + i * group_size_e+          virt_tid <- dPrimVE "virt_tid" $ thd_offset + i * tblock_size_e           dIndexSpace (zip gtids dims') virt_tid           -- Perform the map           let in_bounds =@@ -373,7 +374,7 @@       sComment "Transpose scan inputs" $ do         forM_ (zip transposedArrays private_chunks) $ \(trans, priv) -> do           sFor "i" chunk $ \i -> do-            sharedIdx <- dPrimVE "sharedIdx" $ ltid + i * group_size_e+            sharedIdx <- dPrimVE "sharedIdx" $ ltid + i * tblock_size_e             copyDWIMFix trans [sharedIdx] (Var priv) [i]           sOp local_barrier           sFor "i" chunk $ \i -> do@@ -422,9 +423,9 @@        accs <- mapM (dPrim "acc") tys       sComment "Scan results (with warp scan)" $ do-        groupScan+        blockScan           crossesSegment-          group_size_e+          tblock_size_e           num_virt_threads           scan_op1           prefixArrays@@ -432,7 +433,7 @@         sOp $ Imp.ErrorSync Imp.FenceLocal          let firstThread acc prefixes =-              copyDWIMFix (tvVar acc) [] (Var prefixes) [sExt64 group_size_e - 1]+              copyDWIMFix (tvVar acc) [] (Var prefixes) [sExt64 tblock_size_e - 1]             notFirstThread acc prefixes =               copyDWIMFix (tvVar acc) [] (Var prefixes) [ltid - 1]         sIf@@ -457,11 +458,11 @@             copyDWIMFix (tvVar acc) [] ne []         -- end sWhen -        let warpSize = kernelWaveSize constants-        sWhen (bNot blockNewSgm .&&. ltid32 .<. warpSize) $ do+        let warp_size = kernelWaveSize constants+        sWhen (bNot blockNewSgm .&&. ltid32 .<. warp_size) $ do           sWhen (ltid32 .==. 0) $ do             sIf-              (not_segmented_e .||. boundary .==. sExt32 (group_size_e * chunk))+              (not_segmented_e .||. boundary .==. sExt32 (tblock_size_e * chunk))               ( do                   everythingVolatile $                     forM_ (zip aggregateArrays accs) $ \(aggregateArray, acc) ->@@ -498,10 +499,10 @@                   dPrimV "readOffset" $                     sExt32 $                       tvExp dyn_id - sExt64 (kernelWaveSize constants)-                let loopStop = warpSize * (-1)+                let loopStop = warp_size * (-1)                     sameSegment readIdx                       | segmented =-                          let startIdx = sExt64 (tvExp readIdx + 1) * group_size_e * chunk - 1+                          let startIdx = sExt64 (tvExp readIdx + 1) * tblock_size_e * chunk - 1                            in block_offset - startIdx .<=. sgm_idx                       | otherwise = true                 sWhile (tvExp readOffset .>. loopStop) $ do@@ -533,7 +534,7 @@                   copyDWIMFix warpscan [ltid] (tvSize flag) []                    -- execute warp-parallel reduction but only if the last read flag in not STATUS_P-                  copyDWIMFix (tvVar flag) [] (Var warpscan) [sExt64 warpSize - 1]+                  copyDWIMFix (tvVar flag) [] (Var warpscan) [sExt64 warp_size - 1]                   sWhen (tvExp flag .<. statusP) $ do                     lam' <- renameLambda scan_op1                     inBlockScanLookback@@ -544,15 +545,15 @@                       lam'                    -- all threads of the warp read the result of reduction-                  copyDWIMFix (tvVar flag) [] (Var warpscan) [sExt64 warpSize - 1]+                  copyDWIMFix (tvVar flag) [] (Var warpscan) [sExt64 warp_size - 1]                   forM_ (zip aggrs exchanges) $ \(aggr, exchange) ->-                    copyDWIMFix (tvVar aggr) [] (Var exchange) [sExt64 warpSize - 1]+                    copyDWIMFix (tvVar aggr) [] (Var exchange) [sExt64 warp_size - 1]                   -- update read offset                   sIf                     (tvExp flag .==. statusP)                     (readOffset <-- loopStop)                     ( sWhen (tvExp flag .==. statusA) $ do-                        readOffset <-- tvExp readOffset - zExt32 warpSize+                        readOffset <-- tvExp readOffset - zExt32 warp_size                     )                    -- update prefix if flag different than STATUS_X:@@ -573,7 +574,7 @@             scan_op2 <- renameLambda scan_op1             let xs = map paramName $ take (length tys) $ lambdaParams scan_op2                 ys = map paramName $ drop (length tys) $ lambdaParams scan_op2-            sWhen (boundary .==. sExt32 (group_size_e * chunk)) $ do+            sWhen (boundary .==. sExt32 (tblock_size_e * chunk)) $ do               forM_ (zip xs prefixes) $ \(x, prefix) -> dPrimV_ x $ tvExp prefix               forM_ (zip ys accs) $ \(y, acc) -> dPrimV_ y $ tvExp acc               compileStms mempty (bodyStms $ lambdaBody scan_op2) $@@ -643,7 +644,7 @@             copyDWIMFix locmem [tvExp sharedIdx] (Var priv) [i]           sOp local_barrier           sFor "i" chunk $ \i -> do-            flat_idx <- dPrimVE "flat_idx" $ thd_offset + i * group_size_e+            flat_idx <- dPrimVE "flat_idx" $ thd_offset + i * tblock_size_e             dIndexSpace (zip gtids dims') flat_idx             sWhen (flat_idx .<. n) $ do               copyDWIMFix
src/Futhark/CodeGen/ImpGen/GPU/SegScan/TwoPass.hs view
@@ -21,15 +21,15 @@ -- Aggressively try to reuse memory for different SegBinOps, because -- we will run them sequentially after another. makeLocalArrays ::-  Count GroupSize SubExp ->+  Count BlockSize SubExp ->   SubExp ->   [SegBinOp GPUMem] ->   InKernelGen [[VName]]-makeLocalArrays (Count group_size) num_threads scans = do+makeLocalArrays (Count tblock_size) num_threads scans = do   (arrs, mems_and_sizes) <- runStateT (mapM onScan scans) mempty   let maxSize sizes = Imp.bytes $ foldl' sMax64 1 $ map Imp.unCount sizes   forM_ mems_and_sizes $ \(sizes, mem) ->-    sAlloc_ mem (maxSize sizes) (Space "local")+    sAlloc_ mem (maxSize sizes) (Space "shared")   pure arrs   where     onScan (SegBinOp _ scan_op nes _) = do@@ -46,7 +46,7 @@               pure (arr, [])             _ -> do               let pt = elemType $ paramType p-                  shape = Shape [group_size]+                  shape = Shape [tblock_size]               (sizes, mem') <- getMem pt shape               arr <- lift $ sArrayInMem "scan_arr" pt shape mem'               pure (arr, [(sizes, mem')])@@ -64,7 +64,7 @@           put mems'           pure (size : size', mem)         (Nothing, []) -> do-          mem <- lift $ sDeclareMem "scan_arr_mem" $ Space "local"+          mem <- lift $ sDeclareMem "scan_arr_mem" $ Space "shared"           pure ([size], mem)  type CrossesSegment = Maybe (Imp.TExp Int64 -> Imp.TExp Int64 -> Imp.TExp Bool)@@ -143,25 +143,25 @@   | otherwise =       pure () --- | Produce partially scanned intervals; one per workgroup.+-- | Produce partially scanned intervals; one per threadblock. scanStage1 ::   Pat LetDecMem ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   SegSpace ->   [SegBinOp GPUMem] ->   KernelBody GPUMem ->   CallKernelGen (TV Int32, Imp.TExp Int64, CrossesSegment)-scanStage1 (Pat all_pes) num_groups group_size space scans kbody = do-  let num_groups' = fmap pe64 num_groups-      group_size' = fmap pe64 group_size-  num_threads <- dPrimV "num_threads" $ sExt32 $ unCount num_groups' * unCount group_size'+scanStage1 (Pat all_pes) num_tblocks tblock_size space scans kbody = do+  let num_tblocks' = fmap pe64 num_tblocks+      tblock_size' = fmap pe64 tblock_size+  num_threads <- dPrimV "num_threads" $ sExt32 $ unCount num_tblocks' * unCount tblock_size'    let (gtids, dims) = unzip $ unSegSpace space       dims' = map pe64 dims   let num_elements = product dims'       elems_per_thread = num_elements `divUp` sExt64 (tvExp num_threads)-      elems_per_group = unCount group_size' * elems_per_thread+      elems_per_group = unCount tblock_size' * elems_per_thread    let crossesSegment =         case reverse dims' of@@ -169,9 +169,9 @@             (to - from) .>. (to `rem` segment_size)           _ -> Nothing -  sKernelThread "scan_stage1" (segFlat space) (defKernelAttrs num_groups group_size) $ do+  sKernelThread "scan_stage1" (segFlat space) (defKernelAttrs num_tblocks tblock_size) $ do     constants <- kernelConstants <$> askEnv-    all_local_arrs <- makeLocalArrays group_size (tvSize num_threads) scans+    all_local_arrs <- makeLocalArrays tblock_size (tvSize num_threads) scans      -- The variables from scan_op will be used for the carry and such     -- in the big chunking loop.@@ -183,8 +183,8 @@     sFor "j" elems_per_thread $ \j -> do       chunk_offset <-         dPrimV "chunk_offset" $-          sExt64 (kernelGroupSize constants) * j-            + sExt64 (kernelGroupId constants) * elems_per_group+          sExt64 (kernelBlockSize constants) * j+            + sExt64 (kernelBlockId constants) * elems_per_group       flat_idx <-         dPrimV "flat_idx" $           tvExp chunk_offset + sExt64 (kernelLocalThreadId constants)@@ -259,10 +259,10 @@                -- We need to avoid parameter name clashes.               scan_op_renamed <- renameLambda scan_op-              groupScan+              blockScan                 crossesSegment'                 (sExt64 $ tvExp num_threads)-                (sExt64 $ kernelGroupSize constants)+                (sExt64 $ kernelBlockSize constants)                 scan_op_renamed                 local_arrs @@ -284,10 +284,10 @@                         []                         (Var arr)                         [ if primType $ paramType p-                            then sExt64 (kernelGroupSize constants) - 1+                            then sExt64 (kernelBlockSize constants) - 1                             else-                              (sExt64 (kernelGroupId constants) + 1)-                                * sExt64 (kernelGroupSize constants)+                              (sExt64 (kernelBlockId constants) + 1)+                                * sExt64 (kernelBlockSize constants)                                 - 1                         ]                   load_neutral =@@ -301,11 +301,11 @@                     Just f ->                       f                         ( tvExp chunk_offset-                            + sExt64 (kernelGroupSize constants)+                            + sExt64 (kernelBlockSize constants)                             - 1                         )                         ( tvExp chunk_offset-                            + sExt64 (kernelGroupSize constants)+                            + sExt64 (kernelBlockSize constants)                         )                 should_load_carry <-                   dPrimVE "should_load_carry" $@@ -322,17 +322,17 @@   Pat LetDecMem ->   TV Int32 ->   Imp.TExp Int64 ->-  Count NumGroups SubExp ->+  Count NumBlocks SubExp ->   CrossesSegment ->   SegSpace ->   [SegBinOp GPUMem] ->   CallKernelGen ()-scanStage2 (Pat all_pes) stage1_num_threads elems_per_group num_groups crossesSegment space scans = do+scanStage2 (Pat all_pes) stage1_num_threads elems_per_group num_tblocks crossesSegment space scans = do   let (gtids, dims) = unzip $ unSegSpace space       dims' = map pe64 dims    -- Our group size is the number of groups for the stage 1 kernel.-  let group_size = Count $ unCount num_groups+  let tblock_size = Count $ unCount num_tblocks    let crossesSegment' = do         f <- crossesSegment@@ -341,9 +341,9 @@             ((sExt64 from + 1) * elems_per_group - 1)             ((sExt64 to + 1) * elems_per_group - 1) -  sKernelThread "scan_stage2" (segFlat space) (defKernelAttrs (Count (intConst Int64 1)) group_size) $ do+  sKernelThread "scan_stage2" (segFlat space) (defKernelAttrs (Count (intConst Int64 1)) tblock_size) $ do     constants <- kernelConstants <$> askEnv-    per_scan_local_arrs <- makeLocalArrays group_size (tvSize stage1_num_threads) scans+    per_scan_local_arrs <- makeLocalArrays tblock_size (tvSize stage1_num_threads) scans     let per_scan_rets = map (lambdaReturnType . segBinOpLambda) scans         per_scan_pes = segBinOpChunks scans all_pes @@ -378,10 +378,10 @@            barrier -          groupScan+          blockScan             crossesSegment'             (sExt64 $ tvExp stage1_num_threads)-            (sExt64 $ kernelGroupSize constants)+            (sExt64 $ kernelBlockSize constants)             scan_op             local_arrs @@ -396,30 +396,30 @@  scanStage3 ::   Pat LetDecMem ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   Imp.TExp Int64 ->   CrossesSegment ->   SegSpace ->   [SegBinOp GPUMem] ->   CallKernelGen ()-scanStage3 (Pat all_pes) num_groups group_size elems_per_group crossesSegment space scans = do-  let group_size' = fmap pe64 group_size+scanStage3 (Pat all_pes) num_tblocks tblock_size elems_per_group crossesSegment space scans = do+  let tblock_size' = fmap pe64 tblock_size       (gtids, dims) = unzip $ unSegSpace space       dims' = map pe64 dims   required_groups <-     dPrimVE "required_groups" $       sExt32 $-        product dims' `divUp` sExt64 (unCount group_size')+        product dims' `divUp` sExt64 (unCount tblock_size') -  sKernelThread "scan_stage3" (segFlat space) (defKernelAttrs num_groups group_size) $-    virtualiseGroups SegVirt required_groups $ \virt_group_id -> do+  sKernelThread "scan_stage3" (segFlat space) (defKernelAttrs num_tblocks tblock_size) $+    virtualiseBlocks SegVirt required_groups $ \virt_tblock_id -> do       constants <- kernelConstants <$> askEnv        -- Compute our logical index.       flat_idx <-         dPrimVE "flat_idx" $-          sExt64 virt_group_id * sExt64 (unCount group_size')+          sExt64 virt_tblock_id * sExt64 (unCount tblock_size')             + sExt64 (kernelLocalThreadId constants)       zipWithM_ dPrimV_ gtids $ unflattenIndex dims' flat_idx @@ -494,20 +494,20 @@   -- Since stage 2 involves a group size equal to the number of groups   -- used for stage 1, we have to cap this number to the maximum group   -- size.-  stage1_max_num_groups <- dPrim "stage1_max_num_groups" int64-  sOp $ Imp.GetSizeMax (tvVar stage1_max_num_groups) SizeGroup+  stage1_max_num_tblocks <- dPrim "stage1_max_num_tblocks" int64+  sOp $ Imp.GetSizeMax (tvVar stage1_max_num_tblocks) SizeThreadBlock -  stage1_num_groups <-+  stage1_num_tblocks <-     fmap (Imp.Count . tvSize) $-      dPrimV "stage1_num_groups" $-        sMin64 (tvExp stage1_max_num_groups) $-          pe64 . Imp.unCount . kAttrNumGroups $+      dPrimV "stage1_num_tblocks" $+        sMin64 (tvExp stage1_max_num_tblocks) $+          pe64 . Imp.unCount . kAttrNumBlocks $             attrs    (stage1_num_threads, elems_per_group, crossesSegment) <--    scanStage1 pat stage1_num_groups (kAttrGroupSize attrs) space scans kbody+    scanStage1 pat stage1_num_tblocks (kAttrBlockSize attrs) space scans kbody    emit $ Imp.DebugPrint "elems_per_group" $ Just $ untyped elems_per_group -  scanStage2 pat stage1_num_threads elems_per_group stage1_num_groups crossesSegment space scans-  scanStage3 pat (kAttrNumGroups attrs) (kAttrGroupSize attrs) elems_per_group crossesSegment space scans+  scanStage2 pat stage1_num_threads elems_per_group stage1_num_tblocks crossesSegment space scans+  scanStage3 pat (kAttrNumBlocks attrs) (kAttrBlockSize attrs) elems_per_group crossesSegment space scans
src/Futhark/CodeGen/ImpGen/GPU/ToOpenCL.hs view
@@ -137,7 +137,7 @@  pointerQuals :: String -> [C.TypeQual] pointerQuals "global" = [C.ctyquals|__global|]-pointerQuals "local" = [C.ctyquals|__local|]+pointerQuals "shared" = [C.ctyquals|__local|] pointerQuals "private" = [C.ctyquals|__private|] pointerQuals "constant" = [C.ctyquals|__constant|] pointerQuals "write_only" = [C.ctyquals|__write_only|]@@ -149,11 +149,11 @@ pointerQuals "device" = pointerQuals "global" pointerQuals s = error $ "'" ++ s ++ "' is not an OpenCL kernel address space." --- In-kernel name and per-workgroup size in bytes.-type LocalMemoryUse = (VName, Count Bytes (TExp Int64))+-- In-kernel name and per-threadblock size in bytes.+type SharedMemoryUse = (VName, Count Bytes (TExp Int64))  data KernelState = KernelState-  { kernelLocalMemory :: [LocalMemoryUse],+  { kernelSharedMemory :: [SharedMemoryUse],     kernelFailures :: [FailureMsg],     kernelNextSync :: Int,     -- | Has a potential failure occurred sine the last@@ -330,7 +330,7 @@     toDevice :: HostOp -> KernelOp     toDevice _ = bad -isConst :: GroupDim -> Maybe T.Text+isConst :: BlockDim -> Maybe T.Text isConst (Left (ValueExp (IntValue x))) =   Just $ prettyText $ intToInt64 x isConst (Right (SizeConst v _)) =@@ -359,10 +359,10 @@       (kernel_consts, (const_defs, const_undefs)) =         second unzip $ unzip $ mapMaybe (constDef (kernelName kernel)) $ kernelUses kernel -  let (local_memory_bytes, (local_memory_params, local_memory_args, local_memory_init)) =+  let (shared_memory_bytes, (shared_memory_params, shared_memory_args, shared_memory_init)) =         second unzip3 $           evalState-            (mapAccumLM prepareLocalMemory 0 (kernelLocalMemory kstate))+            (mapAccumLM prepareSharedMemory 0 (kernelSharedMemory kstate))             blankNameSource    let (use_params, unpack_params) =@@ -417,23 +417,23 @@           [C.cparam|__global typename int64_t *global_failure_args|]         ] -      (local_memory_param, prepare_local_memory) =+      (shared_memory_param, prepare_shared_memory) =         case target of           TargetOpenCL ->-            ( [[C.cparam|__local typename uint64_t* local_mem_aligned|]],-              [C.citems|__local unsigned char* local_mem = local_mem_aligned;|]+            ( [[C.cparam|__local typename uint64_t* shared_mem_aligned|]],+              [C.citems|__local unsigned char* shared_mem = (__local unsigned char*)shared_mem_aligned;|]             )           TargetCUDA -> (mempty, mempty)           TargetHIP -> (mempty, mempty)        params =-        local_memory_param+        shared_memory_param           ++ take (numFailureParams safety) failure_params-          ++ local_memory_params+          ++ shared_memory_params           ++ use_params        attribute =-        case mapM isConst $ kernelGroupSize kernel of+        case mapM isConst $ kernelBlockSize kernel of           Just [x, y, z] ->             "FUTHARK_KERNEL_SIZED" <> prettyText (x, y, z) <> "\n"           Just [x, y] ->@@ -448,8 +448,8 @@             [C.cfun|void $id:name ($params:params) {                     $items:(mconcat unpack_params)                     $items:const_defs-                    $items:prepare_local_memory-                    $items:local_memory_init+                    $items:prepare_shared_memory+                    $items:shared_memory_init                     $items:error_init                     $items:kernel_body @@ -466,23 +466,23 @@       }    -- The error handling stuff is automatically added later.-  let args = local_memory_args ++ kernelArgs kernel+  let args = shared_memory_args ++ kernelArgs kernel -  pure $ LaunchKernel safety name local_memory_bytes args num_groups group_size+  pure $ LaunchKernel safety name shared_memory_bytes args num_tblocks tblock_size   where     name = kernelName kernel-    num_groups = kernelNumGroups kernel-    group_size = kernelGroupSize kernel+    num_tblocks = kernelNumBlocks kernel+    tblock_size = kernelBlockSize kernel     padTo8 e = e + ((8 - (e `rem` 8)) `rem` 8) -    prepareLocalMemory (Count offset) (mem, Count size) = do+    prepareSharedMemory (Count offset) (mem, Count size) = do       param <- newVName $ baseString mem ++ "_offset"       let offset' = offset + padTo8 size       pure         ( bytes offset',           ( [C.cparam|typename int64_t $id:param|],             ValueKArg (untyped offset) $ IntType Int64,-            [C.citem|volatile __local $ty:defaultMemBlockType $id:mem = &local_mem[$id:param];|]+            [C.citem|volatile __local $ty:defaultMemBlockType $id:mem = &shared_mem[$id:param];|]           )         ) @@ -614,8 +614,8 @@     fence FenceGlobal = [C.cexp|CLK_GLOBAL_MEM_FENCE | CLK_LOCAL_MEM_FENCE|]      kernelOps :: GC.OpCompiler KernelOp KernelState-    kernelOps (GetGroupId v i) =-      GC.stm [C.cstm|$id:v = get_group_id($int:i);|]+    kernelOps (GetBlockId v i) =+      GC.stm [C.cstm|$id:v = get_tblock_id($int:i);|]     kernelOps (GetLocalId v i) =       GC.stm [C.cstm|$id:v = get_local_id($int:i);|]     kernelOps (GetLocalSize v i) =@@ -632,7 +632,7 @@     kernelOps (LocalAlloc name size) = do       name' <- newVName $ prettyString name ++ "_backing"       GC.modifyUserState $ \s ->-        s {kernelLocalMemory = (name', size) : kernelLocalMemory s}+        s {kernelSharedMemory = (name', size) : kernelSharedMemory s}       GC.stm [C.cstm|$id:name = (__local unsigned char*) $id:name';|]     kernelOps (ErrorSync f) = do       label <- nextErrorLabel
src/Futhark/CodeGen/OpenCL/Heuristics.hs view
@@ -1,13 +1,13 @@--- | Some OpenCL platforms have a SIMD/warp/wavefront-based execution--- model that execute groups of threads in lockstep, permitting us to--- perform cross-thread synchronisation within each such group without--- the use of barriers.  Unfortunately, there seems to be no reliable--- way to query these sizes at runtime.  Instead, we use builtin--- tables to figure out which size we should use for a specific--- platform and device.  If nothing matches here, the wave size should--- be set to one.+-- | Some GPU platforms have a SIMD/warp/wavefront-based execution+-- model that execute blocks of threads in lockstep, permitting us to+-- perform cross-thread synchronisation within each such block without+-- the use of barriers. Unfortunately, there seems to be no reliable+-- way to query these sizes at runtime. Instead, we use builtin tables+-- to figure out which size we should use for a specific platform and+-- device. If nothing matches here, the wave size should be set to+-- one. ----- We also use this to select reasonable default group sizes and group+-- We also use this to select reasonable default block sizes and block -- counts. module Futhark.CodeGen.OpenCL.Heuristics   ( SizeHeuristic (..),@@ -34,7 +34,7 @@   pretty (DeviceInfo s) = "device_info" <> parens (pretty s)  -- | A size that can be assigned a default.-data WhichSize = LockstepWidth | NumGroups | GroupSize | TileSize | RegTileSize | Threshold+data WhichSize = LockstepWidth | NumBlocks | BlockSize | TileSize | RegTileSize | Threshold  -- | A heuristic for setting the default value for something. data SizeHeuristic = SizeHeuristic@@ -50,17 +50,17 @@   [ SizeHeuristic "NVIDIA CUDA" DeviceGPU LockstepWidth 32,     SizeHeuristic "AMD Accelerated Parallel Processing" DeviceGPU LockstepWidth 32,     SizeHeuristic "" DeviceGPU LockstepWidth 1,-    -- We calculate the number of groups to aim for 1024 threads per-    -- compute unit if we also use the default group size.  This seems+    -- We calculate the number of blocks to aim for 1024 threads per+    -- compute unit if we also use the default block size.  This seems     -- to perform well in practice.-    SizeHeuristic "" DeviceGPU NumGroups $ 4 * max_compute_units,-    SizeHeuristic "" DeviceGPU GroupSize 256,+    SizeHeuristic "" DeviceGPU NumBlocks $ 4 * max_compute_units,+    SizeHeuristic "" DeviceGPU BlockSize 256,     SizeHeuristic "" DeviceGPU TileSize 16,     SizeHeuristic "" DeviceGPU RegTileSize 4,     SizeHeuristic "" DeviceGPU Threshold $ 32 * 1024,     SizeHeuristic "" DeviceCPU LockstepWidth 1,-    SizeHeuristic "" DeviceCPU NumGroups max_compute_units,-    SizeHeuristic "" DeviceCPU GroupSize 32,+    SizeHeuristic "" DeviceCPU NumBlocks max_compute_units,+    SizeHeuristic "" DeviceCPU BlockSize 32,     SizeHeuristic "" DeviceCPU TileSize 4,     SizeHeuristic "" DeviceCPU RegTileSize 1,     SizeHeuristic "" DeviceCPU Threshold max_compute_units
src/Futhark/IR/GPU.hs view
@@ -62,3 +62,55 @@   asSegOp (SegOp op) = Just op   asSegOp _ = Nothing   segOp = SegOp++-- Note [GPU Terminology]+--+-- For lack of a better spot to put it, this Note summarises the+-- terminology used for GPU concepts in the Futhark compiler. The+-- terminology is based on CUDA terminology, and tries to match it as+-- closely as possible. However, this was not always the case (issue+-- #2062), so you may find some code that uses e.g. OpenCL+-- terminology. In most cases there is no ambiguity, but there are a+-- few instances where the same term is used for different things.+-- Please fix any instances you find.+--+-- The terminology is as follows:+--+-- Host: Essentially the CPU; whatever is controlling the GPU.+--+-- Kernel: A GPU program that can be launched from the host.+--+-- Grid: The geometry of the thread blocks launched for a kernel. The+-- size of a grid is always in terms of the number of thread blocks+-- ("grid size"). A grid can have up to 3 dimensions, although we do+-- not make much use of it - and not at all prior to code generation.+--+-- Thread block: Just as in CUDA. "Workgroup" in OpenCL. Abbretiation:+-- tblock. Never just call this "block"; there are too many things+-- called "block". Must match the dimensionality of the grid.+--+-- Thread: Just as in CUDA.  "Workitem" in OpenCL.+--+-- Global thread identifier: A globally unique number for a thread+-- along one dimension. Abbreviation: gtid. We also use this term for+-- the identifiers bound by SegOps. In OpenCL, corresponds to+-- get_global_id(). (Except when we virtualise the thread space.)+--+-- Local thread identifier: A locally unique number (within the thread+-- block) for each thread. Abbreviation: ltid. In OpenCL, corresponds+-- to get_local_id().  In CUDA, corresponds to threadIdx.+--+-- Thread block identifier: A number unique to each thread block in a+-- single dimension.  In CUDA, corresponds to blockIdx.+--+-- Local memory: Thread-local private memory. In CUDA, this is+-- sometimes put in registers (if you are very careful in how you use+-- it). In OpenCL, this is called "private memory", and "local memory"+-- is something else entirely.+--+-- Shared memory: Just as in CUDA. Fast scratchpad memory accessible+-- to all threads within the same thread block. In OpenCL, this is+-- "local memory".+--+-- Device memory: Sometimes also called "global memory"; this is the+-- big-but-slow memory on the GPU.
src/Futhark/IR/GPU/Op.hs view
@@ -61,14 +61,14 @@ -- the dimensions specified by @[i,j,k]@. -- -- At the moment, this is only supported for 'SegNoVirtFull'--- intra-group parallelism in GPU code, as we have not yet found it+-- intra-block parallelism in GPU code, as we have not yet found it -- useful anywhere else. newtype SegSeqDims = SegSeqDims {segSeqDims :: [Int]}   deriving (Eq, Ord, Show) --- | Do we need group-virtualisation when generating code for the+-- | Do we need block-virtualisation when generating code for the -- segmented operation?  In most cases, we do, but for some simple--- kernels, we compute the full number of groups in advance, and then+-- kernels, we compute the full number of blocks in advance, and then -- virtualisation is an unnecessary (but generally very small) -- overhead.  This only really matters for fairly trivial but very -- wide @map@ kernels where each thread performs constant-time work on@@ -85,23 +85,23 @@ -- | The actual, physical grid dimensions used for the GPU kernel -- running this 'SegOp'. data KernelGrid = KernelGrid-  { gridNumGroups :: Count NumGroups SubExp,-    gridGroupSize :: Count GroupSize SubExp+  { gridNumBlocks :: Count NumBlocks SubExp,+    gridBlockSize :: Count BlockSize SubExp   }   deriving (Eq, Ord, Show)  -- | At which level the *body* of a t'SegOp' executes. data SegLevel   = SegThread SegVirt (Maybe KernelGrid)-  | SegGroup SegVirt (Maybe KernelGrid)-  | SegThreadInGroup SegVirt+  | SegBlock SegVirt (Maybe KernelGrid)+  | SegThreadInBlock SegVirt   deriving (Eq, Ord, Show)  -- | The 'SegVirt' of the 'SegLevel'. segVirt :: SegLevel -> SegVirt segVirt (SegThread v _) = v-segVirt (SegGroup v _) = v-segVirt (SegThreadInGroup v) = v+segVirt (SegBlock v _) = v+segVirt (SegThreadInBlock v) = v  instance PP.Pretty SegVirt where   pretty SegNoVirt = mempty@@ -109,60 +109,60 @@   pretty SegVirt = "virtualise"  instance PP.Pretty KernelGrid where-  pretty (KernelGrid num_groups group_size) =-    "groups="-      <> pretty num_groups+  pretty (KernelGrid num_tblocks tblock_size) =+    "grid="+      <> pretty num_tblocks       <> PP.semi-        <+> "groupsize="-      <> pretty group_size+        <+> "blocksize="+      <> pretty tblock_size  instance PP.Pretty SegLevel where   pretty (SegThread virt grid) =     PP.parens ("thread" <> PP.semi <+> pretty virt <> PP.semi <+> pretty grid)-  pretty (SegGroup virt grid) =-    PP.parens ("group" <> PP.semi <+> pretty virt <> PP.semi <+> pretty grid)-  pretty (SegThreadInGroup virt) =-    PP.parens ("ingroup" <> PP.semi <+> pretty virt)+  pretty (SegBlock virt grid) =+    PP.parens ("block" <> PP.semi <+> pretty virt <> PP.semi <+> pretty grid)+  pretty (SegThreadInBlock virt) =+    PP.parens ("inblock" <> PP.semi <+> pretty virt)  instance Engine.Simplifiable KernelGrid where-  simplify (KernelGrid num_groups group_size) =+  simplify (KernelGrid num_tblocks tblock_size) =     KernelGrid-      <$> traverse Engine.simplify num_groups-      <*> traverse Engine.simplify group_size+      <$> traverse Engine.simplify num_tblocks+      <*> traverse Engine.simplify tblock_size  instance Engine.Simplifiable SegLevel where   simplify (SegThread virt grid) =     SegThread virt <$> Engine.simplify grid-  simplify (SegGroup virt grid) =-    SegGroup virt <$> Engine.simplify grid-  simplify (SegThreadInGroup virt) =-    pure $ SegThreadInGroup virt+  simplify (SegBlock virt grid) =+    SegBlock virt <$> Engine.simplify grid+  simplify (SegThreadInBlock virt) =+    pure $ SegThreadInBlock virt  instance Substitute KernelGrid where-  substituteNames substs (KernelGrid num_groups group_size) =+  substituteNames substs (KernelGrid num_tblocks tblock_size) =     KernelGrid-      (substituteNames substs num_groups)-      (substituteNames substs group_size)+      (substituteNames substs num_tblocks)+      (substituteNames substs tblock_size)  instance Substitute SegLevel where   substituteNames substs (SegThread virt grid) =     SegThread virt (substituteNames substs grid)-  substituteNames substs (SegGroup virt grid) =-    SegGroup virt (substituteNames substs grid)-  substituteNames _ (SegThreadInGroup virt) =-    SegThreadInGroup virt+  substituteNames substs (SegBlock virt grid) =+    SegBlock virt (substituteNames substs grid)+  substituteNames _ (SegThreadInBlock virt) =+    SegThreadInBlock virt  instance Rename SegLevel where   rename = substituteRename  instance FreeIn KernelGrid where-  freeIn' (KernelGrid num_groups group_size) =-    freeIn' (num_groups, group_size)+  freeIn' (KernelGrid num_tblocks tblock_size) =+    freeIn' (num_tblocks, tblock_size)  instance FreeIn SegLevel where   freeIn' (SegThread _virt grid) = freeIn' grid-  freeIn' (SegGroup _virt grid) = freeIn' grid-  freeIn' (SegThreadInGroup _virt) = mempty+  freeIn' (SegBlock _virt grid) = freeIn' grid+  freeIn' (SegThreadInBlock _virt) = mempty  -- | A simple size-level query or computation. data SizeOp@@ -172,28 +172,28 @@     GetSizeMax SizeClass   | -- | Compare size (likely a threshold) with some integer value.     CmpSizeLe Name SizeClass SubExp-  | -- | @CalcNumGroups w max_num_groups group_size@ calculates the-    -- number of GPU workgroups to use for an input of the given size.+  | -- | @CalcNumBlocks w max_num_tblocks tblock_size@ calculates the+    -- number of GPU threadblocks to use for an input of the given size.     -- The @Name@ is a size name.  Note that @w@ is an i64 to avoid     -- overflow issues.-    CalcNumGroups SubExp Name SubExp+    CalcNumBlocks SubExp Name SubExp   deriving (Eq, Ord, Show)  instance Substitute SizeOp where   substituteNames substs (CmpSizeLe name sclass x) =     CmpSizeLe name sclass (substituteNames substs x)-  substituteNames substs (CalcNumGroups w max_num_groups group_size) =-    CalcNumGroups+  substituteNames substs (CalcNumBlocks w max_num_tblocks tblock_size) =+    CalcNumBlocks       (substituteNames substs w)-      max_num_groups-      (substituteNames substs group_size)+      max_num_tblocks+      (substituteNames substs tblock_size)   substituteNames _ op = op  instance Rename SizeOp where   rename (CmpSizeLe name sclass x) =     CmpSizeLe name sclass <$> rename x-  rename (CalcNumGroups w max_num_groups group_size) =-    CalcNumGroups <$> rename w <*> pure max_num_groups <*> rename group_size+  rename (CalcNumBlocks w max_num_tblocks tblock_size) =+    CalcNumBlocks <$> rename w <*> pure max_num_tblocks <*> rename tblock_size   rename x = pure x  instance IsOp SizeOp where@@ -205,7 +205,7 @@   opType (GetSize _ _) = pure [Prim int64]   opType (GetSizeMax _) = pure [Prim int64]   opType CmpSizeLe {} = pure [Prim Bool]-  opType CalcNumGroups {} = pure [Prim int64]+  opType CalcNumBlocks {} = pure [Prim int64]  instance AliasedOp SizeOp where   opAliases _ = [mempty]@@ -213,7 +213,7 @@  instance FreeIn SizeOp where   freeIn' (CmpSizeLe _ _ x) = freeIn' x-  freeIn' (CalcNumGroups w _ group_size) = freeIn' w <> freeIn' group_size+  freeIn' (CalcNumBlocks w _ tblock_size) = freeIn' w <> freeIn' tblock_size   freeIn' _ = mempty  instance PP.Pretty SizeOp where@@ -226,22 +226,22 @@       <> parens (commasep [pretty name, pretty size_class])         <+> "<="         <+> pretty x-  pretty (CalcNumGroups w max_num_groups group_size) =-    "calc_num_groups" <> parens (commasep [pretty w, pretty max_num_groups, pretty group_size])+  pretty (CalcNumBlocks w max_num_tblocks tblock_size) =+    "calc_num_tblocks" <> parens (commasep [pretty w, pretty max_num_tblocks, pretty tblock_size])  instance OpMetrics SizeOp where   opMetrics GetSize {} = seen "GetSize"   opMetrics GetSizeMax {} = seen "GetSizeMax"   opMetrics CmpSizeLe {} = seen "CmpSizeLe"-  opMetrics CalcNumGroups {} = seen "CalcNumGroups"+  opMetrics CalcNumBlocks {} = seen "CalcNumBlocks"  typeCheckSizeOp :: (TC.Checkable rep) => SizeOp -> TC.TypeM rep () typeCheckSizeOp GetSize {} = pure () typeCheckSizeOp GetSizeMax {} = pure () typeCheckSizeOp (CmpSizeLe _ _ x) = TC.require [Prim int64] x-typeCheckSizeOp (CalcNumGroups w _ group_size) = do+typeCheckSizeOp (CalcNumBlocks w _ tblock_size) = do   TC.require [Prim int64] w-  TC.require [Prim int64] group_size+  TC.require [Prim int64] tblock_size  -- | A host-level operation; parameterised by what else it can do. data HostOp op rep@@ -364,31 +364,31 @@  checkGrid :: (TC.Checkable rep) => KernelGrid -> TC.TypeM rep () checkGrid grid = do-  TC.require [Prim int64] $ unCount $ gridNumGroups grid-  TC.require [Prim int64] $ unCount $ gridGroupSize grid+  TC.require [Prim int64] $ unCount $ gridNumBlocks grid+  TC.require [Prim int64] $ unCount $ gridBlockSize grid  checkSegLevel ::   (TC.Checkable rep) =>   Maybe SegLevel ->   SegLevel ->   TC.TypeM rep ()-checkSegLevel (Just SegGroup {}) (SegThreadInGroup _virt) =+checkSegLevel (Just SegBlock {}) (SegThreadInBlock _virt) =   pure ()-checkSegLevel _ (SegThreadInGroup _virt) =-  TC.bad $ TC.TypeError "ingroup SegOp not in group SegOp."+checkSegLevel _ (SegThreadInBlock _virt) =+  TC.bad $ TC.TypeError "inblock SegOp not in block SegOp." checkSegLevel (Just SegThread {}) _ =   TC.bad $ TC.TypeError "SegOps cannot occur when already at thread level."-checkSegLevel (Just SegThreadInGroup {}) _ =-  TC.bad $ TC.TypeError "SegOps cannot occur when already at ingroup level."+checkSegLevel (Just SegThreadInBlock {}) _ =+  TC.bad $ TC.TypeError "SegOps cannot occur when already at inblock level." checkSegLevel _ (SegThread _virt Nothing) =   pure () checkSegLevel (Just _) SegThread {} =   TC.bad $ TC.TypeError "thread-level SegOp cannot be nested" checkSegLevel Nothing (SegThread _virt grid) =   mapM_ checkGrid grid-checkSegLevel (Just _) SegGroup {} =-  TC.bad $ TC.TypeError "group-level SegOp cannot be nested"-checkSegLevel Nothing (SegGroup _virt grid) =+checkSegLevel (Just _) SegBlock {} =+  TC.bad $ TC.TypeError "block-level SegOp cannot be nested"+checkSegLevel Nothing (SegBlock _virt grid) =   mapM_ checkGrid grid  typeCheckHostOp ::
src/Futhark/IR/GPU/Simplify.hs view
@@ -59,9 +59,9 @@ simplifyKernelOp _ (SizeOp (CmpSizeLe key size_class x)) = do   x' <- Engine.simplify x   pure (SizeOp $ CmpSizeLe key size_class x', mempty)-simplifyKernelOp _ (SizeOp (CalcNumGroups w max_num_groups group_size)) = do+simplifyKernelOp _ (SizeOp (CalcNumBlocks w max_num_tblocks tblock_size)) = do   w' <- Engine.simplify w-  pure (SizeOp $ CalcNumGroups w' max_num_groups group_size, mempty)+  pure (SizeOp $ CalcNumBlocks w' max_num_tblocks tblock_size, mempty) simplifyKernelOp _ (GPUBody ts body) = do   ts' <- Engine.simplify ts   (hoisted, body') <-
src/Futhark/IR/GPU/Sizes.hs view
@@ -5,8 +5,8 @@     sizeDefault,     KernelPath,     Count (..),-    NumGroups,-    GroupSize,+    NumBlocks,+    BlockSize,     NumThreads,   ) where@@ -29,16 +29,17 @@ data SizeClass   = -- | A threshold with an optional default.     SizeThreshold KernelPath (Maybe Int64)-  | SizeGroup-  | SizeNumGroups+  | SizeThreadBlock+  | -- | The number of thread blocks.+    SizeGrid   | SizeTile   | SizeRegTile   | -- | Likely not useful on its own, but querying the     -- maximum can be handy.-    SizeLocalMemory+    SizeSharedMemory   | -- | A bespoke size with a default.     SizeBespoke Name Int64-  | -- | Amount of registers available per workgroup. Mostly+  | -- | Amount of registers available per threadblock. Mostly     -- meaningful for querying the maximum.     SizeRegisters   | -- | Amount of L2 cache memory, in bytes. Mostly meaningful for@@ -53,11 +54,11 @@       pStep (v, True) = pretty v       pStep (v, False) = "!" <> pretty v       def' = maybe "def" pretty def-  pretty SizeGroup = "group_size"-  pretty SizeNumGroups = "num_groups"+  pretty SizeThreadBlock = "thread_block_size"+  pretty SizeGrid = "grid_size"   pretty SizeTile = "tile_size"   pretty SizeRegTile = "reg_tile_size"-  pretty SizeLocalMemory = "local_memory"+  pretty SizeSharedMemory = "shared_memory"   pretty (SizeBespoke k def) =     "bespoke" <> parens (pretty k <> comma <+> pretty def)   pretty SizeRegisters = "registers"@@ -82,11 +83,11 @@ instance Traversable (Count u) where   traverse f (Count x) = Count <$> f x --- | Phantom type for the number of groups of some kernel.-data NumGroups+-- | Phantom type for the number of blocks of some kernel.+data NumBlocks --- | Phantom type for the group size of some kernel.-data GroupSize+-- | Phantom type for the block size of some kernel.+data BlockSize  -- | Phantom type for number of threads. data NumThreads
src/Futhark/IR/Mem.hs view
@@ -914,17 +914,15 @@    TC.context ("in index function " <> prettyText ixfun) $ do     traverse_ (TC.requirePrimExp int64 . untyped) ixfun-    let ixfun_rank = IxFun.rank ixfun-        ident_rank = shapeRank shape-    unless (ixfun_rank == ident_rank) $+    unless (IxFun.shape ixfun == map pe64 (shapeDims shape)) $       TC.bad $         TC.TypeError $-          "Arity of index function ("-            <> prettyText ixfun_rank-            <> ") does not match rank of array "+          "Shape of index function ("+            <> prettyText (IxFun.shape ixfun)+            <> ") does not match shape of array "             <> prettyText name             <> " ("-            <> prettyText ident_rank+            <> prettyText shape             <> ")"  bodyReturnsFromPat ::@@ -1074,7 +1072,7 @@         ( Array pt shape u,           MemArray _ _ _ (ArrayIn mem ixfun)           )-            | Just (i, mem_p) <- isMergeVar mem,+            | Just (i, mem_p) <- isLoopVar mem,               Mem space <- paramType mem_p ->                 pure $ MemArray pt shape u $ Just $ ReturnsNewBlock space i ixfun'             | otherwise ->@@ -1089,7 +1087,7 @@           pure $ MemPrim pt         (Mem space, _) ->           pure $ MemMem space-    isMergeVar v = find ((== v) . paramName . snd) $ zip [0 ..] mergevars+    isLoopVar v = find ((== v) . paramName . snd) $ zip [0 ..] mergevars     mergevars = map fst merge expReturns (Apply _ _ ret _) =   pure $ Just $ map (funReturnsToExpReturns . fst) ret
src/Futhark/IR/Mem/IxFun.hs view
@@ -22,6 +22,7 @@     substituteInIxFun,     substituteInLMAD,     existentialize,+    existentialized,     closeEnough,     disjoint,     disjoint2,@@ -152,13 +153,13 @@ iota :: (IntegralExp num) => Shape num -> IxFun num iota = iotaOffset 0 --- | Create a single-LMAD index function that is--- existential in everything, with the provided permutation.-mkExistential :: Int -> Int -> Int -> IxFun (Ext a)-mkExistential basis_rank lmad_rank start =-  IxFun (LMAD.mkExistential lmad_rank start) basis+-- | Create a single-LMAD index function that is existential in+-- everything except shape, with the provided shape.+mkExistential :: Int -> Shape (Ext a) -> Int -> IxFun (Ext a)+mkExistential basis_rank lmad_shape start =+  IxFun (LMAD.mkExistential lmad_shape start) basis   where-    basis = take basis_rank $ map Ext [start + 1 + lmad_rank * 2 ..]+    basis = take basis_rank $ map Ext [start + 1 + length lmad_shape ..]  -- | Permute dimensions. permute ::@@ -241,18 +242,30 @@           (map onDim (LMAD.dims lmad))    in Just $ IxFun lmad' base --- | Turn all the leaves of the index function into 'Ext's.  We---  require that there's only one LMAD, that the index function is---  contiguous, and the base shape has only one dimension.+-- | Turn all the leaves of the index function into 'Ext's, except for+--  the shape, which where the leaves are simply made 'Free'. existentialize ::+  Int ->   IxFun (TPrimExp Int64 a) ->-  IxFun (TPrimExp Int64 (Ext b))-existentialize ixfun = evalState (traverse (const mkExt) ixfun) 0+  IxFun (TPrimExp Int64 (Ext a))+existentialize start (IxFun lmad base) = evalState (IxFun <$> lmad' <*> base') start   where     mkExt = do       i <- get       put $ i + 1       pure $ TPrimExp $ LeafExp (Ext i) int64+    lmad' = LMAD <$> mkExt <*> mapM onDim (dims lmad)+    base' = traverse (const mkExt) base+    onDim ld = LMADDim <$> mkExt <*> pure (fmap Free (ldShape ld))++-- | Retrieve those elements that 'existentialize' changes. That is,+-- everything except the shape (and in the same order as+-- 'existentialise' existentialises them).+existentialized :: IxFun a -> [a]+existentialized (IxFun (LMAD offset dims) base) =+  offset : concatMap onDim dims <> base+  where+    onDim (LMADDim ldstride _) = [ldstride]  -- | When comparing index functions as part of the type check in KernelsMem, -- we may run into problems caused by the simplifier. As index functions can be
src/Futhark/IR/Mem/LMAD.hs view
@@ -276,11 +276,12 @@ iota off = iotaStrided off 1 {-# NOINLINE iota #-} --- | Create an LMAD that is existential in everything.-mkExistential :: Int -> Int -> LMAD (Ext a)-mkExistential r start = LMAD (Ext start) $ map onDim [0 .. r - 1]+-- | Create an LMAD that is existential in everything except shape.+mkExistential :: Shape (Ext a) -> Int -> LMAD (Ext a)+mkExistential shp start = LMAD (Ext start) $ zipWith onDim shp [0 .. r - 1]   where-    onDim i = LMADDim (Ext (start + 1 + i * 2)) (Ext (start + 2 + i * 2))+    r = length shp+    onDim d i = LMADDim {ldStride = Ext (start + 1 + i), ldShape = d}  -- | Permute dimensions. permute :: LMAD num -> Permutation -> LMAD num
src/Futhark/IR/Mem/Simplify.hs view
@@ -134,8 +134,8 @@       (arr_to_mem, oldmem_to_mem) <-         fmap unzip $           forM fixable $ \(arr_pe, mem_size, oldmem, space) -> do-            size <- toSubExp "size" mem_size-            mem <- letExp "mem" $ Op $ Alloc size space+            size <- toSubExp "unext_mem_size" mem_size+            mem <- letExp "unext_mem" $ Op $ Alloc size space             pure ((patElemName arr_pe, mem), (oldmem, mem))        -- Update the branches to contain Copy expressions putting the
src/Futhark/IR/Parse.hs view
@@ -765,12 +765,11 @@ pSizeClass :: Parser GPU.SizeClass pSizeClass =   choice-    [ keyword "group_size" $> GPU.SizeGroup,-      keyword "num_groups" $> GPU.SizeNumGroups,-      keyword "num_groups" $> GPU.SizeNumGroups,+    [ keyword "thread_block_size" $> GPU.SizeThreadBlock,+      keyword "grid_size" $> GPU.SizeGrid,       keyword "tile_size" $> GPU.SizeTile,       keyword "reg_tile_size" $> GPU.SizeRegTile,-      keyword "local_memory" $> GPU.SizeLocalMemory,+      keyword "shared_memory" $> GPU.SizeSharedMemory,       keyword "threshold"         *> parens           ( flip GPU.SizeThreshold@@ -800,9 +799,9 @@         *> ( parens (GPU.CmpSizeLe <$> pName <* pComma <*> pSizeClass)                <*> (lexeme "<=" *> pSubExp)            ),-      keyword "calc_num_groups"+      keyword "calc_num_tblocks"         *> parens-          ( GPU.CalcNumGroups+          ( GPU.CalcNumBlocks               <$> pSubExp               <* pComma               <*> pName@@ -917,13 +916,13 @@         <*> pSegVirt         <* pSemi         <*> optional pKernelGrid,-      "group"-        $> GPU.SegGroup+      "block"+        $> GPU.SegBlock         <* pSemi         <*> pSegVirt         <* pSemi         <*> optional pKernelGrid,-      "ingroup" $> GPU.SegThreadInGroup <* pSemi <*> pSegVirt+      "inblock" $> GPU.SegThreadInBlock <* pSemi <*> pSegVirt     ]   where     pSegVirt =@@ -938,8 +937,8 @@         ]     pKernelGrid =       GPU.KernelGrid-        <$> (lexeme "groups=" $> GPU.Count <*> pSubExp <* pSemi)-        <*> (lexeme "groupsize=" $> GPU.Count <*> pSubExp)+        <$> (lexeme "grid=" $> GPU.Count <*> pSubExp <* pSemi)+        <*> (lexeme "blocksize=" $> GPU.Count <*> pSubExp)  pHostOp :: PR rep -> Parser (op rep) -> Parser (GPU.HostOp op rep) pHostOp pr pOther =
src/Futhark/IR/SOACS/SOAC.hs view
@@ -31,7 +31,6 @@     isScanSOAC,     isReduceSOAC,     isMapSOAC,-    scremaLambda,     ppScrema,     ppHist,     ppStream,@@ -143,11 +142,16 @@  -- | The essential parts of a 'Screma' factored out (everything -- except the input arrays).-data ScremaForm rep-  = ScremaForm-      [Scan rep]-      [Reduce rep]-      (Lambda rep)+data ScremaForm rep = ScremaForm+  { scremaScans :: [Scan rep],+    scremaReduces :: [Reduce rep],+    -- | The "main" lambda of the Screma. For a map, this is+    -- equivalent to 'isMapSOAC'. Note that the meaning of the return+    -- value of this lambda depends crucially on exactly which Screma+    -- this is. The parameters will correspond exactly to elements of+    -- the input arrays, however.+    scremaLambda :: Lambda rep+  }   deriving (Eq, Ord, Show)  singleBinOp :: (Buildable rep) => [Lambda rep] -> Lambda rep@@ -316,17 +320,9 @@   guard $ null reds   pure map_lam --- | Return the "main" lambda of the Screma.  For a map, this is--- equivalent to 'isMapSOAC'.  Note that the meaning of the return--- value of this lambda depends crucially on exactly which Screma this--- is.  The parameters will correspond exactly to elements of the--- input arrays, however.-scremaLambda :: ScremaForm rep -> Lambda rep-scremaLambda (ScremaForm _ _ map_lam) = map_lam- -- | @groupScatterResults <output specification> <results>@ ----- Groups the index values and result values of <results> according to the+-- Blocks the index values and result values of <results> according to the -- <output specification>. -- -- This function is used for extracting and grouping the results of a@@ -348,7 +344,7 @@  -- | @groupScatterResults' <output specification> <results>@ ----- Groups the index values and result values of <results> according to the+-- Blocks the index values and result values of <results> according to the -- output specification. This is the simpler version of @groupScatterResults@, -- which doesn't return any information about shapes or output arrays. --@@ -625,9 +621,9 @@          in map (is_flat <>) vs   opDependencies (Scatter w arrs lam outputs) =     let deps = lambdaDependencies mempty lam (depsOfArrays w arrs)-     in map flattenGroups (groupScatterResults outputs deps)+     in map flattenBlocks (groupScatterResults outputs deps)     where-      flattenGroups (_, arr, ivs) =+      flattenBlocks (_, arr, ivs) =         oneName arr <> mconcat (map (mconcat . fst) ivs) <> mconcat (map snd ivs)   opDependencies (JVP lam args vec) =     mconcat $
src/Futhark/IR/SegOp.hs view
@@ -189,7 +189,7 @@ data KernelResult   = -- | Each "worker" in the kernel returns this.     -- Whether this is a result-per-thread or a-    -- result-per-group depends on where the 'SegOp' occurs.+    -- result-per-block depends on where the 'SegOp' occurs.     Returns ResultManifest Certs SubExp   | WriteReturns       Certs@@ -209,7 +209,7 @@           SubExp -- reg tile size for this dim.         )       ]-      VName -- Tile returned by this worker/group.+      VName -- Tile returned by this thread/block.   deriving (Eq, Show, Ord)  -- | Get the certs for this 'KernelResult'.@@ -437,7 +437,7 @@ -- as well as a *level*.  The *level* is a representation-specific bit -- of information.  For example, in GPU backends, it is used to -- indicate whether the 'SegOp' is expected to run at the thread-level--- or the group-level.+-- or the block-level. -- -- The type list is usually the type of the element returned by a -- single thread. The result of the SegOp is then an array of that
src/Futhark/IR/TypeCheck.hs view
@@ -650,7 +650,7 @@   [FParam rep] ->   TypeM rep () checkFunParams = mapM_ $ \param ->-  context ("In function parameter " <> prettyText param) $+  context ("In parameter " <> prettyText param) $     checkFParamDec (paramName param) (paramDec param)  checkLambdaParams ::@@ -658,7 +658,7 @@   [LParam rep] ->   TypeM rep () checkLambdaParams = mapM_ $ \param ->-  context ("In lambda parameter " <> prettyText param) $+  context ("In parameter " <> prettyText param) $     checkLParamDec (paramName param) (paramDec param)  checkNoDuplicateParams :: Name -> [VName] -> TypeM rep ()
src/Futhark/Internalise/Exps.hs view
@@ -1728,6 +1728,13 @@               I.ReshapeArbitrary               (reshapeOuter (I.Shape [n', m']) 1 $ I.arrayShape arr_t)               arr'+    handleRest [arr] "manifest" = Just $ \desc -> do+      arrs <- internaliseExpToVars "flatten_arr" arr+      forM arrs $ \arr' -> do+        r <- I.arrayRank <$> lookupType arr'+        if r == 0+          then pure $ I.Var arr'+          else letSubExp desc $ I.BasicOp $ I.Manifest [0 .. r - 1] arr'     handleRest [arr] "flatten" = Just $ \desc -> do       arrs <- internaliseExpToVars "flatten_arr" arr       forM arrs $ \arr' -> do
src/Futhark/Optimise/ArrayShortCircuiting/ArrayCoalescing.hs view
@@ -1525,70 +1525,65 @@   Stm (Aliases rep) ->   ShortCircuitM rep (Maybe [SSPointInfo]) -- CASE a) @let x <- copy(b^{lu})@-genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (Replicate (Shape []) (Var b))))-  | Pat [PatElem x (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat =-      pure $ case (M.lookup x lutab, getScopeMemInfo b scopetab) of-        (Just last_uses, Just (MemBlock tpb shpb m_b ind_b)) ->-          if b `notNameIn` last_uses-            then Nothing-            else Just [(CopyCoal, id, x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)]-        _ -> Nothing+genCoalStmtInfo lutab td_env scopetab (Let pat aux (BasicOp (Replicate (Shape []) (Var b))))+  | Pat [PatElem x (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat,+    Just last_uses <- M.lookup x lutab,+    Just (MemBlock tpb shpb m_b ind_b) <- getScopeMemInfo b scopetab,+    sameSpace td_env m_x m_b,+    b `nameIn` last_uses =+      pure $ Just [(CopyCoal, id, x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)] -- CASE c) @let x[i] = b^{lu}@-genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (Update _ x slice_x (Var b))))-  | Pat [PatElem x' (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat =-      pure $ case (M.lookup x' lutab, getScopeMemInfo b scopetab) of-        (Just last_uses, Just (MemBlock tpb shpb m_b ind_b)) ->-          if b `notNameIn` last_uses-            then Nothing-            else Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)]-        _ -> Nothing+genCoalStmtInfo lutab td_env scopetab (Let pat aux (BasicOp (Update _ x slice_x (Var b))))+  | Pat [PatElem x' (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat,+    Just last_uses <- M.lookup x' lutab,+    Just (MemBlock tpb shpb m_b ind_b) <- getScopeMemInfo b scopetab,+    sameSpace td_env m_x m_b,+    b `nameIn` last_uses =+      pure $ Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)]   where     updateIndFunSlice :: IxFun -> Slice SubExp -> IxFun     updateIndFunSlice ind_fun slc_x =       let slc_x' = map (fmap pe64) $ unSlice slc_x        in IxFun.slice ind_fun $ Slice slc_x'-genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (FlatUpdate x slice_x b)))-  | Pat [PatElem x' (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat =-      pure $ case (M.lookup x' lutab, getScopeMemInfo b scopetab) of-        (Just last_uses, Just (MemBlock tpb shpb m_b ind_b)) ->-          if b `notNameIn` last_uses-            then Nothing-            else Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)]-        _ -> Nothing+genCoalStmtInfo lutab td_env scopetab (Let pat aux (BasicOp (FlatUpdate x slice_x b)))+  | Pat [PatElem x' (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat,+    Just last_uses <- M.lookup x' lutab,+    Just (MemBlock tpb shpb m_b ind_b) <- getScopeMemInfo b scopetab,+    sameSpace td_env m_x m_b,+    b `nameIn` last_uses =+      pure $ Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)]   where     updateIndFunSlice :: IxFun -> FlatSlice SubExp -> IxFun     updateIndFunSlice ind_fun (FlatSlice offset dims) =       IxFun.flatSlice ind_fun $ FlatSlice (pe64 offset) $ map (fmap pe64) dims  -- CASE b) @let x = concat(a, b^{lu})@-genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (Concat concat_dim (b0 :| bs) _)))-  | Pat [PatElem x (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat =-      pure $ case M.lookup x lutab of-        Nothing -> Nothing-        Just last_uses ->-          let zero = pe64 $ intConst Int64 0-              markConcatParts (acc, offs, succ0) b =-                if not succ0-                  then (acc, offs, succ0)-                  else case getScopeMemInfo b scopetab of-                    Just (MemBlock tpb shpb@(Shape dims@(_ : _)) m_b ind_b)-                      | Just d <- maybeNth concat_dim dims ->-                          let offs' = offs + pe64 d-                           in if b `nameIn` last_uses-                                then-                                  let slc =-                                        Slice $-                                          map (unitSlice zero . pe64) (take concat_dim dims)-                                            <> [unitSlice offs (pe64 d)]-                                            <> map (unitSlice zero . pe64) (drop (concat_dim + 1) dims)-                                   in ( acc ++ [(ConcatCoal, (`IxFun.slice` slc), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)],-                                        offs',-                                        True-                                      )-                                else (acc, offs', True)-                    _ -> (acc, offs, False)-              (res, _, _) = foldl markConcatParts ([], zero, True) (b0 : bs)-           in if null res then Nothing else Just res+genCoalStmtInfo lutab td_env scopetab (Let pat aux (BasicOp (Concat concat_dim (b0 :| bs) _)))+  | Pat [PatElem x (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat,+    Just last_uses <- M.lookup x lutab =+      pure $+        let (res, _, _) = foldl (markConcatParts last_uses x m_x ind_x) ([], zero, True) (b0 : bs)+         in if null res then Nothing else Just res+  where+    zero = pe64 $ intConst Int64 0+    markConcatParts _ _ _ _ acc@(_, _, False) _ = acc+    markConcatParts last_uses x m_x ind_x (acc, offs, True) b+      | Just (MemBlock tpb shpb@(Shape dims@(_ : _)) m_b ind_b) <- getScopeMemInfo b scopetab,+        Just d <- maybeNth concat_dim dims,+        offs' <- offs + pe64 d =+          if b `nameIn` last_uses && sameSpace td_env m_x m_b+            then+              let slc =+                    Slice $+                      map (unitSlice zero . pe64) (take concat_dim dims)+                        <> [unitSlice offs (pe64 d)]+                        <> map (unitSlice zero . pe64) (drop (concat_dim + 1) dims)+               in ( acc ++ [(ConcatCoal, (`IxFun.slice` slc), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)],+                    offs',+                    True+                  )+            else (acc, offs', True)+      | otherwise = (acc, offs, False) -- case d) short-circuit points from ops. For instance, the result of a segmap -- can be considered a short-circuit point. genCoalStmtInfo lutab td_env scopetab (Let pat aux (Op op)) = do@@ -1596,6 +1591,13 @@   pure $ ss_op lutab td_env scopetab pat (stmAuxCerts aux) op -- CASE other than a), b), c), or d) not supported genCoalStmtInfo _ _ _ _ = pure Nothing++sameSpace :: (Coalesceable rep inner) => TopdownEnv rep -> VName -> VName -> Bool+sameSpace td_env m_x m_b+  | MemMem pat_space <- runReader (lookupMemInfo m_x) $ removeScopeAliases $ scope td_env,+    MemMem return_space <- runReader (lookupMemInfo m_b) $ removeScopeAliases $ scope td_env =+      pat_space == return_space+  | otherwise = False  data MemBodyResult = MemBodyResult   { patMem :: VName,
src/Futhark/Optimise/BlkRegTiling.hs view
@@ -332,7 +332,7 @@               foldl (\x d -> pe64 d * x) gridxyt_pexp $                 map snd rem_outer_dims_rev -        (grid_size, group_size, segthd_lvl) <- mkNewSegthdLvl tx ty grid_pexp+        (grid_size, tblock_size, segthd_lvl) <- mkNewSegthdLvl tx ty grid_pexp         (gid_x, gid_y, gid_flat) <- mkGidsXYF         gid_t <- newVName "gid_t" @@ -412,8 +412,8 @@             (height_A, width_B, rem_outer_dims)             code2' -        let grid = KernelGrid (Count grid_size) (Count group_size)-            level' = SegGroup SegNoVirt (Just grid)+        let grid = KernelGrid (Count grid_size) (Count tblock_size)+            level' = SegBlock SegNoVirt (Just grid)             space' = SegSpace gid_flat (rem_outer_dims ++ [(gid_t, gridDim_t), (gid_y, gridDim_y), (gid_x, gridDim_x)])             kbody' = KernelBody () stms_seggroup ret_seggroup         pure $ Let pat aux $ Op $ SegOp $ SegMap level' space' ts kbody'@@ -519,7 +519,7 @@         let grid_pexp =               foldl (\x d -> pe64 d * x) gridxy_pexp $                 map snd rem_outer_dims_rev-        (grid_size, group_size, segthd_lvl) <- mkNewSegthdLvl tx ty grid_pexp+        (grid_size, tblock_size, segthd_lvl) <- mkNewSegthdLvl tx ty grid_pexp          (gid_x, gid_y, gid_flat) <- mkGidsXYF @@ -584,8 +584,8 @@             (height_A, width_B, rem_outer_dims)             code2' -        let grid = KernelGrid (Count grid_size) (Count group_size)-            level' = SegGroup SegNoVirt (Just grid)+        let grid = KernelGrid (Count grid_size) (Count tblock_size)+            level' = SegBlock SegNoVirt (Just grid)             space' = SegSpace gid_flat (rem_outer_dims ++ [(gid_y, gridDim_y), (gid_x, gridDim_x)])             kbody' = KernelBody () stms_seggroup ret_seggroup         pure $ Let pat aux $ Op $ SegOp $ SegMap level' space' ts kbody'@@ -780,9 +780,9 @@   Builder GPU (SubExp, SubExp, SegLevel) mkNewSegthdLvl tx ty grid_pexp = do   grid_size <- letSubExp "grid_size" =<< toExp grid_pexp-  group_size <- letSubExp "group_size" =<< toExp (pe64 ty * pe64 tx)-  let segthd_lvl = SegThreadInGroup (SegNoVirtFull (SegSeqDims []))-  pure (grid_size, group_size, segthd_lvl)+  tblock_size <- letSubExp "tblock_size" =<< toExp (pe64 ty * pe64 tx)+  let segthd_lvl = SegThreadInBlock (SegNoVirtFull (SegSeqDims []))+  pure (grid_size, tblock_size, segthd_lvl)  mkGidsXYF :: Builder GPU (VName, VName, VName) mkGidsXYF = do@@ -1094,10 +1094,10 @@         let gridxyz_pexp = pe64 gridDim_z * pe64 gridDim_y * pe64 gridDim_x         let grid_pexp = product $ gridxyz_pexp : map (pe64 . snd) rem_outer_dims_rev         grid_size <- letSubExp "grid_size_tile3d" =<< toExp grid_pexp-        group_size <- letSubExp "group_size_tile3d" =<< toExp (pe64 ty * pe64 tx)-        let segthd_lvl = SegThreadInGroup (SegNoVirtFull (SegSeqDims []))+        tblock_size <- letSubExp "tblock_size_tile3d" =<< toExp (pe64 ty * pe64 tx)+        let segthd_lvl = SegThreadInBlock (SegNoVirtFull (SegSeqDims [])) -        count_shmem <- letSubExp "count_shmem" =<< ceilDiv rz group_size+        count_shmem <- letSubExp "count_shmem" =<< ceilDiv rz tblock_size          gid_x <- newVName "gid_x"         gid_y <- newVName "gid_y"@@ -1137,9 +1137,9 @@                       forM (zip loc_arr_merge2_nms (M.toList tab_out)) $ \(loc_Y_nm, (glb_Y_nm, (ptp_Y, load_Y))) -> do                         ltid_flat <- newVName "ltid_flat"                         ltid <- newVName "ltid"-                        let segspace = SegSpace ltid_flat [(ltid, group_size)]+                        let segspace = SegSpace ltid_flat [(ltid, tblock_size)]                         ((res_v, res_i), stms) <- runBuilder $ do-                          offs <- letExp "offs" =<< toExp (pe64 group_size * le64 tt)+                          offs <- letExp "offs" =<< toExp (pe64 tblock_size * le64 tt)                           loc_ind <- letExp "loc_ind" =<< toExp (le64 ltid + le64 offs)                           letBindNames [gtid_z] =<< toExp (le64 ii + le64 loc_ind)                           let glb_ind = gtid_z@@ -1299,8 +1299,8 @@            pure $ map (RegTileReturns mempty regtile_ret_dims) epilogue_res'         -- END (ret_seggroup, stms_seggroup) <- runBuilder $ do-        let grid = KernelGrid (Count grid_size) (Count group_size)-            level' = SegGroup SegNoVirt (Just grid)+        let grid = KernelGrid (Count grid_size) (Count tblock_size)+            level' = SegBlock SegNoVirt (Just grid)             space' = SegSpace gid_flat (rem_outer_dims ++ [(gid_z, gridDim_z), (gid_y, gridDim_y), (gid_x, gridDim_x)])             kbody' = KernelBody () stms_seggroup ret_seggroup 
src/Futhark/Optimise/DoubleBuffer.hs view
@@ -334,7 +334,7 @@   -- We start out by figuring out which of the merge variables should   -- be double-buffered.   buffered <--    doubleBufferMergeParams+    doubleBufferLoopParams       (zip (map fst merge) (bodyResult body))       (boundInBody body)   -- Then create the allocations of the buffers and copies of the@@ -354,12 +354,12 @@   | NoBuffer   deriving (Show) -doubleBufferMergeParams ::+doubleBufferLoopParams ::   (MonadFreshNames m) =>   [(Param FParamMem, SubExpRes)] ->   Names ->   m [DoubleBuffer]-doubleBufferMergeParams ctx_and_res bound_in_loop =+doubleBufferLoopParams ctx_and_res bound_in_loop =   evalStateT (mapM buffer ctx_and_res) M.empty   where     params = map fst ctx_and_res
src/Futhark/Optimise/Fusion/TryFusion.hs view
@@ -673,11 +673,10 @@   SOAC ->   SOAC.ArrayTransforms ->   TryFusion (SOAC, SOAC.ArrayTransforms)-pullIndex (SOAC.Screma w form inps) ots+pullIndex (SOAC.Screma _ form inps) ots   | SOAC.Index cs slice@(Slice (ds@(DimSlice _ w' _) : inner_ds))       SOAC.:< ots' <-       SOAC.viewf ots,-    w /= w',     Just lam <- isMapSOAC form = do       let sliceInput inp =             SOAC.addTransform
src/Futhark/Optimise/MergeGPUBodies.hs view
@@ -237,9 +237,9 @@ data State = State   { -- | All statements that already have been processed from the sequence,     -- divided into alternating groups of non-GPUBody and GPUBody statements.-    -- Groups at even indices only contain non-GPUBody statements. Groups at+    -- Blocks at even indices only contain non-GPUBody statements. Blocks at     -- odd indices only contain GPUBody statements.-    stateGroups :: Groups,+    stateBlocks :: Blocks,     stateEquivalents :: EquivalenceTable   } @@ -257,14 +257,14 @@     -- In @let res = gpu { x }@ this is @res@.     entryResult :: VName,     -- | The index of the group that `entryResult` is bound in.-    entryGroupIdx :: Int,+    entryBlockIdx :: Int,     -- | If 'False' then the entry key is a variable that binds the same value     -- as the 'entryValue'. Otherwise it binds an array with an outer dimension     -- of one whose row equals that value.     entryStored :: Bool   } -type Groups = SQ.Seq Group+type Blocks = SQ.Seq Group  -- | A group is a subsequence of statements, usually either only GPUBody -- statements or only non-GPUBody statements. The 'Usage' statistics of those@@ -313,14 +313,14 @@ initialState :: State initialState =   State-    { stateGroups = SQ.singleton mempty,+    { stateBlocks = SQ.singleton mempty,       stateEquivalents = mempty     }  -- | Modify the groups that the sequence has been split into so far.-modifyGroups :: (Groups -> Groups) -> ReorderM ()-modifyGroups f =-  modify $ \st -> st {stateGroups = f (stateGroups st)}+modifyBlocks :: (Blocks -> Blocks) -> ReorderM ()+modifyBlocks f =+  modify $ \st -> st {stateBlocks = f (stateBlocks st)}  -- | Remove these keys from the equivalence table. removeEquivalents :: IS.IntSet -> ReorderM ()@@ -352,14 +352,14 @@   let usage' = usage {usageDependencies = deps'}    -- Move the GPUBody.-  grps <- gets stateGroups+  grps <- gets stateBlocks   let f = groupBlocks usage' consumed   let idx = fromMaybe 1 (SQ.findIndexR f grps)   let idx' = case idx `mod` 2 of         0 -> idx + 1         _ | consumes idx grps -> idx + 2         _ -> idx-  modifyGroups $ moveToGrp (stm, usage) idx'+  modifyBlocks $ moveToGrp (stm, usage) idx'    -- Record the kernel equivalents of the bound results.   let pes = patElems (stmPat stm)@@ -382,11 +382,11 @@ -- statement sequence, possibly a new group at the end of sequence. moveOther :: Stm GPU -> Usage -> Consumption -> ReorderM () moveOther stm usage consumed = do-  grps <- gets stateGroups+  grps <- gets stateBlocks   let f = groupBlocks usage consumed   let idx = fromMaybe 0 (SQ.findIndexR f grps)   let idx' = ((idx + 1) `div` 2) * 2-  modifyGroups $ moveToGrp (stm, usage) idx'+  modifyBlocks $ moveToGrp (stm, usage) idx'   recordEquivalentsOf stm idx'  -- | @recordEquivalentsOf stm idx@ records the GPUBody result and/or return@@ -406,11 +406,11 @@   case stm of     Let (Pat [PatElem x _]) _ (BasicOp (SubExp (Var n)))       | Just entry <- IM.lookup (baseTag n) eqs,-        entryGroupIdx entry == idx - 1 ->+        entryBlockIdx entry == idx - 1 ->           recordEquivalent x entry     Let (Pat [PatElem x _]) _ (BasicOp (Index arr slice))       | Just entry <- IM.lookup (baseTag arr) eqs,-        entryGroupIdx entry == idx - 1,+        entryBlockIdx entry == idx - 1,         Slice (DimFix i : dims) <- slice,         i == intConst Int64 0,         dims == map sliceDim (arrayDims $ entryType entry) ->@@ -429,7 +429,7 @@  -- | @moveToGrp stm idx grps@ moves @stm@ into the group at index @idx@ of -- @grps@.-moveToGrp :: (Stm GPU, Usage) -> Int -> Groups -> Groups+moveToGrp :: (Stm GPU, Usage) -> Int -> Blocks -> Blocks moveToGrp stm idx grps   | idx >= SQ.length grps =       moveToGrp stm idx (grps |> mempty)@@ -464,10 +464,10 @@ -- it, merging GPUBody groups into single kernels in the process. collapse :: ReorderM Group collapse = do-  grps <- zip (cycle [False, True]) . toList <$> gets stateGroups+  grps <- zip (cycle [False, True]) . toList <$> gets stateBlocks   grp <- foldM clps mempty grps -  modify $ \st -> st {stateGroups = SQ.singleton grp}+  modify $ \st -> st {stateBlocks = SQ.singleton grp}   pure grp   where     clps grp0 (gpu_bodies, Group stms usage) = do
src/Futhark/Optimise/Simplify/Rules/Loop.hs view
@@ -22,8 +22,8 @@ -- I do not claim that the current implementation of this rule is -- perfect, but it should suffice for many cases, and should never -- generate wrong code.-removeRedundantMergeVariables :: (BuilderOps rep) => BottomUpRuleLoop rep-removeRedundantMergeVariables (_, used) pat aux (merge, form, body)+removeRedundantLoopParams :: (BuilderOps rep) => BottomUpRuleLoop rep+removeRedundantLoopParams (_, used) pat aux (merge, form, body)   | not $ all (usedAfterLoop . fst) merge =       let necessaryForReturned =             findNecessaryForReturned@@ -78,13 +78,13 @@         Var v <- e =           ([paramName p], BasicOp $ Replicate mempty $ Var v)       | otherwise = ([paramName p], BasicOp $ SubExp e)-removeRedundantMergeVariables _ _ _ _ =+removeRedundantLoopParams _ _ _ _ =   Skip  -- We may change the type of the loop if we hoist out a shape -- annotation, in which case we also need to tweak the bound pattern.-hoistLoopInvariantMergeVariables :: (BuilderOps rep) => TopDownRuleLoop rep-hoistLoopInvariantMergeVariables vtable pat aux (merge, form, loopbody) = do+hoistLoopInvariantLoopParams :: (BuilderOps rep) => TopDownRuleLoop rep+hoistLoopInvariantLoopParams vtable pat aux (merge, form, loopbody) = do   -- Figure out which of the elements of loopresult are   -- loop-invariant, and hoist them out.   let explpat = zip (patElems pat) $ map (paramName . fst) merge@@ -103,7 +103,7 @@   where     res = bodyResult loopbody -    namesOfMergeParams = namesFromList $ map (paramName . fst) merge+    namesOfLoopParams = namesFromList $ map (paramName . fst) merge      removeFromResult cs (mergeParam, mergeInit) explpat' =       case partition ((== paramName mergeParam) . snd) explpat' of@@ -167,7 +167,7 @@         namesToList $           freeIn mergeParam `namesSubtract` oneName (paramName mergeParam)     invariantOrNotMergeParam namesOfInvariant name =-      (name `notNameIn` namesOfMergeParams)+      (name `notNameIn` namesOfLoopParams)         || (name `nameIn` namesOfInvariant)  simplifyClosedFormLoop :: (BuilderOps rep) => TopDownRuleLoop rep@@ -214,14 +214,14 @@  topDownRules :: (BuilderOps rep) => [TopDownRule rep] topDownRules =-  [ RuleLoop hoistLoopInvariantMergeVariables,+  [ RuleLoop hoistLoopInvariantLoopParams,     RuleLoop simplifyClosedFormLoop,     RuleLoop simplifyKnownIterationLoop   ]  bottomUpRules :: (BuilderOps rep) => [BottomUpRule rep] bottomUpRules =-  [ RuleLoop removeRedundantMergeVariables+  [ RuleLoop removeRedundantLoopParams   ]  -- | Standard loop simplification rules.
src/Futhark/Optimise/TileLoops.hs view
@@ -415,7 +415,7 @@          let merge' = zip mergeparams' mergeinit' -        let indexMergeParams slice =+        let indexLoopParams slice =               localScope (scopeOfFParams mergeparams') $                 forM_ (zip mergeparams mergeparams') $ \(to, from) ->                   letBindNames [paramName to] . BasicOp . Index (paramName from) $@@ -425,7 +425,7 @@               private <> namesFromList (map paramName mergeparams ++ map paramName mergeparams')              privstms' =-              PrivStms mempty indexMergeParams <> privstms <> inloop_privstms+              PrivStms mempty indexLoopParams <> privstms <> inloop_privstms          loopbody' <-           localScope (scopeOfFParams mergeparams') . runBodyBuilder $@@ -753,14 +753,14 @@       InputDontTile arr  reconstructGtids1D ::-  Count GroupSize SubExp ->+  Count BlockSize SubExp ->   VName ->   VName ->   VName ->   Builder GPU ()-reconstructGtids1D group_size gtid gid ltid =+reconstructGtids1D tblock_size gtid gid ltid =   letBindNames [gtid]-    =<< toExp (le64 gid * pe64 (unCount group_size) + le64 ltid)+    =<< toExp (le64 gid * pe64 (unCount tblock_size) + le64 ltid)  readTile1D ::   SubExp ->@@ -772,7 +772,7 @@   SubExp ->   [InputArray] ->   Builder GPU [InputTile]-readTile1D tile_size gid gtid (KernelGrid _num_groups group_size) kind privstms tile_id inputs =+readTile1D tile_size gid gtid (KernelGrid _num_tblocks tblock_size) kind privstms tile_id inputs =   fmap (inputsToTiles inputs)     . segMap1D "full_tile" lvl ResultNoSimplify tile_size     $ \ltid -> do@@ -780,7 +780,7 @@         letSubExp "j"           =<< toExp (pe64 tile_id * pe64 tile_size + le64 ltid) -      reconstructGtids1D group_size gtid gid ltid+      reconstructGtids1D tblock_size gtid gid ltid       addPrivStms [DimFix $ Var ltid] privstms        let arrs = map fst $ tiledInputs inputs@@ -802,7 +802,7 @@           TileFull ->             mapM readTileElem arrs   where-    lvl = SegThreadInGroup SegNoVirt+    lvl = SegThreadInBlock SegNoVirt  processTile1D ::   VName ->@@ -812,7 +812,7 @@   KernelGrid ->   ProcessTileArgs ->   Builder GPU [VName]-processTile1D gid gtid kdim tile_size (KernelGrid _num_groups group_size) tile_args = do+processTile1D gid gtid kdim tile_size (KernelGrid _num_tblocks tblock_size) tile_args = do   let red_comm = processComm tile_args       privstms = processPrivStms tile_args       map_lam = processMapLam tile_args@@ -821,8 +821,8 @@       tile_id = processTileId tile_args       accs = processAcc tile_args -  segMap1D "acc" lvl ResultPrivate (unCount group_size) $ \ltid -> do-    reconstructGtids1D group_size gtid gid ltid+  segMap1D "acc" lvl ResultPrivate (unCount tblock_size) $ \ltid -> do+    reconstructGtids1D tblock_size gtid gid ltid     addPrivStms [DimFix $ Var ltid] privstms      -- We replace the neutral elements with the accumulators (this is@@ -845,7 +845,7 @@           (eBody [pure $ Op $ OtherOp $ Screma tile_size tiles' form'])           (resultBodyM thread_accs)   where-    lvl = SegThreadInGroup SegNoVirt+    lvl = SegThreadInBlock SegNoVirt  processResidualTile1D ::   VName ->@@ -915,24 +915,24 @@   gid_flat <- newVName "gid_flat"    tile_size_key <- nameFromString . prettyString <$> newVName "tile_size"-  tile_size <- letSubExp "tile_size" $ Op $ SizeOp $ GetSize tile_size_key SizeGroup-  let group_size = tile_size+  tile_size <- letSubExp "tile_size" $ Op $ SizeOp $ GetSize tile_size_key SizeThreadBlock+  let tblock_size = tile_size    (grid, space) <- do     -- How many groups we need to exhaust the innermost dimension.     ldim <-       letSubExp "ldim" . BasicOp $-        BinOp (SDivUp Int64 Unsafe) kdim group_size+        BinOp (SDivUp Int64 Unsafe) kdim tblock_size -    num_groups <--      letSubExp "computed_num_groups"+    num_tblocks <-+      letSubExp "computed_num_tblocks"         =<< foldBinOp (Mul Int64 OverflowUndef) ldim (map snd dims_on_top)      pure-      ( KernelGrid (Count num_groups) (Count group_size),+      ( KernelGrid (Count num_tblocks) (Count tblock_size),         SegSpace gid_flat $ dims_on_top ++ [(gid, ldim)]       )-  let tiling_lvl = SegThreadInGroup SegNoVirt+  let tiling_lvl = SegThreadInBlock SegNoVirt    pure     Tiling@@ -952,7 +952,7 @@           letSubExp "num_whole_tiles" $             BasicOp $               BinOp (SQuot Int64 Unsafe) w tile_size,-        tilingLevel = SegGroup SegNoVirt (Just grid),+        tilingLevel = SegBlock SegNoVirt (Just grid),         tilingSpace = space       } @@ -1079,7 +1079,7 @@    segMap2D     "acc"-    (SegThreadInGroup (SegNoVirtFull (SegSeqDims [])))+    (SegThreadInBlock (SegNoVirtFull (SegSeqDims [])))     ResultPrivate     (tile_size, tile_size)     $ \(ltid_x, ltid_y) -> do@@ -1187,31 +1187,31 @@    tile_size_key <- nameFromString . prettyString <$> newVName "tile_size"   tile_size <- letSubExp "tile_size" $ Op $ SizeOp $ GetSize tile_size_key SizeTile-  group_size <- letSubExp "group_size" $ BasicOp $ BinOp (Mul Int64 OverflowUndef) tile_size tile_size+  tblock_size <- letSubExp "tblock_size" $ BasicOp $ BinOp (Mul Int64 OverflowUndef) tile_size tile_size -  num_groups_x <--    letSubExp "num_groups_x" $+  num_tblocks_x <-+    letSubExp "num_tblocks_x" $       BasicOp $         BinOp (SDivUp Int64 Unsafe) kdim_x tile_size-  num_groups_y <--    letSubExp "num_groups_y" $+  num_tblocks_y <-+    letSubExp "num_tblocks_y" $       BasicOp $         BinOp (SDivUp Int64 Unsafe) kdim_y tile_size -  num_groups <--    letSubExp "num_groups_top"+  num_tblocks <-+    letSubExp "num_tblocks_top"       =<< foldBinOp         (Mul Int64 OverflowUndef)-        num_groups_x-        (num_groups_y : map snd dims_on_top)+        num_tblocks_x+        (num_tblocks_y : map snd dims_on_top)    gid_flat <- newVName "gid_flat"-  let grid = KernelGrid (Count num_groups) (Count group_size)-      lvl = SegGroup (SegNoVirtFull (SegSeqDims [])) (Just grid)+  let grid = KernelGrid (Count num_tblocks) (Count tblock_size)+      lvl = SegBlock (SegNoVirtFull (SegSeqDims [])) (Just grid)       space =         SegSpace gid_flat $-          dims_on_top ++ [(gid_x, num_groups_x), (gid_y, num_groups_y)]-      tiling_lvl = SegThreadInGroup SegNoVirt+          dims_on_top ++ [(gid_x, num_tblocks_x), (gid_y, num_tblocks_y)]+      tiling_lvl = SegThreadInBlock SegNoVirt    pure     Tiling
src/Futhark/Optimise/TileLoops/Shared.hs view
@@ -175,7 +175,7 @@    let segspace = SegSpace ltid_flat $ seq_space ++ [(ltid_y, dim_y), (ltid_x, dim_x)]       lvl =-        SegThreadInGroup+        SegThreadInBlock           (SegNoVirtFull (SegSeqDims [0 .. length seq_dims - 1]))    ((res_v, res_i), stms) <-
src/Futhark/Pass/ExpandAllocations.hs view
@@ -190,17 +190,17 @@ ensureGridKnown lvl =   case lvl of     SegThread _ (Just grid) -> pure (mempty, lvl, grid)-    SegGroup _ (Just grid) -> pure (mempty, lvl, grid)+    SegBlock _ (Just grid) -> pure (mempty, lvl, grid)     SegThread virt Nothing -> mkGrid (SegThread virt)-    SegGroup virt Nothing -> mkGrid (SegGroup virt)-    SegThreadInGroup {} -> error "ensureGridKnown: SegThreadInGroup"+    SegBlock virt Nothing -> mkGrid (SegBlock virt)+    SegThreadInBlock {} -> error "ensureGridKnown: SegThreadInBlock"   where     mkGrid f = do       (grid, stms) <-         runBuilder $           KernelGrid-            <$> (Count <$> getSize "num_groups" SizeNumGroups)-            <*> (Count <$> getSize "group_size" SizeGroup)+            <$> (Count <$> getSize "num_tblocks" SizeGrid)+            <*> (Count <$> getSize "tblock_size" SizeThreadBlock)       pure (stms, f $ Just grid, grid)      getSize desc size_class = do@@ -237,9 +237,9 @@       pure ()    case lvl of-    SegGroup {}+    SegBlock {}       | not $ null variant_allocs ->-          throwError "Cannot handle invariant allocations in SegGroup."+          throwError "Cannot handle invariant allocations in SegBlock."     _ ->       pure () @@ -297,15 +297,15 @@     runBuilder . letSubExp "num_threads" . BasicOp $       BinOp         (Mul Int64 OverflowUndef)-        (unCount $ gridNumGroups grid)-        (unCount $ gridGroupSize grid)+        (unCount $ gridNumBlocks grid)+        (unCount $ gridBlockSize grid)    (invariant_alloc_stms, invariant_alloc_offsets) <-     inScopeOf num_threads_stms $       expandedInvariantAllocations         num_threads-        (gridNumGroups grid)-        (gridGroupSize grid)+        (gridNumBlocks grid)+        (gridBlockSize grid)         invariant_allocs    (variant_alloc_stms, variant_alloc_offsets) <-@@ -385,7 +385,7 @@    in (set_stms (stmsFromList stms) body, allocs)  expandable, notScalar :: Space -> Bool-expandable (Space "local") = False+expandable (Space "shared") = False expandable ScalarSpace {} = False expandable _ = True notScalar ScalarSpace {} = False@@ -474,9 +474,9 @@             product dims           ) -    newBase user@(SegThreadInGroup {}, _) = newBaseThread user+    newBase user@(SegThreadInBlock {}, _) = newBaseThread user     newBase user@(SegThread {}, _) = newBaseThread user-    newBase user@(SegGroup {}, _) = \_old_shape ->+    newBase user@(SegBlock {}, _) = \_old_shape ->       let (users_shape, user_ids) = getNumUsers user           dims = map pe64 (shapeDims users_shape)        in ( flattenIndex dims user_ids,@@ -485,18 +485,18 @@  expandedInvariantAllocations ::   SubExp ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->+  Count NumBlocks SubExp ->+  Count BlockSize SubExp ->   Extraction ->   ExpandM (Stms GPUMem, RebaseMap)-expandedInvariantAllocations num_threads (Count num_groups) (Count group_size) =+expandedInvariantAllocations num_threads (Count num_tblocks) (Count tblock_size) =   genericExpandedInvariantAllocations getNumUsers   where     getNumUsers (SegThread {}, [gtid]) = (Shape [num_threads], [gtid])-    getNumUsers (SegThread {}, [gid, ltid]) = (Shape [num_groups, group_size], [gid, ltid])-    getNumUsers (SegThreadInGroup {}, [gtid]) = (Shape [num_threads], [gtid])-    getNumUsers (SegThreadInGroup {}, [gid, ltid]) = (Shape [num_groups, group_size], [gid, ltid])-    getNumUsers (SegGroup {}, [gid]) = (Shape [num_groups], [gid])+    getNumUsers (SegThread {}, [gid, ltid]) = (Shape [num_tblocks, tblock_size], [gid, ltid])+    getNumUsers (SegThreadInBlock {}, [gtid]) = (Shape [num_threads], [gtid])+    getNumUsers (SegThreadInBlock {}, [gid, ltid]) = (Shape [num_tblocks, tblock_size], [gid, ltid])+    getNumUsers (SegBlock {}, [gid]) = (Shape [num_tblocks], [gid])     getNumUsers user = error $ "getNumUsers: unhandled " ++ show user  expandedVariantAllocations ::
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -41,7 +41,6 @@ import Control.Monad.Writer import Data.Bifunctor (first) import Data.Either (partitionEithers)-import Data.Foldable (toList) import Data.List (foldl', transpose, zip4) import Data.Map.Strict qualified as M import Data.Maybe@@ -392,11 +391,11 @@ ensureArrayIn space (Var v) = do   (mem', v') <- lift $ ensureRowMajorArray (Just space) v   (_, ixfun) <- lift $ lookupArraySummary v'-  ctx <- lift $ mapM (letSubExp "ixfun_arg" <=< toExp) (toList ixfun)+  ctx <- lift $ mapM (letSubExp "ixfun_arg" <=< toExp) (IxFun.existentialized ixfun)   tell ([Var mem'], ctx)   pure $ Var v' -allocInMergeParams ::+allocInLoopParams ::   (Allocable fromrep torep inner) =>   [(FParam fromrep, SubExp)] ->   ( [(FParam torep, SubExp)] ->@@ -404,7 +403,7 @@     AllocM fromrep torep a   ) ->   AllocM fromrep torep a-allocInMergeParams merge m = do+allocInLoopParams merge m = do   ((valparams, valargs, handle_loop_subexps), (mem_params, ctx_params)) <-     runWriterT $ unzip3 <$> mapM allocInMergeParam merge   let mergeparams' = mem_params <> ctx_params <> valparams@@ -474,11 +473,13 @@                     )             _ -> do               (v_mem', v') <- lift $ ensureRowMajorArray Nothing v-              (_, v_ixfun') <- lift $ lookupArraySummary v'+              let ixfun_ext =+                    IxFun.existentialize 0 $ IxFun.iota $ map pe64 $ shapeDims shape+               v_mem_space' <- lift $ lookupMemSpace v_mem'                ctx_params <--                replicateM (length v_ixfun') $+                replicateM (length (IxFun.existentialized ixfun_ext)) $                   newParam "ctx_param_ext" (MemPrim int64)                param_ixfun <-@@ -487,7 +488,7 @@                     ( M.fromList . zip (fmap Ext [0 ..]) $                         map (le64 . Free . paramName) ctx_params                     )-                    (IxFun.existentialize v_ixfun')+                    ixfun_ext                mem_param <- newParam "mem_param" $ MemMem v_mem_space'               tell ([mem_param], ctx_params)@@ -775,16 +776,16 @@  contextRets :: MemReqType -> [MemInfo d u r] contextRets (MemArray _ shape _ (MemReq space (Rank base_rank))) =-  -- Memory + offset + base_rank + (stride,size)*rank.+  -- Memory + offset + base_rank + stride*rank.   MemMem space     : MemPrim int64     : replicate base_rank (MemPrim int64)-    ++ replicate (2 * shapeRank shape) (MemPrim int64)+    ++ replicate (shapeRank shape) (MemPrim int64) contextRets (MemArray _ shape _ (NeedsNormalisation space)) =-  -- Memory + offset + (base,stride,size)*rank.+  -- Memory + offset + (base,stride)*rank.   MemMem space     : MemPrim int64-    : replicate (3 * shapeRank shape) (MemPrim int64)+    : replicate (2 * shapeRank shape) (MemPrim int64) contextRets _ = []  -- Add memory information to the body, but do not return memory/ixfun@@ -837,7 +838,8 @@       let shape' = fmap (adjustExt num_new_ctx) shape           (space, base_rank) = arrayInfo (shapeRank shape) req        in MemArray pt shape' u . ReturnsNewBlock space ctx_offset $-            convert <$> IxFun.mkExistential base_rank (shapeRank shape) (ctx_offset + 1)+            convert+              <$> IxFun.mkExistential base_rank (shapeDims shape') (ctx_offset + 1)     inspect _ (MemAcc acc ispace ts u) = MemAcc acc ispace ts u     inspect _ (MemPrim pt) = MemPrim pt     inspect _ (MemMem space) = MemMem space@@ -874,7 +876,7 @@         MemAcc {} -> pure []         MemMem {} -> pure [] -- should not happen         MemArray _ _ _ (ArrayIn mem ixfun) -> do-          ixfun_exts <- mapM (letSubExp "ixfun_ext" <=< toExp) $ toList ixfun+          ixfun_exts <- mapM (letSubExp "ixfun_ext" <=< toExp) $ IxFun.existentialized ixfun           pure $ subExpRes (Var mem) : subExpsRes ixfun_exts  -- Do a a simple form of invariance analysis to simplify a Match.  It@@ -928,7 +930,7 @@   Exp fromrep ->   AllocM fromrep torep (Exp torep) allocInExp (Loop merge form (Body () bodystms bodyres)) =-  allocInMergeParams merge $ \merge' mk_loop_val -> do+  allocInLoopParams merge $ \merge' mk_loop_val -> do     localScope (scopeOfLoopForm form) $ do       body' <-         buildBody_ . allocInStms bodystms $ do
src/Futhark/Pass/ExplicitAllocations/GPU.hs view
@@ -30,9 +30,9 @@     }   where     space = case lvl of-      SegGroup {} -> Space "local"+      SegBlock {} -> Space "shared"       SegThread {} -> Space "device"-      SegThreadInGroup {} -> Space "device"+      SegThreadInBlock {} -> Space "device"  handleSegOp ::   Maybe SegLevel ->@@ -44,7 +44,7 @@       -- This implies we are in the intragroup parallelism situation.       -- Just allocate for a single group; memory expansion will       -- handle the rest later.-      (Just (SegGroup _ (Just grid)), _) -> pure $ unCount $ gridGroupSize grid+      (Just (SegBlock _ (Just grid)), _) -> pure $ unCount $ gridBlockSize grid       _ ->         letSubExp "num_threads"           =<< case maybe_grid of@@ -52,8 +52,8 @@               pure . BasicOp $                 BinOp                   (Mul Int64 OverflowUndef)-                  (unCount (gridNumGroups grid))-                  (unCount (gridGroupSize grid))+                  (unCount (gridNumBlocks grid))+                  (unCount (gridBlockSize grid))             Nothing ->               foldBinOp                 (Mul Int64 OverflowUndef)@@ -64,9 +64,9 @@     maybe_grid =       case (outer_lvl, segLevel op) of         (Just (SegThread _ (Just grid)), _) -> Just grid-        (Just (SegGroup _ (Just grid)), _) -> Just grid+        (Just (SegBlock _ (Just grid)), _) -> Just grid         (_, SegThread _ (Just grid)) -> Just grid-        (_, SegGroup _ (Just grid)) -> Just grid+        (_, SegBlock _ (Just grid)) -> Just grid         _ -> Nothing     scope = scopeOfSegSpace $ segSpace op     mapper num_threads =@@ -79,8 +79,8 @@         }     f = case segLevel op of       SegThread {} -> inThread-      SegThreadInGroup {} -> inThread-      SegGroup {} -> inGroup+      SegThreadInBlock {} -> inThread+      SegBlock {} -> inGroup     inThread env = env {envExpHints = inThreadExpHints}     inGroup env = env {envExpHints = inGroupExpHints} 
src/Futhark/Pass/ExtractKernels.hs view
@@ -802,7 +802,7 @@      checkSuffIntraPar       path'-      ((_intra_min_par, intra_avail_par), group_size, _, intra_prelude, intra_stms) = do+      ((_intra_min_par, intra_avail_par), tblock_size, _, intra_prelude, intra_stms) = do         -- We must check that all intra-group parallelism fits in a group.         ((intra_ok, intra_suff_key), intra_suff_stms) <- do           ((intra_suff, suff_key), check_suff_stms) <-@@ -815,12 +815,12 @@           runBuilder $ do             addStms intra_prelude -            max_group_size <--              letSubExp "max_group_size" $ Op $ SizeOp $ GetSizeMax SizeGroup+            max_tblock_size <-+              letSubExp "max_tblock_size" $ Op $ SizeOp $ GetSizeMax SizeThreadBlock             fits <-               letSubExp "fits" $                 BasicOp $-                  CmpOp (CmpSle Int64) group_size max_group_size+                  CmpOp (CmpSle Int64) tblock_size max_tblock_size              addStms check_suff_stms 
src/Futhark/Pass/ExtractKernels/Intragroup.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE TypeFamilies #-}  -- | Extract limited nested parallelism for execution inside--- individual kernel workgroups.+-- individual kernel threadblocks. module Futhark.Pass.ExtractKernels.Intragroup (intraGroupParallelise) where  import Control.Monad@@ -51,15 +51,15 @@ intraGroupParallelise knest lam = runMaybeT $ do   (ispace, inps) <- lift $ flatKernel knest -  (num_groups, w_stms) <-+  (num_tblocks, w_stms) <-     lift $       runBuilder $-        letSubExp "intra_num_groups"+        letSubExp "intra_num_tblocks"           =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) (map snd ispace)    let body = lambdaBody lam -  group_size <- newVName "computed_group_size"+  tblock_size <- newVName "computed_tblock_size"   (wss_min, wss_avail, log, kbody) <-     lift . localScope (scopeOfLParams $ lambdaParams lam) $       intraGroupParalleliseBody body@@ -92,12 +92,12 @@       -- The group size is either the maximum of the minimum parallelism       -- exploited, or the desired parallelism (bounded by the max group       -- size) in case there is no minimum.-      letBindNames [group_size]+      letBindNames [tblock_size]         =<< if null ws_min           then             eBinOp               (SMin Int64)-              (eSubExp =<< letSubExp "max_group_size" (Op $ SizeOp $ GetSizeMax SizeGroup))+              (eSubExp =<< letSubExp "max_tblock_size" (Op $ SizeOp $ GetSizeMax SizeThreadBlock))               (eSubExp intra_avail_par)           else foldBinOp' (SMax Int64) ws_min @@ -106,22 +106,22 @@        addStms w_stms       read_input_stms <- runBuilder_ $ mapM readGroupKernelInput used_inps-      space <- SegSpace <$> newVName "phys_group_id" <*> pure ispace+      space <- SegSpace <$> newVName "phys_tblock_id" <*> pure ispace       pure (intra_avail_par, space, read_input_stms)    let kbody' = kbody {kernelBodyStms = read_input_stms <> kernelBodyStms kbody}    let nested_pat = loopNestingPat first_nest       rts = map (length ispace `stripArray`) $ patTypes nested_pat-      grid = KernelGrid (Count num_groups) (Count $ Var group_size)-      lvl = SegGroup SegNoVirt (Just grid)+      grid = KernelGrid (Count num_tblocks) (Count $ Var tblock_size)+      lvl = SegBlock SegNoVirt (Just grid)       kstm =         Let nested_pat aux $ Op $ SegOp $ SegMap lvl kspace rts kbody'    let intra_min_par = intra_avail_par   pure     ( (intra_min_par, intra_avail_par),-      Var group_size,+      Var tblock_size,       log,       prelude_stms,       oneStm kstm@@ -253,7 +253,7 @@             =<< runDistNestT env (distributeMapBodyStms acc (bodyStms $ lambdaBody lam))     Op (Screma w arrs form)       | Just (scans, mapfun) <- isScanomapSOAC form,-        -- FIXME: Futhark.CodeGen.ImpGen.GPU.Group.compileGroupOp+        -- FIXME: Futhark.CodeGen.ImpGen.GPU.Block.compileGroupOp         -- cannot handle multiple scan operators yet.         Scan scanfun nes <- singleScan scans -> do           let scanfun' = soacsLambdaToGPU scanfun
src/Futhark/Pass/ExtractKernels/StreamKernel.hs view
@@ -37,24 +37,24 @@   }   deriving (Eq, Ord, Show) -numberOfGroups ::+numberOfBlocks ::   (MonadBuilder m, Op (Rep m) ~ HostOp inner (Rep m)) =>   String ->   SubExp ->   SubExp ->   m (SubExp, SubExp)-numberOfGroups desc w group_size = do-  max_num_groups_key <- nameFromString . prettyString <$> newVName (desc ++ "_num_groups")-  num_groups <--    letSubExp "num_groups" $+numberOfBlocks desc w tblock_size = do+  max_num_tblocks_key <- nameFromString . prettyString <$> newVName (desc ++ "_num_tblocks")+  num_tblocks <-+    letSubExp "num_tblocks" $       Op $         SizeOp $-          CalcNumGroups w max_num_groups_key group_size+          CalcNumBlocks w max_num_tblocks_key tblock_size   num_threads <-     letSubExp "num_threads" $       BasicOp $-        BinOp (Mul Int64 OverflowUndef) num_groups group_size-  pure (num_groups, num_threads)+        BinOp (Mul Int64 OverflowUndef) num_tblocks tblock_size+  pure (num_tblocks, num_threads)  -- | Like 'segThread', but cap the thread count to the input size. -- This is more efficient for small kernels, e.g. summing a small@@ -64,7 +64,7 @@   w <-     letSubExp "nest_size"       =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) ws-  group_size <- getSize (desc ++ "_group_size") SizeGroup+  tblock_size <- getSize (desc ++ "_tblock_size") SizeThreadBlock    case r of     ManyThreads -> do@@ -73,10 +73,10 @@           =<< eBinOp             (SDivUp Int64 Unsafe)             (eSubExp w)-            (eSubExp =<< asIntS Int64 group_size)-      let grid = KernelGrid (Count usable_groups) (Count group_size)+            (eSubExp =<< asIntS Int64 tblock_size)+      let grid = KernelGrid (Count usable_groups) (Count tblock_size)       pure $ SegThread SegNoVirt (Just grid)     NoRecommendation v -> do-      (num_groups, _) <- numberOfGroups desc w group_size-      let grid = KernelGrid (Count num_groups) (Count group_size)+      (num_tblocks, _) <- numberOfBlocks desc w tblock_size+      let grid = KernelGrid (Count num_tblocks) (Count tblock_size)       pure $ SegThread v (Just grid)
src/Futhark/Pass/ExtractKernels/ToGPU.hs view
@@ -37,8 +37,8 @@   where     kernelGrid =       KernelGrid-        <$> (Count <$> getSize (desc ++ "_num_groups") SizeNumGroups)-        <*> (Count <$> getSize (desc ++ "_group_size") SizeGroup)+        <$> (Count <$> getSize (desc ++ "_num_tblocks") SizeGrid)+        <*> (Count <$> getSize (desc ++ "_tblock_size") SizeThreadBlock)  injectSOACS ::   ( Monad m,
src/Futhark/Pass/KernelBabysitting.hs view
@@ -68,7 +68,7 @@ transformStm :: ExpMap -> Stm GPU -> BabysitM ExpMap transformStm expmap (Let pat aux (Op (SegOp op)))   -- FIXME: We only make coalescing optimisations for SegThread-  -- SegOps, because that's what the analysis assumes.  For SegGroup+  -- SegOps, because that's what the analysis assumes.  For SegBlock   -- we should probably look at the component SegThreads, but it   -- apparently hasn't come up in practice yet.   | SegThread {} <- segLevel op = do
src/Language/Futhark/Interpreter.hs view
@@ -1919,6 +1919,7 @@                 <> prettyText (asInt64 m)                 <> "]"           else pure $ toArray shape $ map (toArray rowshape) $ chunk (asInt m) xs'+    def "manifest" = Just $ fun1 pure     def "vjp2" = Just $       fun3 $         \_ _ _ -> bad noLoc mempty "Interpreter does not support autodiff."
src/Language/Futhark/Prop.hs view
@@ -698,7 +698,15 @@         ++ zipWith           namify           [intrinsicStart ..]-          ( [ ( "flatten",+          ( [ ( "manifest",+                IntrinsicPolyFun+                  [tp_a]+                  [Scalar $ t_a mempty]+                  $ RetType []+                  $ Scalar+                  $ t_a mempty+              ),+              ( "flatten",                 IntrinsicPolyFun                   [tp_a, sp_n, sp_m]                   [Array Observe (shape [n, m]) $ t_a mempty]
unittests/Futhark/Optimise/MemoryBlockMerging/GreedyColoringTests.hs view
@@ -22,12 +22,12 @@   testCase "psumTest"     $ assertEqual       "Color simple 1-2-3 using two colors"-      ( [(0, "local"), (1, "local")] :: [(Int, String)],+      ( [(0, "shared"), (1, "shared")] :: [(Int, String)],         [(1 :: Int, 0), (2, 1), (3, 0)]       )     $ (M.toList *** M.toList)     $ GreedyColoring.colorGraph-      (M.fromList [(1, "local"), (2, "local"), (3, "local")])+      (M.fromList [(1, "shared"), (2, "shared"), (3, "shared")])     $ S.fromList [(1, 2), (2, 3)]  allIntersect :: TestTree@@ -35,12 +35,12 @@   testCase "allIntersect"     $ assertEqual       "Color a graph where all values intersect"-      ( [(0, "local"), (1, "local"), (2, "local")] :: [(Int, String)],+      ( [(0, "shared"), (1, "shared"), (2, "shared")] :: [(Int, String)],         [(1 :: Int, 2), (2, 1), (3, 0)]       )     $ (M.toList *** M.toList)     $ GreedyColoring.colorGraph-      (M.fromList [(1, "local"), (2, "local"), (3, "local")])+      (M.fromList [(1, "shared"), (2, "shared"), (3, "shared")])     $ S.fromList [(1, 2), (2, 3), (1, 3)]  emptyGraph :: TestTree@@ -56,12 +56,12 @@ noIntersections :: TestTree noIntersections =   GreedyColoring.colorGraph-    (M.fromList [(1, "local"), (2, "local"), (3, "local")])+    (M.fromList [(1, "shared"), (2, "shared"), (3, "shared")])     (S.fromList [])     & M.toList *** M.toList     & assertEqual       "Color nodes with no intersections"-      ( [(0, "local")] :: [(Int, String)],+      ( [(0, "shared")] :: [(Int, String)],         [(1, 0), (2, 0), (3, 0)] :: [(Int, Int)]       )     & testCase "noIntersections"