packages feed

futhark 0.21.15 → 0.22.1

raw patch · 51 files changed

+2571/−3689 lines, 51 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Futhark.Builder.Class: type family Rep m :: Type;
- Futhark.CodeGen.ImpGen.GPU.Base: compileGroupResult :: SegSpace -> PatElem LetDecMem -> KernelResult -> InKernelGen ()
- Futhark.CodeGen.ImpGen.GPU.Base: computeThreadChunkSize :: SplitOrdering -> TExp Int64 -> Count Elements (TExp Int64) -> Count Elements (TExp Int64) -> TV Int64 -> ImpM rep r op ()
- Futhark.CodeGen.ImpGen.GPU.Base: data Precomputed
- Futhark.CodeGen.ImpGen.GPU.Base: precomputeConstants :: Count GroupSize (TExp Int64) -> Stms GPUMem -> CallKernelGen Precomputed
- Futhark.CodeGen.ImpGen.GPU.Base: precomputedConstants :: Precomputed -> InKernelGen a -> InKernelGen a
- Futhark.CodeGen.ImpGen.GPU.Base: sKernelGroup :: String -> VName -> KernelAttrs -> InKernelGen () -> CallKernelGen ()
- Futhark.Construct: eRoundToMultipleOf :: MonadBuilder m => IntType -> m (Exp (Rep m)) -> m (Exp (Rep m)) -> m (Exp (Rep m))
- Futhark.Construct: eSliceArray :: MonadBuilder m => Int -> VName -> m (Exp (Rep m)) -> m (Exp (Rep m)) -> m (Exp (Rep m))
- Futhark.IR.GPU: Disorder :: StreamOrd
- Futhark.IR.GPU: InOrder :: StreamOrd
- Futhark.IR.GPU: Parallel :: StreamOrd -> Commutativity -> Lambda rep -> StreamForm rep
- Futhark.IR.GPU: Sequential :: StreamForm rep
- Futhark.IR.GPU: data StreamForm rep
- Futhark.IR.GPU: data StreamOrd
- Futhark.IR.GPU.Op: SplitSpace :: SplitOrdering -> SubExp -> SubExp -> SubExp -> SizeOp
- Futhark.IR.MC: Disorder :: StreamOrd
- Futhark.IR.MC: InOrder :: StreamOrd
- Futhark.IR.MC: Parallel :: StreamOrd -> Commutativity -> Lambda rep -> StreamForm rep
- Futhark.IR.MC: Sequential :: StreamForm rep
- Futhark.IR.MC: data StreamForm rep
- Futhark.IR.MC: data StreamOrd
- Futhark.IR.Prop.Aliases: type family OpWithAliases op :: Type;
- Futhark.IR.Rep: type family Op l :: Type;
- Futhark.IR.SOACS.SOAC: Disorder :: StreamOrd
- Futhark.IR.SOACS.SOAC: InOrder :: StreamOrd
- Futhark.IR.SOACS.SOAC: Parallel :: StreamOrd -> Commutativity -> Lambda rep -> StreamForm rep
- Futhark.IR.SOACS.SOAC: Sequential :: StreamForm rep
- Futhark.IR.SOACS.SOAC: data StreamForm rep
- Futhark.IR.SOACS.SOAC: data StreamOrd
- Futhark.IR.SOACS.SOAC: instance Futhark.IR.Prop.ASTRep rep => Futhark.IR.Prop.Names.FreeIn (Futhark.IR.SOACS.SOAC.StreamForm rep)
- Futhark.IR.SOACS.SOAC: instance Futhark.IR.Rep.RepTypes rep => GHC.Classes.Eq (Futhark.IR.SOACS.SOAC.StreamForm rep)
- Futhark.IR.SOACS.SOAC: instance Futhark.IR.Rep.RepTypes rep => GHC.Classes.Ord (Futhark.IR.SOACS.SOAC.StreamForm rep)
- Futhark.IR.SOACS.SOAC: instance Futhark.IR.Rep.RepTypes rep => GHC.Show.Show (Futhark.IR.SOACS.SOAC.StreamForm rep)
- Futhark.IR.SOACS.SOAC: instance GHC.Classes.Eq Futhark.IR.SOACS.SOAC.StreamOrd
- Futhark.IR.SOACS.SOAC: instance GHC.Classes.Ord Futhark.IR.SOACS.SOAC.StreamOrd
- Futhark.IR.SOACS.SOAC: instance GHC.Show.Show Futhark.IR.SOACS.SOAC.StreamOrd
- Futhark.IR.SegOp: ConcatReturns :: Certs -> SplitOrdering -> SubExp -> SubExp -> VName -> KernelResult
- Futhark.IR.SegOp: SplitContiguous :: SplitOrdering
- Futhark.IR.SegOp: SplitStrided :: SubExp -> SplitOrdering
- Futhark.IR.SegOp: data SplitOrdering
- Futhark.IR.SegOp: instance Futhark.IR.Prop.Names.FreeIn Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: instance Futhark.Optimise.Simplify.Engine.Simplifiable Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: instance Futhark.Transform.Rename.Rename Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: instance Futhark.Transform.Substitute.Substitute Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: instance GHC.Classes.Eq Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: instance GHC.Classes.Ord Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: instance GHC.Show.Show Futhark.IR.SegOp.SplitOrdering
- Futhark.IR.SegOp: type family SegOpLevel rep;
- Futhark.Internalise.Lambdas: internaliseStreamLambda :: InternaliseLambda -> Exp -> [Type] -> InternaliseM ([LParam SOACS], Body SOACS)
- Futhark.Internalise.Lambdas: internaliseStreamMapLambda :: InternaliseLambda -> Exp -> [SubExp] -> InternaliseM (Lambda SOACS)
- Futhark.Optimise.Fusion.Composing: mergeReduceOps :: Lambda rep -> Lambda rep -> Lambda rep
- Futhark.Optimise.Simplify.Rep: type family OpWithWisdom op :: Type;
- Futhark.Pass.ExplicitAllocations: [chunkMap] :: AllocEnv fromrep torep -> ChunkMap
- Futhark.Pass.ExplicitAllocations: dimAllocationSize :: ChunkMap -> SubExp -> SubExp
- Futhark.Pass.ExplicitAllocations: opSizeSubst :: SizeSubst op => Pat dec -> op -> ChunkMap
- Futhark.Pass.ExplicitAllocations: type ChunkMap = Map VName SubExp
- Futhark.Pass.ExtractKernels.StreamKernel: streamMap :: (MonadFreshNames m, HasScope GPU m) => MkSegLevel GPU m -> [String] -> [PatElem Type] -> SubExp -> Commutativity -> Lambda GPU -> [SubExp] -> [VName] -> m ((SubExp, [VName]), Stms GPU)
- Futhark.Pass.ExtractKernels.StreamKernel: streamRed :: (MonadFreshNames m, HasScope GPU m) => MkSegLevel GPU m -> Pat Type -> SubExp -> Commutativity -> Lambda GPU -> Lambda GPU -> [SubExp] -> [VName] -> m (Stms GPU)
+ Futhark.Builder.Class: type Rep m :: Type;
+ Futhark.CodeGen.ImpGen.GPU.Base: allocLocal :: AllocCompiler GPUMem r KernelOp
+ Futhark.CodeGen.ImpGen.GPU.Base: fenceForArrays :: [VName] -> InKernelGen Fence
+ Futhark.CodeGen.ImpGen.GPU.Base: groupLoop :: IntExp t => TExp t -> (TExp t -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: kernelAlloc :: Pat LetDecMem -> SubExp -> Space -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: sKernel :: Operations GPUMem KernelEnv KernelOp -> (KernelConstants -> TExp Int32) -> String -> VName -> KernelAttrs -> InKernelGen () -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.GPU.Base: threadOperations :: Operations GPUMem KernelEnv KernelOp
+ Futhark.CodeGen.ImpGen.GPU.Base: updateAcc :: VName -> [SubExp] -> [SubExp] -> 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.IR.Prop.Aliases: type OpWithAliases op :: Type;
+ Futhark.IR.SegOp: type SegOpLevel rep;
+ Futhark.Optimise.Simplify.Rep: type OpWithWisdom op :: Type;
+ Language.Futhark.Parser.Lexer.Tokens: COMMENT :: Text -> Token
- Futhark.Analysis.HORep.SOAC: Stream :: SubExp -> StreamForm rep -> Lambda rep -> [SubExp] -> [Input] -> SOAC rep
+ Futhark.Analysis.HORep.SOAC: Stream :: SubExp -> Lambda rep -> [SubExp] -> [Input] -> SOAC rep
- Futhark.IR.GPU: Stream :: SubExp -> [VName] -> StreamForm rep -> [SubExp] -> Lambda rep -> SOAC rep
+ Futhark.IR.GPU: Stream :: SubExp -> [VName] -> [SubExp] -> Lambda rep -> SOAC rep
- Futhark.IR.GPU: ppStream :: (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> StreamForm rep -> [SubExp] -> Lambda rep -> Doc
+ Futhark.IR.GPU: ppStream :: (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> [SubExp] -> Lambda rep -> Doc
- Futhark.IR.MC: Stream :: SubExp -> [VName] -> StreamForm rep -> [SubExp] -> Lambda rep -> SOAC rep
+ Futhark.IR.MC: Stream :: SubExp -> [VName] -> [SubExp] -> Lambda rep -> SOAC rep
- Futhark.IR.MC: ppStream :: (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> StreamForm rep -> [SubExp] -> Lambda rep -> Doc
+ Futhark.IR.MC: ppStream :: (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> [SubExp] -> Lambda rep -> Doc
- Futhark.IR.Mem: class TypedOp op => OpReturns op
+ Futhark.IR.Mem: class IsOp op => OpReturns op
- Futhark.IR.Mem.Simplify: type SimplifyMemory rep inner = (SimplifiableRep rep, LetDec rep ~ LetDecMem, ExpDec rep ~ (), BodyDec rep ~ (), CanBeWise (Op rep), BuilderOps (Wise rep), OpReturns (OpWithWisdom inner), Mem rep inner)
+ Futhark.IR.Mem.Simplify: type SimplifyMemory rep inner = (SimplifiableRep rep, LetDec rep ~ LetDecMem, ExpDec rep ~ (), BodyDec rep ~ (), CanBeWise (Op rep), BuilderOps (Wise rep), OpReturns (OpWithWisdom inner), IndexOp (OpWithWisdom inner), AliasedOp (OpWithWisdom inner), Mem rep inner)
- Futhark.IR.SOACS.SOAC: Stream :: SubExp -> [VName] -> StreamForm rep -> [SubExp] -> Lambda rep -> SOAC rep
+ Futhark.IR.SOACS.SOAC: Stream :: SubExp -> [VName] -> [SubExp] -> Lambda rep -> SOAC rep
- Futhark.IR.SOACS.SOAC: ppStream :: (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> StreamForm rep -> [SubExp] -> Lambda rep -> Doc
+ Futhark.IR.SOACS.SOAC: ppStream :: (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> [SubExp] -> Lambda rep -> Doc
- Futhark.Pass.ExplicitAllocations: AllocEnv :: ChunkMap -> Bool -> Space -> Set VName -> (Op fromrep -> AllocM fromrep torep (Op torep)) -> (Exp torep -> AllocM fromrep torep [ExpHint]) -> AllocEnv fromrep torep
+ Futhark.Pass.ExplicitAllocations: AllocEnv :: Bool -> Space -> Set VName -> (Op fromrep -> AllocM fromrep torep (Op torep)) -> (Exp torep -> AllocM fromrep torep [ExpHint]) -> AllocEnv fromrep torep
- Futhark.Pass.ExplicitAllocations: simplifiable :: (SimplifiableRep rep, ExpDec rep ~ (), BodyDec rep ~ (), LetDec rep ~ LetDecMem, OpReturns (OpWithWisdom inner), Mem rep inner) => (OpWithWisdom inner -> UsageTable) -> (OpWithWisdom inner -> SimpleM rep (OpWithWisdom inner, Stms (Wise rep))) -> SimpleOps rep
+ Futhark.Pass.ExplicitAllocations: simplifiable :: (SimplifiableRep rep, LetDec rep ~ LetDecMem, ExpDec rep ~ (), BodyDec rep ~ (), OpReturns (OpWithWisdom inner), AliasedOp (OpWithWisdom inner), IndexOp (OpWithWisdom inner), Mem rep inner) => (OpWithWisdom inner -> UsageTable) -> (OpWithWisdom inner -> SimpleM rep (OpWithWisdom inner, Stms (Wise rep))) -> SimpleOps rep
- Futhark.Pass.ExplicitAllocations.Seq: simplifiable :: (SimplifiableRep rep, ExpDec rep ~ (), BodyDec rep ~ (), LetDec rep ~ LetDecMem, OpReturns (OpWithWisdom inner), Mem rep inner) => (OpWithWisdom inner -> UsageTable) -> (OpWithWisdom inner -> SimpleM rep (OpWithWisdom inner, Stms (Wise rep))) -> SimpleOps rep
+ Futhark.Pass.ExplicitAllocations.Seq: simplifiable :: (SimplifiableRep rep, LetDec rep ~ LetDecMem, ExpDec rep ~ (), BodyDec rep ~ (), OpReturns (OpWithWisdom inner), AliasedOp (OpWithWisdom inner), IndexOp (OpWithWisdom inner), Mem rep inner) => (OpWithWisdom inner -> UsageTable) -> (OpWithWisdom inner -> SimpleM rep (OpWithWisdom inner, Stms (Wise rep))) -> SimpleOps rep

Files

docs/error-index.rst view
@@ -475,11 +475,10 @@ "Parameter *x* used as size would go out of scope." ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This error tends to happen because higher-order functions are used in-a way that causes a size requirement to become impossible to-constrait.  Real programs that run into this issue are quite complex,-but to illustrate the problem, consider the following contrived-function:+This error tends to happen when higher-order functions are used in a+way that causes a size requirement to become impossible to express.+Real programs that encounter this issue tend to be complicated, but to+illustrate the problem, consider the following contrived function:  .. code-block:: futhark 
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:           futhark-version:        0.21.15+version:        0.22.1 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to@@ -190,6 +190,7 @@       Futhark.CodeGen.ImpGen.CUDA       Futhark.CodeGen.ImpGen.GPU       Futhark.CodeGen.ImpGen.GPU.Base+      Futhark.CodeGen.ImpGen.GPU.Group       Futhark.CodeGen.ImpGen.GPU.SegHist       Futhark.CodeGen.ImpGen.GPU.SegMap       Futhark.CodeGen.ImpGen.GPU.SegRed
prelude/soacs.fut view
@@ -195,54 +195,6 @@   let (as', is) = intrinsics.partition (3, p', as)   in (as'[0:is[0]], as'[is[0]:is[0]+is[1]], as'[is[0]+is[1]:n]) --- | `reduce_stream op f as` splits `as` into chunks, applies `f` to each--- of these in parallel, and uses `op` (which must be associative) to--- combine the per-chunk results into a final result.  The `i64`--- passed to `f` is the size of the chunk.  This SOAC is useful when--- `f` can be given a particularly work-efficient sequential--- implementation.  Operationally, we can imagine that `as` is divided--- among as many threads as necessary to saturate the machine, with--- each thread operating sequentially.------ A chunk may be empty, and `f 0 []` must produce the neutral element for--- `op`.------ **Work:** *O(n ✕ W(op) + W(f))*------ **Span:** *O(log(n) ✕ W(op))*-def reduce_stream [n] 'a 'b (op: b -> b -> b) (f: (k: i64) -> [k]a -> b) (as: [n]a): b =-  intrinsics.reduce_stream (op, f, as)---- | As `reduce_stream`@term, but the chunks do not necessarily--- correspond to subsequences of the original array (they may be--- interleaved).------ **Work:** *O(n ✕ W(op) + W(f))*------ **Span:** *O(log(n) ✕ W(op))*-def reduce_stream_per [n] 'a 'b (op: b -> b -> b) (f: (k: i64) -> [k]a -> b) (as: [n]a): b =-  intrinsics.reduce_stream_per (op, f, as)---- | Similar to `reduce_stream`@term, except that each chunk must produce--- an array *of the same size*.  The per-chunk results are--- concatenated.------ **Work:** *O(n ✕ W(f))*------ **Span:** *O(S(f))*-def map_stream [n] 'a 'b (f: (k: i64) -> [k]a -> [k]b) (as: [n]a): *[n]b =-  intrinsics.map_stream (f, as)---- | Similar to `map_stream`@term, but the chunks do not necessarily--- correspond to subsequences of the original array (they may be--- interleaved).------ **Work:** *O(n ✕ W(f))*------ **Span:** *O(S(f))*-def map_stream_per [n] 'a 'b (f: (k: i64) -> [k]a -> [k]b) (as: [n]a): *[n]b =-  intrinsics.map_stream_per (f, as)- -- | Return `true` if the given function returns `true` for all -- elements in the array. --
src/Futhark/AD/Fwd.hs view
@@ -311,19 +311,13 @@             redLambda = op',             redNeutral = redNeutral red `interleave` map Var neutral_tans           }-fwdSOAC pat aux (Stream size xs form nes lam) = do+fwdSOAC pat aux (Stream size xs nes lam) = do   pat' <- bundleNew pat   lam' <- fwdStreamLambda lam   xs' <- bundleTan xs   nes_tan <- mapM (fmap Var . zeroFromSubExp) nes   let nes' = interleave nes nes_tan-  case form of-    Sequential ->-      addStm $ Let pat' aux $ Op $ Stream size xs' Sequential nes' lam'-    Parallel o comm lam0 -> do-      lam0' <- fwdLambda lam0-      let form' = Parallel o comm lam0'-      addStm $ Let pat' aux $ Op $ Stream size xs' form' nes' lam'+  addStm $ Let pat' aux $ Op $ Stream size xs' nes' lam' fwdSOAC pat aux (Hist w arrs ops bucket_fun) = do   pat' <- bundleNew pat   ops' <- mapM fwdHist ops
src/Futhark/Analysis/HORep/SOAC.hs view
@@ -85,8 +85,6 @@ import Futhark.IR.SOACS.SOAC   ( HistOp (..),     ScremaForm (..),-    StreamForm (..),-    StreamOrd (..),     scremaType,   ) import qualified Futhark.IR.SOACS.SOAC as Futhark@@ -383,7 +381,7 @@  -- | A definite representation of a SOAC expression. data SOAC rep-  = Stream SubExp (StreamForm rep) (Lambda rep) [SubExp] [Input]+  = Stream SubExp (Lambda rep) [SubExp] [Input]   | Scatter SubExp (Lambda rep) [Input] [(Shape, Int, VName)]   | Screma SubExp (ScremaForm rep) [Input]   | Hist SubExp [HistOp rep] (Lambda rep) [Input]@@ -412,20 +410,20 @@ instance PrettyRep rep => PP.Pretty (SOAC rep) where   ppr (Screma w form arrs) = Futhark.ppScrema w arrs form   ppr (Hist len ops bucket_fun imgs) = Futhark.ppHist len imgs ops bucket_fun-  ppr (Stream w form lam nes arrs) = Futhark.ppStream w arrs form nes lam+  ppr (Stream w lam nes arrs) = Futhark.ppStream w arrs nes lam   ppr (Scatter w lam arrs dests) = Futhark.ppScatter w arrs lam dests  -- | Returns the inputs used in a SOAC. inputs :: SOAC rep -> [Input]-inputs (Stream _ _ _ _ arrs) = arrs+inputs (Stream _ _ _ arrs) = arrs inputs (Scatter _len _lam ivs _as) = ivs inputs (Screma _ _ arrs) = arrs inputs (Hist _ _ _ inps) = inps  -- | Set the inputs to a SOAC. setInputs :: [Input] -> SOAC rep -> SOAC rep-setInputs arrs (Stream w form lam nes _) =-  Stream (newWidth arrs w) form lam nes arrs+setInputs arrs (Stream w lam nes _) =+  Stream (newWidth arrs w) lam nes arrs setInputs arrs (Scatter w lam _ivs as) =   Scatter (newWidth arrs w) lam arrs as setInputs arrs (Screma w form _) =@@ -439,15 +437,15 @@  -- | The lambda used in a given SOAC. lambda :: SOAC rep -> Lambda rep-lambda (Stream _ _ lam _ _) = lam+lambda (Stream _ lam _ _) = lam lambda (Scatter _len lam _ivs _as) = lam lambda (Screma _ (ScremaForm _ _ lam) _) = lam lambda (Hist _ _ lam _) = lam  -- | Set the lambda used in the SOAC. setLambda :: Lambda rep -> SOAC rep -> SOAC rep-setLambda lam (Stream w form _ nes arrs) =-  Stream w form lam nes arrs+setLambda lam (Stream w _ nes arrs) =+  Stream w lam nes arrs setLambda lam (Scatter len _lam ivs as) =   Scatter len lam ivs as setLambda lam (Screma w (ScremaForm scan red _) arrs) =@@ -457,7 +455,7 @@  -- | The return type of a SOAC. typeOf :: SOAC rep -> [Type]-typeOf (Stream w _ lam nes _) =+typeOf (Stream w lam nes _) =   let accrtps = take (length nes) $ lambdaReturnType lam       arrtps =         [ arrayOf (stripArray 1 t) (Shape [w]) NoUniqueness@@ -479,7 +477,7 @@ -- | The "width" of a SOAC is the expected outer size of its array -- inputs _after_ input-transforms have been carried out. width :: SOAC rep -> SubExp-width (Stream w _ _ _ _) = w+width (Stream w _ _ _) = w width (Scatter len _lam _ivs _as) = len width (Screma w _ _) = w width (Hist w _ _ _) = w@@ -493,8 +491,8 @@  -- | Convert a SOAC to a Futhark-level SOAC. toSOAC :: MonadBuilder m => SOAC (Rep m) -> m (Futhark.SOAC (Rep m))-toSOAC (Stream w form lam nes inps) =-  Futhark.Stream w <$> inputsToSubExps inps <*> pure form <*> pure nes <*> pure lam+toSOAC (Stream w lam nes inps) =+  Futhark.Stream w <$> inputsToSubExps inps <*> pure nes <*> pure lam toSOAC (Scatter w lam ivs dests) =   Futhark.Scatter w <$> inputsToSubExps ivs <*> pure lam <*> pure dests toSOAC (Screma w form arrs) =@@ -516,8 +514,8 @@   (Op rep ~ Futhark.SOAC rep, HasScope rep m) =>   Exp rep ->   m (Either NotSOAC (SOAC rep))-fromExp (Op (Futhark.Stream w as form nes lam)) =-  Right . Stream w form lam nes <$> traverse varInput as+fromExp (Op (Futhark.Stream w as nes lam)) =+  Right . Stream w lam nes <$> traverse varInput as fromExp (Op (Futhark.Scatter w ivs lam as)) =   Right <$> (Scatter w lam <$> traverse varInput ivs <*> pure as) fromExp (Op (Futhark.Screma w arrs form)) =@@ -566,9 +564,8 @@               strmbdy = mkBody (oneStm insstm) $ map (subExpRes . Var . identName) strm_resids               strmpar = chunk_param : strm_inpids               strmlam = Lambda strmpar strmbdy loutps-              empty_lam = Lambda [] (mkBody mempty []) []           -- map(f,a) creates a stream with NO accumulators-          pure (Stream w (Parallel Disorder Commutative empty_lam) strmlam [] inps, [])+          pure (Stream w strmlam [] inps, [])       | Just (scans, _) <- Futhark.isScanomapSOAC form,         Futhark.Scan scan_lam nes <- Futhark.singleScan scans -> do           -- scanomap(scan_lam,nes,map_lam,a) => is translated in strem's body to:@@ -628,7 +625,7 @@             addStms addlelstm             pure $ addlelres ++ map (subExpRes . Var) (strm_resids ++ map_resids)           pure-            ( Stream w Sequential strmlam nes inps,+            ( Stream w strmlam nes inps,               map paramIdent inpacc_ids             )       | Just (reds, _) <- Futhark.isRedomapSOAC form,@@ -666,8 +663,7 @@                   addaccres ++ map (subExpRes . Var . identName) strm_resids               strmpar = chunk_param : inpacc_ids ++ strm_inpids               strmlam = Lambda strmpar strmbdy (accrtps ++ loutps')-          lam0 <- renameLambda lamin-          pure (Stream w (Parallel InOrder comm lam0) strmlam nes inps, [])+          pure (Stream w strmlam nes inps, [])      -- Otherwise it cannot become a stream.     _ -> pure (soac, [])
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -38,7 +38,6 @@ import Futhark.CodeGen.Backends.GenericC.Types import Futhark.CodeGen.ImpCode import Futhark.CodeGen.RTS.C (cacheH, contextH, contextPrototypesH, errorsH, halfH, lockH, timingH, utilH)-import Futhark.IR.Prop (isBuiltInFunction) import qualified Futhark.Manifest as Manifest import Futhark.MonadFreshNames import Futhark.Util.Pretty (prettyText)@@ -49,15 +48,8 @@ defCall :: CallCompiler op s defCall dests fname args = do   let out_args = [[C.cexp|&$id:d|] | d <- dests]-      args'-        | isBuiltInFunction fname = args-        | otherwise = [C.cexp|ctx|] : out_args ++ args-  case dests of-    [dest]-      | isBuiltInFunction fname ->-          stm [C.cstm|$id:dest = $id:(funName fname)($args:args');|]-    _ ->-      item [C.citem|if ($id:(funName fname)($args:args') != 0) { err = 1; goto cleanup; }|]+      args' = [C.cexp|ctx|] : out_args ++ args+  item [C.citem|if ($id:(funName fname)($args:args') != 0) { err = 1; goto cleanup; }|]  defError :: ErrorCompiler op s defError msg stacktrace = do
src/Futhark/CodeGen/Backends/GenericC/Code.hs view
@@ -21,6 +21,7 @@ import Data.Maybe import Futhark.CodeGen.Backends.GenericC.Monad import Futhark.CodeGen.ImpCode+import Futhark.IR.Prop (isBuiltInFunction) import Futhark.MonadFreshNames import qualified Language.C.Quote.OpenCL as C import qualified Language.C.Syntax as C@@ -134,6 +135,37 @@ assignmentOperator Mul {} = Just $ \d e -> [C.cexp|$id:d *= $exp:e|] assignmentOperator _ = Nothing +compileRead ::+  VName ->+  Count u (TPrimExp t VName) ->+  PrimType ->+  Space ->+  Volatility ->+  CompilerM op s C.Exp+compileRead _ _ Unit _ _ =+  pure [C.cexp|$exp:(UnitValue)|]+compileRead src (Count iexp) restype DefaultSpace vol = do+  src' <- rawMem src+  fmap (fromStorage restype) $+    derefPointer src'+      <$> compileExp (untyped iexp)+      <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primStorageType restype)*|]+compileRead src (Count iexp) restype (Space space) vol =+  fmap (fromStorage restype) . join $+    asks (opsReadScalar . envOperations)+      <*> rawMem src+      <*> compileExp (untyped iexp)+      <*> pure (primStorageType restype)+      <*> pure space+      <*> pure vol+compileRead src (Count iexp) _ ScalarSpace {} _ = do+  iexp' <- compileExp $ untyped iexp+  pure [C.cexp|$id:src[$exp:iexp']|]++compileArg :: Arg -> CompilerM op s C.Exp+compileArg (MemArg m) = pure [C.cexp|$exp:m|]+compileArg (ExpArg e) = compileExp e+ compileCode :: Code op -> CompilerM op s () compileCode (Op op) =   join $ asks (opsCompiler . envOperations) <*> pure op@@ -175,6 +207,19 @@           e' <- compileExp e           item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $exp:e';|]           go code+    go (DeclareScalar name vol t : Read dest src i restype space read_vol : code)+      | name == dest = do+          let ct = primTypeToCType t+          e <- compileRead src i restype space read_vol+          item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $exp:e;|]+          go code+    go (DeclareScalar name vol t : Call [dest] fname args : code)+      | name == dest,+        isBuiltInFunction fname = do+          let ct = primTypeToCType t+          args' <- mapM compileArg args+          item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $id:(funName fname)($args:args');|]+          go code     go (x : xs) = compileCode x >> go xs     go [] = pure () compileCode (Assert e msg (loc, locs)) = do@@ -276,29 +321,9 @@       <*> pure space       <*> pure vol       <*> (toStorage elemtype <$> compileExp elemexp)-compileCode (Read x _ _ Unit __ _) =-  stm [C.cstm|$id:x = $exp:(UnitValue);|]-compileCode (Read x src (Count iexp) restype DefaultSpace vol) = do-  src' <- rawMem src-  e <--    fmap (fromStorage restype) $-      derefPointer src'-        <$> compileExp (untyped iexp)-        <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primStorageType restype)*|]-  stm [C.cstm|$id:x = $exp:e;|]-compileCode (Read x src (Count iexp) restype (Space space) vol) = do-  e <--    fmap (fromStorage restype) . join $-      asks (opsReadScalar . envOperations)-        <*> rawMem src-        <*> compileExp (untyped iexp)-        <*> pure (primStorageType restype)-        <*> pure space-        <*> pure vol+compileCode (Read x src i restype space vol) = do+  e <- compileRead src i restype space vol   stm [C.cstm|$id:x = $exp:e;|]-compileCode (Read x src (Count iexp) _ ScalarSpace {} _) = do-  iexp' <- compileExp $ untyped iexp-  stm [C.cstm|$id:x = $id:src[$exp:iexp'];|] compileCode (DeclareMem name space) =   declMem name space compileCode (DeclareScalar name vol t) = do@@ -345,12 +370,13 @@   stm [C.cstm|$id:dest = $exp:src';|] compileCode (SetMem dest src space) =   setMem dest src space+compileCode (Call [dest] fname args)+  | isBuiltInFunction fname = do+      args' <- mapM compileArg args+      stm [C.cstm|$id:dest = $id:(funName fname)($args:args');|] compileCode (Call dests fname args) =   join $     asks (opsCall . envOperations)       <*> pure dests       <*> pure fname       <*> mapM compileArg args-  where-    compileArg (MemArg m) = pure [C.cexp|$exp:m|]-    compileArg (ExpArg e) = compileExp e
src/Futhark/CodeGen/ImpGen/GPU/Base.hs view
@@ -4,2157 +4,1385 @@  module Futhark.CodeGen.ImpGen.GPU.Base   ( KernelConstants (..),-    keyWithEntryPoint,-    CallKernelGen,-    InKernelGen,-    Locks (..),-    HostEnv (..),-    Target (..),-    KernelEnv (..),-    computeThreadChunkSize,-    groupReduce,-    groupScan,-    isActive,-    sKernelThread,-    sKernelGroup,-    KernelAttrs (..),-    defKernelAttrs,-    sReplicate,-    sIota,-    sRotateKernel,-    sCopy,-    compileThreadResult,-    compileGroupResult,-    virtualiseGroups,-    kernelLoop,-    groupCoverSpace,-    Precomputed,-    precomputeConstants,-    precomputedConstants,-    atomicUpdateLocking,-    AtomicBinOp,-    Locking (..),-    AtomicUpdate (..),-    DoAtomicUpdate,-  )-where--import Control.Monad.Except-import Data.Bifunctor-import Data.List (foldl', partition, zip4)-import qualified Data.Map.Strict as M-import Data.Maybe-import qualified Data.Set as S-import qualified Futhark.CodeGen.ImpCode.GPU as Imp-import Futhark.CodeGen.ImpGen-import Futhark.Construct (fullSliceNum)-import Futhark.Error-import Futhark.IR.GPUMem-import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.MonadFreshNames-import Futhark.Transform.Rename-import Futhark.Util (chunks, dropLast, mapAccumLM, nubOrd, splitFromEnd, takeLast)-import Futhark.Util.IntegralExp (divUp, quot, rem)-import Prelude hiding (quot, rem)---- | Which target are we ultimately generating code for?  While most--- of the kernels code is the same, there are some cases where we--- generate special code based on the ultimate low-level API we are--- targeting.-data Target = CUDA | OpenCL---- | Information about the locks available for accumulators.-data Locks = Locks-  { locksArray :: VName,-    locksCount :: Int-  }--data HostEnv = HostEnv-  { hostAtomics :: AtomicBinOp,-    hostTarget :: Target,-    hostLocks :: M.Map VName Locks-  }--data KernelEnv = KernelEnv-  { kernelAtomics :: AtomicBinOp,-    kernelConstants :: KernelConstants,-    kernelLocks :: M.Map VName Locks-  }--type CallKernelGen = ImpM GPUMem HostEnv Imp.HostOp--type InKernelGen = ImpM GPUMem KernelEnv Imp.KernelOp--data KernelConstants = KernelConstants-  { kernelGlobalThreadId :: Imp.TExp Int32,-    kernelLocalThreadId :: Imp.TExp Int32,-    kernelGroupId :: 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,-    kernelNumThreads :: Imp.TExp Int32,-    kernelWaveSize :: Imp.TExp Int32,-    -- | A mapping from dimensions of nested SegOps to already-    -- computed local thread IDs.  Only valid in non-virtualised case.-    kernelLocalIdMap :: M.Map [SubExp] [Imp.TExp Int32],-    -- | Mapping from dimensions of nested SegOps to how many-    -- iterations the virtualisation loop needs.-    kernelChunkItersMap :: M.Map [SubExp] (Imp.TExp Int32)-  }---- | The sizes of nested iteration spaces in the kernel.-type SegOpSizes = S.Set [SubExp]---- | Find the sizes of nested parallelism in a t'SegOp' body.-segOpSizes :: Stms GPUMem -> SegOpSizes-segOpSizes = onStms-  where-    onStms = foldMap (onExp . stmExp)-    onExp (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-    onExp (BasicOp (Replicate shape _)) =-      S.singleton $ shapeDims shape-    onExp (Match _ cases defbody _) =-      foldMap (onStms . bodyStms . caseBody) cases <> onStms (bodyStms defbody)-    onExp (DoLoop _ _ body) =-      onStms (bodyStms body)-    onExp _ = mempty---- | Various useful precomputed information.-data Precomputed = Precomputed-  { pcSegOpSizes :: SegOpSizes,-    pcChunkItersMap :: M.Map [SubExp] (Imp.TExp Int32)-  }---- | 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')--keyWithEntryPoint :: Maybe Name -> Name -> Name-keyWithEntryPoint fname key =-  nameFromString $ maybe "" ((++ ".") . nameToString) fname ++ nameToString key--allocLocal :: AllocCompiler GPUMem r Imp.KernelOp-allocLocal mem size =-  sOp $ Imp.LocalAlloc mem size--kernelAlloc ::-  Pat LetDecMem ->-  SubExp ->-  Space ->-  InKernelGen ()-kernelAlloc (Pat [_]) _ ScalarSpace {} =-  -- 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") =-  allocLocal (patElemName mem) $ Imp.bytes $ pe64 size-kernelAlloc (Pat [mem]) _ _ =-  compilerLimitationS $ "Cannot allocate memory block " ++ pretty mem ++ " in kernel."-kernelAlloc dest _ _ =-  error $ "Invalid target for in-kernel allocation: " ++ show dest--splitSpace ::-  Pat LetDecMem ->-  SplitOrdering ->-  SubExp ->-  SubExp ->-  SubExp ->-  ImpM rep r op ()-splitSpace (Pat [size]) o w i elems_per_thread = do-  num_elements <- Imp.elements . TPrimExp <$> toExp w-  let i' = pe64 i-  elems_per_thread' <- Imp.elements . TPrimExp <$> toExp elems_per_thread-  computeThreadChunkSize o i' elems_per_thread' num_elements (mkTV (patElemName size) int64)-splitSpace pat _ _ _ _ =-  error $ "Invalid target for splitSpace: " ++ pretty pat--updateAcc :: VName -> [SubExp] -> [SubExp] -> InKernelGen ()-updateAcc acc is vs = sComment "UpdateAcc" $ do-  -- See the ImpGen implementation of UpdateAcc for general notes.-  let is' = map pe64 is-  (c, space, arrs, dims, op) <- lookupAcc acc is'-  sWhen (inBounds (Slice (map DimFix is')) dims) $-    case op of-      Nothing ->-        forM_ (zip arrs vs) $ \(arr, v) -> copyDWIMFix arr is' v []-      Just lam -> do-        dLParams $ lambdaParams lam-        let (_x_params, y_params) =-              splitAt (length vs) $ map paramName $ lambdaParams lam-        forM_ (zip y_params vs) $ \(yp, v) -> copyDWIM yp [] v []-        atomics <- kernelAtomics <$> askEnv-        case atomicUpdateLocking atomics lam of-          AtomicPrim f -> f space arrs is'-          AtomicCAS f -> f space arrs is'-          AtomicLocking f -> do-            c_locks <- M.lookup c . kernelLocks <$> askEnv-            case c_locks of-              Just (Locks locks num_locks) -> do-                let locking =-                      Locking locks 0 1 0 $-                        pure . (`rem` fromIntegral num_locks) . flattenIndex dims-                f locking space arrs is'-              Nothing ->-                error $ "Missing locks for " ++ pretty acc--compileThreadExp :: ExpCompiler GPUMem KernelEnv Imp.KernelOp-compileThreadExp (Pat [pe]) (BasicOp (Opaque _ se)) =-  -- Cannot print in GPU code.-  copyDWIM (patElemName pe) [] se []-compileThreadExp (Pat [dest]) (BasicOp (ArrayLit es _)) =-  forM_ (zip [0 ..] es) $ \(i, e) ->-    copyDWIMFix (patElemName dest) [fromIntegral (i :: Int64)] e []-compileThreadExp _ (BasicOp (UpdateAcc acc is vs)) =-  updateAcc acc is vs-compileThreadExp dest e =-  defCompileExp dest e---- | Assign iterations of a for-loop to all threads in the kernel.--- The passed-in function is invoked with the (symbolic) iteration.--- The body must contain thread-level code.  For multidimensional--- loops, use 'groupCoverSpace'.-kernelLoop ::-  IntExp t =>-  Imp.TExp t ->-  Imp.TExp t ->-  Imp.TExp t ->-  (Imp.TExp t -> InKernelGen ()) ->-  InKernelGen ()-kernelLoop tid num_threads n f =-  localOps threadOperations $-    if n == num_threads-      then f tid-      else do-        num_chunks <- dPrimVE "num_chunks" $ n `divUp` num_threads-        sFor "chunk_i" num_chunks $ \chunk_i -> do-          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--- passed-in function is invoked with the (symbolic) iteration.  For--- multidimensional loops, use 'groupCoverSpace'.-groupLoop ::-  IntExp t =>-  Imp.TExp t ->-  (Imp.TExp t -> InKernelGen ()) ->-  InKernelGen ()-groupLoop n f = do-  constants <- kernelConstants <$> askEnv-  kernelLoop-    (kernelLocalThreadId constants `sExtAs` n)-    (kernelGroupSize constants `sExtAs` n)-    n-    f---- | Iterate collectively though a multidimensional space, such that--- all threads in the group participate.  The passed-in function is--- invoked with a (symbolic) point in the index space.-groupCoverSpace ::-  IntExp t =>-  [Imp.TExp t] ->-  ([Imp.TExp t] -> InKernelGen ()) ->-  InKernelGen ()-groupCoverSpace ds f = do-  constants <- kernelConstants <$> askEnv-  let group_size = kernelGroupSize constants-  case splitFromEnd 1 ds of-    -- Optimise the case where the inner dimension of the space is-    -- equal to the group size.-    (ds', [last_d])-      | last_d == (group_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--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 ..])--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 -> 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)) =-  updateAcc acc is vs-compileGroupExp (Pat [dest]) (BasicOp (Replicate ds se)) = do-  flat <- newVName "rep_flat"-  is <- replicateM (shapeRank ds) (newVName "rep_i")-  let is' = map le64 is-  groupCoverSegSpace SegVirt (SegSpace flat $ zip is $ shapeDims ds) $-    copyDWIMFix (patElemName dest) is' se []-  sOp $ Imp.Barrier Imp.FenceLocal-compileGroupExp (Pat [dest]) (BasicOp (Rotate rs arr)) = do-  ds <- map pe64 . arrayDims <$> lookupType arr-  groupCoverSpace ds $ \is -> do-    is' <- sequence $ zipWith3 rotate ds rs is-    copyDWIMFix (patElemName dest) is (Var arr) is'-  sOp $ Imp.Barrier Imp.FenceLocal-  where-    rotate d r i = dPrimVE "rot_i" $ rotateIndex d (pe64 r) i-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 =-  defCompileExp dest e--sanityCheckLevel :: SegLevel -> InKernelGen ()-sanityCheckLevel SegThread {} = pure ()-sanityCheckLevel SegGroup {} =-  error "compileGroupOp: unexpected group-level SegOp."--compileFlatId :: SegLevel -> SegSpace -> InKernelGen ()-compileFlatId lvl space = do-  sanityCheckLevel lvl-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  dPrimV_ (segFlat space) ltid---- Construct the necessary lock arrays for an intra-group histogram.-prepareIntraGroupSegHist ::-  Count GroupSize SubExp ->-  [HistOp GPUMem] ->-  InKernelGen [[Imp.TExp Int64] -> InKernelGen ()]-prepareIntraGroupSegHist group_size =-  fmap snd . mapAccumLM onOp Nothing-  where-    onOp l op = do-      constants <- kernelConstants <$> askEnv-      atomicBinOp <- kernelAtomics <$> askEnv--      let local_subhistos = histDest op--      case (l, atomicUpdateLocking atomicBinOp $ histOp op) of-        (_, AtomicPrim f) -> 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 (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 $-            IxFun.iota . 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)---- Which fence do we need to protect shared access to this memory space?-fenceForSpace :: Space -> Imp.Fence-fenceForSpace (Space "local") = Imp.FenceLocal-fenceForSpace _ = Imp.FenceGlobal---- If we are touching these arrays, which kind of fence do we need?-fenceForArrays :: [VName] -> InKernelGen Imp.Fence-fenceForArrays = fmap (foldl' max Imp.FenceLocal) . mapM need-  where-    need arr =-      fmap (fenceForSpace . entryMemSpace)-        . lookupMemory-        . memLocName-        . entryArrayLoc-        =<< lookupArray arr--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--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-    $ IxFun.slice ixfun slice---- | @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) $-    IxFun.reshape (memLocIxFun arr_loc) $-      map pe64 $-        shapeDims flat_shape---- | @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--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-          (zip arrs $ repeat [DimFix $ sExt64 chunk_start])-          ( zip (map Var arrs) (repeat [DimFix carry_idx])-              ++ zip (map Var arrs) (repeat [DimFix $ sExt64 chunk_start])-          )--    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--compileGroupOp :: OpCompiler GPUMem KernelEnv Imp.KernelOp-compileGroupOp pat (Alloc size space) =-  kernelAlloc pat size space-compileGroupOp pat (Inner (SizeOp (SplitSpace o w i elems_per_thread))) =-  splitSpace pat o w i elems_per_thread-compileGroupOp pat (Inner (SegOp (SegMap lvl space _ body))) = do-  compileFlatId lvl 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 lvl 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 lvl 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)-                (zip tmps $ repeat [DimFix $ sExt64 chunk_start])-                ( zip (map (Var . patElemName) red_pes) (repeat [])-                    ++ zip (map Var tmps) (repeat [DimFix $ sExt64 chunk_start])-                )--        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--        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--      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--    nonvirtCase [dim'] tmps_for_ops = do-      -- Nonsegmented case (or rather, a single segment) - this we can-      -- handle directly with a group-level reduction.-      forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->-        groupReduce (sExt32 dim') (segBinOpLambda op) tmps-      sOp $ Imp.ErrorSync Imp.FenceLocal-      forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->-        copyDWIMFix (patElemName pe) [] (Var arr) [0]-    ---    nonvirtCase dims' tmps_for_ops = do-      -- Segmented intra-group reductions are turned into (regular)-      -- segmented scans.  It is possible that this can be done-      -- better, but at least this approach is simple.--      -- groupScan operates on flattened arrays.  This does not-      -- involve copying anything; merely playing with the index-      -- function.-      dims_flat <- dPrimV "dims_flat" $ product dims'-      let 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--      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 lvl 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--  ops' <- prepareIntraGroupSegHist (segGroupSize lvl) 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 (map 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 " ++ pretty pat--compileThreadOp :: OpCompiler GPUMem KernelEnv Imp.KernelOp-compileThreadOp pat (Alloc size space) =-  kernelAlloc pat size space-compileThreadOp pat (Inner (SizeOp (SplitSpace o w i elems_per_thread))) =-  splitSpace pat o w i elems_per_thread-compileThreadOp pat _ =-  compilerBugS $ "compileThreadOp: cannot compile rhs of binding " ++ pretty pat---- | Locking strategy used for an atomic update.-data Locking = Locking-  { -- | Array containing the lock.-    lockingArray :: VName,-    -- | Value for us to consider the lock free.-    lockingIsUnlocked :: Imp.TExp Int32,-    -- | What to write when we lock it.-    lockingToLock :: Imp.TExp Int32,-    -- | What to write when we unlock it.-    lockingToUnlock :: Imp.TExp Int32,-    -- | A transformation from the logical lock index to the-    -- physical position in the array.  This can also be used-    -- to make the lock array smaller.-    lockingMapping :: [Imp.TExp Int64] -> [Imp.TExp Int64]-  }---- | A function for generating code for an atomic update.  Assumes--- that the bucket is in-bounds.-type DoAtomicUpdate rep r =-  Space -> [VName] -> [Imp.TExp Int64] -> ImpM rep r Imp.KernelOp ()---- | The mechanism that will be used for performing the atomic update.--- Approximates how efficient it will be.  Ordered from most to least--- efficient.-data AtomicUpdate rep r-  = -- | Supported directly by primitive.-    AtomicPrim (DoAtomicUpdate rep r)-  | -- | Can be done by efficient swaps.-    AtomicCAS (DoAtomicUpdate rep r)-  | -- | Requires explicit locking.-    AtomicLocking (Locking -> DoAtomicUpdate rep r)---- | Is there an atomic t'BinOp' corresponding to this t'BinOp'?-type AtomicBinOp =-  BinOp ->-  Maybe (VName -> VName -> Count Imp.Elements (Imp.TExp Int64) -> Imp.Exp -> Imp.AtomicOp)---- | Do an atomic update corresponding to a binary operator lambda.-atomicUpdateLocking ::-  AtomicBinOp ->-  Lambda GPUMem ->-  AtomicUpdate GPUMem KernelEnv-atomicUpdateLocking atomicBinOp lam-  | Just ops_and_ts <- lamIsBinOp lam,-    all (\(_, t, _, _) -> primBitSize t `elem` [32, 64]) ops_and_ts =-      primOrCas ops_and_ts $ \space arrs bucket ->-        -- If the operator is a vectorised binary operator on 32/64-bit-        -- values, we can use a particularly efficient-        -- implementation. If the operator has an atomic implementation-        -- we use that, otherwise it is still a binary operator which-        -- can be implemented by atomic compare-and-swap if 32/64 bits.-        forM_ (zip arrs ops_and_ts) $ \(a, (op, t, x, y)) -> do-          -- Common variables.-          old <- dPrim "old" t--          (arr', _a_space, bucket_offset) <- fullyIndexArray a bucket--          case opHasAtomicSupport space (tvVar old) arr' bucket_offset op of-            Just f -> sOp $ f $ Imp.var y t-            Nothing ->-              atomicUpdateCAS space t a (tvVar old) bucket x $-                x <~~ Imp.BinOpExp op (Imp.var x t) (Imp.var y t)-  where-    opHasAtomicSupport space old arr' bucket' bop = do-      let atomic f = Imp.Atomic space . f old arr' bucket'-      atomic <$> atomicBinOp bop--    primOrCas ops-      | all isPrim ops = AtomicPrim-      | otherwise = AtomicCAS--    isPrim (op, _, _, _) = isJust $ atomicBinOp op---- If the operator functions purely on single 32/64-bit values, we can--- use an implementation based on CAS, no matter what the operator--- does.-atomicUpdateLocking _ op-  | [Prim t] <- lambdaReturnType op,-    [xp, _] <- lambdaParams op,-    primBitSize t `elem` [32, 64] = AtomicCAS $ \space [arr] bucket -> do-      old <- dPrim "old" t-      atomicUpdateCAS space t arr (tvVar old) bucket (paramName xp) $-        compileBody' [xp] $-          lambdaBody op-atomicUpdateLocking _ op = AtomicLocking $ \locking space arrs bucket -> do-  old <- dPrim "old" int32-  continue <- dPrimVol "continue" Bool true--  -- Correctly index into locks.-  (locks', _locks_space, locks_offset) <--    fullyIndexArray (lockingArray locking) $ lockingMapping locking bucket--  -- Critical section-  let try_acquire_lock =-        sOp $-          Imp.Atomic space $-            Imp.AtomicCmpXchg-              int32-              (tvVar old)-              locks'-              locks_offset-              (untyped $ lockingIsUnlocked locking)-              (untyped $ lockingToLock locking)-      lock_acquired = tvExp old .==. lockingIsUnlocked locking-      -- Even the releasing is done with an atomic rather than a-      -- simple write, for memory coherency reasons.-      release_lock =-        sOp $-          Imp.Atomic space $-            Imp.AtomicCmpXchg-              int32-              (tvVar old)-              locks'-              locks_offset-              (untyped $ lockingToLock locking)-              (untyped $ lockingToUnlock locking)-      break_loop = continue <-- false--  -- Preparing parameters. It is assumed that the caller has already-  -- filled the arr_params. We copy the current value to the-  -- accumulator parameters.-  ---  -- Note the use of 'everythingVolatile' when reading and writing the-  -- buckets.  This was necessary to ensure correct execution on a-  -- newer NVIDIA GPU (RTX 2080).  The 'volatile' modifiers likely-  -- make the writes pass through the (SM-local) L1 cache, which is-  -- necessary here, because we are really doing device-wide-  -- synchronisation without atomics (naughty!).-  let (acc_params, _arr_params) = splitAt (length arrs) $ lambdaParams op-      bind_acc_params =-        everythingVolatile $-          sComment "bind lhs" $-            forM_ (zip acc_params arrs) $ \(acc_p, arr) ->-              copyDWIMFix (paramName acc_p) [] (Var arr) bucket--  let op_body =-        sComment "execute operation" $-          compileBody' acc_params $-            lambdaBody op--      do_hist =-        everythingVolatile $-          sComment "update global result" $-            zipWithM_ (writeArray bucket) arrs $-              map (Var . paramName) acc_params--      fence = sOp $ Imp.MemFence $ fenceForSpace space--  -- While-loop: Try to insert your value-  sWhile (tvExp continue) $ do-    try_acquire_lock-    sWhen lock_acquired $ do-      dLParams acc_params-      bind_acc_params-      op_body-      do_hist-      fence-      release_lock-      break_loop-    fence-  where-    writeArray bucket arr val = copyDWIMFix arr bucket val []--atomicUpdateCAS ::-  Space ->-  PrimType ->-  VName ->-  VName ->-  [Imp.TExp Int64] ->-  VName ->-  InKernelGen () ->-  InKernelGen ()-atomicUpdateCAS space t arr old bucket x do_op = do-  -- Code generation target:-  ---  -- old = d_his[idx];-  -- do {-  --   assumed = old;-  --   x = do_op(assumed, y);-  --   old = atomicCAS(&d_his[idx], assumed, tmp);-  -- } while(assumed != old);-  assumed <- tvVar <$> dPrim "assumed" t-  run_loop <- dPrimV "run_loop" true--  -- XXX: CUDA may generate really bad code if this is not a volatile-  -- read.  Unclear why.  The later reads are volatile, so maybe-  -- that's it.-  everythingVolatile $ copyDWIMFix old [] (Var arr) bucket--  (arr', _a_space, bucket_offset) <- fullyIndexArray arr bucket--  -- While-loop: Try to insert your value-  let (toBits, fromBits) =-        case t of-          FloatType Float16 ->-            ( \v -> Imp.FunExp "to_bits16" [v] int16,-              \v -> Imp.FunExp "from_bits16" [v] t-            )-          FloatType Float32 ->-            ( \v -> Imp.FunExp "to_bits32" [v] int32,-              \v -> Imp.FunExp "from_bits32" [v] t-            )-          FloatType Float64 ->-            ( \v -> Imp.FunExp "to_bits64" [v] int64,-              \v -> Imp.FunExp "from_bits64" [v] t-            )-          _ -> (id, id)--      int-        | primBitSize t == 16 = int16-        | primBitSize t == 32 = int32-        | otherwise = int64--  sWhile (tvExp run_loop) $ do-    assumed <~~ Imp.var old t-    x <~~ Imp.var assumed t-    do_op-    old_bits_v <- newVName "old_bits"-    dPrim_ old_bits_v int-    let old_bits = Imp.var old_bits_v int-    sOp . Imp.Atomic space $-      Imp.AtomicCmpXchg-        int-        old_bits_v-        arr'-        bucket_offset-        (toBits (Imp.var assumed t))-        (toBits (Imp.var x t))-    old <~~ fromBits old_bits-    let won = CmpOpExp (CmpEq int) (toBits (Imp.var assumed t)) old_bits-    sWhen (isBool won) (run_loop <-- false)--computeKernelUses ::-  FreeIn a =>-  a ->-  [VName] ->-  CallKernelGen [Imp.KernelUse]-computeKernelUses kernel_body bound_in_kernel = do-  let actually_free = freeIn kernel_body `namesSubtract` namesFromList bound_in_kernel-  -- Compute the variables that we need to pass to the kernel.-  nubOrd <$> readsFromSet actually_free--readsFromSet :: Names -> CallKernelGen [Imp.KernelUse]-readsFromSet = fmap catMaybes . mapM f . namesToList-  where-    f var = do-      t <- lookupType var-      vtable <- getVTable-      case t of-        Array {} -> pure Nothing-        Acc {} -> pure Nothing-        Mem (Space "local") -> pure Nothing-        Mem {} -> pure $ Just $ Imp.MemoryUse var-        Prim bt ->-          isConstExp vtable (Imp.var var bt) >>= \case-            Just ce -> pure $ Just $ Imp.ConstUse var ce-            Nothing -> pure $ Just $ Imp.ScalarUse var bt--isConstExp ::-  VTable GPUMem ->-  Imp.Exp ->-  ImpM rep r op (Maybe Imp.KernelConstExp)-isConstExp vtable size = do-  fname <- askFunction-  let onLeaf name _ = lookupConstExp name-      lookupConstExp name =-        constExp =<< hasExp =<< M.lookup name vtable-      constExp (Op (Inner (SizeOp (GetSize key _)))) =-        Just $ LeafExp (Imp.SizeConst $ keyWithEntryPoint fname key) int32-      constExp e = primExpFromExp lookupConstExp e-  pure $ replaceInPrimExpM onLeaf size-  where-    hasExp (ArrayVar e _) = e-    hasExp (AccVar e _) = e-    hasExp (ScalarVar e _) = e-    hasExp (MemVar e _) = e--computeThreadChunkSize ::-  SplitOrdering ->-  Imp.TExp Int64 ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  TV Int64 ->-  ImpM rep r op ()-computeThreadChunkSize (SplitStrided stride) thread_index elements_per_thread num_elements chunk_var =-  chunk_var-    <-- sMin64-      (Imp.unCount elements_per_thread)-      ((Imp.unCount num_elements - thread_index) `divUp` pe64 stride)-computeThreadChunkSize SplitContiguous thread_index elements_per_thread num_elements chunk_var = do-  starting_point <--    dPrimV "starting_point" $-      thread_index * Imp.unCount elements_per_thread-  remaining_elements <--    dPrimV "remaining_elements" $-      Imp.unCount num_elements - tvExp starting_point--  let no_remaining_elements = tvExp remaining_elements .<=. 0-      beyond_bounds = Imp.unCount num_elements .<=. tvExp starting_point--  sIf-    (no_remaining_elements .||. beyond_bounds)-    (chunk_var <-- 0)-    ( sIf-        is_last_thread-        (chunk_var <-- Imp.unCount last_thread_elements)-        (chunk_var <-- Imp.unCount elements_per_thread)-    )-  where-    last_thread_elements =-      num_elements - Imp.elements thread_index * elements_per_thread-    is_last_thread =-      Imp.unCount num_elements-        .<. (thread_index + 1)-          * Imp.unCount elements_per_thread--kernelInitialisationSimple ::-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->-  CallKernelGen (KernelConstants, InKernelGen ())-kernelInitialisationSimple num_groups group_size = do-  global_tid <- newVName "global_tid"-  local_tid <- newVName "local_tid"-  group_id <- newVName "group_tid"-  wave_size <- newVName "wave_size"-  inner_group_size <- newVName "group_size"-  let num_groups' = Imp.pe64 (unCount num_groups)-      group_size' = Imp.pe64 (unCount group_size)-      constants =-        KernelConstants-          { kernelGlobalThreadId = Imp.le32 global_tid,-            kernelLocalThreadId = Imp.le32 local_tid,-            kernelGroupId = Imp.le32 group_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'),-            kernelWaveSize = Imp.le32 wave_size,-            kernelLocalIdMap = mempty,-            kernelChunkItersMap = mempty-          }--  let set_constants = do-        dPrim_ local_tid int32-        dPrim_ inner_group_size int64-        dPrim_ wave_size int32-        dPrim_ group_id int32--        sOp (Imp.GetLocalId local_tid 0)-        sOp (Imp.GetLocalSize inner_group_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--  pure (constants, set_constants)--isActive :: [(VName, SubExp)] -> Imp.TExp Bool-isActive limit = case actives of-  [] -> true-  x : xs -> foldl (.&&.) x xs-  where-    (is, ws) = unzip limit-    actives = zipWith active is $ map pe64 ws-    active i = (Imp.le64 i .<.)---- | Change every memory block to be in the global address space,--- except those who are in the local memory space.  This only affects--- generated code - we still need to make sure that the memory is--- actually present on the device (and declared as variables in the--- kernel).-makeAllMemoryGlobal :: CallKernelGen a -> CallKernelGen a-makeAllMemoryGlobal =-  localDefaultSpace (Imp.Space "global") . localVTable (M.map globalMemory)-  where-    globalMemory (MemVar _ entry)-      | entryMemSpace entry /= Space "local" =-          MemVar Nothing entry {entryMemSpace = Imp.Space "global"}-    globalMemory entry =-      entry--groupReduce ::-  Imp.TExp Int32 ->-  Lambda GPUMem ->-  [VName] ->-  InKernelGen ()-groupReduce w lam arrs = do-  offset <- dPrim "offset" int32-  groupReduceWithOffset offset w lam arrs--groupReduceWithOffset ::-  TV Int32 ->-  Imp.TExp Int32 ->-  Lambda GPUMem ->-  [VName] ->-  InKernelGen ()-groupReduceWithOffset offset w lam arrs = do-  constants <- kernelConstants <$> askEnv--  let local_tid = kernelLocalThreadId constants-      global_tid = kernelGlobalThreadId constants--      barrier-        | all primType $ lambdaReturnType lam = sOp $ Imp.Barrier Imp.FenceLocal-        | otherwise = sOp $ Imp.Barrier Imp.FenceGlobal--      readReduceArgument param arr-        | Prim _ <- paramType param = do-            let i = local_tid + tvExp offset-            copyDWIMFix (paramName param) [] (Var arr) [sExt64 i]-        | otherwise = do-            let i = global_tid + tvExp offset-            copyDWIMFix (paramName param) [] (Var arr) [sExt64 i]--      writeReduceOpResult param arr-        | Prim _ <- paramType param =-            copyDWIMFix arr [sExt64 local_tid] (Var $ paramName param) []-        | otherwise =-            pure ()--  let (reduce_acc_params, reduce_arr_params) = splitAt (length arrs) $ lambdaParams lam--  skip_waves <- dPrimV "skip_waves" (1 :: Imp.TExp Int32)-  dLParams $ lambdaParams lam--  offset <-- (0 :: Imp.TExp Int32)--  comment "participating threads read initial accumulator" $-    sWhen (local_tid .<. w) $-      zipWithM_ readReduceArgument reduce_acc_params arrs--  let do_reduce = do-        comment "read array element" $-          zipWithM_ readReduceArgument reduce_arr_params arrs-        comment "apply reduction operation" $-          compileBody' reduce_acc_params $-            lambdaBody lam-        comment "write result of operation" $-          zipWithM_ writeReduceOpResult reduce_acc_params arrs-      in_wave_reduce = everythingVolatile do_reduce--      wave_size = kernelWaveSize constants-      group_size = kernelGroupSize constants-      wave_id = local_tid `quot` wave_size-      in_wave_id = local_tid - wave_id * wave_size-      num_waves = (sExt32 group_size + wave_size - 1) `quot` wave_size-      arg_in_bounds = local_tid + tvExp offset .<. w--      doing_in_wave_reductions =-        tvExp offset .<. wave_size-      apply_in_in_wave_iteration =-        (in_wave_id .&. (2 * tvExp offset - 1)) .==. 0-      in_wave_reductions = do-        offset <-- (1 :: Imp.TExp Int32)-        sWhile doing_in_wave_reductions $ do-          sWhen-            (arg_in_bounds .&&. apply_in_in_wave_iteration)-            in_wave_reduce-          offset <-- tvExp offset * 2--      doing_cross_wave_reductions =-        tvExp skip_waves .<. num_waves-      is_first_thread_in_wave =-        in_wave_id .==. 0-      wave_not_skipped =-        (wave_id .&. (2 * tvExp skip_waves - 1)) .==. 0-      apply_in_cross_wave_iteration =-        arg_in_bounds .&&. is_first_thread_in_wave .&&. wave_not_skipped-      cross_wave_reductions =-        sWhile doing_cross_wave_reductions $ do-          barrier-          offset <-- tvExp skip_waves * wave_size-          sWhen-            apply_in_cross_wave_iteration-            do_reduce-          skip_waves <-- tvExp skip_waves * 2--  in_wave_reductions-  cross_wave_reductions--groupScan ::-  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->-  Imp.TExp Int64 ->-  Imp.TExp Int64 ->-  Lambda GPUMem ->-  [VName] ->-  InKernelGen ()-groupScan seg_flag arrs_full_size w lam arrs = do-  constants <- kernelConstants <$> askEnv-  renamed_lam <- renameLambda lam--  let ltid32 = kernelLocalThreadId constants-      ltid = sExt64 ltid32-      (x_params, y_params) = splitAt (length arrs) $ lambdaParams lam--  dLParams (lambdaParams lam ++ lambdaParams renamed_lam)--  ltid_in_bounds <- dPrimVE "ltid_in_bounds" $ ltid .<. w--  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.-  ---  -- We hardcode the block size here.  The only requirement is that-  -- it should not be less than the square root of the group size.-  -- With 32, we will work on groups of size 1024 or smaller, which-  -- fits every device Troels has seen.  Still, it would be nicer if-  -- it were a runtime parameter.  Some day.-  let block_size = 32-      simd_width = kernelWaveSize constants-      block_id = ltid32 `quot` block_size-      in_block_id = ltid32 - block_id * block_size-      doInBlockScan seg_flag' active =-        inBlockScan-          constants-          seg_flag'-          arrs_full_size-          simd_width-          block_size-          active-          arrs-          barrier-      array_scan = not $ all primType $ lambdaReturnType lam-      barrier-        | array_scan =-            sOp $ Imp.Barrier Imp.FenceGlobal-        | otherwise =-            sOp $ Imp.Barrier fence--      group_offset = sExt64 (kernelGroupId constants) * kernelGroupSize constants--      writeBlockResult p arr-        | primType $ paramType p =-            copyDWIM arr [DimFix $ sExt64 block_id] (Var $ paramName p) []-        | otherwise =-            copyDWIM arr [DimFix $ group_offset + sExt64 block_id] (Var $ paramName p) []--      readPrevBlockResult p arr-        | primType $ paramType p =-            copyDWIM (paramName p) [] (Var arr) [DimFix $ sExt64 block_id - 1]-        | otherwise =-            copyDWIM (paramName p) [] (Var arr) [DimFix $ group_offset + sExt64 block_id - 1]--  doInBlockScan seg_flag ltid_in_bounds lam-  barrier--  let is_first_block = block_id .==. 0-  when array_scan $ do-    sComment "save correct values for first block" $-      sWhen is_first_block $-        forM_ (zip x_params arrs) $ \(x, arr) ->-          unless (primType $ paramType x) $-            copyDWIM arr [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid] (Var $ paramName x) []--    barrier--  let last_in_block = in_block_id .==. block_size - 1-  sComment "last thread of block 'i' writes its result to offset 'i'" $-    sWhen (last_in_block .&&. ltid_in_bounds) $-      everythingVolatile $-        zipWithM_ writeBlockResult x_params arrs--  barrier--  let first_block_seg_flag = do-        flag_true <- seg_flag-        Just $ \from to ->-          flag_true (from * block_size + block_size - 1) (to * block_size + block_size - 1)-  comment-    "scan the first block, after which offset 'i' contains carry-in for block 'i+1'"-    $ doInBlockScan first_block_seg_flag (is_first_block .&&. ltid_in_bounds) renamed_lam--  barrier--  when array_scan $ do-    sComment "move correct values for first block back a block" $-      sWhen is_first_block $-        forM_ (zip x_params arrs) $ \(x, arr) ->-          unless (primType $ paramType x) $-            copyDWIM-              arr-              [DimFix $ arrs_full_size + group_offset + ltid]-              (Var arr)-              [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid]--    barrier--  no_carry_in <- dPrimVE "no_carry_in" $ is_first_block .||. bNot ltid_in_bounds--  let read_carry_in = sUnless no_carry_in $ do-        forM_ (zip x_params y_params) $ \(x, y) ->-          copyDWIM (paramName y) [] (Var (paramName x)) []-        zipWithM_ readPrevBlockResult x_params arrs--      op_to_x-        | Nothing <- seg_flag =-            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-            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-            -- hit this barrier (if applicable).-            when array_scan barrier-            sUnless no_carry_in $ sUnless inactive $ compileBody' x_params $ lambdaBody lam--      write_final_result =-        forM_ (zip x_params arrs) $ \(p, arr) ->-          when (primType $ paramType p) $-            copyDWIM arr [DimFix ltid] (Var $ paramName p) []--  sComment "carry-in for every block except the first" $ do-    sComment "read operands" read_carry_in-    sComment "perform operation" op_to_x-    sComment "write final result" $ sUnless no_carry_in write_final_result--  barrier--  sComment "restore correct values for first block" $-    sWhen (is_first_block .&&. ltid_in_bounds) $-      forM_ (zip3 x_params y_params arrs) $ \(x, y, arr) ->-        if primType (paramType y)-          then copyDWIM arr [DimFix ltid] (Var $ paramName y) []-          else copyDWIM (paramName x) [] (Var arr) [DimFix $ arrs_full_size + group_offset + ltid]--  barrier--inBlockScan ::-  KernelConstants ->-  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->-  Imp.TExp Int64 ->-  Imp.TExp Int32 ->-  Imp.TExp Int32 ->-  Imp.TExp Bool ->-  [VName] ->-  InKernelGen () ->-  Lambda GPUMem ->-  InKernelGen ()-inBlockScan constants seg_flag arrs_full_size lockstep_width block_size active arrs barrier scan_lam = everythingVolatile $ do-  skip_threads <- dPrim "skip_threads" int32-  let actual_params = lambdaParams scan_lam-      (x_params, y_params) =-        splitAt (length actual_params `div` 2) actual_params-      y_to_x =-        forM_ (zip x_params y_params) $ \(x, y) ->-          when (primType (paramType x)) $-            copyDWIM (paramName x) [] (Var (paramName y)) []--  -- Set initial y values-  sComment "read input for in-block scan" $-    sWhen active $ do-      zipWithM_ readInitial y_params arrs-      -- Since the final result is expected to be in x_params, we may-      -- need to copy it there for the first thread in the block.-      sWhen (in_block_id .==. 0) y_to_x--  when array_scan barrier--  let op_to_x in_block_thread_active-        | Nothing <- seg_flag =-            sWhen in_block_thread_active $-              compileBody' x_params $-                lambdaBody scan_lam-        | Just flag_true <- seg_flag = do-            inactive <--              dPrimVE "inactive" $ flag_true (ltid32 - tvExp skip_threads) ltid32-            sWhen (in_block_thread_active .&&. inactive) $-              forM_ (zip x_params y_params) $ \(x, y) ->-                copyDWIM (paramName x) [] (Var (paramName y)) []-            -- The convoluted control flow is to ensure all threads-            -- hit this barrier (if applicable).-            when array_scan barrier-            sWhen in_block_thread_active . sUnless inactive $-              compileBody' x_params $-                lambdaBody scan_lam--      maybeBarrier =-        sWhen-          (lockstep_width .<=. tvExp skip_threads)-          barrier--  sComment "in-block scan (hopefully no barriers needed)" $ do-    skip_threads <-- 1-    sWhile (tvExp skip_threads .<. block_size) $ do-      thread_active <--        dPrimVE "thread_active" $ tvExp skip_threads .<=. in_block_id .&&. active--      sWhen thread_active . sComment "read operands" $-        zipWithM_ (readParam (sExt64 $ tvExp skip_threads)) x_params arrs-      sComment "perform operation" $ op_to_x thread_active--      maybeBarrier--      sWhen thread_active . sComment "write result" $-        sequence_ $-          zipWith3 writeResult x_params y_params arrs--      maybeBarrier--      skip_threads <-- tvExp skip_threads * 2-  where-    block_id = ltid32 `quot` block_size-    in_block_id = ltid32 - block_id * block_size-    ltid32 = kernelLocalThreadId constants-    ltid = sExt64 ltid32-    gtid = sExt64 $ kernelGlobalThreadId constants-    array_scan = not $ all primType $ lambdaReturnType scan_lam--    readInitial p arr-      | primType $ paramType p =-          copyDWIM (paramName p) [] (Var arr) [DimFix ltid]-      | otherwise =-          copyDWIM (paramName p) [] (Var arr) [DimFix gtid]--    readParam behind p arr-      | primType $ paramType p =-          copyDWIM (paramName p) [] (Var arr) [DimFix $ ltid - behind]-      | otherwise =-          copyDWIM (paramName p) [] (Var arr) [DimFix $ gtid - behind + arrs_full_size]--    writeResult x y arr-      | primType $ paramType x = do-          copyDWIM arr [DimFix ltid] (Var $ paramName x) []-          copyDWIM (paramName y) [] (Var $ paramName x) []-      | otherwise =-          copyDWIM (paramName y) [] (Var $ paramName x) []--simpleKernelGroups ::-  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-  fname <- askFunction-  let group_size_key = keyWithEntryPoint fname $ nameFromString $ pretty $ 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)--simpleKernelConstants ::-  Imp.TExp Int64 ->-  String ->-  CallKernelGen-    ( (Imp.TExp Int64 -> InKernelGen ()) -> InKernelGen (),-      KernelConstants-    )-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-  -- GPU will possibly need.  Feel free to come back and laugh at me-  -- in the future.-  let max_num_groups = 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--      constants =-        KernelConstants-          { kernelGlobalThreadId = Imp.le32 thread_gtid,-            kernelLocalThreadId = Imp.le32 thread_ltid,-            kernelGroupId = Imp.le32 group_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'),-            kernelWaveSize = 0,-            kernelLocalIdMap = mempty,-            kernelChunkItersMap = mempty-          }--      wrapKernel m = do-        dPrim_ thread_ltid int32-        dPrim_ inner_group_size int64-        dPrim_ group_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-          global_tid <--            dPrimVE "global_tid" $-              sExt64 virt_group_id * sExt64 (le32 inner_group_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--- double duty; we put an outer loop to accomplish this.  The--- advantage over just launching a bazillion threads is that the cost--- of memory expansion should be proportional to the number of--- *physical* threads (hardware parallelism), not the amount of--- application parallelism.-virtualiseGroups ::-  SegVirt ->-  Imp.TExp Int32 ->-  (Imp.TExp Int32 -> InKernelGen ()) ->-  InKernelGen ()-virtualiseGroups SegVirt required_groups m = do-  constants <- kernelConstants <$> askEnv-  phys_group_id <- dPrim "phys_group_id" int32-  sOp $ Imp.GetGroupId (tvVar phys_group_id) 0-  iterations <--    dPrimVE "iterations" $-      (required_groups - tvExp phys_group_id) `divUp` sExt32 (kernelNumGroups constants)--  sFor "i" iterations $ \i -> do-    m . tvExp-      =<< dPrimV-        "virt_group_id"-        (tvExp phys_group_id + i * sExt32 (kernelNumGroups constants))-    -- Make sure the virtual group is actually done before we let-    -- another virtual group have its way with it.-    sOp $ Imp.Barrier Imp.FenceGlobal-virtualiseGroups _ _ m = do-  gid <- kernelGroupIdVar . kernelConstants <$> askEnv-  m $ Imp.le32 gid---- | Various extra configuration of the kernel being generated.-data KernelAttrs = KernelAttrs-  { -- | 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-  }---- | The default kernel attributes.-defKernelAttrs ::-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->-  KernelAttrs-defKernelAttrs num_groups group_size =-  KernelAttrs-    { kAttrFailureTolerant = False,-      kAttrCheckLocalMemory = True,-      kAttrNumGroups = num_groups,-      kAttrGroupSize = group_size-    }--sKernel ::-  Operations GPUMem KernelEnv Imp.KernelOp ->-  (KernelConstants -> Imp.TExp Int32) ->-  String ->-  VName ->-  KernelAttrs ->-  InKernelGen () ->-  CallKernelGen ()-sKernel ops flatf name v attrs f = do-  (constants, set_constants) <--    kernelInitialisationSimple (kAttrNumGroups attrs) (kAttrGroupSize attrs)-  name' <- nameForFun $ name ++ "_" ++ show (baseTag v)-  sKernelOp attrs constants ops name' $ do-    set_constants-    dPrimV_ v $ flatf constants-    f--sKernelThread ::-  String ->-  VName ->-  KernelAttrs ->-  InKernelGen () ->-  CallKernelGen ()-sKernelThread = sKernel threadOperations kernelGlobalThreadId--sKernelGroup ::-  String ->-  VName ->-  KernelAttrs ->-  InKernelGen () ->-  CallKernelGen ()-sKernelGroup = sKernel groupOperations kernelGroupId--sKernelOp ::-  KernelAttrs ->-  KernelConstants ->-  Operations GPUMem KernelEnv Imp.KernelOp ->-  Name ->-  InKernelGen () ->-  CallKernelGen ()-sKernelOp attrs constants ops name m = do-  HostEnv atomics _ locks <- askEnv-  body <- makeAllMemoryGlobal $ subImpM_ (KernelEnv atomics constants locks) ops m-  uses <- computeKernelUses body mempty-  emit . Imp.Op . Imp.CallKernel $-    Imp.Kernel-      { Imp.kernelBody = body,-        Imp.kernelUses = uses,-        Imp.kernelNumGroups = [untyped $ kernelNumGroups constants],-        Imp.kernelGroupSize = [untyped $ kernelGroupSize constants],-        Imp.kernelName = name,-        Imp.kernelFailureTolerant = kAttrFailureTolerant attrs,-        Imp.kernelCheckLocalMemory = kAttrCheckLocalMemory attrs-      }--sKernelFailureTolerant ::-  Bool ->-  Operations GPUMem KernelEnv Imp.KernelOp ->-  KernelConstants ->-  Name ->-  InKernelGen () ->-  CallKernelGen ()-sKernelFailureTolerant tol ops constants name m = do-  sKernelOp attrs constants ops name m-  where-    attrs =-      ( defKernelAttrs-          (kernelNumGroupsCount constants)-          (kernelGroupSizeCount constants)-      )-        { kAttrFailureTolerant = tol-        }--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_ixfun = memLocIxFun srcloc-      dims = IxFun.shape src_ixfun-      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)-      copyElementWise-        pt-        (sliceMemLoc destloc destslice')-        (sliceMemLoc srcloc srcslice')-    _ -> do-      groupCoverSpace (map sExt32 dims) $ \is ->-        copyElementWise-          pt-          (sliceMemLoc destloc (Slice $ map (DimFix . sExt64) is))-          (sliceMemLoc srcloc (Slice $ map (DimFix . sExt64) is))-      sOp $ Imp.Barrier Imp.FenceLocal--threadOperations, groupOperations :: Operations GPUMem KernelEnv Imp.KernelOp-threadOperations =-  (defaultOperations compileThreadOp)-    { opsCopyCompiler = copyElementWise,-      opsExpCompiler = compileThreadExp,-      opsStmsCompiler = \_ -> defCompileStms mempty,-      opsAllocCompilers =-        M.fromList [(Space "local", allocLocal)]-    }-groupOperations =-  (defaultOperations compileGroupOp)-    { opsCopyCompiler = copyInGroup,-      opsExpCompiler = compileGroupExp,-      opsStmsCompiler = \_ -> defCompileStms mempty,-      opsAllocCompilers =-        M.fromList [(Space "local", allocLocal)]-    }---- | Perform a Replicate with a kernel.-sReplicateKernel :: VName -> SubExp -> CallKernelGen ()-sReplicateKernel arr se = do-  t <- subExpType se-  ds <- dropLast (arrayRank t) . arrayDims <$> lookupType arr--  let dims = map pe64 $ ds ++ arrayDims t-  n <- dPrimVE "replicate_n" $ product $ map sExt64 dims-  (virtualise, constants) <- simpleKernelConstants n "replicate"--  fname <- askFunction-  let name =-        keyWithEntryPoint fname $-          nameFromString $-            "replicate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)--  sKernelFailureTolerant True threadOperations constants name $-    virtualise $ \gtid -> do-      is' <- dIndexSpace' "rep_i" dims gtid-      sWhen (gtid .<. n) $-        copyDWIMFix arr is' se $-          drop (length ds) is'--replicateName :: PrimType -> String-replicateName bt = "replicate_" ++ pretty bt--replicateForType :: PrimType -> CallKernelGen Name-replicateForType bt = do-  let fname = nameFromString $ "builtin#" <> replicateName bt--  exists <- hasFunction fname-  unless exists $ do-    mem <- newVName "mem"-    num_elems <- newVName "num_elems"-    val <- newVName "val"--    let params =-          [ Imp.MemParam mem (Space "device"),-            Imp.ScalarParam num_elems int64,-            Imp.ScalarParam val bt-          ]-        shape = Shape [Var num_elems]-    function fname [] params $ do-      arr <--        sArray "arr" bt shape mem $ IxFun.iota $ map pe64 $ shapeDims shape-      sReplicateKernel arr $ Var val--  pure fname--replicateIsFill :: VName -> SubExp -> CallKernelGen (Maybe (CallKernelGen ()))-replicateIsFill arr v = do-  ArrayEntry (MemLoc arr_mem arr_shape arr_ixfun) _ <- lookupArray arr-  v_t <- subExpType v-  case v_t of-    Prim v_t'-      | IxFun.isLinear arr_ixfun -> pure $-          Just $ do-            fname <- replicateForType v_t'-            emit $-              Imp.Call-                []-                fname-                [ Imp.MemArg arr_mem,-                  Imp.ExpArg $ untyped $ product $ map pe64 arr_shape,-                  Imp.ExpArg $ toExp' v_t' v-                ]-    _ -> pure Nothing---- | Perform a Replicate with a kernel.-sReplicate :: VName -> SubExp -> CallKernelGen ()-sReplicate arr se = do-  -- If the replicate is of a particularly common and simple form-  -- (morally a memset()/fill), then we use a common function.-  is_fill <- replicateIsFill arr se--  case is_fill of-    Just m -> m-    Nothing -> sReplicateKernel arr se---- | Perform an Iota with a kernel.-sIotaKernel ::-  VName ->-  Imp.TExp Int64 ->-  Imp.Exp ->-  Imp.Exp ->-  IntType ->-  CallKernelGen ()-sIotaKernel arr n x s et = do-  destloc <- entryArrayLoc <$> lookupArray arr-  (virtualise, constants) <- simpleKernelConstants n "iota"--  fname <- askFunction-  let name =-        keyWithEntryPoint fname $-          nameFromString $-            "iota_"-              ++ pretty et-              ++ "_"-              ++ show (baseTag $ kernelGlobalThreadIdVar constants)--  sKernelFailureTolerant True threadOperations constants name $-    virtualise $ \gtid ->-      sWhen (gtid .<. n) $ do-        (destmem, destspace, destidx) <- fullyIndexArray' destloc [gtid]--        emit $-          Imp.Write destmem destidx (IntType et) destspace Imp.Nonvolatile $-            BinOpExp-              (Add et OverflowWrap)-              (BinOpExp (Mul et OverflowWrap) (Imp.sExt et $ untyped gtid) s)-              x--iotaName :: IntType -> String-iotaName bt = "iota_" ++ pretty bt--iotaForType :: IntType -> CallKernelGen Name-iotaForType bt = do-  let fname = nameFromString $ "builtin#" <> iotaName bt--  exists <- hasFunction fname-  unless exists $ do-    mem <- newVName "mem"-    n <- newVName "n"-    x <- newVName "x"-    s <- newVName "s"--    let params =-          [ Imp.MemParam mem (Space "device"),-            Imp.ScalarParam n int32,-            Imp.ScalarParam x $ IntType bt,-            Imp.ScalarParam s $ IntType bt-          ]-        shape = Shape [Var n]-        n' = Imp.le64 n-        x' = Imp.var x $ IntType bt-        s' = Imp.var s $ IntType bt--    function fname [] params $ do-      arr <--        sArray "arr" (IntType bt) shape mem $-          IxFun.iota $-            map pe64 $-              shapeDims shape-      sIotaKernel arr (sExt64 n') x' s' bt--  pure fname---- | Perform an Iota with a kernel.-sIota ::-  VName ->-  Imp.TExp Int64 ->-  Imp.Exp ->-  Imp.Exp ->-  IntType ->-  CallKernelGen ()-sIota arr n x s et = do-  ArrayEntry (MemLoc arr_mem _ arr_ixfun) _ <- lookupArray arr-  if IxFun.isLinear arr_ixfun-    then do-      fname <- iotaForType et-      emit $-        Imp.Call-          []-          fname-          [Imp.MemArg arr_mem, Imp.ExpArg $ untyped n, Imp.ExpArg x, Imp.ExpArg s]-    else sIotaKernel arr n x s et--sCopy :: CopyCompiler GPUMem HostEnv Imp.HostOp-sCopy pt destloc@(MemLoc destmem _ _) srcloc@(MemLoc srcmem srcdims _) = do-  -- Note that the shape of the destination and the source are-  -- necessarily the same.-  let shape = map pe64 srcdims-      kernel_size = product shape--  (virtualise, constants) <- simpleKernelConstants kernel_size "copy"--  fname <- askFunction-  let name =-        keyWithEntryPoint fname $-          nameFromString $-            "copy_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)--  sKernelFailureTolerant True threadOperations constants name $-    virtualise $ \gtid -> do-      is <- dIndexSpace' "copy_i" shape gtid--      (_, destspace, destidx) <- fullyIndexArray' destloc is-      (_, srcspace, srcidx) <- fullyIndexArray' srcloc is--      sWhen (gtid .<. kernel_size) $ do-        tmp <- tvVar <$> dPrim "tmp" pt-        emit $ Imp.Read tmp srcmem srcidx pt srcspace Imp.Nonvolatile-        emit $ Imp.Write destmem destidx pt destspace Imp.Nonvolatile $ Imp.var tmp pt---- | Perform a Rotate with a kernel.-sRotateKernel :: VName -> [Imp.TExp Int64] -> VName -> CallKernelGen ()-sRotateKernel dest rs src = do-  t <- lookupType src-  let ds = map pe64 $ arrayDims t-  n <- dPrimVE "rotate_n" $ product ds-  (virtualise, constants) <- simpleKernelConstants n "rotate"--  fname <- askFunction-  let name =-        keyWithEntryPoint fname $-          nameFromString $-            "rotate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)--  sKernelFailureTolerant True threadOperations constants name $-    virtualise $ \gtid -> sWhen (gtid .<. n) $ do-      is' <- dIndexSpace' "rep_i" ds gtid-      is'' <- sequence $ zipWith3 rotate ds rs is'-      copyDWIMFix dest is' (Var src) is''-  where-    rotate d r i = dPrimVE "rot_i" $ rotateIndex d r i--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."-compileGroupResult _ _ ConcatReturns {} =-  compilerLimitationS "compileGroupResult: ConcatReturns not handled yet."--compileThreadResult ::-  SegSpace ->-  PatElem LetDecMem ->-  KernelResult ->-  InKernelGen ()-compileThreadResult _ _ RegTileReturns {} =-  compilerLimitationS "compileThreadResult: RegTileReturns not yet handled."-compileThreadResult space pe (Returns _ _ what) = do-  let is = map (Imp.le64 . fst) $ unSegSpace space-  copyDWIMFix (patElemName pe) is what []-compileThreadResult _ pe (ConcatReturns _ SplitContiguous _ per_thread_elems what) = do-  constants <- kernelConstants <$> askEnv-  let offset =-        pe64 per_thread_elems-          * sExt64 (kernelGlobalThreadId constants)-  n <- pe64 . arraySize 0 <$> lookupType what-  copyDWIM (patElemName pe) [DimSlice offset n 1] (Var what) []-compileThreadResult _ pe (ConcatReturns _ (SplitStrided stride) _ _ what) = do-  offset <- sExt64 . kernelGlobalThreadId . kernelConstants <$> askEnv-  n <- pe64 . arraySize 0 <$> lookupType what-  copyDWIM (patElemName pe) [DimSlice offset n $ pe64 stride] (Var what) []-compileThreadResult _ pe (WriteReturns _ (Shape rws) _arr dests) = do-  let rws' = map pe64 rws-  forM_ dests $ \(slice, e) -> do-    let slice' = fmap pe64 slice-        write = inBounds slice' rws'-    sWhen write $ copyDWIM (patElemName pe) (unSlice slice') e []-compileThreadResult _ _ TileReturns {} =-  compilerBugS "compileThreadResult: TileReturns unhandled."--arrayInLocalMemory :: SubExp -> InKernelGen Bool-arrayInLocalMemory (Var name) = do-  res <- lookupVar name-  case res of-    ArrayVar _ entry ->-      (Space "local" ==) . entryMemSpace-        <$> lookupMemory (memLocName (entryArrayLoc entry))-    _ -> pure False-arrayInLocalMemory Constant {} = pure False+    threadOperations,+    keyWithEntryPoint,+    CallKernelGen,+    InKernelGen,+    Locks (..),+    HostEnv (..),+    Target (..),+    KernelEnv (..),+    groupReduce,+    groupScan,+    groupLoop,+    isActive,+    sKernel,+    sKernelThread,+    KernelAttrs (..),+    defKernelAttrs,+    allocLocal,+    kernelAlloc,+    compileThreadResult,+    virtualiseGroups,+    kernelLoop,+    groupCoverSpace,+    fenceForArrays,+    updateAcc,++    -- * Host-level bulk operations+    sReplicate,+    sIota,+    sRotateKernel,+    sCopy,++    -- * Atomics+    AtomicBinOp,+    atomicUpdateLocking,+    Locking (..),+    AtomicUpdate (..),+    DoAtomicUpdate,+  )+where++import Control.Monad.Except+import Data.List (foldl')+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Futhark.CodeGen.ImpCode.GPU as Imp+import Futhark.CodeGen.ImpGen+import Futhark.Error+import Futhark.IR.GPUMem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+import Futhark.Transform.Rename+import Futhark.Util (dropLast, nubOrd, splitFromEnd)+import Futhark.Util.IntegralExp (divUp, quot, rem)+import Prelude hiding (quot, rem)++-- | Which target are we ultimately generating code for?  While most+-- of the kernels code is the same, there are some cases where we+-- generate special code based on the ultimate low-level API we are+-- targeting.+data Target = CUDA | OpenCL++-- | Information about the locks available for accumulators.+data Locks = Locks+  { locksArray :: VName,+    locksCount :: Int+  }++data HostEnv = HostEnv+  { hostAtomics :: AtomicBinOp,+    hostTarget :: Target,+    hostLocks :: M.Map VName Locks+  }++data KernelEnv = KernelEnv+  { kernelAtomics :: AtomicBinOp,+    kernelConstants :: KernelConstants,+    kernelLocks :: M.Map VName Locks+  }++type CallKernelGen = ImpM GPUMem HostEnv Imp.HostOp++type InKernelGen = ImpM GPUMem KernelEnv Imp.KernelOp++data KernelConstants = KernelConstants+  { kernelGlobalThreadId :: Imp.TExp Int32,+    kernelLocalThreadId :: Imp.TExp Int32,+    kernelGroupId :: 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,+    kernelNumThreads :: Imp.TExp Int32,+    kernelWaveSize :: Imp.TExp Int32,+    -- | A mapping from dimensions of nested SegOps to already+    -- computed local thread IDs.  Only valid in non-virtualised case.+    kernelLocalIdMap :: M.Map [SubExp] [Imp.TExp Int32],+    -- | Mapping from dimensions of nested SegOps to how many+    -- iterations the virtualisation loop needs.+    kernelChunkItersMap :: M.Map [SubExp] (Imp.TExp Int32)+  }++keyWithEntryPoint :: Maybe Name -> Name -> Name+keyWithEntryPoint fname key =+  nameFromString $ maybe "" ((++ ".") . nameToString) fname ++ nameToString key++allocLocal :: AllocCompiler GPUMem r Imp.KernelOp+allocLocal mem size =+  sOp $ Imp.LocalAlloc mem size++kernelAlloc ::+  Pat LetDecMem ->+  SubExp ->+  Space ->+  InKernelGen ()+kernelAlloc (Pat [_]) _ ScalarSpace {} =+  -- 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") =+  allocLocal (patElemName mem) $ Imp.bytes $ pe64 size+kernelAlloc (Pat [mem]) _ _ =+  compilerLimitationS $ "Cannot allocate memory block " ++ pretty mem ++ " in kernel."+kernelAlloc dest _ _ =+  error $ "Invalid target for in-kernel allocation: " ++ show dest++updateAcc :: VName -> [SubExp] -> [SubExp] -> InKernelGen ()+updateAcc acc is vs = sComment "UpdateAcc" $ do+  -- See the ImpGen implementation of UpdateAcc for general notes.+  let is' = map pe64 is+  (c, space, arrs, dims, op) <- lookupAcc acc is'+  sWhen (inBounds (Slice (map DimFix is')) dims) $+    case op of+      Nothing ->+        forM_ (zip arrs vs) $ \(arr, v) -> copyDWIMFix arr is' v []+      Just lam -> do+        dLParams $ lambdaParams lam+        let (_x_params, y_params) =+              splitAt (length vs) $ map paramName $ lambdaParams lam+        forM_ (zip y_params vs) $ \(yp, v) -> copyDWIM yp [] v []+        atomics <- kernelAtomics <$> askEnv+        case atomicUpdateLocking atomics lam of+          AtomicPrim f -> f space arrs is'+          AtomicCAS f -> f space arrs is'+          AtomicLocking f -> do+            c_locks <- M.lookup c . kernelLocks <$> askEnv+            case c_locks of+              Just (Locks locks num_locks) -> do+                let locking =+                      Locking locks 0 1 0 $+                        pure . (`rem` fromIntegral num_locks) . flattenIndex dims+                f locking space arrs is'+              Nothing ->+                error $ "Missing locks for " ++ pretty acc++compileThreadExp :: ExpCompiler GPUMem KernelEnv Imp.KernelOp+compileThreadExp (Pat [pe]) (BasicOp (Opaque _ se)) =+  -- Cannot print in GPU code.+  copyDWIM (patElemName pe) [] se []+compileThreadExp (Pat [dest]) (BasicOp (ArrayLit es _)) =+  forM_ (zip [0 ..] es) $ \(i, e) ->+    copyDWIMFix (patElemName dest) [fromIntegral (i :: Int64)] e []+compileThreadExp _ (BasicOp (UpdateAcc acc is vs)) =+  updateAcc acc is vs+compileThreadExp dest e =+  defCompileExp dest e++-- | Assign iterations of a for-loop to all threads in the kernel.+-- The passed-in function is invoked with the (symbolic) iteration.+-- The body must contain thread-level code.  For multidimensional+-- loops, use 'groupCoverSpace'.+kernelLoop ::+  IntExp t =>+  Imp.TExp t ->+  Imp.TExp t ->+  Imp.TExp t ->+  (Imp.TExp t -> InKernelGen ()) ->+  InKernelGen ()+kernelLoop tid num_threads n f =+  localOps threadOperations $+    if n == num_threads+      then f tid+      else do+        num_chunks <- dPrimVE "num_chunks" $ n `divUp` num_threads+        sFor "chunk_i" num_chunks $ \chunk_i -> do+          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+-- passed-in function is invoked with the (symbolic) iteration.  For+-- multidimensional loops, use 'groupCoverSpace'.+groupLoop ::+  IntExp t =>+  Imp.TExp t ->+  (Imp.TExp t -> InKernelGen ()) ->+  InKernelGen ()+groupLoop n f = do+  constants <- kernelConstants <$> askEnv+  kernelLoop+    (kernelLocalThreadId constants `sExtAs` n)+    (kernelGroupSize constants `sExtAs` n)+    n+    f++-- | Iterate collectively though a multidimensional space, such that+-- all threads in the group participate.  The passed-in function is+-- invoked with a (symbolic) point in the index space.+groupCoverSpace ::+  IntExp t =>+  [Imp.TExp t] ->+  ([Imp.TExp t] -> InKernelGen ()) ->+  InKernelGen ()+groupCoverSpace ds f = do+  constants <- kernelConstants <$> askEnv+  let group_size = kernelGroupSize constants+  case splitFromEnd 1 ds of+    -- Optimise the case where the inner dimension of the space is+    -- equal to the group size.+    (ds', [last_d])+      | last_d == (group_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++-- Which fence do we need to protect shared access to this memory space?+fenceForSpace :: Space -> Imp.Fence+fenceForSpace (Space "local") = Imp.FenceLocal+fenceForSpace _ = Imp.FenceGlobal++-- | If we are touching these arrays, which kind of fence do we need?+fenceForArrays :: [VName] -> InKernelGen Imp.Fence+fenceForArrays = fmap (foldl' max Imp.FenceLocal) . mapM need+  where+    need arr =+      fmap (fenceForSpace . entryMemSpace)+        . lookupMemory+        . memLocName+        . entryArrayLoc+        =<< lookupArray arr++inBlockScan ::+  KernelConstants ->+  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->+  Imp.TExp Int64 ->+  Imp.TExp Int32 ->+  Imp.TExp Int32 ->+  Imp.TExp Bool ->+  [VName] ->+  InKernelGen () ->+  Lambda GPUMem ->+  InKernelGen ()+inBlockScan constants seg_flag arrs_full_size lockstep_width block_size active arrs barrier scan_lam = everythingVolatile $ do+  skip_threads <- dPrim "skip_threads" int32+  let actual_params = lambdaParams scan_lam+      (x_params, y_params) =+        splitAt (length actual_params `div` 2) actual_params+      y_to_x =+        forM_ (zip x_params y_params) $ \(x, y) ->+          when (primType (paramType x)) $+            copyDWIM (paramName x) [] (Var (paramName y)) []++  -- Set initial y values+  sComment "read input for in-block scan" $+    sWhen active $ do+      zipWithM_ readInitial y_params arrs+      -- Since the final result is expected to be in x_params, we may+      -- need to copy it there for the first thread in the block.+      sWhen (in_block_id .==. 0) y_to_x++  when array_scan barrier++  let op_to_x in_block_thread_active+        | Nothing <- seg_flag =+            sWhen in_block_thread_active $+              compileBody' x_params $+                lambdaBody scan_lam+        | Just flag_true <- seg_flag = do+            inactive <-+              dPrimVE "inactive" $ flag_true (ltid32 - tvExp skip_threads) ltid32+            sWhen (in_block_thread_active .&&. inactive) $+              forM_ (zip x_params y_params) $ \(x, y) ->+                copyDWIM (paramName x) [] (Var (paramName y)) []+            -- The convoluted control flow is to ensure all threads+            -- hit this barrier (if applicable).+            when array_scan barrier+            sWhen in_block_thread_active . sUnless inactive $+              compileBody' x_params $+                lambdaBody scan_lam++      maybeBarrier =+        sWhen+          (lockstep_width .<=. tvExp skip_threads)+          barrier++  sComment "in-block scan (hopefully no barriers needed)" $ do+    skip_threads <-- 1+    sWhile (tvExp skip_threads .<. block_size) $ do+      thread_active <-+        dPrimVE "thread_active" $ tvExp skip_threads .<=. in_block_id .&&. active++      sWhen thread_active . sComment "read operands" $+        zipWithM_ (readParam (sExt64 $ tvExp skip_threads)) x_params arrs+      sComment "perform operation" $ op_to_x thread_active++      maybeBarrier++      sWhen thread_active . sComment "write result" $+        sequence_ $+          zipWith3 writeResult x_params y_params arrs++      maybeBarrier++      skip_threads <-- tvExp skip_threads * 2+  where+    block_id = ltid32 `quot` block_size+    in_block_id = ltid32 - block_id * block_size+    ltid32 = kernelLocalThreadId constants+    ltid = sExt64 ltid32+    gtid = sExt64 $ kernelGlobalThreadId constants+    array_scan = not $ all primType $ lambdaReturnType scan_lam++    readInitial p arr+      | primType $ paramType p =+          copyDWIM (paramName p) [] (Var arr) [DimFix ltid]+      | otherwise =+          copyDWIM (paramName p) [] (Var arr) [DimFix gtid]++    readParam behind p arr+      | primType $ paramType p =+          copyDWIM (paramName p) [] (Var arr) [DimFix $ ltid - behind]+      | otherwise =+          copyDWIM (paramName p) [] (Var arr) [DimFix $ gtid - behind + arrs_full_size]++    writeResult x y arr+      | primType $ paramType x = do+          copyDWIM arr [DimFix ltid] (Var $ paramName x) []+          copyDWIM (paramName y) [] (Var $ paramName x) []+      | otherwise =+          copyDWIM (paramName y) [] (Var $ paramName x) []++groupScan ::+  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+  constants <- kernelConstants <$> askEnv+  renamed_lam <- renameLambda lam++  let ltid32 = kernelLocalThreadId constants+      ltid = sExt64 ltid32+      (x_params, y_params) = splitAt (length arrs) $ lambdaParams lam++  dLParams (lambdaParams lam ++ lambdaParams renamed_lam)++  ltid_in_bounds <- dPrimVE "ltid_in_bounds" $ ltid .<. w++  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.+  --+  -- We hardcode the block size here.  The only requirement is that+  -- it should not be less than the square root of the group size.+  -- With 32, we will work on groups of size 1024 or smaller, which+  -- fits every device Troels has seen.  Still, it would be nicer if+  -- it were a runtime parameter.  Some day.+  let block_size = 32+      simd_width = kernelWaveSize constants+      block_id = ltid32 `quot` block_size+      in_block_id = ltid32 - block_id * block_size+      doInBlockScan seg_flag' active =+        inBlockScan+          constants+          seg_flag'+          arrs_full_size+          simd_width+          block_size+          active+          arrs+          barrier+      array_scan = not $ all primType $ lambdaReturnType lam+      barrier+        | array_scan =+            sOp $ Imp.Barrier Imp.FenceGlobal+        | otherwise =+            sOp $ Imp.Barrier fence++      group_offset = sExt64 (kernelGroupId constants) * kernelGroupSize constants++      writeBlockResult p arr+        | primType $ paramType p =+            copyDWIM arr [DimFix $ sExt64 block_id] (Var $ paramName p) []+        | otherwise =+            copyDWIM arr [DimFix $ group_offset + sExt64 block_id] (Var $ paramName p) []++      readPrevBlockResult p arr+        | primType $ paramType p =+            copyDWIM (paramName p) [] (Var arr) [DimFix $ sExt64 block_id - 1]+        | otherwise =+            copyDWIM (paramName p) [] (Var arr) [DimFix $ group_offset + sExt64 block_id - 1]++  doInBlockScan seg_flag ltid_in_bounds lam+  barrier++  let is_first_block = block_id .==. 0+  when array_scan $ do+    sComment "save correct values for first block" $+      sWhen is_first_block $+        forM_ (zip x_params arrs) $ \(x, arr) ->+          unless (primType $ paramType x) $+            copyDWIM arr [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid] (Var $ paramName x) []++    barrier++  let last_in_block = in_block_id .==. block_size - 1+  sComment "last thread of block 'i' writes its result to offset 'i'" $+    sWhen (last_in_block .&&. ltid_in_bounds) $+      everythingVolatile $+        zipWithM_ writeBlockResult x_params arrs++  barrier++  let first_block_seg_flag = do+        flag_true <- seg_flag+        Just $ \from to ->+          flag_true (from * block_size + block_size - 1) (to * block_size + block_size - 1)+  comment+    "scan the first block, after which offset 'i' contains carry-in for block 'i+1'"+    $ doInBlockScan first_block_seg_flag (is_first_block .&&. ltid_in_bounds) renamed_lam++  barrier++  when array_scan $ do+    sComment "move correct values for first block back a block" $+      sWhen is_first_block $+        forM_ (zip x_params arrs) $ \(x, arr) ->+          unless (primType $ paramType x) $+            copyDWIM+              arr+              [DimFix $ arrs_full_size + group_offset + ltid]+              (Var arr)+              [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid]++    barrier++  no_carry_in <- dPrimVE "no_carry_in" $ is_first_block .||. bNot ltid_in_bounds++  let read_carry_in = sUnless no_carry_in $ do+        forM_ (zip x_params y_params) $ \(x, y) ->+          copyDWIM (paramName y) [] (Var (paramName x)) []+        zipWithM_ readPrevBlockResult x_params arrs++      op_to_x+        | Nothing <- seg_flag =+            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+            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+            -- hit this barrier (if applicable).+            when array_scan barrier+            sUnless no_carry_in $ sUnless inactive $ compileBody' x_params $ lambdaBody lam++      write_final_result =+        forM_ (zip x_params arrs) $ \(p, arr) ->+          when (primType $ paramType p) $+            copyDWIM arr [DimFix ltid] (Var $ paramName p) []++  sComment "carry-in for every block except the first" $ do+    sComment "read operands" read_carry_in+    sComment "perform operation" op_to_x+    sComment "write final result" $ sUnless no_carry_in write_final_result++  barrier++  sComment "restore correct values for first block" $+    sWhen (is_first_block .&&. ltid_in_bounds) $+      forM_ (zip3 x_params y_params arrs) $ \(x, y, arr) ->+        if primType (paramType y)+          then copyDWIM arr [DimFix ltid] (Var $ paramName y) []+          else copyDWIM (paramName x) [] (Var arr) [DimFix $ arrs_full_size + group_offset + ltid]++  barrier++groupReduce ::+  Imp.TExp Int32 ->+  Lambda GPUMem ->+  [VName] ->+  InKernelGen ()+groupReduce w lam arrs = do+  offset <- dPrim "offset" int32+  groupReduceWithOffset offset w lam arrs++groupReduceWithOffset ::+  TV Int32 ->+  Imp.TExp Int32 ->+  Lambda GPUMem ->+  [VName] ->+  InKernelGen ()+groupReduceWithOffset offset w lam arrs = do+  constants <- kernelConstants <$> askEnv++  let local_tid = kernelLocalThreadId constants+      global_tid = kernelGlobalThreadId constants++      barrier+        | all primType $ lambdaReturnType lam = sOp $ Imp.Barrier Imp.FenceLocal+        | otherwise = sOp $ Imp.Barrier Imp.FenceGlobal++      readReduceArgument param arr+        | Prim _ <- paramType param = do+            let i = local_tid + tvExp offset+            copyDWIMFix (paramName param) [] (Var arr) [sExt64 i]+        | otherwise = do+            let i = global_tid + tvExp offset+            copyDWIMFix (paramName param) [] (Var arr) [sExt64 i]++      writeReduceOpResult param arr+        | Prim _ <- paramType param =+            copyDWIMFix arr [sExt64 local_tid] (Var $ paramName param) []+        | otherwise =+            pure ()++  let (reduce_acc_params, reduce_arr_params) = splitAt (length arrs) $ lambdaParams lam++  skip_waves <- dPrimV "skip_waves" (1 :: Imp.TExp Int32)+  dLParams $ lambdaParams lam++  offset <-- (0 :: Imp.TExp Int32)++  comment "participating threads read initial accumulator" $+    sWhen (local_tid .<. w) $+      zipWithM_ readReduceArgument reduce_acc_params arrs++  let do_reduce = do+        comment "read array element" $+          zipWithM_ readReduceArgument reduce_arr_params arrs+        comment "apply reduction operation" $+          compileBody' reduce_acc_params $+            lambdaBody lam+        comment "write result of operation" $+          zipWithM_ writeReduceOpResult reduce_acc_params arrs+      in_wave_reduce = everythingVolatile do_reduce++      wave_size = kernelWaveSize constants+      group_size = kernelGroupSize constants+      wave_id = local_tid `quot` wave_size+      in_wave_id = local_tid - wave_id * wave_size+      num_waves = (sExt32 group_size + wave_size - 1) `quot` wave_size+      arg_in_bounds = local_tid + tvExp offset .<. w++      doing_in_wave_reductions =+        tvExp offset .<. wave_size+      apply_in_in_wave_iteration =+        (in_wave_id .&. (2 * tvExp offset - 1)) .==. 0+      in_wave_reductions = do+        offset <-- (1 :: Imp.TExp Int32)+        sWhile doing_in_wave_reductions $ do+          sWhen+            (arg_in_bounds .&&. apply_in_in_wave_iteration)+            in_wave_reduce+          offset <-- tvExp offset * 2++      doing_cross_wave_reductions =+        tvExp skip_waves .<. num_waves+      is_first_thread_in_wave =+        in_wave_id .==. 0+      wave_not_skipped =+        (wave_id .&. (2 * tvExp skip_waves - 1)) .==. 0+      apply_in_cross_wave_iteration =+        arg_in_bounds .&&. is_first_thread_in_wave .&&. wave_not_skipped+      cross_wave_reductions =+        sWhile doing_cross_wave_reductions $ do+          barrier+          offset <-- tvExp skip_waves * wave_size+          sWhen+            apply_in_cross_wave_iteration+            do_reduce+          skip_waves <-- tvExp skip_waves * 2++  in_wave_reductions+  cross_wave_reductions++compileThreadOp :: OpCompiler GPUMem KernelEnv Imp.KernelOp+compileThreadOp pat (Alloc size space) =+  kernelAlloc pat size space+compileThreadOp pat _ =+  compilerBugS $ "compileThreadOp: cannot compile rhs of binding " ++ pretty pat++-- | Locking strategy used for an atomic update.+data Locking = Locking+  { -- | Array containing the lock.+    lockingArray :: VName,+    -- | Value for us to consider the lock free.+    lockingIsUnlocked :: Imp.TExp Int32,+    -- | What to write when we lock it.+    lockingToLock :: Imp.TExp Int32,+    -- | What to write when we unlock it.+    lockingToUnlock :: Imp.TExp Int32,+    -- | A transformation from the logical lock index to the+    -- physical position in the array.  This can also be used+    -- to make the lock array smaller.+    lockingMapping :: [Imp.TExp Int64] -> [Imp.TExp Int64]+  }++-- | A function for generating code for an atomic update.  Assumes+-- that the bucket is in-bounds.+type DoAtomicUpdate rep r =+  Space -> [VName] -> [Imp.TExp Int64] -> ImpM rep r Imp.KernelOp ()++-- | The mechanism that will be used for performing the atomic update.+-- Approximates how efficient it will be.  Ordered from most to least+-- efficient.+data AtomicUpdate rep r+  = -- | Supported directly by primitive.+    AtomicPrim (DoAtomicUpdate rep r)+  | -- | Can be done by efficient swaps.+    AtomicCAS (DoAtomicUpdate rep r)+  | -- | Requires explicit locking.+    AtomicLocking (Locking -> DoAtomicUpdate rep r)++-- | Is there an atomic t'BinOp' corresponding to this t'BinOp'?+type AtomicBinOp =+  BinOp ->+  Maybe (VName -> VName -> Count Imp.Elements (Imp.TExp Int64) -> Imp.Exp -> Imp.AtomicOp)++-- | Do an atomic update corresponding to a binary operator lambda.+atomicUpdateLocking ::+  AtomicBinOp ->+  Lambda GPUMem ->+  AtomicUpdate GPUMem KernelEnv+atomicUpdateLocking atomicBinOp lam+  | Just ops_and_ts <- lamIsBinOp lam,+    all (\(_, t, _, _) -> primBitSize t `elem` [32, 64]) ops_and_ts =+      primOrCas ops_and_ts $ \space arrs bucket ->+        -- If the operator is a vectorised binary operator on 32/64-bit+        -- values, we can use a particularly efficient+        -- implementation. If the operator has an atomic implementation+        -- we use that, otherwise it is still a binary operator which+        -- can be implemented by atomic compare-and-swap if 32/64 bits.+        forM_ (zip arrs ops_and_ts) $ \(a, (op, t, x, y)) -> do+          -- Common variables.+          old <- dPrim "old" t++          (arr', _a_space, bucket_offset) <- fullyIndexArray a bucket++          case opHasAtomicSupport space (tvVar old) arr' bucket_offset op of+            Just f -> sOp $ f $ Imp.var y t+            Nothing ->+              atomicUpdateCAS space t a (tvVar old) bucket x $+                x <~~ Imp.BinOpExp op (Imp.var x t) (Imp.var y t)+  where+    opHasAtomicSupport space old arr' bucket' bop = do+      let atomic f = Imp.Atomic space . f old arr' bucket'+      atomic <$> atomicBinOp bop++    primOrCas ops+      | all isPrim ops = AtomicPrim+      | otherwise = AtomicCAS++    isPrim (op, _, _, _) = isJust $ atomicBinOp op++-- If the operator functions purely on single 32/64-bit values, we can+-- use an implementation based on CAS, no matter what the operator+-- does.+atomicUpdateLocking _ op+  | [Prim t] <- lambdaReturnType op,+    [xp, _] <- lambdaParams op,+    primBitSize t `elem` [32, 64] = AtomicCAS $ \space [arr] bucket -> do+      old <- dPrim "old" t+      atomicUpdateCAS space t arr (tvVar old) bucket (paramName xp) $+        compileBody' [xp] $+          lambdaBody op+atomicUpdateLocking _ op = AtomicLocking $ \locking space arrs bucket -> do+  old <- dPrim "old" int32+  continue <- dPrimVol "continue" Bool true++  -- Correctly index into locks.+  (locks', _locks_space, locks_offset) <-+    fullyIndexArray (lockingArray locking) $ lockingMapping locking bucket++  -- Critical section+  let try_acquire_lock =+        sOp $+          Imp.Atomic space $+            Imp.AtomicCmpXchg+              int32+              (tvVar old)+              locks'+              locks_offset+              (untyped $ lockingIsUnlocked locking)+              (untyped $ lockingToLock locking)+      lock_acquired = tvExp old .==. lockingIsUnlocked locking+      -- Even the releasing is done with an atomic rather than a+      -- simple write, for memory coherency reasons.+      release_lock =+        sOp $+          Imp.Atomic space $+            Imp.AtomicCmpXchg+              int32+              (tvVar old)+              locks'+              locks_offset+              (untyped $ lockingToLock locking)+              (untyped $ lockingToUnlock locking)+      break_loop = continue <-- false++  -- Preparing parameters. It is assumed that the caller has already+  -- filled the arr_params. We copy the current value to the+  -- accumulator parameters.+  --+  -- Note the use of 'everythingVolatile' when reading and writing the+  -- buckets.  This was necessary to ensure correct execution on a+  -- newer NVIDIA GPU (RTX 2080).  The 'volatile' modifiers likely+  -- make the writes pass through the (SM-local) L1 cache, which is+  -- necessary here, because we are really doing device-wide+  -- synchronisation without atomics (naughty!).+  let (acc_params, _arr_params) = splitAt (length arrs) $ lambdaParams op+      bind_acc_params =+        everythingVolatile $+          sComment "bind lhs" $+            forM_ (zip acc_params arrs) $ \(acc_p, arr) ->+              copyDWIMFix (paramName acc_p) [] (Var arr) bucket++  let op_body =+        sComment "execute operation" $+          compileBody' acc_params $+            lambdaBody op++      do_hist =+        everythingVolatile $+          sComment "update global result" $+            zipWithM_ (writeArray bucket) arrs $+              map (Var . paramName) acc_params++      fence = sOp $ Imp.MemFence $ fenceForSpace space++  -- While-loop: Try to insert your value+  sWhile (tvExp continue) $ do+    try_acquire_lock+    sWhen lock_acquired $ do+      dLParams acc_params+      bind_acc_params+      op_body+      do_hist+      fence+      release_lock+      break_loop+    fence+  where+    writeArray bucket arr val = copyDWIMFix arr bucket val []++atomicUpdateCAS ::+  Space ->+  PrimType ->+  VName ->+  VName ->+  [Imp.TExp Int64] ->+  VName ->+  InKernelGen () ->+  InKernelGen ()+atomicUpdateCAS space t arr old bucket x do_op = do+  -- Code generation target:+  --+  -- old = d_his[idx];+  -- do {+  --   assumed = old;+  --   x = do_op(assumed, y);+  --   old = atomicCAS(&d_his[idx], assumed, tmp);+  -- } while(assumed != old);+  assumed <- tvVar <$> dPrim "assumed" t+  run_loop <- dPrimV "run_loop" true++  -- XXX: CUDA may generate really bad code if this is not a volatile+  -- read.  Unclear why.  The later reads are volatile, so maybe+  -- that's it.+  everythingVolatile $ copyDWIMFix old [] (Var arr) bucket++  (arr', _a_space, bucket_offset) <- fullyIndexArray arr bucket++  -- While-loop: Try to insert your value+  let (toBits, fromBits) =+        case t of+          FloatType Float16 ->+            ( \v -> Imp.FunExp "to_bits16" [v] int16,+              \v -> Imp.FunExp "from_bits16" [v] t+            )+          FloatType Float32 ->+            ( \v -> Imp.FunExp "to_bits32" [v] int32,+              \v -> Imp.FunExp "from_bits32" [v] t+            )+          FloatType Float64 ->+            ( \v -> Imp.FunExp "to_bits64" [v] int64,+              \v -> Imp.FunExp "from_bits64" [v] t+            )+          _ -> (id, id)++      int+        | primBitSize t == 16 = int16+        | primBitSize t == 32 = int32+        | otherwise = int64++  sWhile (tvExp run_loop) $ do+    assumed <~~ Imp.var old t+    x <~~ Imp.var assumed t+    do_op+    old_bits_v <- newVName "old_bits"+    dPrim_ old_bits_v int+    let old_bits = Imp.var old_bits_v int+    sOp . Imp.Atomic space $+      Imp.AtomicCmpXchg+        int+        old_bits_v+        arr'+        bucket_offset+        (toBits (Imp.var assumed t))+        (toBits (Imp.var x t))+    old <~~ fromBits old_bits+    let won = CmpOpExp (CmpEq int) (toBits (Imp.var assumed t)) old_bits+    sWhen (isBool won) (run_loop <-- false)++computeKernelUses ::+  FreeIn a =>+  a ->+  [VName] ->+  CallKernelGen [Imp.KernelUse]+computeKernelUses kernel_body bound_in_kernel = do+  let actually_free = freeIn kernel_body `namesSubtract` namesFromList bound_in_kernel+  -- Compute the variables that we need to pass to the kernel.+  nubOrd <$> readsFromSet actually_free++readsFromSet :: Names -> CallKernelGen [Imp.KernelUse]+readsFromSet = fmap catMaybes . mapM f . namesToList+  where+    f var = do+      t <- lookupType var+      vtable <- getVTable+      case t of+        Array {} -> pure Nothing+        Acc {} -> pure Nothing+        Mem (Space "local") -> pure Nothing+        Mem {} -> pure $ Just $ Imp.MemoryUse var+        Prim bt ->+          isConstExp vtable (Imp.var var bt) >>= \case+            Just ce -> pure $ Just $ Imp.ConstUse var ce+            Nothing -> pure $ Just $ Imp.ScalarUse var bt++isConstExp ::+  VTable GPUMem ->+  Imp.Exp ->+  ImpM rep r op (Maybe Imp.KernelConstExp)+isConstExp vtable size = do+  fname <- askFunction+  let onLeaf name _ = lookupConstExp name+      lookupConstExp name =+        constExp =<< hasExp =<< M.lookup name vtable+      constExp (Op (Inner (SizeOp (GetSize key _)))) =+        Just $ LeafExp (Imp.SizeConst $ keyWithEntryPoint fname key) int32+      constExp e = primExpFromExp lookupConstExp e+  pure $ replaceInPrimExpM onLeaf size+  where+    hasExp (ArrayVar e _) = e+    hasExp (AccVar e _) = e+    hasExp (ScalarVar e _) = e+    hasExp (MemVar e _) = e++kernelInitialisationSimple ::+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  CallKernelGen (KernelConstants, InKernelGen ())+kernelInitialisationSimple num_groups group_size = do+  global_tid <- newVName "global_tid"+  local_tid <- newVName "local_tid"+  group_id <- newVName "group_tid"+  wave_size <- newVName "wave_size"+  inner_group_size <- newVName "group_size"+  let num_groups' = Imp.pe64 (unCount num_groups)+      group_size' = Imp.pe64 (unCount group_size)+      constants =+        KernelConstants+          { kernelGlobalThreadId = Imp.le32 global_tid,+            kernelLocalThreadId = Imp.le32 local_tid,+            kernelGroupId = Imp.le32 group_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'),+            kernelWaveSize = Imp.le32 wave_size,+            kernelLocalIdMap = mempty,+            kernelChunkItersMap = mempty+          }++  let set_constants = do+        dPrim_ local_tid int32+        dPrim_ inner_group_size int64+        dPrim_ wave_size int32+        dPrim_ group_id int32++        sOp (Imp.GetLocalId local_tid 0)+        sOp (Imp.GetLocalSize inner_group_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++  pure (constants, set_constants)++isActive :: [(VName, SubExp)] -> Imp.TExp Bool+isActive limit = case actives of+  [] -> true+  x : xs -> foldl (.&&.) x xs+  where+    (is, ws) = unzip limit+    actives = zipWith active is $ map pe64 ws+    active i = (Imp.le64 i .<.)++-- | Change every memory block to be in the global address space,+-- except those who are in the local memory space.  This only affects+-- generated code - we still need to make sure that the memory is+-- actually present on the device (and declared as variables in the+-- kernel).+makeAllMemoryGlobal :: CallKernelGen a -> CallKernelGen a+makeAllMemoryGlobal =+  localDefaultSpace (Imp.Space "global") . localVTable (M.map globalMemory)+  where+    globalMemory (MemVar _ entry)+      | entryMemSpace entry /= Space "local" =+          MemVar Nothing entry {entryMemSpace = Imp.Space "global"}+    globalMemory entry =+      entry++simpleKernelGroups ::+  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+  fname <- askFunction+  let group_size_key = keyWithEntryPoint fname $ nameFromString $ pretty $ 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)++simpleKernelConstants ::+  Imp.TExp Int64 ->+  String ->+  CallKernelGen+    ( (Imp.TExp Int64 -> InKernelGen ()) -> InKernelGen (),+      KernelConstants+    )+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+  -- GPU will possibly need.  Feel free to come back and laugh at me+  -- in the future.+  let max_num_groups = 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++      constants =+        KernelConstants+          { kernelGlobalThreadId = Imp.le32 thread_gtid,+            kernelLocalThreadId = Imp.le32 thread_ltid,+            kernelGroupId = Imp.le32 group_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'),+            kernelWaveSize = 0,+            kernelLocalIdMap = mempty,+            kernelChunkItersMap = mempty+          }++      wrapKernel m = do+        dPrim_ thread_ltid int32+        dPrim_ inner_group_size int64+        dPrim_ group_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+          global_tid <-+            dPrimVE "global_tid" $+              sExt64 virt_group_id * sExt64 (le32 inner_group_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+-- double duty; we put an outer loop to accomplish this.  The+-- advantage over just launching a bazillion threads is that the cost+-- of memory expansion should be proportional to the number of+-- *physical* threads (hardware parallelism), not the amount of+-- application parallelism.+virtualiseGroups ::+  SegVirt ->+  Imp.TExp Int32 ->+  (Imp.TExp Int32 -> InKernelGen ()) ->+  InKernelGen ()+virtualiseGroups SegVirt required_groups m = do+  constants <- kernelConstants <$> askEnv+  phys_group_id <- dPrim "phys_group_id" int32+  sOp $ Imp.GetGroupId (tvVar phys_group_id) 0+  iterations <-+    dPrimVE "iterations" $+      (required_groups - tvExp phys_group_id) `divUp` sExt32 (kernelNumGroups constants)++  sFor "i" iterations $ \i -> do+    m . tvExp+      =<< dPrimV+        "virt_group_id"+        (tvExp phys_group_id + i * sExt32 (kernelNumGroups constants))+    -- Make sure the virtual group is actually done before we let+    -- another virtual group have its way with it.+    sOp $ Imp.Barrier Imp.FenceGlobal+virtualiseGroups _ _ m = do+  gid <- kernelGroupIdVar . kernelConstants <$> askEnv+  m $ Imp.le32 gid++-- | Various extra configuration of the kernel being generated.+data KernelAttrs = KernelAttrs+  { -- | 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+  }++-- | The default kernel attributes.+defKernelAttrs ::+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  KernelAttrs+defKernelAttrs num_groups group_size =+  KernelAttrs+    { kAttrFailureTolerant = False,+      kAttrCheckLocalMemory = True,+      kAttrNumGroups = num_groups,+      kAttrGroupSize = group_size+    }++sKernel ::+  Operations GPUMem KernelEnv Imp.KernelOp ->+  (KernelConstants -> Imp.TExp Int32) ->+  String ->+  VName ->+  KernelAttrs ->+  InKernelGen () ->+  CallKernelGen ()+sKernel ops flatf name v attrs f = do+  (constants, set_constants) <-+    kernelInitialisationSimple (kAttrNumGroups attrs) (kAttrGroupSize attrs)+  name' <- nameForFun $ name ++ "_" ++ show (baseTag v)+  sKernelOp attrs constants ops name' $ do+    set_constants+    dPrimV_ v $ flatf constants+    f++sKernelThread ::+  String ->+  VName ->+  KernelAttrs ->+  InKernelGen () ->+  CallKernelGen ()+sKernelThread = sKernel threadOperations kernelGlobalThreadId++sKernelOp ::+  KernelAttrs ->+  KernelConstants ->+  Operations GPUMem KernelEnv Imp.KernelOp ->+  Name ->+  InKernelGen () ->+  CallKernelGen ()+sKernelOp attrs constants ops name m = do+  HostEnv atomics _ locks <- askEnv+  body <- makeAllMemoryGlobal $ subImpM_ (KernelEnv atomics constants locks) ops m+  uses <- computeKernelUses body mempty+  emit . Imp.Op . Imp.CallKernel $+    Imp.Kernel+      { Imp.kernelBody = body,+        Imp.kernelUses = uses,+        Imp.kernelNumGroups = [untyped $ kernelNumGroups constants],+        Imp.kernelGroupSize = [untyped $ kernelGroupSize constants],+        Imp.kernelName = name,+        Imp.kernelFailureTolerant = kAttrFailureTolerant attrs,+        Imp.kernelCheckLocalMemory = kAttrCheckLocalMemory attrs+      }++sKernelFailureTolerant ::+  Bool ->+  Operations GPUMem KernelEnv Imp.KernelOp ->+  KernelConstants ->+  Name ->+  InKernelGen () ->+  CallKernelGen ()+sKernelFailureTolerant tol ops constants name m = do+  sKernelOp attrs constants ops name m+  where+    attrs =+      ( defKernelAttrs+          (kernelNumGroupsCount constants)+          (kernelGroupSizeCount constants)+      )+        { kAttrFailureTolerant = tol+        }++threadOperations :: Operations GPUMem KernelEnv Imp.KernelOp+threadOperations =+  (defaultOperations compileThreadOp)+    { opsCopyCompiler = copyElementWise,+      opsExpCompiler = compileThreadExp,+      opsStmsCompiler = \_ -> defCompileStms mempty,+      opsAllocCompilers =+        M.fromList [(Space "local", allocLocal)]+    }++-- | Perform a Replicate with a kernel.+sReplicateKernel :: VName -> SubExp -> CallKernelGen ()+sReplicateKernel arr se = do+  t <- subExpType se+  ds <- dropLast (arrayRank t) . arrayDims <$> lookupType arr++  let dims = map pe64 $ ds ++ arrayDims t+  n <- dPrimVE "replicate_n" $ product $ map sExt64 dims+  (virtualise, constants) <- simpleKernelConstants n "replicate"++  fname <- askFunction+  let name =+        keyWithEntryPoint fname $+          nameFromString $+            "replicate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)++  sKernelFailureTolerant True threadOperations constants name $+    virtualise $ \gtid -> do+      is' <- dIndexSpace' "rep_i" dims gtid+      sWhen (gtid .<. n) $+        copyDWIMFix arr is' se $+          drop (length ds) is'++replicateName :: PrimType -> String+replicateName bt = "replicate_" ++ pretty bt++replicateForType :: PrimType -> CallKernelGen Name+replicateForType bt = do+  let fname = nameFromString $ "builtin#" <> replicateName bt++  exists <- hasFunction fname+  unless exists $ do+    mem <- newVName "mem"+    num_elems <- newVName "num_elems"+    val <- newVName "val"++    let params =+          [ Imp.MemParam mem (Space "device"),+            Imp.ScalarParam num_elems int64,+            Imp.ScalarParam val bt+          ]+        shape = Shape [Var num_elems]+    function fname [] params $ do+      arr <-+        sArray "arr" bt shape mem $ IxFun.iota $ map pe64 $ shapeDims shape+      sReplicateKernel arr $ Var val++  pure fname++replicateIsFill :: VName -> SubExp -> CallKernelGen (Maybe (CallKernelGen ()))+replicateIsFill arr v = do+  ArrayEntry (MemLoc arr_mem arr_shape arr_ixfun) _ <- lookupArray arr+  v_t <- subExpType v+  case v_t of+    Prim v_t'+      | IxFun.isLinear arr_ixfun -> pure $+          Just $ do+            fname <- replicateForType v_t'+            emit $+              Imp.Call+                []+                fname+                [ Imp.MemArg arr_mem,+                  Imp.ExpArg $ untyped $ product $ map pe64 arr_shape,+                  Imp.ExpArg $ toExp' v_t' v+                ]+    _ -> pure Nothing++-- | Perform a Replicate with a kernel.+sReplicate :: VName -> SubExp -> CallKernelGen ()+sReplicate arr se = do+  -- If the replicate is of a particularly common and simple form+  -- (morally a memset()/fill), then we use a common function.+  is_fill <- replicateIsFill arr se++  case is_fill of+    Just m -> m+    Nothing -> sReplicateKernel arr se++-- | Perform an Iota with a kernel.+sIotaKernel ::+  VName ->+  Imp.TExp Int64 ->+  Imp.Exp ->+  Imp.Exp ->+  IntType ->+  CallKernelGen ()+sIotaKernel arr n x s et = do+  destloc <- entryArrayLoc <$> lookupArray arr+  (virtualise, constants) <- simpleKernelConstants n "iota"++  fname <- askFunction+  let name =+        keyWithEntryPoint fname $+          nameFromString $+            "iota_"+              ++ pretty et+              ++ "_"+              ++ show (baseTag $ kernelGlobalThreadIdVar constants)++  sKernelFailureTolerant True threadOperations constants name $+    virtualise $ \gtid ->+      sWhen (gtid .<. n) $ do+        (destmem, destspace, destidx) <- fullyIndexArray' destloc [gtid]++        emit $+          Imp.Write destmem destidx (IntType et) destspace Imp.Nonvolatile $+            BinOpExp+              (Add et OverflowWrap)+              (BinOpExp (Mul et OverflowWrap) (Imp.sExt et $ untyped gtid) s)+              x++iotaName :: IntType -> String+iotaName bt = "iota_" ++ pretty bt++iotaForType :: IntType -> CallKernelGen Name+iotaForType bt = do+  let fname = nameFromString $ "builtin#" <> iotaName bt++  exists <- hasFunction fname+  unless exists $ do+    mem <- newVName "mem"+    n <- newVName "n"+    x <- newVName "x"+    s <- newVName "s"++    let params =+          [ Imp.MemParam mem (Space "device"),+            Imp.ScalarParam n int32,+            Imp.ScalarParam x $ IntType bt,+            Imp.ScalarParam s $ IntType bt+          ]+        shape = Shape [Var n]+        n' = Imp.le64 n+        x' = Imp.var x $ IntType bt+        s' = Imp.var s $ IntType bt++    function fname [] params $ do+      arr <-+        sArray "arr" (IntType bt) shape mem $+          IxFun.iota $+            map pe64 $+              shapeDims shape+      sIotaKernel arr (sExt64 n') x' s' bt++  pure fname++-- | Perform an Iota with a kernel.+sIota ::+  VName ->+  Imp.TExp Int64 ->+  Imp.Exp ->+  Imp.Exp ->+  IntType ->+  CallKernelGen ()+sIota arr n x s et = do+  ArrayEntry (MemLoc arr_mem _ arr_ixfun) _ <- lookupArray arr+  if IxFun.isLinear arr_ixfun+    then do+      fname <- iotaForType et+      emit $+        Imp.Call+          []+          fname+          [Imp.MemArg arr_mem, Imp.ExpArg $ untyped n, Imp.ExpArg x, Imp.ExpArg s]+    else sIotaKernel arr n x s et++sCopy :: CopyCompiler GPUMem HostEnv Imp.HostOp+sCopy pt destloc@(MemLoc destmem _ _) srcloc@(MemLoc srcmem srcdims _) = do+  -- Note that the shape of the destination and the source are+  -- necessarily the same.+  let shape = map pe64 srcdims+      kernel_size = product shape++  (virtualise, constants) <- simpleKernelConstants kernel_size "copy"++  fname <- askFunction+  let name =+        keyWithEntryPoint fname $+          nameFromString $+            "copy_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)++  sKernelFailureTolerant True threadOperations constants name $+    virtualise $ \gtid -> do+      is <- dIndexSpace' "copy_i" shape gtid++      (_, destspace, destidx) <- fullyIndexArray' destloc is+      (_, srcspace, srcidx) <- fullyIndexArray' srcloc is++      sWhen (gtid .<. kernel_size) $ do+        tmp <- tvVar <$> dPrim "tmp" pt+        emit $ Imp.Read tmp srcmem srcidx pt srcspace Imp.Nonvolatile+        emit $ Imp.Write destmem destidx pt destspace Imp.Nonvolatile $ Imp.var tmp pt++-- | Perform a Rotate with a kernel.+sRotateKernel :: VName -> [Imp.TExp Int64] -> VName -> CallKernelGen ()+sRotateKernel dest rs src = do+  t <- lookupType src+  let ds = map pe64 $ arrayDims t+  n <- dPrimVE "rotate_n" $ product ds+  (virtualise, constants) <- simpleKernelConstants n "rotate"++  fname <- askFunction+  let name =+        keyWithEntryPoint fname $+          nameFromString $+            "rotate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)++  sKernelFailureTolerant True threadOperations constants name $+    virtualise $ \gtid -> sWhen (gtid .<. n) $ do+      is' <- dIndexSpace' "rep_i" ds gtid+      is'' <- sequence $ zipWith3 rotate ds rs is'+      copyDWIMFix dest is' (Var src) is''+  where+    rotate d r i = dPrimVE "rot_i" $ rotateIndex d r i++compileThreadResult ::+  SegSpace ->+  PatElem LetDecMem ->+  KernelResult ->+  InKernelGen ()+compileThreadResult _ _ RegTileReturns {} =+  compilerLimitationS "compileThreadResult: RegTileReturns not yet handled."+compileThreadResult space pe (Returns _ _ what) = do+  let is = map (Imp.le64 . fst) $ unSegSpace space+  copyDWIMFix (patElemName pe) is what []+compileThreadResult _ pe (WriteReturns _ (Shape rws) _arr dests) = do+  let rws' = map pe64 rws+  forM_ dests $ \(slice, e) -> do+    let slice' = fmap pe64 slice+        write = inBounds slice' rws'+    sWhen write $ copyDWIM (patElemName pe) (unSlice slice') e []+compileThreadResult _ _ TileReturns {} =+  compilerBugS "compileThreadResult: TileReturns unhandled."
+ src/Futhark/CodeGen/ImpGen/GPU/Group.hs view
@@ -0,0 +1,735 @@+{-# LANGUAGE FlexibleContexts #-}+{-# 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.Except+import Data.Bifunctor+import Data.List (partition, zip4)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import qualified Futhark.CodeGen.ImpCode.GPU as Imp+import Futhark.CodeGen.ImpGen+import Futhark.CodeGen.ImpGen.GPU.Base+import Futhark.Construct (fullSliceNum)+import Futhark.Error+import Futhark.IR.GPUMem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+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) $+    IxFun.reshape (memLocIxFun 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+    $ IxFun.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+          (zip arrs $ repeat [DimFix $ sExt64 chunk_start])+          ( zip (map Var arrs) (repeat [DimFix carry_idx])+              ++ zip (map Var arrs) (repeat [DimFix $ sExt64 chunk_start])+          )++    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_ixfun = memLocIxFun srcloc+      dims = IxFun.shape src_ixfun+      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)+      copyElementWise+        pt+        (sliceMemLoc destloc destslice')+        (sliceMemLoc srcloc srcslice')+    _ -> do+      groupCoverSpace (map sExt32 dims) $ \is ->+        copyElementWise+          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 ..])++sanityCheckLevel :: SegLevel -> InKernelGen ()+sanityCheckLevel SegThread {} = pure ()+sanityCheckLevel SegGroup {} =+  error "compileGroupOp: unexpected group-level SegOp."++compileFlatId :: SegLevel -> SegSpace -> InKernelGen ()+compileFlatId lvl space = do+  sanityCheckLevel lvl+  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+  dPrimV_ (segFlat space) ltid++-- Construct the necessary lock arrays for an intra-group histogram.+prepareIntraGroupSegHist ::+  Count GroupSize SubExp ->+  [HistOp GPUMem] ->+  InKernelGen [[Imp.TExp Int64] -> InKernelGen ()]+prepareIntraGroupSegHist group_size =+  fmap snd . mapAccumLM onOp Nothing+  where+    onOp l op = do+      constants <- kernelConstants <$> askEnv+      atomicBinOp <- kernelAtomics <$> askEnv++      let local_subhistos = histDest op++      case (l, atomicUpdateLocking atomicBinOp $ histOp op) of+        (_, AtomicPrim f) -> 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 (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 $+            IxFun.iota . 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 -> 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)) =+  updateAcc acc is vs+compileGroupExp (Pat [dest]) (BasicOp (Replicate ds se)) = do+  flat <- newVName "rep_flat"+  is <- replicateM (shapeRank ds) (newVName "rep_i")+  let is' = map le64 is+  groupCoverSegSpace SegVirt (SegSpace flat $ zip is $ shapeDims ds) $+    copyDWIMFix (patElemName dest) is' se []+  sOp $ Imp.Barrier Imp.FenceLocal+compileGroupExp (Pat [dest]) (BasicOp (Rotate rs arr)) = do+  ds <- map pe64 . arrayDims <$> lookupType arr+  groupCoverSpace ds $ \is -> do+    is' <- sequence $ zipWith3 rotate ds rs is+    copyDWIMFix (patElemName dest) is (Var arr) is'+  sOp $ Imp.Barrier Imp.FenceLocal+  where+    rotate d r i = dPrimVE "rot_i" $ rotateIndex d (pe64 r) i+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 =+  defCompileExp dest e++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 lvl 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 lvl 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 lvl 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)+                (zip tmps $ repeat [DimFix $ sExt64 chunk_start])+                ( zip (map (Var . patElemName) red_pes) (repeat [])+                    ++ zip (map Var tmps) (repeat [DimFix $ sExt64 chunk_start])+                )++        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++        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++      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++    nonvirtCase [dim'] tmps_for_ops = do+      -- Nonsegmented case (or rather, a single segment) - this we can+      -- handle directly with a group-level reduction.+      forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->+        groupReduce (sExt32 dim') (segBinOpLambda op) tmps+      sOp $ Imp.ErrorSync Imp.FenceLocal+      forM_ (zip red_pes $ concat tmps_for_ops) $ \(pe, arr) ->+        copyDWIMFix (patElemName pe) [] (Var arr) [0]+    --+    nonvirtCase dims' tmps_for_ops = do+      -- Segmented intra-group reductions are turned into (regular)+      -- segmented scans.  It is possible that this can be done+      -- better, but at least this approach is simple.++      -- groupScan operates on flattened arrays.  This does not+      -- involve copying anything; merely playing with the index+      -- function.+      dims_flat <- dPrimV "dims_flat" $ product dims'+      let 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++      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 lvl 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++  ops' <- prepareIntraGroupSegHist (segGroupSize lvl) 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 (map 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 " ++ pretty 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 (onExp . stmExp)+    onExp (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+    onExp (BasicOp (Replicate shape _)) =+      S.singleton $ shapeDims shape+    onExp (Match _ cases defbody _) =+      foldMap (onStms . bodyStms . caseBody) cases <> onStms (bodyStms defbody)+    onExp (DoLoop _ _ body) =+      onStms (bodyStms body)+    onExp _ = 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/SegMap.hs view
@@ -11,6 +11,7 @@ import qualified Futhark.CodeGen.ImpCode.GPU as Imp import Futhark.CodeGen.ImpGen import Futhark.CodeGen.ImpGen.GPU.Base+import Futhark.CodeGen.ImpGen.GPU.Group import Futhark.IR.GPUMem import Futhark.Util.IntegralExp (divUp) import Prelude hiding (quot, rem)
src/Futhark/CodeGen/ImpGen/GPU/SegRed.hs view
@@ -224,7 +224,7 @@         num_elements         global_tid         elems_per_thread-        (tvVar num_threads)+        (tvExp num_threads)         slugs         body @@ -470,7 +470,7 @@           num_elements           global_tid           elems_per_thread-          (tvVar threads_per_segment)+          (tvExp threads_per_segment)           slugs           body @@ -576,13 +576,53 @@       | otherwise =           pure (param_arr, [sExt64 local_tid, sExt64 group_id]) +computeThreadChunkSize ::+  Commutativity ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  TV Int64 ->+  ImpM rep r op ()+computeThreadChunkSize Commutative threads_per_segment thread_index elements_per_thread num_elements chunk_var =+  chunk_var+    <-- sMin64+      (Imp.unCount elements_per_thread)+      ((Imp.unCount num_elements - thread_index) `divUp` threads_per_segment)+computeThreadChunkSize Noncommutative _ thread_index elements_per_thread num_elements chunk_var = do+  starting_point <-+    dPrimV "starting_point" $+      thread_index * Imp.unCount elements_per_thread+  remaining_elements <-+    dPrimV "remaining_elements" $+      Imp.unCount num_elements - tvExp starting_point++  let no_remaining_elements = tvExp remaining_elements .<=. 0+      beyond_bounds = Imp.unCount num_elements .<=. tvExp starting_point++  sIf+    (no_remaining_elements .||. beyond_bounds)+    (chunk_var <-- 0)+    ( sIf+        is_last_thread+        (chunk_var <-- Imp.unCount last_thread_elements)+        (chunk_var <-- Imp.unCount elements_per_thread)+    )+  where+    last_thread_elements =+      num_elements - Imp.elements thread_index * elements_per_thread+    is_last_thread =+      Imp.unCount num_elements+        .<. (thread_index + 1)+          * Imp.unCount elements_per_thread+ reductionStageZero ::   KernelConstants ->   [(VName, Imp.TExp Int64)] ->   Imp.Count Imp.Elements (Imp.TExp Int64) ->   Imp.TExp Int64 ->   Imp.Count Imp.Elements (Imp.TExp Int64) ->-  VName ->+  Imp.TExp Int64 ->   [SegBinOpSlug] ->   DoSegBody ->   InKernelGen ([Lambda GPUMem], InKernelGen ())@@ -593,10 +633,13 @@    -- Figure out how many elements this thread should process.   chunk_size <- dPrim "chunk_size" int64-  let ordering = case slugsComm slugs of-        Commutative -> SplitStrided $ Var threads_per_segment-        Noncommutative -> SplitContiguous-  computeThreadChunkSize ordering (sExt64 global_tid) elems_per_thread num_elements chunk_size+  computeThreadChunkSize+    (slugsComm slugs)+    threads_per_segment+    (sExt64 global_tid)+    elems_per_thread+    num_elements+    chunk_size    dScope Nothing $ scopeOfLParams $ concatMap slugParams slugs @@ -645,7 +688,7 @@     gtid       <-- case comm of         Commutative ->-          global_tid + Imp.le64 threads_per_segment * i+          global_tid + threads_per_segment * i         Noncommutative ->           let index_in_segment = global_tid `quot` kernelGroupSize constants            in sExt64 local_tid@@ -696,7 +739,7 @@   Imp.Count Imp.Elements (Imp.TExp Int64) ->   Imp.TExp Int64 ->   Imp.Count Imp.Elements (Imp.TExp Int64) ->-  VName ->+  Imp.TExp Int64 ->   [SegBinOpSlug] ->   DoSegBody ->   InKernelGen [Lambda GPUMem]
src/Futhark/CodeGen/ImpGen/GPU/ToOpenCL.hs view
@@ -24,7 +24,6 @@ import qualified Futhark.CodeGen.ImpCode.OpenCL as ImpOpenCL import Futhark.CodeGen.RTS.C (atomicsH, halfH) import Futhark.Error (compilerLimitationS)-import Futhark.IR.Prop (isBuiltInFunction) import Futhark.MonadFreshNames import Futhark.Util (zEncodeString) import Futhark.Util.Pretty (prettyOneLine, prettyText)@@ -787,20 +786,17 @@               then [C.citems|return 1;|]               else [C.citems|return;|] -    callInKernel dests fname args-      | isBuiltInFunction fname =-          GC.opsCall GC.defaultOperations dests fname args-      | otherwise = do-          let out_args = [[C.cexp|&$id:d|] | d <- dests]-              args' =-                [C.cexp|global_failure|]-                  : [C.cexp|global_failure_args|]-                  : out_args-                  ++ args+    callInKernel dests fname args = do+      let out_args = [[C.cexp|&$id:d|] | d <- dests]+          args' =+            [C.cexp|global_failure|]+              : [C.cexp|global_failure_args|]+              : out_args+              ++ args -          what_next <- whatNext+      what_next <- whatNext -          GC.item [C.citem|if ($id:(funName fname)($args:args') != 0) { $items:what_next; }|]+      GC.item [C.citem|if ($id:(funName fname)($args:args') != 0) { $items:what_next; }|]      errorInKernel msg@(ErrorMsg parts) backtrace = do       n <- length . kernelFailures <$> GC.getUserState
src/Futhark/CodeGen/ImpGen/Multicore/Base.hs view
@@ -136,8 +136,6 @@ compileThreadResult space pe (Returns _ _ what) = do   let is = map (Imp.le64 . fst) $ unSegSpace space   copyDWIMFix (patElemName pe) is what []-compileThreadResult _ _ ConcatReturns {} =-  compilerBugS "compileThreadResult: ConcatReturn unhandled." compileThreadResult _ _ WriteReturns {} =   compilerBugS "compileThreadResult: WriteReturns unhandled." compileThreadResult _ _ TileReturns {} =
src/Futhark/Construct.hs view
@@ -84,8 +84,6 @@     eCopy,     eBody,     eLambda,-    eRoundToMultipleOf,-    eSliceArray,     eBlank,     eAll,     eAny,@@ -386,39 +384,6 @@   bodyBind $ lambdaBody lam   where     bindParam param arg = letBindNames [paramName param] =<< arg---- | @eRoundToMultipleOf t x d@ produces an expression that rounds the--- integer expression @x@ upwards to be a multiple of @d@, with @t@--- being the integer type of the expressions.-eRoundToMultipleOf ::-  MonadBuilder m =>-  IntType ->-  m (Exp (Rep m)) ->-  m (Exp (Rep m)) ->-  m (Exp (Rep m))-eRoundToMultipleOf t x d =-  ePlus x (eMod (eMinus d (eMod x d)) d)-  where-    eMod = eBinOp (SMod t Unsafe)-    eMinus = eBinOp (Sub t OverflowWrap)-    ePlus = eBinOp (Add t OverflowWrap)---- | Construct an 'Index' expressions that slices an array with unit stride.-eSliceArray ::-  MonadBuilder m =>-  Int ->-  VName ->-  m (Exp (Rep m)) ->-  m (Exp (Rep m)) ->-  m (Exp (Rep m))-eSliceArray d arr i n = do-  arr_t <- lookupType arr-  let skips = map (slice (constant (0 :: Int64))) $ take d $ arrayDims arr_t-  i' <- letSubExp "slice_i" =<< i-  n' <- letSubExp "slice_n" =<< n-  pure $ BasicOp $ Index arr $ fullSlice arr_t $ skips ++ [slice i' n']-  where-    slice j m = DimSlice j m (constant (1 :: Int64))  -- | @eInBoundsForDim w i@ produces @0 <= i < w@. eDimInBounds :: MonadBuilder m => m (Exp (Rep m)) -> m (Exp (Rep m)) -> m (Exp (Rep m))
src/Futhark/Doc/Generator.hs view
@@ -80,7 +80,7 @@  type DocM = ReaderT Context (WriterT Documented (Writer Warnings)) -data IndexWhat = IndexValue | IxFun | IndexModule | IndexModuleType | IndexType+data IndexWhat = IndexValue | IndexFunction | IndexModule | IndexModuleType | IndexType  -- | We keep a mapping of the names we have actually documented, so we -- can generate an index.@@ -302,7 +302,7 @@                 baseString name           what' = case what of             IndexValue -> "value"-            IxFun -> "function"+            IndexFunction -> "function"             IndexType -> "type"             IndexModuleType -> "module type"             IndexModule -> "module"@@ -849,7 +849,7 @@     orderZero t =       IndexValue   | otherwise =-      IxFun+      IndexFunction  describeSpecs :: [Spec] -> DocM Html describeSpecs specs =@@ -864,7 +864,7 @@   where     what =       case t of-        TEArrow {} -> IxFun+        TEArrow {} -> IndexFunction         _ -> IndexValue describeSpec (TypeAbbrSpec vb) =   describeGeneric (typeAlias vb) IndexType (typeDoc vb) (`typeBindHtml` vb)
src/Futhark/IR/GPU/Op.hs view
@@ -114,29 +114,7 @@  -- | A simple size-level query or computation. data SizeOp-  = -- | @SplitSpace o w i elems_per_thread@.-    ---    -- Computes how to divide array elements to-    -- threads in a kernel.  Returns the number of-    -- elements in the chunk that the current thread-    -- should take.-    ---    -- @w@ is the length of the outer dimension in-    -- the array. @i@ is the current thread-    -- index. Each thread takes at most-    -- @elems_per_thread@ elements.-    ---    -- If the order @o@ is 'SplitContiguous', thread with index @i@-    -- should receive elements-    -- @i*elems_per_tread, i*elems_per_thread + 1,-    -- ..., i*elems_per_thread + (elems_per_thread-1)@.-    ---    -- If the order @o@ is @'SplitStrided' stride@,-    -- the thread will receive elements @i,-    -- i+stride, i+2*stride, ...,-    -- i+(elems_per_thread-1)*stride@.-    SplitSpace SplitOrdering SubExp SubExp SubExp-  | -- | Produce some runtime-configurable size.+  = -- | Produce some runtime-configurable size.     GetSize Name SizeClass   | -- | The maximum size of some class.     GetSizeMax SizeClass@@ -150,12 +128,6 @@   deriving (Eq, Ord, Show)  instance Substitute SizeOp where-  substituteNames subst (SplitSpace o w i elems_per_thread) =-    SplitSpace-      (substituteNames subst o)-      (substituteNames subst w)-      (substituteNames subst i)-      (substituteNames subst elems_per_thread)   substituteNames substs (CmpSizeLe name sclass x) =     CmpSizeLe name sclass (substituteNames substs x)   substituteNames substs (CalcNumGroups w max_num_groups group_size) =@@ -166,12 +138,6 @@   substituteNames _ op = op  instance Rename SizeOp where-  rename (SplitSpace o w i elems_per_thread) =-    SplitSpace-      <$> rename o-      <*> rename w-      <*> rename i-      <*> rename elems_per_thread   rename (CmpSizeLe name sclass x) =     CmpSizeLe name sclass <$> rename x   rename (CalcNumGroups w max_num_groups group_size) =@@ -183,7 +149,6 @@   cheapOp _ = True  instance TypedOp SizeOp where-  opType SplitSpace {} = pure [Prim int64]   opType (GetSize _ _) = pure [Prim int64]   opType (GetSizeMax _) = pure [Prim int64]   opType CmpSizeLe {} = pure [Prim Bool]@@ -194,19 +159,11 @@   consumedInOp _ = mempty  instance FreeIn SizeOp where-  freeIn' (SplitSpace o w i elems_per_thread) =-    freeIn' o <> freeIn' [w, i, elems_per_thread]   freeIn' (CmpSizeLe _ _ x) = freeIn' x   freeIn' (CalcNumGroups w _ group_size) = freeIn' w <> freeIn' group_size   freeIn' _ = mempty  instance PP.Pretty SizeOp where-  ppr (SplitSpace SplitContiguous w i elems_per_thread) =-    text "split_space"-      <> parens (commasep [ppr w, ppr i, ppr elems_per_thread])-  ppr (SplitSpace (SplitStrided stride) w i elems_per_thread) =-    text "split_space_strided"-      <> parens (commasep [ppr stride, ppr w, ppr i, ppr elems_per_thread])   ppr (GetSize name size_class) =     text "get_size" <> parens (commasep [ppr name, ppr size_class])   ppr (GetSizeMax size_class) =@@ -219,18 +176,12 @@     text "calc_num_groups" <> parens (commasep [ppr w, ppr max_num_groups, ppr group_size])  instance OpMetrics SizeOp where-  opMetrics SplitSpace {} = seen "SplitSpace"   opMetrics GetSize {} = seen "GetSize"   opMetrics GetSizeMax {} = seen "GetSizeMax"   opMetrics CmpSizeLe {} = seen "CmpSizeLe"   opMetrics CalcNumGroups {} = seen "CalcNumGroups"  typeCheckSizeOp :: TC.Checkable rep => SizeOp -> TC.TypeM rep ()-typeCheckSizeOp (SplitSpace o w i elems_per_thread) = do-  case o of-    SplitContiguous -> pure ()-    SplitStrided stride -> TC.require [Prim int64] stride-  mapM_ (TC.require [Prim int64]) [w, i, elems_per_thread] typeCheckSizeOp GetSize {} = pure () typeCheckSizeOp GetSizeMax {} = pure () typeCheckSizeOp (CmpSizeLe _ _ x) = TC.require [Prim int64] x
src/Futhark/IR/GPU/Simplify.hs view
@@ -56,17 +56,6 @@ simplifyKernelOp _ (SegOp op) = do   (op', hoisted) <- simplifySegOp op   pure (SegOp op', hoisted)-simplifyKernelOp _ (SizeOp (SplitSpace o w i elems_per_thread)) =-  (,)-    <$> ( SizeOp-            <$> ( SplitSpace-                    <$> Engine.simplify o-                    <*> Engine.simplify w-                    <*> Engine.simplify i-                    <*> Engine.simplify elems_per_thread-                )-        )-    <*> pure mempty simplifyKernelOp _ (SizeOp (GetSize key size_class)) =   pure (SizeOp $ GetSize key size_class, mempty) simplifyKernelOp _ (SizeOp (GetSizeMax size_class)) =
src/Futhark/IR/Mem.hs view
@@ -1124,7 +1124,7 @@     & MemArray et (Shape (flatSliceDims slice)) NoUniqueness     & pure -class TypedOp op => OpReturns op where+class IsOp op => OpReturns op where   opReturns :: (Mem rep inner, Monad m, HasScope rep m) => op -> m [ExpReturns]   opReturns op = extReturns <$> opType op 
src/Futhark/IR/Mem/Simplify.hs view
@@ -19,6 +19,7 @@ import Futhark.Construct import Futhark.IR.Mem import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.IR.Prop.Aliases (AliasedOp) import qualified Futhark.Optimise.Simplify as Simplify import qualified Futhark.Optimise.Simplify.Engine as Engine import Futhark.Optimise.Simplify.Rep@@ -28,6 +29,20 @@ import Futhark.Pass.ExplicitAllocations (simplifiable) import Futhark.Util +-- | Some constraints that must hold for the simplification rules to work.+type SimplifyMemory rep inner =+  ( Simplify.SimplifiableRep rep,+    LetDec rep ~ LetDecMem,+    ExpDec rep ~ (),+    BodyDec rep ~ (),+    CanBeWise (Op rep),+    BuilderOps (Wise rep),+    OpReturns (OpWithWisdom inner),+    ST.IndexOp (OpWithWisdom inner),+    AliasedOp (OpWithWisdom inner),+    Mem rep inner+  )+ simpleGeneric ::   (SimplifyMemory rep inner) =>   (OpWithWisdom inner -> UT.UsageTable) ->@@ -92,18 +107,6 @@       Engine.blockHoistSeq = isResultAlloc,       Engine.isAllocation = isAlloc mempty mempty     }---- | Some constraints that must hold for the simplification rules to work.-type SimplifyMemory rep inner =-  ( Simplify.SimplifiableRep rep,-    LetDec rep ~ LetDecMem,-    ExpDec rep ~ (),-    BodyDec rep ~ (),-    CanBeWise (Op rep),-    BuilderOps (Wise rep),-    OpReturns (OpWithWisdom inner),-    Mem rep inner-  )  callKernelRules :: SimplifyMemory rep inner => RuleBook (Wise rep) callKernelRules =
src/Futhark/IR/Parse.hs view
@@ -736,28 +736,7 @@             <*> braces (pSubExp `sepBy` pComma)             <* pComma             <*> pLambda pr-    pStream =-      choice-        [ keyword "streamParComm" *> pStreamPar SOAC.InOrder Commutative,-          keyword "streamPar" *> pStreamPar SOAC.InOrder Noncommutative,-          keyword "streamParPerComm" *> pStreamPar SOAC.Disorder Commutative,-          keyword "streamParPer" *> pStreamPar SOAC.Disorder Noncommutative,-          keyword "streamSeq" *> pStreamSeq-        ]-    pStreamPar order comm =-      parens $-        SOAC.Stream-          <$> pSubExp-          <* pComma-          <*> braces (pVName `sepBy` pComma)-          <* pComma-          <*> pParForm order comm-          <* pComma-          <*> braces (pSubExp `sepBy` pComma)-          <* pComma-          <*> pLambda pr-    pParForm order comm =-      SOAC.Parallel order comm <$> pLambda pr+    pStream = keyword "streamSeq" *> pStreamSeq     pStreamSeq =       parens $         SOAC.Stream@@ -765,7 +744,6 @@           <* pComma           <*> braces (pVName `sepBy` pComma)           <* pComma-          <*> pure SOAC.Sequential           <*> braces (pSubExp `sepBy` pComma)           <* pComma           <*> pLambda pr@@ -832,26 +810,6 @@               <*> pName               <* pComma               <*> pSubExp-          ),-      keyword "split_space"-        *> parens-          ( GPU.SplitSpace GPU.SplitContiguous-              <$> pSubExp-              <* pComma-              <*> pSubExp-              <* pComma-              <*> pSubExp-          ),-      keyword "split_space_strided"-        *> parens-          ( GPU.SplitSpace-              <$> (GPU.SplitStrided <$> pSubExp)-              <* pComma-              <*> pSubExp-              <* pComma-              <*> pSubExp-              <* pComma-              <*> pSubExp           )     ] @@ -888,24 +846,6 @@         <*> pVName,       try "blkreg_tile"         *> parens (SegOp.RegTileReturns cs <$> (pRegTile `sepBy` pComma))-        <*> pVName,-      keyword "concat"-        *> parens-          ( SegOp.ConcatReturns cs SegOp.SplitContiguous-              <$> pSubExp-              <* pComma-              <*> pSubExp-          )-        <*> pVName,-      keyword "concat_strided"-        *> parens-          ( SegOp.ConcatReturns cs-              <$> (SegOp.SplitStrided <$> pSubExp)-              <* pComma-              <*> pSubExp-              <* pComma-              <*> pSubExp-          )         <*> pVName     ]   where
src/Futhark/IR/SOACS.hs view
@@ -57,7 +57,7 @@     lamUsesAD = bodyUsesAD . lambdaBody     expUsesAD (Op JVP {}) = True     expUsesAD (Op VJP {}) = True-    expUsesAD (Op (Stream _ _ _ _ lam)) = lamUsesAD lam+    expUsesAD (Op (Stream _ _ _ lam)) = lamUsesAD lam     expUsesAD (Op (Screma _ _ (ScremaForm scans reds lam))) =       lamUsesAD lam         || any (lamUsesAD . scanLambda) scans
src/Futhark/IR/SOACS/SOAC.hs view
@@ -9,8 +9,6 @@ -- the main form of parallelism in the early stages of the compiler. module Futhark.IR.SOACS.SOAC   ( SOAC (..),-    StreamOrd (..),-    StreamForm (..),     ScremaForm (..),     HistOp (..),     Scan (..),@@ -81,7 +79,7 @@  -- | A second-order array combinator (SOAC). data SOAC rep-  = Stream SubExp [VName] (StreamForm rep) [SubExp] (Lambda rep)+  = Stream SubExp [VName] [SubExp] (Lambda rep)   | -- | @Scatter <length> <inputs> <lambda> <outputs>@     --     -- Scatter maps values from a set of input arrays to indices and values of a@@ -145,19 +143,6 @@   }   deriving (Eq, Ord, Show) --- | Is the stream chunk required to correspond to a contiguous--- subsequence of the original input ('InOrder') or not?  'Disorder'--- streams can be more efficient, but not all algorithms work with--- this.-data StreamOrd = InOrder | Disorder-  deriving (Eq, Ord, Show)---- | What kind of stream is this?-data StreamForm rep-  = Parallel StreamOrd Commutativity (Lambda rep)-  | Sequential-  deriving (Eq, Ord, Show)- -- | The essential parts of a 'Screma' factored out (everything -- except the input arrays). data ScremaForm rep@@ -416,18 +401,12 @@     <$> mapOnSOACLambda tv lam     <*> mapM (mapOnSOACSubExp tv) args     <*> mapM (mapOnSOACSubExp tv) vec-mapSOACM tv (Stream size arrs form accs lam) =+mapSOACM tv (Stream size arrs accs lam) =   Stream     <$> mapOnSOACSubExp tv size     <*> mapM (mapOnSOACVName tv) arrs-    <*> mapOnStreamForm form     <*> mapM (mapOnSOACSubExp tv) accs     <*> mapOnSOACLambda tv lam-  where-    mapOnStreamForm (Parallel o comm lam0) =-      Parallel o comm <$> mapOnSOACLambda tv lam0-    mapOnStreamForm Sequential =-      pure Sequential mapSOACM tv (Scatter w ivs lam as) =   Scatter     <$> mapOnSOACSubExp tv w@@ -494,10 +473,6 @@   freeIn' (ScremaForm scans reds lam) =     freeIn' scans <> freeIn' reds <> freeIn' lam -instance ASTRep rep => FreeIn (StreamForm rep) where-  freeIn' Sequential = mempty-  freeIn' (Parallel _ _ lam) = freeIn' lam- instance ASTRep rep => FreeIn (HistOp rep) where   freeIn' (HistOp w rf dests nes lam) =     freeIn' w <> freeIn' rf <> freeIn' dests <> freeIn' nes <> freeIn' lam@@ -537,7 +512,7 @@ soacType (VJP lam _ _) =   lambdaReturnType lam     ++ map paramType (lambdaParams lam)-soacType (Stream outersize _ _ accs lam) =+soacType (Stream outersize _ accs lam) =   map (substNamesInType substs) rtp   where     nms = map paramName $ take (1 + length accs) params@@ -570,14 +545,8 @@     where       consumedArray v = fromMaybe v $ lookup v params_to_arrs       params_to_arrs = zip (map paramName $ lambdaParams map_lam) arrs-  consumedInOp (Stream _ arrs form accs lam) =-    namesFromList $-      subExpVars $-        case form of-          Sequential ->-            map consumedArray $ namesToList $ consumedByLambda lam-          Parallel {} ->-            map consumedArray $ namesToList $ consumedByLambda lam+  consumedInOp (Stream _ arrs accs lam) =+    namesFromList $ subExpVars $ map consumedArray $ namesToList $ consumedByLambda lam     where       consumedArray v = fromMaybe (Var v) $ lookup v paramsToInput       -- Drop the chunk parameter, which cannot alias anything.@@ -608,13 +577,8 @@     JVP (Alias.analyseLambda aliases lam) args vec   addOpAliases aliases (VJP lam args vec) =     VJP (Alias.analyseLambda aliases lam) args vec-  addOpAliases aliases (Stream size arr form accs lam) =-    Stream size arr (analyseStreamForm form) accs $-      Alias.analyseLambda aliases lam-    where-      analyseStreamForm (Parallel o comm lam0) =-        Parallel o comm (Alias.analyseLambda aliases lam0)-      analyseStreamForm Sequential = Sequential+  addOpAliases aliases (Stream size arr accs lam) =+    Stream size arr accs $ Alias.analyseLambda aliases lam   addOpAliases aliases (Scatter len arrs lam dests) =     Scatter len arrs (Alias.analyseLambda aliases lam) dests   addOpAliases aliases (Hist w arrs ops bucket_fun) =@@ -720,7 +684,7 @@         </> PP.indent 2 (ppr $ map TC.argType args')         </> "does not match type of seed vector"         </> PP.indent 2 (ppr vec_ts)-typeCheckSOAC (Stream size arrexps form accexps lam) = do+typeCheckSOAC (Stream size arrexps accexps lam) = do   TC.require [Prim int64] size   accargs <- mapM TC.checkArg accexps   arrargs <- mapM lookupType arrexps@@ -734,21 +698,6 @@   unless (map TC.argType accargs == lamrtp) $     TC.bad $       TC.TypeError "Stream with inconsistent accumulator type in lambda."-  -- check reduce's lambda, if any-  _ <- case form of-    Parallel _ _ lam0 -> do-      let acct = map TC.argType accargs-          outerRetType = lambdaReturnType lam0-      TC.checkLambda lam0 $ map TC.noArgAliases $ accargs ++ accargs-      unless (acct == outerRetType) $-        TC.bad $-          TC.TypeError $-            "Initial value is of type "-              ++ prettyTuple acct-              ++ ", but stream's reduce lambda returns type "-              ++ prettyTuple outerRetType-              ++ "."-    Sequential -> pure ()   -- just get the dflow of lambda on the fakearg, which does not alias   -- arr, so we can later check that aliases of arr are not used inside lam.   let fake_lamarrs' = map asArg lamarrs'@@ -901,7 +850,7 @@     inside "VJP" $ lambdaMetrics lam   opMetrics (JVP lam _ _) =     inside "JVP" $ lambdaMetrics lam-  opMetrics (Stream _ _ _ _ lam) =+  opMetrics (Stream _ _ _ lam) =     inside "Stream" $ lambdaMetrics lam   opMetrics (Scatter _len _ lam _) =     inside "Scatter" $ lambdaMetrics lam@@ -930,8 +879,8 @@               </> PP.braces (commasep $ map ppr args) <> comma               </> PP.braces (commasep $ map ppr vec)         )-  ppr (Stream size arrs form acc lam) =-    ppStream size arrs form acc lam+  ppr (Stream size arrs acc lam) =+    ppStream size arrs acc lam   ppr (Scatter w arrs lam dests) =     ppScatter w arrs lam dests   ppr (Hist w arrs ops bucket_fun) =@@ -978,30 +927,15 @@  -- | Prettyprint the given Stream. ppStream ::-  (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> StreamForm rep -> [SubExp] -> Lambda rep -> Doc-ppStream size arrs form acc lam =-  case form of-    Parallel o comm lam0 ->-      let ord_str = if o == Disorder then "Per" else ""-          comm_str = case comm of-            Commutative -> "Comm"-            Noncommutative -> ""-       in text ("streamPar" ++ ord_str ++ comm_str)-            <> parens-              ( ppr size <> comma-                  </> ppTuple' arrs <> comma-                  </> ppr lam0 <> comma-                  </> ppTuple' acc <> comma-                  </> ppr lam-              )-    Sequential ->-      text "streamSeq"-        <> parens-          ( ppr size <> comma-              </> ppTuple' arrs <> comma-              </> ppTuple' acc <> comma-              </> ppr lam-          )+  (PrettyRep rep, Pretty inp) => SubExp -> [inp] -> [SubExp] -> Lambda rep -> Doc+ppStream size arrs acc lam =+  text "streamSeq"+    <> parens+      ( ppr size <> comma+          </> ppTuple' arrs <> comma+          </> ppTuple' acc <> comma+          </> ppr lam+      )  -- | Prettyprint the given Scatter. ppScatter ::
src/Futhark/IR/SOACS/Simplify.hs view
@@ -96,22 +96,12 @@   arr' <- mapM Engine.simplify arr   vec' <- mapM Engine.simplify vec   pure (JVP lam' arr' vec', hoisted)-simplifySOAC (Stream outerdim arr form nes lam) = do+simplifySOAC (Stream outerdim arr nes lam) = do   outerdim' <- Engine.simplify outerdim-  (form', form_hoisted) <- simplifyStreamForm form   nes' <- mapM Engine.simplify nes   arr' <- mapM Engine.simplify arr   (lam', lam_hoisted) <- Engine.enterLoop $ Engine.simplifyLambda lam-  pure-    ( Stream outerdim' arr' form' nes' lam',-      form_hoisted <> lam_hoisted-    )-  where-    simplifyStreamForm (Parallel o comm lam0) = do-      (lam0', hoisted) <- Engine.simplifyLambda lam0-      pure (Parallel o comm lam0', hoisted)-    simplifyStreamForm Sequential =-      pure (Sequential, mempty)+  pure (Stream outerdim' arr' nes' lam', lam_hoisted) simplifySOAC (Scatter w ivs lam as) = do   w' <- Engine.simplify w   (lam', hoisted) <- Engine.enterLoop $ Engine.simplifyLambda lam@@ -317,7 +307,7 @@ liftIdentityMapping _ _ _ _ = Skip  liftIdentityStreaming :: BottomUpRuleOp (Wise SOACS)-liftIdentityStreaming _ (Pat pes) aux (Stream w arrs form nes lam)+liftIdentityStreaming _ (Pat pes) aux (Stream w arrs nes lam)   | (variant_map, invariant_map) <-       partitionEithers $ map isInvariantRes $ zip3 map_ts map_pes map_res,     not $ null invariant_map = Simplify $ do@@ -331,10 +321,8 @@                 lambdaReturnType = fold_ts ++ variant_map_ts               } -      auxing aux $-        letBind (Pat $ fold_pes ++ variant_map_pes) $-          Op $-            Stream w arrs form nes lam'+      auxing aux . letBind (Pat $ fold_pes ++ variant_map_pes) . Op $+        Stream w arrs nes lam'   where     num_folds = length nes     (fold_pes, map_pes) = splitAt num_folds pes@@ -680,7 +668,7 @@       zipWithM_ bindResult red_pes red_res       zipWithM_ bindArrayResult map_pes map_res simplifyKnownIterationSOAC _ pat _ op-  | Just (Stream (Constant k) arrs _ nes fold_lam) <- asSOAC op,+  | Just (Stream (Constant k) arrs nes fold_lam) <- asSOAC op,     oneIsh k = Simplify $ do       let (chunk_param, acc_params, slice_params) =             partitionChunkedFoldParameters (length nes) (lambdaParams fold_lam)
src/Futhark/IR/SegOp.hs view
@@ -36,7 +36,6 @@     KernelResult (..),     kernelResultCerts,     kernelResultSubExp,-    SplitOrdering (..),      -- ** Generic traversal     SegOpMapper (..),@@ -105,28 +104,6 @@ import qualified Futhark.Util.Pretty as PP import Prelude hiding (id, (.)) --- | How an array is split into chunks.-data SplitOrdering-  = SplitContiguous-  | SplitStrided SubExp-  deriving (Eq, Ord, Show)--instance FreeIn SplitOrdering where-  freeIn' SplitContiguous = mempty-  freeIn' (SplitStrided stride) = freeIn' stride--instance Substitute SplitOrdering where-  substituteNames _ SplitContiguous =-    SplitContiguous-  substituteNames subst (SplitStrided stride) =-    SplitStrided $ substituteNames subst stride--instance Rename SplitOrdering where-  rename SplitContiguous =-    pure SplitContiguous-  rename (SplitStrided stride) =-    SplitStrided <$> rename stride- -- | An operator for 'SegHist'. data HistOp rep = HistOp   { histShape :: Shape,@@ -227,13 +204,6 @@       Shape -- Size of array.  Must match number of dims.       VName -- Which array       [(Slice SubExp, SubExp)]-  | -- Arbitrary number of index/value pairs.-    ConcatReturns-      Certs-      SplitOrdering -- Permuted?-      SubExp -- The final size.-      SubExp -- Per-thread/group (max) chunk size.-      VName -- Chunk by this worker.   | TileReturns       Certs       [(SubExp, SubExp)] -- Total/tile for each dimension@@ -255,7 +225,6 @@ kernelResultCerts :: KernelResult -> Certs kernelResultCerts (Returns _ cs _) = cs kernelResultCerts (WriteReturns cs _ _ _) = cs-kernelResultCerts (ConcatReturns cs _ _ _ _) = cs kernelResultCerts (TileReturns cs _ _) = cs kernelResultCerts (RegTileReturns cs _ _) = cs @@ -263,15 +232,12 @@ kernelResultSubExp :: KernelResult -> SubExp kernelResultSubExp (Returns _ _ se) = se kernelResultSubExp (WriteReturns _ _ arr _) = Var arr-kernelResultSubExp (ConcatReturns _ _ _ _ v) = Var v kernelResultSubExp (TileReturns _ _ v) = Var v kernelResultSubExp (RegTileReturns _ _ v) = Var v  instance FreeIn KernelResult where   freeIn' (Returns _ cs what) = freeIn' cs <> freeIn' what   freeIn' (WriteReturns cs rws arr res) = freeIn' cs <> freeIn' rws <> freeIn' arr <> freeIn' res-  freeIn' (ConcatReturns cs o w per_thread_elems v) =-    freeIn' cs <> freeIn' o <> freeIn' w <> freeIn' per_thread_elems <> freeIn' v   freeIn' (TileReturns cs dims v) =     freeIn' cs <> freeIn' dims <> freeIn' v   freeIn' (RegTileReturns cs dims_n_tiles v) =@@ -299,13 +265,6 @@       (substituteNames subst rws)       (substituteNames subst arr)       (substituteNames subst res)-  substituteNames subst (ConcatReturns cs o w per_thread_elems v) =-    ConcatReturns-      (substituteNames subst cs)-      (substituteNames subst o)-      (substituteNames subst w)-      (substituteNames subst per_thread_elems)-      (substituteNames subst v)   substituteNames subst (TileReturns cs dims v) =     TileReturns       (substituteNames subst cs)@@ -412,18 +371,6 @@                 ++ pretty shape                 ++ ", but destination array has type "                 ++ pretty arr_t-    checkKernelResult (ConcatReturns cs o w per_thread_elems v) t = do-      TC.checkCerts cs-      case o of-        SplitContiguous -> pure ()-        SplitStrided stride -> TC.require [Prim int64] stride-      TC.require [Prim int64] w-      TC.require [Prim int64] per_thread_elems-      vt <- lookupType v-      unless (vt == t `arrayOfRow` arraySize 0 vt) $-        TC.bad $-          TC.TypeError $-            "Invalid type for ConcatReturns " ++ pretty v     checkKernelResult (TileReturns cs dims v) t = do       TC.checkCerts cs       forM_ dims $ \(dim, tile) -> do@@ -484,20 +431,6 @@            ]     where       ppRes (slice, e) = ppr slice <+> text "=" <+> ppr e-  ppr (ConcatReturns cs SplitContiguous w per_thread_elems v) =-    PP.spread $-      certAnnots cs-        ++ [ "concat"-               <> parens (commasep [ppr w, ppr per_thread_elems])-               <+> ppr v-           ]-  ppr (ConcatReturns cs (SplitStrided stride) w per_thread_elems v) =-    PP.spread $-      certAnnots cs-        ++ [ "concat_strided"-               <> parens (commasep [ppr stride, ppr w, ppr per_thread_elems])-               <+> ppr v-           ]   ppr (TileReturns cs dims v) =     PP.spread $ certAnnots cs ++ ["tile" <> parens (commasep $ map onDim dims) <+> ppr v]     where@@ -613,8 +546,6 @@   t `arrayOfShape` shape segResultShape space t Returns {} =   foldr (flip arrayOfRow) t $ segSpaceDims space-segResultShape _ t (ConcatReturns _ _ w _ _) =-  t `arrayOfRow` w segResultShape _ t (TileReturns _ dims _) =   t `arrayOfShape` Shape (map fst dims) segResultShape _ t (RegTileReturns _ dims_n_tiles _) =@@ -1123,12 +1054,6 @@  --- Simplification -instance Engine.Simplifiable SplitOrdering where-  simplify SplitContiguous =-    pure SplitContiguous-  simplify (SplitStrided stride) =-    SplitStrided <$> Engine.simplify stride- instance Engine.Simplifiable SegSpace where   simplify (SegSpace phys dims) =     SegSpace phys <$> mapM (traverse Engine.simplify) dims@@ -1142,13 +1067,6 @@       <*> Engine.simplify ws       <*> Engine.simplify a       <*> Engine.simplify res-  simplify (ConcatReturns cs o w pte what) =-    ConcatReturns-      <$> Engine.simplify cs-      <*> Engine.simplify o-      <*> Engine.simplify w-      <*> Engine.simplify pte-      <*> Engine.simplify what   simplify (TileReturns cs dims what) =     TileReturns <$> Engine.simplify cs <*> Engine.simplify dims <*> Engine.simplify what   simplify (RegTileReturns cs dims_n_tiles what) =
src/Futhark/Internalise/Exps.hs view
@@ -1165,82 +1165,6 @@   letValExp' desc . I.Op $     I.Hist w_img (buckets' ++ img') [HistOp shape_hist rf' hist' ne_shp op'] lam' -internaliseStreamMap ::-  String ->-  StreamOrd ->-  E.Exp ->-  E.Exp ->-  InternaliseM [SubExp]-internaliseStreamMap desc o lam arr = do-  arrs <- internaliseExpToVars "stream_input" arr-  lam' <- internaliseStreamMapLambda internaliseLambda lam $ map I.Var arrs-  w <- arraysSize 0 <$> mapM lookupType arrs-  let form = I.Parallel o Commutative (I.Lambda [] (mkBody mempty []) [])-  letValExp' desc $ I.Op $ I.Stream w arrs form [] lam'--internaliseStreamRed ::-  String ->-  StreamOrd ->-  Commutativity ->-  E.Exp ->-  E.Exp ->-  E.Exp ->-  InternaliseM [SubExp]-internaliseStreamRed desc o comm lam0 lam arr = do-  arrs <- internaliseExpToVars "stream_input" arr-  rowts <- mapM (fmap I.rowType . lookupType) arrs-  (lam_params, lam_body) <--    internaliseStreamLambda internaliseLambda lam rowts-  let (chunk_param, _, lam_val_params) =-        partitionChunkedFoldParameters 0 lam_params--  -- Synthesize neutral elements by applying the fold function-  -- to an empty chunk.-  letBindNames [I.paramName chunk_param] $-    I.BasicOp $-      I.SubExp $-        constant (0 :: Int64)-  forM_ lam_val_params $ \p ->-    letBindNames [I.paramName p] $-      I.BasicOp . I.Scratch (I.elemType $ I.paramType p) $-        I.arrayDims $-          I.paramType p-  nes <- bodyBind =<< renameBody lam_body--  nes_ts <- mapM I.subExpResType nes-  outsz <- arraysSize 0 <$> mapM lookupType arrs-  let acc_arr_tps = [I.arrayOf t (I.Shape [outsz]) NoUniqueness | t <- nes_ts]-  lam0' <- internaliseFoldLambda internaliseLambda lam0 nes_ts acc_arr_tps--  let lam0_acc_params = take (length nes) $ I.lambdaParams lam0'-  lam_acc_params <- forM lam0_acc_params $ \p -> do-    name <- newVName $ baseString $ I.paramName p-    pure p {I.paramName = name}--  -- Make sure the chunk size parameter comes first.-  let lam_params' = chunk_param : lam_acc_params ++ lam_val_params--  lam' <- mkLambda lam_params' $ do-    lam_res <- bodyBind lam_body-    lam_res' <--      ensureArgShapes-        "shape of chunk function result does not match shape of initial value"-        (srclocOf lam)-        []-        (map I.typeOf $ I.lambdaParams lam0')-        (map resSubExp lam_res)-    ensureResultShape-      "shape of result does not match shape of initial value"-      (srclocOf lam0)-      nes_ts-      =<< ( eLambda lam0' . map eSubExp $-              map (I.Var . paramName) lam_acc_params ++ lam_res'-          )--  let form = I.Parallel o comm lam0'-  w <- arraysSize 0 <$> mapM lookupType arrs-  letValExp' desc $ I.Op $ I.Stream w arrs form (map resSubExp nes) lam'- internaliseStreamAcc ::   String ->   E.Exp ->@@ -1694,14 +1618,6 @@       where         reduce w scan_lam nes arrs =           I.Screma w arrs <$> I.scanSOAC [Scan scan_lam nes]-    handleSOACs [TupLit [op, f, arr] _] "reduce_stream" = Just $ \desc ->-      internaliseStreamRed desc InOrder Noncommutative op f arr-    handleSOACs [TupLit [op, f, arr] _] "reduce_stream_per" = Just $ \desc ->-      internaliseStreamRed desc Disorder Commutative op f arr-    handleSOACs [TupLit [f, arr] _] "map_stream" = Just $ \desc ->-      internaliseStreamMap desc InOrder f arr-    handleSOACs [TupLit [f, arr] _] "map_stream_per" = Just $ \desc ->-      internaliseStreamMap desc Disorder f arr     handleSOACs [TupLit [rf, dest, op, ne, buckets, img] _] "hist_1d" = Just $ \desc ->       internaliseHist 1 desc rf dest op ne buckets img loc     handleSOACs [TupLit [rf, dest, op, ne, buckets, img] _] "hist_2d" = Just $ \desc ->
src/Futhark/Internalise/Lambdas.hs view
@@ -2,9 +2,7 @@  module Futhark.Internalise.Lambdas   ( InternaliseLambda,-    internaliseStreamMapLambda,     internaliseFoldLambda,-    internaliseStreamLambda,     internalisePartitionLambda,   ) where@@ -18,31 +16,6 @@ type InternaliseLambda =   E.Exp -> [I.Type] -> InternaliseM ([I.LParam SOACS], I.Body SOACS, [I.Type]) -internaliseStreamMapLambda ::-  InternaliseLambda ->-  E.Exp ->-  [I.SubExp] ->-  InternaliseM (I.Lambda SOACS)-internaliseStreamMapLambda internaliseLambda lam args = do-  chunk_size <- newVName "chunk_size"-  let chunk_param = I.Param mempty chunk_size (I.Prim int64)-      outer = (`setOuterSize` I.Var chunk_size)-  localScope (scopeOfLParams [chunk_param]) $ do-    argtypes <- mapM I.subExpType args-    (lam_params, orig_body, rettype) <--      internaliseLambda lam $ I.Prim int64 : map outer argtypes-    let orig_chunk_param : params = lam_params-    body <- runBodyBuilder $ do-      letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size-      pure orig_body-    mkLambda (chunk_param : params) $ do-      letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size-      ensureResultShape-        (ErrorMsg [ErrorString "not all iterations produce same shape"])-        (srclocOf lam)-        (map outer rettype)-        =<< bodyBind body- internaliseFoldLambda ::   InternaliseLambda ->   E.Exp ->@@ -64,24 +37,6 @@       (srclocOf lam)       rettype'       =<< bodyBind body--internaliseStreamLambda ::-  InternaliseLambda ->-  E.Exp ->-  [I.Type] ->-  InternaliseM ([LParam SOACS], Body SOACS)-internaliseStreamLambda internaliseLambda lam rowts = do-  chunk_size <- newVName "chunk_size"-  let chunk_param = I.Param mempty chunk_size $ I.Prim int64-      chunktypes = map (`arrayOfRow` I.Var chunk_size) rowts-  localScope (scopeOfLParams [chunk_param]) $ do-    (lam_params, orig_body, _) <--      internaliseLambda lam $ I.Prim int64 : chunktypes-    let orig_chunk_param : params = lam_params-    body <- runBodyBuilder $ do-      letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size-      pure orig_body-    pure (chunk_param : params, body)  -- Given @k@ lambdas, this will return a lambda that returns an -- (k+2)-element tuple of integers.  The first element is the
src/Futhark/Optimise/Fusion.hs view
@@ -414,7 +414,7 @@   SoacNode ots pat soac aux -> do     let lam = H.lambda soac     lam' <- localScope (scopeOf lam) $ case soac of-      H.Stream _ Sequential {} _ _ _ ->+      H.Stream {} ->         dontFuseScans $ doFusionLambda lam       _ ->         doFuseScans $ doFusionLambda lam
src/Futhark/Optimise/Fusion/Composing.hs view
@@ -13,7 +13,6 @@ module Futhark.Optimise.Fusion.Composing   ( fuseMaps,     fuseRedomap,-    mergeReduceOps,   ) where @@ -279,14 +278,3 @@                   ++ extra_map_ts             }      in (res_lam', new_inp)--mergeReduceOps :: Lambda rep -> Lambda rep -> Lambda rep-mergeReduceOps (Lambda par1 bdy1 rtp1) (Lambda par2 bdy2 rtp2) =-  let body' =-        Body-          (bodyDec bdy1)-          (bodyStms bdy1 <> bodyStms bdy2)-          (bodyResult bdy1 ++ bodyResult bdy2)-      (len1, len2) = (length rtp1, length rtp2)-      par' = take len1 par1 ++ take len2 par2 ++ drop len1 par1 ++ drop len2 par2-   in Lambda par' body' (rtp1 ++ rtp2)
src/Futhark/Optimise/Fusion/GraphRep.hs view
@@ -386,8 +386,8 @@   Futhark.Screma w is form -> inputs is <> freeClassifications (w, form)   Futhark.Hist w is ops lam -> inputs is <> freeClassifications (w, ops, lam)   Futhark.Scatter w is lam iws -> inputs is <> freeClassifications (w, lam, iws)-  Futhark.Stream w is form nes lam ->-    inputs is <> freeClassifications (w, form, nes, lam)+  Futhark.Stream w is nes lam ->+    inputs is <> freeClassifications (w, nes, lam)   Futhark.JVP {} -> freeClassifications soac   Futhark.VJP {} -> freeClassifications soac   where
src/Futhark/Optimise/Fusion/TryFusion.hs view
@@ -389,12 +389,8 @@     ----------------------------     -- Stream-Stream Fusions: --     -----------------------------    (SOAC.Stream _ Sequential _ _ _, SOAC.Stream _ Sequential _ _ _) -> do-      -- fuse two SEQUENTIAL streams-      (res_nms, res_stream) <- fuseStreamHelper (fsOutNames ker) unfus_set outVars outPairs soac_c soac_p-      success res_nms res_stream     (SOAC.Stream {}, SOAC.Stream {}) -> do-      -- fuse two PARALLEL streams+      -- fuse two SEQUENTIAL streams       (res_nms, res_stream) <- fuseStreamHelper (fsOutNames ker) unfus_set outVars outPairs soac_c soac_p       success res_nms res_stream     -------------------------------------------------------------------@@ -406,23 +402,17 @@     ---   we could run in an infinite recursion, i.e., repeatedly   ---     ---   fusing map o scan into an infinity of Stream levels!      ---     --------------------------------------------------------------------    (SOAC.Stream _ form2 _ _ _, _) -> do+    (SOAC.Stream {}, _) -> do       -- If this rule is matched then soac_p is NOT a stream.       -- To fuse a stream kernel, we transform soac_p to a stream, which       -- borrows the sequential/parallel property of the soac_c Stream,       -- and recursively perform stream-stream fusion.       (soac_p', newacc_ids) <- SOAC.soacToStream soac_p-      soac_p'' <- case form2 of-        Sequential {} -> maybe (fail "not a stream") pure (toSeqStream soac_p')-        _ -> pure soac_p'-      if soac_p' == soac_p-        then fail "SOAC could not be turned into stream."-        else-          fuseSOACwithKer-            (namesFromList (map identName newacc_ids) <> unfus_set)-            (map identName newacc_ids ++ outVars)-            soac_p''-            ker+      fuseSOACwithKer+        (namesFromList (map identName newacc_ids) <> unfus_set)+        (map identName newacc_ids ++ outVars)+        soac_p'+        ker     (_, SOAC.Screma _ form _) | Just _ <- Futhark.isScanomapSOAC form -> do       -- A Scan soac can be currently only fused as a (sequential) stream,       -- hence it is first translated to a (sequential) Stream and then@@ -436,33 +426,27 @@             soac_p'             ker         else fail "SOAC could not be turned into stream."-    (_, SOAC.Stream _ form_p _ _ _) -> do+    (_, SOAC.Stream {}) -> do       -- If it reached this case then soac_c is NOT a Stream kernel,       -- hence transform the kernel's soac to a stream and attempt       -- stream-stream fusion recursivelly.       -- The newly created stream corresponding to soac_c borrows the       -- sequential/parallel property of the soac_p stream.       (soac_c', newacc_ids) <- SOAC.soacToStream soac_c-      when (soac_c' == soac_c) $ fail "SOAC could not be turned into stream."-      soac_c'' <- case form_p of-        Sequential -> maybe (fail "not a stream") pure (toSeqStream soac_c')-        _ -> pure soac_c'--      fuseSOACwithKer-        (namesFromList (map identName newacc_ids) <> unfus_set)-        outVars-        soac_p-        $ ker {fsSOAC = soac_c'', fsOutNames = map identName newacc_ids ++ fsOutNames ker}+      if soac_c' /= soac_c+        then+          fuseSOACwithKer+            (namesFromList (map identName newacc_ids) <> unfus_set)+            outVars+            soac_p+            $ ker {fsSOAC = soac_c', fsOutNames = map identName newacc_ids ++ fsOutNames ker}+        else fail "SOAC could not be turned into stream."      ---------------------------------     --- DEFAULT, CANNOT FUSE CASE ---     ---------------------------------     _ -> fail "Cannot fuse" -getStreamOrder :: StreamForm rep -> StreamOrd-getStreamOrder (Parallel o _ _) = o-getStreamOrder Sequential = InOrder- fuseStreamHelper ::   [VName] ->   Names ->@@ -476,56 +460,38 @@   unfus_set   outVars   outPairs-  (SOAC.Stream w2 form2 lam2 nes2 inp2_arr)-  (SOAC.Stream _ form1 lam1 nes1 inp1_arr) =-    if getStreamOrder form2 /= getStreamOrder form1-      then fail "fusion conditions not met!"-      else do-        -- very similar to redomap o redomap composition, but need-        -- to remove first the `chunk' parameters of streams'-        -- lambdas and put them in the resulting stream lambda.-        let chunk1 = head $ lambdaParams lam1-            chunk2 = head $ lambdaParams lam2-            hmnms = M.fromList [(paramName chunk2, paramName chunk1)]-            lam20 = substituteNames hmnms lam2-            lam1' = lam1 {lambdaParams = tail $ lambdaParams lam1}-            lam2' = lam20 {lambdaParams = tail $ lambdaParams lam20}-            (res_lam', new_inp) =-              fuseRedomap-                unfus_set-                outVars-                lam1'-                []-                nes1-                inp1_arr-                outPairs-                lam2'-                []-                nes2-                inp2_arr-            res_lam'' = res_lam' {lambdaParams = chunk1 : lambdaParams res_lam'}-            unfus_accs = take (length nes1) outVars-            unfus_arrs = filter (`notElem` unfus_accs) $ filter (`nameIn` unfus_set) outVars-        let res_form = mergeForms form2 form1-        pure-          ( unfus_accs ++ out_kernms ++ unfus_arrs,-            SOAC.Stream w2 res_form res_lam'' (nes1 ++ nes2) new_inp-          )+  (SOAC.Stream w2 lam2 nes2 inp2_arr)+  (SOAC.Stream _ lam1 nes1 inp1_arr) = do+    -- very similar to redomap o redomap composition, but need+    -- to remove first the `chunk' parameters of streams'+    -- lambdas and put them in the resulting stream lambda.+    let chunk1 = head $ lambdaParams lam1+        chunk2 = head $ lambdaParams lam2+        hmnms = M.fromList [(paramName chunk2, paramName chunk1)]+        lam20 = substituteNames hmnms lam2+        lam1' = lam1 {lambdaParams = tail $ lambdaParams lam1}+        lam2' = lam20 {lambdaParams = tail $ lambdaParams lam20}+        (res_lam', new_inp) =+          fuseRedomap+            unfus_set+            outVars+            lam1'+            []+            nes1+            inp1_arr+            outPairs+            lam2'+            []+            nes2+            inp2_arr+        res_lam'' = res_lam' {lambdaParams = chunk1 : lambdaParams res_lam'}+        unfus_accs = take (length nes1) outVars+        unfus_arrs = filter (`notElem` unfus_accs) $ filter (`nameIn` unfus_set) outVars+    pure+      ( unfus_accs ++ out_kernms ++ unfus_arrs,+        SOAC.Stream w2 res_lam'' (nes1 ++ nes2) new_inp+      ) fuseStreamHelper _ _ _ _ _ _ = fail "Cannot Fuse Streams!"--mergeForms :: StreamForm SOACS -> StreamForm SOACS -> StreamForm SOACS-mergeForms Sequential Sequential = Sequential-mergeForms (Parallel _ comm2 lam2r) (Parallel o1 comm1 lam1r) =-  Parallel o1 (comm1 <> comm2) (mergeReduceOps lam1r lam2r)-mergeForms _ _ = error "Fusing sequential to parallel stream disallowed!"---- | If a Stream is passed as argument then it converts it to a---   Sequential Stream.-toSeqStream :: SOAC -> Maybe SOAC-toSeqStream s@(SOAC.Stream _ Sequential _ _ _) = Just s-toSeqStream (SOAC.Stream w Parallel {} l acc inps) =-  Just $ SOAC.Stream w Sequential l acc inps-toSeqStream _ = Nothing  -- Here follows optimizations and transforms to expose fusability. 
src/Futhark/Optimise/ReduceDeviceSyncs.hs view
@@ -15,7 +15,7 @@ import Data.Bifunctor (second) import Data.Foldable import qualified Data.IntMap.Strict as IM-import Data.List (transpose)+import Data.List (transpose, zip4) import qualified Data.Map.Strict as M import Data.Sequence ((<|), (><), (|>)) import qualified Data.Text as T@@ -170,8 +170,9 @@                 else do                   -- Otherwise, ensure all results are migrated.                   (all_stms', arrs) <--                    fmap unzip $ forM (zip all_stms reses) $ \(stms, res) ->-                      storeScalar stms (resSubExp res) (patElemType pe)+                    fmap unzip $+                      forM (zip all_stms reses) $ \(stms, res) ->+                        storeScalar stms (resSubExp res) (patElemType pe)                    pe' <- arrayizePatElem pe                   let bt' = staticShapes1 (patElemType pe')@@ -202,40 +203,55 @@          -- Update statement bound variables and parameters if their values         -- have been migrated to device.-        let lmerge (res, stms) (pe, (Param _ pn pt, pval), MoveToDevice) = do-              -- Rewrite the bound variable.+        let lmerge (res, stms, rebinds) (pe, param, StayOnHost) =+              pure ((pe, param) : res, stms, rebinds)+            lmerge (res, stms, rebinds) (pe, (Param _ pn pt, pval), _) = do+              -- Migrate the bound variable.               pe' <- arrayizePatElem pe -              -- Move the initial value to device if not already there.+              -- Move the initial value to device if not already there to+              -- ensure that the parameter argument and loop return value+              -- converge.               (stms', arr) <- storeScalar stms pval (fromDecl pt) -              -- Rewrite the parameter.+              -- Migrate the parameter.               pn' <- newName pn               let pt' = toDecl (patElemType pe') Nonunique               let pval' = Var arr               let param' = (Param mempty pn' pt', pval') -              -- Record the migration.-              Ident pn (fromDecl pt) `movedTo` pn'+              -- Record the migration and rebind the parameter inside the+              -- loop body if necessary.+              rebinds' <- (pe {patElemName = pn}) `migratedTo` (pn', rebinds) -              pure ((pe', param') : res, stms')-            lmerge _ (_, _, UsedOnHost) =-              -- Initial loop parameter value and loop result should have-              -- been made available on host instead.-              compilerBugS "optimizeStm: unhandled host usage of loop param"-            lmerge (res, stms) (pe, param, StayOnHost) =-              pure ((pe, param) : res, stms)+              pure ((pe', param') : res, stms', rebinds')          mt <- ask-         let pes = patElems (stmPat stm)         let mss = map (\(Param _ n _, _) -> statusOf n mt) params-        (zipped', out') <- foldM lmerge ([], out) (zip3 pes params mss)+        (zipped', out', rebinds) <-+          foldM lmerge ([], out, mempty) (zip3 pes params mss)         let (pes', params') = unzip (reverse zipped') +        -- Rewrite body.+        let body1 = body {bodyStms = rebinds >< bodyStms body}+        body2 <- optimizeBody body1+        let zipped =+              zip4+                mss+                (bodyResult body2)+                (map resSubExp $ bodyResult body)+                (map patElemType pes)+        let rstore (bstms, res) (StayOnHost, r, _, _) =+              pure (bstms, r : res)+            rstore (bstms, res) (_, SubExpRes certs _, se, t) = do+              (bstms', dev) <- storeScalar bstms se t+              pure (bstms', SubExpRes certs (Var dev) : res)+        (bstms, res) <- foldM rstore (bodyStms body2, []) zipped+        let body3 = body2 {bodyStms = bstms, bodyResult = reverse res}+         -- Rewrite statement.-        body' <- optimizeBody body-        let e' = DoLoop params' lform body'+        let e' = DoLoop params' lform body3         let stm' = Let (Pat pes') (stmAux stm) e'          -- Read migrated scalars that are used on host.
src/Futhark/Optimise/ReduceDeviceSyncs/MigrationTable.hs view
@@ -443,9 +443,10 @@     BasicOp (Index arr s) -> do       graphInefficientReturn (sliceDims s) e       one bs `reuses` arr-    BasicOp (Update _ arr _ _) -> do-      graphInefficientReturn [] e-      one bs `reuses` arr+    BasicOp (Update _ arr slice _)+      | isFixed slice -> do+          graphInefficientReturn [] e+          one bs `reuses` arr     BasicOp (FlatIndex arr s) -> do       -- Migrating a FlatIndex leads to a memory allocation error.       --@@ -494,6 +495,8 @@       -- Whether the rows are primitive constants or arrays, without any scalar       -- variable operands such ArrayLit cannot directly prevent a scalar read.       graphHostOnly e+    BasicOp Update {} ->+      graphHostOnly e     BasicOp Concat {} ->       -- Is unlikely to prevent a scalar read as the only SubExp operand in       -- practice is a computation of host-only size variables.@@ -692,7 +695,7 @@   compilerBugS "Loop statement bound no variable; should have been eliminated." graphLoop (b : bs) params lform body = do   -- Graph loop params and body while capturing statistics.-  g0 <- getGraph+  g <- getGraph   stats <- captureBodyStats (subgraphId `graphIdFor` graphTheLoop)    -- Record aliases for copyable memory backing returned arrays.@@ -701,18 +704,23 @@   let results = map resSubExp (bodyResult body)   may_copy_results <- reusesBranches (b : bs) [args, results] -  -- Connect loop condition to a sink if the loop cannot be migrated.-  -- The migration status of the condition is what determines whether the-  -- loop may be migrated as a whole or not. See 'shouldMoveStm'.+  -- Connect the loop condition to a sink if the loop cannot be migrated,+  -- ensuring that it will be available to the host. The migration status+  -- of the condition is what determines whether the loop may be migrated+  -- as a whole or not. See 'shouldMoveStm'.   let may_migrate = not (bodyHostOnly stats) && may_copy_results   unless may_migrate $ case lform of     ForLoop _ _ (Var n) _ -> connectToSink (nameToId n)-    WhileLoop n -> connectToSink (nameToId n)+    WhileLoop n+      | (_, p, _, res) <- loopValueFor n -> do+          connectToSink p+          case res of+            Var v -> connectToSink (nameToId v)+            _ -> pure ()     _ -> pure ()    -- Connect graphed return values to their loop parameters.-  g1 <- getGraph-  mapM_ (mergeLoopParam g1) loopValues+  mapM_ mergeLoopParam loopValues    -- Route the sources within the loop body in isolation.   -- The loop graph must not be altered after this point.@@ -724,8 +732,8 @@   -- If a device read is delayed from one iteration to the next the   -- corresponding variables bound by the statement must be treated as   -- sources.-  g2 <- getGraph-  let (dbs, rbc) = foldl' (deviceBindings g2) (IS.empty, MG.none) srcs+  g' <- getGraph+  let (dbs, rbc) = foldl' (deviceBindings g') (IS.empty, MG.none) srcs   modifySources $ second (IS.toList dbs <>)    -- Connect operands to sinks if they can reach a sink within the loop.@@ -733,7 +741,7 @@   -- reach and exhaust their normal entry edges into the loop.   -- This means a read can be delayed through a loop but not into it if   -- that would increase the number of reads done by any given iteration.-  let ops = IS.filter (`MG.member` g0) (bodyOperands stats)+  let ops = IS.filter (`MG.member` g) (bodyOperands stats)   foldM_ connectOperand rbc (IS.elems ops)    -- It might be beneficial to move the whole loop to device, to avoid@@ -807,14 +815,12 @@             ops <- onlyGraphedScalarSubExp arg             addEdges (MG.oneEdge p) ops -    mergeLoopParam :: Graph -> LoopValue -> Grapher ()-    mergeLoopParam g (_, p, _, res)+    mergeLoopParam :: LoopValue -> Grapher ()+    mergeLoopParam (_, p, _, res)       | Var n <- res,         ret <- nameToId n,         ret /= p =-          if MG.isSinkConnected p g-            then connectToSink ret-            else addEdges (MG.oneEdge p) (IS.singleton ret)+          addEdges (MG.oneEdge p) (IS.singleton ret)       | otherwise =           pure ()     deviceBindings ::
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}  -- | A generic transformation for adding memory allocations to a -- Futhark program.  Specialised by specific representations in@@ -26,8 +27,6 @@     arraySizeInBytesExp,     mkLetNamesB',     mkLetNamesB'',-    dimAllocationSize,-    ChunkMap,      -- * Module re-exports @@ -53,9 +52,11 @@ import qualified Data.Map.Strict as M import Data.Maybe import qualified Data.Set as S+import Futhark.Analysis.SymbolTable (IndexOp) import qualified Futhark.Analysis.UsageTable as UT import Futhark.IR.Mem import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.IR.Prop.Aliases (AliasedOp) import Futhark.MonadFreshNames import Futhark.Optimise.Simplify.Engine (SimpleOps (..)) import qualified Futhark.Optimise.Simplify.Engine as Engine@@ -64,16 +65,6 @@ import Futhark.Tools import Futhark.Util (maybeNth, splitAt3) --- | The subexpression giving the number of elements we should--- allocate space for.  See 'ChunkMap' comment.-dimAllocationSize :: ChunkMap -> SubExp -> SubExp-dimAllocationSize chunkmap (Var v) =-  -- It is important to recurse here, as the substitution may itself-  -- be a chunk size.-  maybe (Var v) (dimAllocationSize chunkmap) $ M.lookup v chunkmap-dimAllocationSize _ size =-  size- type Allocable fromrep torep inner =   ( PrettyRep fromrep,     PrettyRep torep,@@ -90,15 +81,8 @@     BuilderOps torep   ) --- | A mapping from chunk names to their maximum size.  XXX FIXME--- HACK: This is part of a hack to add loop-invariant allocations to--- reduce kernels, because memory expansion does not use range--- analysis yet (it should).-type ChunkMap = M.Map VName SubExp- data AllocEnv fromrep torep = AllocEnv-  { chunkMap :: ChunkMap,-    -- | Aggressively try to reuse memory in do-loops -+  { -- | Aggressively try to reuse memory in do-loops -     -- should be True inside kernels, False outside.     aggressiveReuse :: Bool,     -- | When allocating memory, put it in this memory space.@@ -132,9 +116,8 @@    mkLetNamesM names e = do     def_space <- askDefaultSpace-    chunkmap <- asks chunkMap     hints <- expHints e-    pat <- patWithAllocations def_space chunkmap names e hints+    pat <- patWithAllocations def_space names e hints     pure $ Let pat (defAux ()) e    mkBodyM stms res = pure $ Body () stms res@@ -161,8 +144,7 @@   where     env =       AllocEnv-        { chunkMap = mempty,-          aggressiveReuse = False,+        { aggressiveReuse = False,           allocSpace = DefaultSpace,           envConsts = mempty,           allocInOp = handleOp,@@ -176,26 +158,25 @@ arraySizeInBytesExp t =   untyped $ foldl' (*) (elemSize t) $ map pe64 (arrayDims t) -arraySizeInBytesExpM :: MonadBuilder m => ChunkMap -> Type -> m (PrimExp VName)-arraySizeInBytesExpM chunkmap t = do-  let dim_prod_i64 = product $ map (pe64 . dimAllocationSize chunkmap) (arrayDims t)+arraySizeInBytesExpM :: MonadBuilder m => Type -> m (PrimExp VName)+arraySizeInBytesExpM t = do+  let dim_prod_i64 = product $ map pe64 (arrayDims t)       elm_size_i64 = elemSize t   pure $     BinOpExp (SMax Int64) (ValueExp $ IntValue $ Int64Value 0) $       untyped $         dim_prod_i64 * elm_size_i64 -arraySizeInBytes :: MonadBuilder m => ChunkMap -> Type -> m SubExp-arraySizeInBytes chunkmap = letSubExp "bytes" <=< toExp <=< arraySizeInBytesExpM chunkmap+arraySizeInBytes :: MonadBuilder m => Type -> m SubExp+arraySizeInBytes = letSubExp "bytes" <=< toExp <=< arraySizeInBytesExpM  allocForArray' ::   (MonadBuilder m, Op (Rep m) ~ MemOp inner) =>-  ChunkMap ->   Type ->   Space ->   m VName-allocForArray' chunkmap t space = do-  size <- arraySizeInBytes chunkmap t+allocForArray' t space = do+  size <- arraySizeInBytes t   letExp "mem" $ Op $ Alloc size space  -- | Allocate memory for a value of the given type.@@ -205,8 +186,7 @@   Space ->   AllocM fromrep torep VName allocForArray t space = do-  chunkmap <- asks chunkMap-  allocForArray' chunkmap t space+  allocForArray' t space  allocsForStm ::   (Allocable fromrep torep inner) =>@@ -215,26 +195,24 @@   AllocM fromrep torep (Stm torep) allocsForStm idents e = do   def_space <- askDefaultSpace-  chunkmap <- asks chunkMap   hints <- expHints e   rts <- expReturns e-  pes <- allocsForPat def_space chunkmap idents rts hints+  pes <- allocsForPat def_space idents rts hints   dec <- mkExpDecM (Pat pes) e   pure $ Let (Pat pes) (defAux dec) e  patWithAllocations ::   (MonadBuilder m, Mem (Rep m) inner) =>   Space ->-  ChunkMap ->   [VName] ->   Exp (Rep m) ->   [ExpHint] ->   m (Pat LetDecMem)-patWithAllocations def_space chunkmap names e hints = do+patWithAllocations def_space names e hints = do   ts' <- instantiateShapes' names <$> expExtType e   let idents = zipWith Ident names ts'   rts <- expReturns e-  Pat <$> allocsForPat def_space chunkmap idents rts hints+  Pat <$> allocsForPat def_space idents rts hints  mkMissingIdents :: MonadFreshNames m => [Ident] -> [ExpReturns] -> m [Ident] mkMissingIdents idents rts =@@ -247,19 +225,18 @@ allocsForPat ::   (MonadBuilder m, Op (Rep m) ~ MemOp inner) =>   Space ->-  ChunkMap ->   [Ident] ->   [ExpReturns] ->   [ExpHint] ->   m [PatElem LetDecMem]-allocsForPat def_space chunkmap some_idents rts hints = do+allocsForPat def_space some_idents rts hints = do   idents <- mkMissingIdents some_idents rts    forM (zip3 idents rts hints) $ \(ident, rt, hint) -> do     let ident_shape = arrayShape $ identType ident     case rt of       MemPrim _ -> do-        summary <- summaryForBindage def_space chunkmap (identType ident) hint+        summary <- summaryForBindage def_space (identType ident) hint         pure $ PatElem (identName ident) summary       MemMem space ->         pure $ PatElem (identName ident) $ MemMem space@@ -268,7 +245,7 @@         pure . PatElem (identName ident) . MemArray bt ident_shape u $ ArrayIn mem ixfn       MemArray _ extshape _ Nothing         | Just _ <- knownShape extshape -> do-            summary <- summaryForBindage def_space chunkmap (identType ident) hint+            summary <- summaryForBindage def_space (identType ident) hint             pure $ PatElem (identName ident) summary       MemArray bt _ u (Just (ReturnsNewBlock _ i extixfn)) -> do         let ixfn = instantiateExtIxFun idents extixfn@@ -302,20 +279,19 @@ summaryForBindage ::   (MonadBuilder m, Op (Rep m) ~ MemOp inner) =>   Space ->-  ChunkMap ->   Type ->   ExpHint ->   m (MemBound NoUniqueness)-summaryForBindage _ _ (Prim bt) _ =+summaryForBindage _ (Prim bt) _ =   pure $ MemPrim bt-summaryForBindage _ _ (Mem space) _ =+summaryForBindage _ (Mem space) _ =   pure $ MemMem space-summaryForBindage _ _ (Acc acc ispace ts u) _ =+summaryForBindage _ (Acc acc ispace ts u) _ =   pure $ MemAcc acc ispace ts u-summaryForBindage def_space chunkmap t@(Array pt shape u) NoHint = do-  m <- allocForArray' chunkmap t def_space+summaryForBindage def_space t@(Array pt shape u) NoHint = do+  m <- allocForArray' t def_space   pure $ MemArray pt shape u $ ArrayIn m $ IxFun.iota $ map pe64 $ arrayDims t-summaryForBindage _ _ t@(Array pt _ _) (Hint ixfun space) = do+summaryForBindage _ t@(Array pt _ _) (Hint ixfun space) = do   bytes <-     letSubExp "bytes" <=< toExp . untyped $       product@@ -424,11 +400,10 @@       -- _must_ be in ScalarSpace and have the right index function.       (res_mem, res_ixfun) <- lift $ lookupArraySummary res       res_mem_space <- lift $ lookupMemSpace res_mem-      chunkmap <- asks chunkMap       (res_mem', res') <-         if (res_mem_space, res_ixfun) == (v_mem_space, v_ixfun)           then pure (res_mem, res)-          else lift $ arrayWithIxFun chunkmap v_mem_space v_ixfun (fromDecl param_t) res+          else lift $ arrayWithIxFun v_mem_space v_ixfun (fromDecl param_t) res       tell ([Var res_mem'], [])       pure $ Var res'     scalarRes _ _ _ se = pure se@@ -500,15 +475,14 @@  arrayWithIxFun ::   (MonadBuilder m, Op (Rep m) ~ MemOp inner, LetDec (Rep m) ~ LetDecMem) =>-  ChunkMap ->   Space ->   IxFun ->   Type ->   VName ->   m (VName, VName)-arrayWithIxFun chunkmap space ixfun v_t v = do+arrayWithIxFun space ixfun v_t v = do   let Array pt shape u = v_t-  mem <- allocForArray' chunkmap v_t space+  mem <- allocForArray' v_t space   v_copy <- newVName $ baseString v <> "_scalcopy"   letBind (Pat [PatElem v_copy $ MemArray pt shape u $ ArrayIn mem ixfun]) $ BasicOp $ Copy v   pure (mem, v_copy)@@ -705,13 +679,8 @@     allocInStms' (stm : stms) = do       allocstms <- collectStms_ $ auxing (stmAux stm) $ allocInStm stm       addStms allocstms-      let stms_substs = foldMap sizeSubst allocstms-          stms_consts = foldMap stmConsts allocstms-          f env =-            env-              { chunkMap = stms_substs <> chunkMap env,-                envConsts = stms_consts <> envConsts env-              }+      let stms_consts = foldMap stmConsts allocstms+          f env = env {envConsts = stms_consts <> envConsts env}       local f $ allocInStms' stms  allocInStm ::@@ -1035,24 +1004,15 @@           pure (p {paramDec = MemAcc acc ispace ts u}, a)  class SizeSubst op where-  opSizeSubst :: Pat dec -> op -> ChunkMap   opIsConst :: op -> Bool   opIsConst = const False -instance SizeSubst () where-  opSizeSubst _ _ = mempty+instance SizeSubst ()  instance SizeSubst op => SizeSubst (MemOp op) where-  opSizeSubst pat (Inner op) = opSizeSubst pat op-  opSizeSubst _ _ = mempty-   opIsConst (Inner op) = opIsConst op   opIsConst _ = False -sizeSubst :: SizeSubst (Op rep) => Stm rep -> ChunkMap-sizeSubst (Let pat _ (Op op)) = opSizeSubst pat op-sizeSubst _ = mempty- stmConsts :: SizeSubst (Op rep) => Stm rep -> S.Set VName stmConsts (Let pat _ (Op op))   | opIsConst op = S.fromList $ patNames pat@@ -1069,7 +1029,7 @@   Exp (Rep m) ->   m (Stm (Rep m)) mkLetNamesB' dec names e = do-  pat <- patWithAllocations DefaultSpace mempty names e nohints+  pat <- patWithAllocations DefaultSpace names e nohints   pure $ Let pat (defAux dec) e   where     nohints = map (const NoHint) names@@ -1088,7 +1048,7 @@   Exp (Engine.Wise rep) ->   m (Stm (Engine.Wise rep)) mkLetNamesB'' names e = do-  pat <- patWithAllocations DefaultSpace mempty names e nohints+  pat <- patWithAllocations DefaultSpace names e nohints   let pat' = Engine.addWisdomToPat pat e       dec = Engine.mkWiseExpDec pat' () e   pure $ Let pat' (defAux dec) e@@ -1097,10 +1057,12 @@  simplifiable ::   ( Engine.SimplifiableRep rep,+    LetDec rep ~ LetDecMem,     ExpDec rep ~ (),     BodyDec rep ~ (),-    LetDec rep ~ LetDecMem,     OpReturns (Engine.OpWithWisdom inner),+    AliasedOp (Engine.OpWithWisdom inner),+    IndexOp (Engine.OpWithWisdom inner),     Mem rep inner   ) =>   (Engine.OpWithWisdom inner -> UT.UsageTable) ->
src/Futhark/Pass/ExplicitAllocations/GPU.hs view
@@ -10,7 +10,6 @@   ) where -import qualified Data.Map as M import qualified Data.Set as S import Futhark.IR.GPU import Futhark.IR.GPUMem@@ -19,10 +18,6 @@ import Futhark.Pass.ExplicitAllocations.SegOp  instance SizeSubst (HostOp rep op) where-  opSizeSubst (Pat [size]) (SizeOp (SplitSpace _ _ _ elems_per_thread)) =-    M.singleton (patElemName size) elems_per_thread-  opSizeSubst _ _ = mempty-   opIsConst (SizeOp GetSize {}) = True   opIsConst (SizeOp GetSizeMax {}) = True   opIsConst _ = False@@ -102,11 +97,8 @@   Type ->   KernelResult ->   AllocM GPU GPUMem ExpHint-mapResultHint lvl space = hint+mapResultHint _lvl space = hint   where-    num_threads =-      pe64 (unCount $ segNumGroups lvl) * pe64 (unCount $ segGroupSize lvl)-     -- Heuristic: do not rearrange for returned arrays that are     -- sufficiently small.     coalesceReturnOfShape _ [] = False@@ -115,19 +107,8 @@      hint t Returns {}       | coalesceReturnOfShape (primByteSize (elemType t)) $ arrayDims t = do-          chunkmap <- asks chunkMap           let space_dims = segSpaceDims space-              t_dims = map (dimAllocationSize chunkmap) $ arrayDims t-          pure $ Hint (innermost space_dims t_dims) DefaultSpace-    hint t (ConcatReturns _ SplitStrided {} w _ _) = do-      chunkmap <- asks chunkMap-      let t_dims = map (dimAllocationSize chunkmap) $ arrayDims t-      pure $ Hint (innermost [w] t_dims) DefaultSpace-    hint Prim {} (ConcatReturns _ SplitContiguous w elems_per_thread _) = do-      let ixfun_base = IxFun.iota [sExt64 num_threads, pe64 elems_per_thread]-          ixfun_tr = IxFun.permute ixfun_base [1, 0]-          ixfun = IxFun.reshape ixfun_tr [pe64 w]-      pure $ Hint ixfun DefaultSpace+          pure $ Hint (innermost space_dims (arrayDims t)) DefaultSpace     hint _ _ = pure NoHint  innermost :: [SubExp] -> [SubExp] -> IxFun
src/Futhark/Pass/ExplicitAllocations/MC.hs view
@@ -11,8 +11,7 @@ import Futhark.Pass.ExplicitAllocations import Futhark.Pass.ExplicitAllocations.SegOp -instance SizeSubst (MCOp rep op) where-  opSizeSubst _ _ = mempty+instance SizeSubst (MCOp rep op)  handleSegOp :: SegOp () MC -> AllocM MC MCMem (SegOp () MCMem) handleSegOp op = do
src/Futhark/Pass/ExplicitAllocations/SegOp.hs view
@@ -13,8 +13,7 @@ import qualified Futhark.IR.Mem.IxFun as IxFun import Futhark.Pass.ExplicitAllocations -instance SizeSubst (SegOp lvl rep) where-  opSizeSubst _ _ = mempty+instance SizeSubst (SegOp lvl rep)  allocInKernelBody ::   Allocable fromrep torep inner =>
src/Futhark/Pass/ExtractKernels.hs view
@@ -280,7 +280,7 @@         bodyStms body      -- XXX - our notion of balancing is probably still too naive.-    unbalancedStm bound (Op (Stream w _ _ _ _)) =+    unbalancedStm bound (Op (Stream w _ _ _)) =       w `subExpBound` bound     unbalancedStm bound (Op (Screma w _ _)) =       w `subExpBound` bound@@ -449,96 +449,13 @@           inner_stms <- innerParallelBody ((outer_suff_key, False) : path)            (suff_stms <>) <$> kernelAlternatives pat inner_stms [(outer_suff, outer_stms)]---- Streams can be handled in two different ways - either we--- sequentialise the body or we keep it parallel and distribute.-transformStm path (Let pat aux@(StmAux cs _ _) (Op (Stream w arrs Parallel {} [] map_fun)))-  | not ("sequential_inner" `inAttrs` stmAuxAttrs aux) = do-      -- No reduction part.  Remove the stream and leave the body-      -- parallel.  It will be distributed.-      types <- asksScope scopeForSOACs-      transformStms path . stmsToList . snd-        =<< runBuilderT (certifying cs $ sequentialStreamWholeArray pat w [] map_fun arrs) types-transformStm path (Let pat aux@(StmAux cs _ _) (Op (Stream w arrs (Parallel o comm red_fun) nes fold_fun)))-  | "sequential_inner" `inAttrs` stmAuxAttrs aux =-      paralleliseOuter path-  | otherwise = do-      ((outer_suff, outer_suff_key), suff_stms) <--        sufficientParallelism "suff_outer_stream" [w] path Nothing--      outer_stms <- outerParallelBody ((outer_suff_key, True) : path)-      inner_stms <- innerParallelBody ((outer_suff_key, False) : path)--      (suff_stms <>)-        <$> kernelAlternatives pat inner_stms [(outer_suff, outer_stms)]-  where-    paralleliseOuter path'-      | not $ all primType $ lambdaReturnType red_fun = do-          -- Split into a chunked map and a reduction, with the latter-          -- further transformed.-          let fold_fun' = soacsLambdaToGPU fold_fun--          let (red_pat_elems, concat_pat_elems) =-                splitAt (length nes) $ patElems pat-              red_pat = Pat red_pat_elems--          ((num_threads, red_results), stms) <--            streamMap-              segThreadCapped-              (map (baseString . patElemName) red_pat_elems)-              concat_pat_elems-              w-              Noncommutative-              fold_fun'-              nes-              arrs--          reduce_soac <- reduceSOAC [Reduce comm' red_fun nes]--          (stms <>)-            <$> inScopeOf-              stms-              ( transformStm path' $-                  Let red_pat aux {stmAuxAttrs = mempty} $-                    Op (Screma num_threads red_results reduce_soac)-              )-      | otherwise = do-          let red_fun_sequential = soacsLambdaToGPU red_fun-              fold_fun_sequential = soacsLambdaToGPU fold_fun-          fmap (certify cs)-            <$> streamRed-              segThreadCapped-              pat-              w-              comm'-              red_fun_sequential-              fold_fun_sequential-              nes-              arrs--    outerParallelBody path' =-      renameBody-        =<< (mkBody <$> paralleliseOuter path' <*> pure (varsRes (patNames pat)))--    paralleliseInner path' = do-      types <- asksScope scopeForSOACs-      transformStms path' . fmap (certify cs) . stmsToList . snd-        =<< runBuilderT (sequentialStreamWholeArray pat w nes fold_fun arrs) types--    innerParallelBody path' =-      renameBody-        =<< (mkBody <$> paralleliseInner path' <*> pure (varsRes (patNames pat)))--    comm'-      | commutativeLambda red_fun, o /= InOrder = Commutative-      | otherwise = comm transformStm path (Let pat (StmAux cs _ _) (Op (Screma w arrs form))) = do   -- This screma is too complicated for us to immediately do   -- anything, so split it up and try again.   scope <- asksScope scopeForSOACs   transformStms path . map (certify cs) . stmsToList . snd     =<< runBuilderT (dissectScrema pat w form arrs) scope-transformStm path (Let pat _ (Op (Stream w arrs Sequential nes fold_fun))) = do+transformStm path (Let pat _ (Op (Stream w arrs nes fold_fun))) = do   -- Remove the stream and leave the body parallel.  It will be   -- distributed.   types <- asksScope scopeForSOACs@@ -618,7 +535,7 @@             (map (bodyInterest . caseBody) cases)       | Op (Screma w _ (ScremaForm _ _ lam')) <- stmExp stm =           zeroIfTooSmall w + bodyInterest (lambdaBody lam')-      | Op (Stream _ _ Sequential _ lam') <- stmExp stm =+      | Op (Stream _ _ _ lam') <- stmExp stm =           bodyInterest $ lambdaBody lam'       | otherwise =           0@@ -755,9 +672,11 @@    types <- askScope +  let only_intra = onlyExploitIntra (stmAuxAttrs aux)+      may_intra = worthIntraGroup lam && mayExploitIntra attrs+   intra <--    if onlyExploitIntra (stmAuxAttrs aux)-      || (worthIntraGroup lam && mayExploitIntra attrs)+    if only_intra || may_intra       then flip runReaderT types $ intraGroupParallelise loopnest lam       else pure Nothing @@ -767,7 +686,8 @@       kernelAlternatives pat seq_body []     --     Nothing-      | Just m <- mkSeqAlts -> do+      | not only_intra,+        Just m <- mkSeqAlts -> do           (outer_suff, outer_suff_key, outer_suff_stms, seq_body) <- m           par_body <-             renameBody@@ -781,7 +701,7 @@           kernelAlternatives pat par_body []     --     Just intra'@(_, _, log, intra_prelude, intra_stms)-      | onlyExploitIntra attrs -> do+      | only_intra -> do           addLog log           group_par_body <- renameBody $ mkBody intra_stms res           (intra_prelude <>) <$> kernelAlternatives pat group_par_body []
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -339,7 +339,7 @@ distributeMapBodyStms orig_acc = distribute <=< onStms orig_acc . stmsToList   where     onStms acc [] = pure acc-    onStms acc (Let pat (StmAux cs _ _) (Op (Stream w arrs Sequential accs lam)) : stms) = do+    onStms acc (Let pat (StmAux cs _ _) (Op (Stream w arrs accs lam)) : stms) = do       types <- asksScope scopeForSOACs       stream_stms <-         snd <$> runBuilderT (sequentialStreamWholeArray pat w accs lam arrs) types
src/Futhark/Pass/ExtractKernels/Interchange.hs view
@@ -46,7 +46,7 @@   Let pat (defAux ()) $ DoLoop merge form body  interchangeLoop ::-  (MonadBuilder m, LocalScope SOACS m) =>+  (MonadBuilder m, Rep m ~ SOACS) =>   (VName -> Maybe VName) ->   SeqLoop ->   LoopNesting ->
src/Futhark/Pass/ExtractKernels/Intragroup.hs view
@@ -275,7 +275,7 @@       certifying (stmAuxCerts aux) $         addStms =<< segHist lvl' pat w [] [] ops' bucket_fun' arrs       parallelMin [w]-    Op (Stream w arrs Sequential accs lam)+    Op (Stream w arrs accs lam)       | chunk_size_param : _ <- lambdaParams lam -> do           types <- asksScope castScope           ((), stream_stms) <-
src/Futhark/Pass/ExtractKernels/StreamKernel.hs view
@@ -4,13 +4,10 @@  module Futhark.Pass.ExtractKernels.StreamKernel   ( segThreadCapped,-    streamRed,-    streamMap,   ) where  import Control.Monad-import Control.Monad.Writer import Data.List () import Futhark.Analysis.PrimExp import Futhark.IR@@ -60,233 +57,6 @@       BasicOp $         BinOp (Mul Int64 OverflowUndef) num_groups group_size   pure (num_groups, num_threads)--blockedKernelSize ::-  (MonadBuilder m, Rep m ~ GPU) =>-  String ->-  SubExp ->-  m KernelSize-blockedKernelSize desc w = do-  group_size <- getSize (desc ++ "_group_size") SizeGroup--  (_, num_threads) <- numberOfGroups desc w group_size--  per_thread_elements <--    letSubExp "per_thread_elements"-      =<< eBinOp (SDivUp Int64 Unsafe) (eSubExp w) (eSubExp num_threads)--  pure $ KernelSize per_thread_elements num_threads--splitArrays ::-  (MonadBuilder m, Rep m ~ GPU) =>-  VName ->-  [VName] ->-  SplitOrdering ->-  SubExp ->-  SubExp ->-  SubExp ->-  [VName] ->-  m ()-splitArrays chunk_size split_bound ordering w i elems_per_i arrs = do-  letBindNames [chunk_size] $ Op $ SizeOp $ SplitSpace ordering w i elems_per_i-  case ordering of-    SplitContiguous -> do-      offset <- letSubExp "slice_offset" $ BasicOp $ BinOp (Mul Int64 OverflowUndef) i elems_per_i-      zipWithM_ (contiguousSlice offset) split_bound arrs-    SplitStrided stride -> zipWithM_ (stridedSlice stride) split_bound arrs-  where-    contiguousSlice offset slice_name arr = do-      arr_t <- lookupType arr-      let slice = fullSlice arr_t [DimSlice offset (Var chunk_size) (constant (1 :: Int64))]-      letBindNames [slice_name] $ BasicOp $ Index arr slice--    stridedSlice stride slice_name arr = do-      arr_t <- lookupType arr-      let slice = fullSlice arr_t [DimSlice i (Var chunk_size) stride]-      letBindNames [slice_name] $ BasicOp $ Index arr slice--partitionChunkedKernelFoldParameters ::-  Int ->-  [Param dec] ->-  (VName, Param dec, [Param dec], [Param dec])-partitionChunkedKernelFoldParameters num_accs (i_param : chunk_param : params) =-  let (acc_params, arr_params) = splitAt num_accs params-   in (paramName i_param, chunk_param, acc_params, arr_params)-partitionChunkedKernelFoldParameters _ _ =-  error "partitionChunkedKernelFoldParameters: lambda takes too few parameters"--blockedPerThread ::-  (MonadBuilder m, Rep m ~ GPU) =>-  VName ->-  SubExp ->-  KernelSize ->-  StreamOrd ->-  Lambda (Rep m) ->-  Int ->-  [VName] ->-  m ([PatElem Type], [PatElem Type])-blockedPerThread thread_gtid w kernel_size ordering lam num_nonconcat arrs = do-  let (_, chunk_size, [], arr_params) =-        partitionChunkedKernelFoldParameters 0 $ lambdaParams lam--      ordering' =-        case ordering of-          InOrder -> SplitContiguous-          Disorder -> SplitStrided $ kernelNumThreads kernel_size-      red_ts = take num_nonconcat $ lambdaReturnType lam-      map_ts = map rowType $ drop num_nonconcat $ lambdaReturnType lam--  per_thread <- asIntS Int64 $ kernelElementsPerThread kernel_size-  splitArrays-    (paramName chunk_size)-    (map paramName arr_params)-    ordering'-    w-    (Var thread_gtid)-    per_thread-    arrs--  chunk_red_pes <- forM red_ts $ \red_t -> do-    pe_name <- newVName "chunk_fold_red"-    pure $ PatElem pe_name red_t-  chunk_map_pes <- forM map_ts $ \map_t -> do-    pe_name <- newVName "chunk_fold_map"-    pure $ PatElem pe_name $ map_t `arrayOfRow` Var (paramName chunk_size)--  let (chunk_red_ses, chunk_map_ses) =-        splitAt num_nonconcat $ bodyResult $ lambdaBody lam--  addStms $-    bodyStms (lambdaBody lam)-      <> stmsFromList-        [ certify cs $ Let (Pat [pe]) (defAux ()) $ BasicOp $ SubExp se-          | (pe, SubExpRes cs se) <- zip chunk_red_pes chunk_red_ses-        ]-      <> stmsFromList-        [ certify cs $ Let (Pat [pe]) (defAux ()) $ BasicOp $ SubExp se-          | (pe, SubExpRes cs se) <- zip chunk_map_pes chunk_map_ses-        ]--  pure (chunk_red_pes, chunk_map_pes)---- | Given a chunked fold lambda that takes its initial accumulator--- value as parameters, bind those parameters to the neutral element--- instead.-kerneliseLambda ::-  MonadFreshNames m =>-  [SubExp] ->-  Lambda GPU ->-  m (Lambda GPU)-kerneliseLambda nes lam = do-  thread_index_param <- newParam "thread_index" $ Prim int64-  let (fold_chunk_param, fold_acc_params, fold_inp_params) =-        partitionChunkedFoldParameters (length nes) $ lambdaParams lam--      mkAccInit p (Var v)-        | not $ primType $ paramType p =-            mkLet [paramIdent p] $ BasicOp $ Copy v-      mkAccInit p x = mkLet [paramIdent p] $ BasicOp $ SubExp x-      acc_init_stms = stmsFromList $ zipWith mkAccInit fold_acc_params nes-  pure-    lam-      { lambdaBody = insertStms acc_init_stms $ lambdaBody lam,-        lambdaParams = thread_index_param : fold_chunk_param : fold_inp_params-      }--prepareStream ::-  (MonadBuilder m, Rep m ~ GPU) =>-  KernelSize ->-  [(VName, SubExp)] ->-  SubExp ->-  Commutativity ->-  Lambda GPU ->-  [SubExp] ->-  [VName] ->-  m (SubExp, SegSpace, [Type], KernelBody GPU)-prepareStream size ispace w comm fold_lam nes arrs = do-  let (KernelSize elems_per_thread num_threads) = size-  let (ordering, split_ordering) =-        case comm of-          Commutative -> (Disorder, SplitStrided num_threads)-          Noncommutative -> (InOrder, SplitContiguous)--  fold_lam' <- kerneliseLambda nes fold_lam--  gtid <- newVName "gtid"-  space <- mkSegSpace $ ispace ++ [(gtid, num_threads)]-  kbody <- fmap (uncurry (flip (KernelBody ()))) $-    runBuilder $-      localScope (scopeOfSegSpace space) $ do-        (chunk_red_pes, chunk_map_pes) <--          blockedPerThread gtid w size ordering fold_lam' (length nes) arrs-        let concatReturns pe =-              ConcatReturns mempty split_ordering w elems_per_thread $ patElemName pe-        pure-          ( map (Returns ResultMaySimplify mempty . Var . patElemName) chunk_red_pes-              ++ map concatReturns chunk_map_pes-          )--  let (redout_ts, mapout_ts) = splitAt (length nes) $ lambdaReturnType fold_lam-      ts = redout_ts ++ map rowType mapout_ts--  pure (num_threads, space, ts, kbody)--streamRed ::-  (MonadFreshNames m, HasScope GPU m) =>-  MkSegLevel GPU m ->-  Pat Type ->-  SubExp ->-  Commutativity ->-  Lambda GPU ->-  Lambda GPU ->-  [SubExp] ->-  [VName] ->-  m (Stms GPU)-streamRed mk_lvl pat w comm red_lam fold_lam nes arrs = runBuilderT'_ $ do-  -- The strategy here is to rephrase the stream reduction as a-  -- non-segmented SegRed that does explicit chunking within its body.-  -- First, figure out how many threads to use for this.-  size <- blockedKernelSize "stream_red" w--  let (redout_pes, mapout_pes) = splitAt (length nes) $ patElems pat-  (redout_pat, ispace, read_dummy) <- dummyDim $ Pat redout_pes-  let pat' = Pat $ patElems redout_pat ++ mapout_pes--  (_, kspace, ts, kbody) <- prepareStream size ispace w comm fold_lam nes arrs--  lvl <- mk_lvl [w] "stream_red" $ NoRecommendation SegNoVirt-  letBind pat' . Op . SegOp $-    SegRed lvl kspace [SegBinOp comm red_lam nes mempty] ts kbody--  read_dummy---- Similar to streamRed, but without the last reduction.-streamMap ::-  (MonadFreshNames m, HasScope GPU m) =>-  MkSegLevel GPU m ->-  [String] ->-  [PatElem Type] ->-  SubExp ->-  Commutativity ->-  Lambda GPU ->-  [SubExp] ->-  [VName] ->-  m ((SubExp, [VName]), Stms GPU)-streamMap mk_lvl out_desc mapout_pes w comm fold_lam nes arrs = runBuilderT' $ do-  size <- blockedKernelSize "stream_map" w--  (threads, kspace, ts, kbody) <- prepareStream size [] w comm fold_lam nes arrs--  let redout_ts = take (length nes) ts--  redout_pes <- forM (zip out_desc redout_ts) $ \(desc, t) ->-    PatElem <$> newVName desc <*> pure (t `arrayOfRow` threads)--  let pat = Pat $ redout_pes ++ mapout_pes-  lvl <- mk_lvl [w] "stream_map" $ NoRecommendation SegNoVirt-  letBind pat $ Op $ SegOp $ SegMap lvl kspace ts kbody--  pure (threads, map patElemName redout_pes)  -- | Like 'segThread', but cap the thread count to the input size. -- This is more efficient for small kernels, e.g. summing a small
src/Futhark/Pass/ExtractMulticore.hs view
@@ -25,12 +25,10 @@     Stm,   ) import qualified Futhark.IR.SOACS as SOACS-import qualified Futhark.IR.SOACS.Simplify as SOACS import Futhark.Pass import Futhark.Pass.ExtractKernels.DistributeNests import Futhark.Pass.ExtractKernels.ToGPU (injectSOACS) import Futhark.Tools-import qualified Futhark.Transform.FirstOrderTransform as FOT import Futhark.Transform.Rename (Rename, renameSomething) import Futhark.Util (takeLast) import Futhark.Util.Log@@ -163,56 +161,6 @@   body' <- localScope (scopeOfFParams params) $ transformBody body   pure $ FunDef entry attrs name rettype params body' --- Sets the chunk size to one.-unstreamLambda :: Attrs -> [SubExp] -> Lambda SOACS -> ExtractM (Lambda SOACS)-unstreamLambda attrs nes lam = do-  let (chunk_param, acc_params, slice_params) =-        partitionChunkedFoldParameters (length nes) (lambdaParams lam)--  inp_params <- forM slice_params $ \(Param _ p t) ->-    newParam (baseString p) (rowType t)--  body <- runBodyBuilder $-    localScope (scopeOfLParams inp_params) $ do-      letBindNames [paramName chunk_param] $-        BasicOp $-          SubExp $-            intConst Int64 1--      forM_ (zip acc_params nes) $ \(p, ne) ->-        letBindNames [paramName p] $ BasicOp $ SubExp ne--      forM_ (zip slice_params inp_params) $ \(slice, v) ->-        letBindNames [paramName slice] $-          BasicOp $-            ArrayLit [Var $ paramName v] (paramType v)--      (red_res, map_res) <- splitAt (length nes) <$> bodyBind (lambdaBody lam)--      map_res' <- forM map_res $ \(SubExpRes cs se) -> do-        v <- letExp "map_res" $ BasicOp $ SubExp se-        v_t <- lookupType v-        certifying cs . letSubExp "chunk" . BasicOp $-          Index v $-            fullSlice v_t [DimFix $ intConst Int64 0]--      pure $ mkBody mempty $ red_res <> subExpsRes map_res'--  let (red_ts, map_ts) = splitAt (length nes) $ lambdaReturnType lam-      map_lam =-        Lambda-          { lambdaReturnType = red_ts ++ map rowType map_ts,-            lambdaParams = inp_params,-            lambdaBody = body-          }--  soacs_scope <- castScope <$> askScope-  map_lam' <- runReaderT (SOACS.simplifyLambda map_lam) soacs_scope--  if "sequential_inner" `inAttrs` attrs-    then FOT.transformLambda map_lam'-    else pure map_lam'- -- Code generation for each parallel basic block is parameterised over -- how we handle parallelism in the body (whether it's sequentialised -- by keeping it as SOACs, or turned into SegOps).@@ -270,25 +218,6 @@       SegHist () space hists' (lambdaReturnType map_lam) kbody   pure (hists_stms, op') -transformParStream ::-  NeedsRename ->-  (Body SOACS -> ExtractM (Body MC)) ->-  SubExp ->-  Commutativity ->-  Lambda SOACS ->-  [SubExp] ->-  Lambda SOACS ->-  [VName] ->-  ExtractM (Stms MC, SegOp () MC)-transformParStream rename onBody w comm red_lam red_nes map_lam arrs = do-  (gtid, space) <- mkSegSpace w-  kbody <- mapLambdaToKernelBody onBody gtid map_lam arrs-  (red_stms, red) <- reduceToSegBinOp $ Reduce comm red_lam red_nes-  op <--    renameIfNeeded rename $-      SegRed () space [red] (lambdaReturnType map_lam) kbody-  pure (red_stms, op)- transformSOAC :: Pat Type -> Attrs -> SOAC SOACS -> ExtractM (Stms MC) transformSOAC _ _ JVP {} =   error "transformSOAC: unhandled JVP"@@ -368,33 +297,7 @@       pure $         mconcat seq_hist_stms           <> oneStm (Let pat (defAux ()) $ Op $ ParOp Nothing seq_op)-transformSOAC pat attrs (Stream w arrs (Parallel _ comm red_lam) red_nes fold_lam)-  | not $ null red_nes = do-      map_lam <- unstreamLambda attrs red_nes fold_lam-      (seq_red_stms, seq_op) <--        transformParStream-          DoNotRename-          sequentialiseBody-          w-          comm-          red_lam-          red_nes-          map_lam-          arrs--      if lambdaContainsParallelism map_lam-        then do-          (par_red_stms, par_op) <--            transformParStream DoRename transformBody w comm red_lam red_nes map_lam arrs-          pure $-            seq_red_stms-              <> par_red_stms-              <> oneStm (Let pat (defAux ()) $ Op $ ParOp (Just par_op) seq_op)-        else-          pure $-            seq_red_stms-              <> oneStm (Let pat (defAux ()) $ Op $ ParOp Nothing seq_op)-transformSOAC pat _ (Stream w arrs _ nes lam) = do+transformSOAC pat _ (Stream w arrs nes lam) = do   -- Just remove the stream and transform the resulting stms.   soacs_scope <- castScope <$> askScope   stream_stms <-
src/Futhark/Pass/KernelBabysitting.hs view
@@ -8,7 +8,6 @@ import Control.Monad.State.Strict import Data.Foldable import Data.List (elemIndex, isPrefixOf, sort)-import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.Map.Strict as M import Data.Maybe import Futhark.IR@@ -91,7 +90,7 @@       let mapper =             identitySegOpMapper               { mapOnSegOpBody =-                  transformKernelBody expmap (segLevel op) (segSpace op)+                  transformKernelBody expmap (segSpace op)               }       op' <- mapSegOpM mapper op       let stm' = Let pat aux $ Op $ SegOp op'@@ -109,30 +108,22 @@  transformKernelBody ::   ExpMap ->-  SegLevel ->   SegSpace ->   KernelBody GPU ->   BabysitM (KernelBody GPU)-transformKernelBody expmap lvl space kbody = do+transformKernelBody expmap space kbody = do   -- Go spelunking for accesses to arrays that are defined outside the   -- kernel body and where the indices are kernel thread indices.   scope <- askScope   let thread_gids = map fst $ unSegSpace space       thread_local = namesFromList $ segFlat space : thread_gids       free_ker_vars = freeIn kbody `namesSubtract` getKerVariantIds space-  num_threads <--    letSubExp "num_threads" $-      BasicOp $-        BinOp-          (Mul Int64 OverflowUndef)-          (unCount $ segNumGroups lvl)-          (unCount $ segGroupSize lvl)   evalStateT     ( traverseKernelBodyArrayIndexes         free_ker_vars         thread_local         (scope <> scopeOfSegSpace space)-        (ensureCoalescedAccess expmap (unSegSpace space) num_threads)+        (ensureCoalescedAccess expmap (unSegSpace space))         kbody     )     mempty@@ -143,7 +134,6 @@   Names ->   (VName -> Bool) -> -- thread local?   (VName -> SubExp -> Bool) -> -- variant to a certain gid (given as first param)?-  (SubExp -> Maybe SubExp) -> -- split substitution?   Scope GPU -> -- type environment   VName ->   Slice SubExp ->@@ -162,29 +152,27 @@     <$> mapM       ( onStm           ( varianceInStms mempty kstms,-            mkSizeSubsts kstms,             outer_scope           )       )       (stmsToList kstms)     <*> pure kres   where-    onLambda (variance, szsubst, scope) lam =+    onLambda (variance, scope) lam =       (\body' -> lam {lambdaBody = body'})-        <$> onBody (variance, szsubst, scope') (lambdaBody lam)+        <$> onBody (variance, scope') (lambdaBody lam)       where         scope' = scope <> scopeOfLParams (lambdaParams lam) -    onBody (variance, szsubst, scope) (Body bdec stms bres) = do-      stms' <- stmsFromList <$> mapM (onStm (variance', szsubst', scope')) (stmsToList stms)+    onBody (variance, scope) (Body bdec stms bres) = do+      stms' <- stmsFromList <$> mapM (onStm (variance', scope')) (stmsToList stms)       pure $ Body bdec stms' bres       where         variance' = varianceInStms variance stms-        szsubst' = mkSizeSubsts stms <> szsubst         scope' = scope <> scopeOf stms -    onStm (variance, szsubst, _) (Let pat dec (BasicOp (Index arr is))) =-      Let pat dec . oldOrNew <$> f free_ker_vars isThreadLocal isGidVariant sizeSubst outer_scope arr is+    onStm (variance, _) (Let pat dec (BasicOp (Index arr is))) =+      Let pat dec . oldOrNew <$> f free_ker_vars isThreadLocal isGidVariant outer_scope arr is       where         oldOrNew Nothing =           BasicOp $ Index arr is@@ -198,14 +186,8 @@         isThreadLocal v =           thread_variant             `namesIntersect` M.findWithDefault (oneName v) v variance--        sizeSubst (Constant v) = Just $ Constant v-        sizeSubst (Var v)-          | v `M.member` outer_scope = Just $ Var v-          | Just v' <- M.lookup v szsubst = sizeSubst v'-          | otherwise = Nothing-    onStm (variance, szsubst, scope) (Let pat dec e) =-      Let pat dec <$> mapExpM (mapper (variance, szsubst, scope)) e+    onStm (variance, scope) (Let pat dec e) =+      Let pat dec <$> mapExpM (mapper (variance, scope)) e      onOp ctx (OtherOp soac) =       OtherOp <$> mapSOACM identitySOACMapper {mapOnSOACLambda = onLambda ctx} soac@@ -217,28 +199,19 @@           mapOnOp = onOp ctx         } -    mkSizeSubsts = foldMap mkStmSizeSubst-      where-        mkStmSizeSubst (Let (Pat [pe]) _ (Op (SizeOp (SplitSpace _ _ _ elems_per_i)))) =-          M.singleton (patElemName pe) elems_per_i-        mkStmSizeSubst _ = mempty- type Replacements = M.Map (VName, Slice SubExp) VName  ensureCoalescedAccess ::   MonadBuilder m =>   ExpMap ->   [(VName, SubExp)] ->-  SubExp ->   ArrayIndexTransform (StateT Replacements m) ensureCoalescedAccess   expmap   thread_space-  num_threads   free_ker_vars   isThreadLocal   isGidVariant-  sizeSubst   outer_scope   arr   slice = do@@ -302,28 +275,6 @@             let perm = coalescingPermutation (length is) $ arrayRank t             replace =<< lift (rearrangeInput (nonlinearInMemory arr expmap) perm arr) -        -- We are taking a slice of the array with a unit stride.  We-        -- assume that the slice will be traversed sequentially.-        ---        -- We will really want to treat the sliced dimension like two-        -- dimensions so we can transpose them.  This may require-        -- padding.-        | (is, rem_slice) <- splitSlice slice,-          and $ zipWith (==) is $ map Var thread_gids,-          DimSlice offset len (Constant stride) : _ <- unSlice rem_slice,-          isThreadLocalSubExp offset,-          Just {} <- sizeSubst len,-          oneIsh stride -> do-            let num_chunks =-                  if null is-                    then untyped $ pe32 num_threads-                    else-                      untyped $-                        product $-                          map pe64 $-                            drop (length is) thread_gdims-            replace =<< lift (rearrangeSlice (length is) (arraySize (length is) t) num_chunks arr)-         -- Everything is fine... assuming that the array is in row-major         -- order!  Make sure that is the case.         | Just {} <- nonlinearInMemory arr expmap ->@@ -336,15 +287,12 @@               _ -> replace =<< lift (rowMajorArray arr)       _ -> pure Nothing     where-      (thread_gids, thread_gdims) = unzip thread_space+      (thread_gids, _thread_gdims) = unzip thread_space        replace arr' = do         modify $ M.insert (arr, slice) arr'         pure $ Just (arr', slice) -      isThreadLocalSubExp (Var v) = isThreadLocal v-      isThreadLocalSubExp Constant {} = False- -- Heuristic for avoiding rearranging too small arrays. tooSmallSlice :: Int32 -> Slice SubExp -> Bool tooSmallSlice bs = fst . foldl comb (True, bs) . sliceDims@@ -449,68 +397,6 @@ rowMajorArray arr = do   rank <- arrayRank <$> lookupType arr   letExp (baseString arr ++ "_rowmajor") $ BasicOp $ Manifest [0 .. rank - 1] arr--rearrangeSlice ::-  MonadBuilder m =>-  Int ->-  SubExp ->-  PrimExp VName ->-  VName ->-  m VName-rearrangeSlice d w num_chunks arr = do-  num_chunks' <- toSubExp "num_chunks" num_chunks--  (w_padded, padding) <- paddedScanReduceInput w num_chunks'--  per_chunk <--    letSubExp "per_chunk" $-      BasicOp $-        BinOp (SQuot Int64 Unsafe) w_padded num_chunks'-  arr_t <- lookupType arr-  arr_padded <- padArray w_padded padding arr_t-  rearrange num_chunks' w_padded per_chunk (baseString arr) arr_padded arr_t-  where-    padArray w_padded padding arr_t = do-      let arr_shape = arrayShape arr_t-          padding_shape = setDim d arr_shape padding-      arr_padding <--        letExp (baseString arr <> "_padding") . BasicOp $-          Scratch (elemType arr_t) (shapeDims padding_shape)-      letExp (baseString arr <> "_padded") . BasicOp $-        Concat d (arr :| [arr_padding]) w_padded--    rearrange num_chunks' w_padded per_chunk arr_name arr_padded arr_t = do-      let arr_dims = arrayDims arr_t-          pre_dims = take d arr_dims-          post_dims = drop (d + 1) arr_dims-          extradim_shape = Shape $ pre_dims ++ [num_chunks', per_chunk] ++ post_dims-          tr_perm = [0 .. d - 1] ++ map (+ d) ([1] ++ [2 .. shapeRank extradim_shape - 1 - d] ++ [0])-      arr_extradim <--        letExp (arr_name <> "_extradim") . BasicOp $-          Reshape ReshapeArbitrary extradim_shape arr_padded-      arr_extradim_tr <--        letExp (arr_name <> "_extradim_tr") . BasicOp $-          Manifest tr_perm arr_extradim-      arr_inv_tr <--        letExp (arr_name <> "_inv_tr") . BasicOp $-          Reshape-            ReshapeArbitrary-            (Shape $ pre_dims ++ w_padded : post_dims)-            arr_extradim_tr-      letExp (arr_name <> "_inv_tr_init")-        =<< eSliceArray d arr_inv_tr (eSubExp $ constant (0 :: Int64)) (eSubExp w)--paddedScanReduceInput ::-  MonadBuilder m =>-  SubExp ->-  SubExp ->-  m (SubExp, SubExp)-paddedScanReduceInput w stride = do-  w_padded <--    letSubExp "padded_size"-      =<< eRoundToMultipleOf Int64 (eSubExp w) (eSubExp stride)-  padding <- letSubExp "padding" $ BasicOp $ BinOp (Sub Int64 OverflowUndef) w_padded w-  pure (w_padded, padding)  --- Computing variance. 
src/Futhark/Test/Spec.hs view
@@ -45,6 +45,7 @@ import Futhark.Util.Pretty (prettyOneLine) import System.Exit import System.FilePath+import System.IO import System.IO.Error import Text.Megaparsec hiding (many, some) import Text.Megaparsec.Char@@ -407,7 +408,7 @@   spec_or_err <- testSpecFromProgram prog   case spec_or_err of     Left err -> do-      putStrLn err+      hPutStrLn stderr err       exitFailure     Right spec -> pure spec @@ -443,7 +444,7 @@   specs_or_err <- testSpecsFromPaths dirs   case specs_or_err of     Left err -> do-      putStrLn err+      hPutStrLn stderr err       exitFailure     Right specs -> pure specs @@ -462,6 +463,6 @@   spec_or_err <- testSpecFromFile dirs   case spec_or_err of     Left err -> do-      putStrLn err+      hPutStrLn stderr err       exitFailure     Right spec -> pure spec
src/Futhark/Transform/FirstOrderTransform.hs view
@@ -230,7 +230,7 @@     (++ patNames pat)       <$> replicateM (length scanacc_params) (newVName "discard")   letBindNames names $ DoLoop merge loopform loop_body-transformSOAC pat (Stream w arrs _ nes lam) = do+transformSOAC pat (Stream w arrs nes lam) = do   -- Create a loop that repeatedly applies the lambda body to a   -- chunksize of 1.  Hopefully this will lead to this outer loop   -- being the only one, as all the innermost one can be simplified
src/Language/Futhark/Parser/Lexer.x view
@@ -67,7 +67,7 @@                                       map (T.drop 3 . T.stripStart) .                                            T.split (== '\n') . ("--"<>) .                                            T.drop 4 }-  "--".*                            ;+  "--".*                   { tokenS COMMENT }   "="                      { tokenC EQU }   "("                      { tokenC LPAR }   ")"                      { tokenC RPAR }
src/Language/Futhark/Parser/Lexer/Tokens.hs view
@@ -54,6 +54,7 @@ -- with a source position. data Token   = ID Name+  | COMMENT T.Text   | INDEXING Name   | QUALINDEXING [Name] Name   | QUALPAREN [Name] Name
src/Language/Futhark/Parser/Monad.hs view
@@ -246,6 +246,9 @@             xs -> do               putTokens (xs, pos')               lexer cont+    (L _ (COMMENT _) : xs) -> do+      putTokens (xs, pos)+      lexer cont     (x : xs) -> do       putTokens (xs, pos)       cont x
src/Language/Futhark/Parser/Parser.y view
@@ -39,7 +39,7 @@ import Language.Futhark.Syntax hiding (ID) import Language.Futhark.Prop import Language.Futhark.Pretty-import Language.Futhark.Parser.Lexer+import Language.Futhark.Parser.Lexer (Token(..)) import Futhark.Util.Pretty import Futhark.Util.Loc import Language.Futhark.Parser.Monad@@ -564,66 +564,22 @@      | Exp2 %prec ':'   { $1 }  Exp2 :: { UncheckedExp }-     : if Exp then Exp else Exp %prec ifprec-                      { AppExp (If $2 $4 $6 (srcspan $1 $>)) NoInfo }--     | loop Pat LoopForm do Exp %prec ifprec-         {% fmap (\t -> AppExp (DoLoop [] $2 t $3 $5 (srcspan $1 $>)) NoInfo) (patternExp $2) }--     | loop Pat '=' Exp LoopForm do Exp %prec ifprec-         { AppExp (DoLoop [] $2 $4 $5 $7 (srcspan $1 $>)) NoInfo }-+     : IfExp                { $1 }+     | LoopExp              { $1 }      | LetExp %prec letprec { $1 }--     | MatchExp { $1 }+     | MatchExp             { $1 }       | assert Atom Atom    { Assert $2 $3 NoInfo (srcspan $1 $>) }      | '#[' AttrInfo ']' Exp %prec bottom                            { Attr $2 $4 (srcspan $1 $>) } -     | Exp2 '+...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '-...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '-' Exp2       { binOp $1 (L $2 (SYMBOL Minus [] (nameFromString "-"))) $3 }-     | Exp2 '*...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '*' Exp2       { binOp $1 (L $2 (SYMBOL Times [] (nameFromString "*"))) $3 }-     | Exp2 '/...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '%...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '//...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '%%...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '**...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '>>...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '<<...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '&...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '|...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '|' Exp2       { binOp $1 (L $2 (SYMBOL Bor [] (nameFromString "|"))) $3 }-     | Exp2 '&&...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '||...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '^...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '^' Exp2       { binOp $1 (L $2 (SYMBOL Xor [] (nameFromString "^"))) $3 }-     | Exp2 '==...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '!=...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '<...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '<=...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '>...' Exp2    { binOp $1 $2 $3 }-     | Exp2 '>=...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '|>...' Exp2   { binOp $1 $2 $3 }-     | Exp2 '<|...' Exp2   { binOp $1 $2 $3 }--     | Exp2 '<' Exp2              { binOp $1 (L $2 (SYMBOL Less [] (nameFromString "<"))) $3 }-     | Exp2 '`' QualName '`' Exp2 { AppExp (BinOp (second srclocOf $3) NoInfo ($1, NoInfo) ($5, NoInfo) (srcspan $1 $>)) NoInfo }--     | Exp2 '...' Exp2           { AppExp (Range $1 Nothing (ToInclusive $3) (srcspan $1 $>)) NoInfo }-     | Exp2 '..<' Exp2           { AppExp (Range $1 Nothing (UpToExclusive $3) (srcspan $1 $>)) NoInfo }-     | Exp2 '..>' Exp2           { AppExp (Range $1 Nothing (DownToExclusive $3)  (srcspan $1 $>)) NoInfo }-     | Exp2 '..' Exp2 '...' Exp2 { AppExp (Range $1 (Just $3) (ToInclusive $5) (srcspan $1 $>)) NoInfo }-     | Exp2 '..' Exp2 '..<' Exp2 { AppExp (Range $1 (Just $3) (UpToExclusive $5) (srcspan $1 $>)) NoInfo }-     | Exp2 '..' Exp2 '..>' Exp2 { AppExp (Range $1 (Just $3) (DownToExclusive $5) (srcspan $1 $>)) NoInfo }+     | BinOpExp                  { $1 }+     | RangeExp                  { $1 }      | Exp2 '..' Atom            {% twoDotsRange $2 }      | Atom '..' Exp2            {% twoDotsRange $2 }      | '-' Exp2  %prec juxtprec  { Negate $2 (srcspan $1 $>) }      | '!' Exp2 %prec juxtprec   { Not $2 (srcspan $1 $>) } -      | Exp2 with '[' DimIndices ']' '=' Exp2        { Update $1 $4 $7 (srcspan $1 $>) } @@ -633,10 +589,7 @@      | '\\' FunParams1 maybeAscription(TypeExpTerm) '->' Exp %prec letprec        { Lambda (fst $2 : snd $2) $5 $3 NoInfo (srcspan $1 $>) } -     | Apply_ { $1 }--Apply_ :: { UncheckedExp }-       : ApplyList {% applyExp $1 }+     | ApplyList {% applyExp $1 }  ApplyList :: { [UncheckedExp] }           : ApplyList Atom %prec juxtprec@@ -676,25 +629,7 @@        { let L loc (QUALPAREN qs name) = $1 in          QualParens (QualName qs name, srclocOf loc) $2 (srcspan $1 $>) } -     -- Operator sections.-     | '(' '-' ')'-        { OpSection (qualName (nameFromString "-")) NoInfo (srcspan $1 $>) }-     | '(' Exp2 '-' ')'-       { OpSectionLeft (qualName (nameFromString "-"))-         NoInfo $2 (NoInfo, NoInfo) (NoInfo, NoInfo) (srcspan $1 $>) }-     | '(' BinOp Exp2 ')'-       { OpSectionRight (fst $2) NoInfo $3 (NoInfo, NoInfo) NoInfo (srcspan $1 $>) }-     | '(' Exp2 BinOp ')'-       { OpSectionLeft (fst $3) NoInfo $2 (NoInfo, NoInfo) (NoInfo, NoInfo) (srcspan $1 $>) }-     | '(' BinOp ')'-       { OpSection (fst $2) NoInfo (srcspan $1 $>) }--     | '(' FieldAccess FieldAccesses ')'-       { ProjectSection (map fst ($2:$3)) NoInfo (srcspan $1 $>) }--     | '(' '.' '[' DimIndices ']' ')'-       { IndexSection $4 NoInfo (srcspan $1 $>) }-+     | SectionExp { $1 }  NumLit :: { (PrimValue, Loc) }         : i8lit   { let L loc (I8LIT num)  = $1 in (SignedValue $ Int8Value num, loc) }@@ -771,6 +706,74 @@     | def {% parseErrorAt $1 (Just "Unexpected \"def\" - missing \"in\"?") }     | type {% parseErrorAt $1 (Just "Unexpected \"type\" - missing \"in\"?") }     | module {% parseErrorAt $1 (Just "Unexpected \"module\" - missing \"in\"?") }++BinOpExp :: { UncheckedExp }+  : Exp2 '+...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '-...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '-' Exp2       { binOp $1 (L $2 (SYMBOL Minus [] (nameFromString "-"))) $3 }+  | Exp2 '*...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '*' Exp2       { binOp $1 (L $2 (SYMBOL Times [] (nameFromString "*"))) $3 }+  | Exp2 '/...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '%...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '//...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '%%...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '**...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '>>...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '<<...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '&...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '|...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '|' Exp2       { binOp $1 (L $2 (SYMBOL Bor [] (nameFromString "|"))) $3 }+  | Exp2 '&&...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '||...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '^...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '^' Exp2       { binOp $1 (L $2 (SYMBOL Xor [] (nameFromString "^"))) $3 }+  | Exp2 '==...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '!=...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '<...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '<=...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '>...' Exp2    { binOp $1 $2 $3 }+  | Exp2 '>=...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '|>...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '<|...' Exp2   { binOp $1 $2 $3 }+  | Exp2 '<' Exp2              { binOp $1 (L $2 (SYMBOL Less [] (nameFromString "<"))) $3 }+  | Exp2 '`' QualName '`' Exp2 { AppExp (BinOp (second srclocOf $3) NoInfo ($1, NoInfo) ($5, NoInfo) (srcspan $1 $>)) NoInfo }++SectionExp :: { UncheckedExp }+  : '(' '-' ')'+     { OpSection (qualName (nameFromString "-")) NoInfo (srcspan $1 $>) }+  | '(' Exp2 '-' ')'+    { OpSectionLeft (qualName (nameFromString "-"))+      NoInfo $2 (NoInfo, NoInfo) (NoInfo, NoInfo) (srcspan $1 $>) }+  | '(' BinOp Exp2 ')'+    { OpSectionRight (fst $2) NoInfo $3 (NoInfo, NoInfo) NoInfo (srcspan $1 $>) }+  | '(' Exp2 BinOp ')'+    { OpSectionLeft (fst $3) NoInfo $2 (NoInfo, NoInfo) (NoInfo, NoInfo) (srcspan $1 $>) }+  | '(' BinOp ')'+    { OpSection (fst $2) NoInfo (srcspan $1 $>) }++  | '(' FieldAccess FieldAccesses ')'+    { ProjectSection (map fst ($2:$3)) NoInfo (srcspan $1 $>) }++  | '(' '.' '[' DimIndices ']' ')'+    { IndexSection $4 NoInfo (srcspan $1 $>) }++RangeExp :: { UncheckedExp }+  : Exp2 '...' Exp2           { AppExp (Range $1 Nothing (ToInclusive $3) (srcspan $1 $>)) NoInfo }+  | Exp2 '..<' Exp2           { AppExp (Range $1 Nothing (UpToExclusive $3) (srcspan $1 $>)) NoInfo }+  | Exp2 '..>' Exp2           { AppExp (Range $1 Nothing (DownToExclusive $3)  (srcspan $1 $>)) NoInfo }+  | Exp2 '..' Exp2 '...' Exp2 { AppExp (Range $1 (Just $3) (ToInclusive $5) (srcspan $1 $>)) NoInfo }+  | Exp2 '..' Exp2 '..<' Exp2 { AppExp (Range $1 (Just $3) (UpToExclusive $5) (srcspan $1 $>)) NoInfo }+  | Exp2 '..' Exp2 '..>' Exp2 { AppExp (Range $1 (Just $3) (DownToExclusive $5) (srcspan $1 $>)) NoInfo }++IfExp :: { UncheckedExp }+       : if Exp then Exp else Exp %prec ifprec+         { AppExp (If $2 $4 $6 (srcspan $1 $>)) NoInfo }++LoopExp :: { UncheckedExp }+         : loop Pat LoopForm do Exp %prec ifprec+           {% fmap (\t -> AppExp (DoLoop [] $2 t $3 $5 (srcspan $1 $>)) NoInfo) (patternExp $2) }+         | loop Pat '=' Exp LoopForm do Exp %prec ifprec+           { AppExp (DoLoop [] $2 $4 $5 $7 (srcspan $1 $>)) NoInfo }  MatchExp :: { UncheckedExp }           : match Exp Cases
src/Language/Futhark/Prop.hs view
@@ -919,46 +919,6 @@                          ]                    )                ),-               ( "map_stream",-                 IntrinsicPolyFun-                   [tp_a, tp_b, sp_n]-                   [ Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` arr_kb),-                     arr_a $ shape [n]-                   ]-                   $ RetType []-                   $ uarr_b-                   $ shape [n]-               ),-               ( "map_stream_per",-                 IntrinsicPolyFun-                   [tp_a, tp_b, sp_n]-                   [ Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` arr_kb),-                     arr_a $ shape [n]-                   ]-                   $ RetType []-                   $ uarr_b-                   $ shape [n]-               ),-               ( "reduce_stream",-                 IntrinsicPolyFun-                   [tp_a, tp_b, sp_n]-                   [ Scalar t_b `arr` (Scalar t_b `arr` Scalar t_b),-                     Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` Scalar t_b),-                     arr_a $ shape [n]-                   ]-                   $ RetType []-                   $ Scalar t_b-               ),-               ( "reduce_stream_per",-                 IntrinsicPolyFun-                   [tp_a, tp_b, sp_n]-                   [ Scalar t_b `arr` (Scalar t_b `arr` Scalar t_b),-                     Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` Scalar t_b),-                     arr_a $ shape [n]-                   ]-                   $ RetType []-                   $ Scalar t_b-               ),                ( "acc_write",                  IntrinsicPolyFun                    [sp_k, tp_a]@@ -1143,8 +1103,6 @@      arr_ka = Array () Nonunique (Shape [NamedSize $ qualName k]) t_a     uarr_ka = Array () Unique (Shape [NamedSize $ qualName k]) t_a-    arr_kb = Array () Nonunique (Shape [NamedSize $ qualName k]) t_b-    karr x y = Scalar $ Arrow mempty (Named k) x (RetType [] y)      accType t =       TypeVar () Unique (qualName (fst intrinsicAcc)) [TypeArgType t mempty]
unittests/Futhark/IR/Mem/IxFunTests.hs view
@@ -5,7 +5,7 @@   ) where -import qualified Data.List as DL+import qualified Data.List as L import qualified Futhark.IR.Mem.IxFun as IxFunLMAD import qualified Futhark.IR.Mem.IxFun.Alg as IxFunAlg import Futhark.IR.Mem.IxFunWrapper@@ -30,7 +30,7 @@ allPoints :: [Int] -> [[Int]] allPoints dims =   let total = product dims-      strides = drop 1 $ DL.reverse $ scanl (*) 1 $ DL.reverse dims+      strides = drop 1 $ L.reverse $ scanl (*) 1 $ L.reverse dims    in map (unflatInd strides) [0 .. total - 1]   where     unflatInd :: [Int] -> Int -> [Int]