futhark 0.21.13 → 0.21.14
raw patch · 119 files changed
+5035/−5156 lines, 119 filesdep ~base
Dependency ranges changed: base
Files
- futhark.cabal +10/−3
- prelude/array.fut +6/−1
- rts/c/context.h +41/−0
- rts/c/context_prototypes.h +11/−0
- rts/c/ispc_util.h +15/−16
- rts/c/util.h +0/−18
- src/Futhark/AD/Fwd.hs +6/−6
- src/Futhark/AD/Rev.hs +16/−16
- src/Futhark/AD/Rev/Monad.hs +2/−2
- src/Futhark/Analysis/Alias.hs +11/−10
- src/Futhark/Analysis/DataDependencies.hs +13/−9
- src/Futhark/Analysis/HORep/SOAC.hs +93/−110
- src/Futhark/Analysis/Interference.hs +12/−16
- src/Futhark/Analysis/LastUse.hs +12/−7
- src/Futhark/Analysis/MemAlias.hs +4/−6
- src/Futhark/Analysis/Metrics.hs +6/−1
- src/Futhark/Analysis/PrimExp.hs +9/−0
- src/Futhark/Analysis/PrimExp/Generalize.hs +0/−73
- src/Futhark/Analysis/SymbolTable.hs +3/−2
- src/Futhark/Analysis/UsageTable.hs +2/−2
- src/Futhark/Builder.hs +1/−1
- src/Futhark/CLI/Dev.hs +6/−6
- src/Futhark/CLI/Main.hs +1/−1
- src/Futhark/CLI/Test.hs +3/−22
- src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs +2/−0
- src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs +2/−0
- src/Futhark/CodeGen/Backends/GenericC.hs +743/−2536
- src/Futhark/CodeGen/Backends/GenericC/Code.hs +356/−0
- src/Futhark/CodeGen/Backends/GenericC/EntryPoints.hs +264/−0
- src/Futhark/CodeGen/Backends/GenericC/Monad.hs +671/−0
- src/Futhark/CodeGen/Backends/GenericC/Types.hs +586/−0
- src/Futhark/CodeGen/Backends/MulticoreC.hs +2/−0
- src/Futhark/CodeGen/Backends/MulticoreISPC.hs +2/−4
- src/Futhark/CodeGen/Backends/SequentialC/Boilerplate.hs +2/−0
- src/Futhark/CodeGen/ImpCode.hs +5/−3
- src/Futhark/CodeGen/ImpGen.hs +45/−27
- src/Futhark/CodeGen/ImpGen/GPU.hs +14/−9
- src/Futhark/CodeGen/ImpGen/GPU/Base.hs +149/−110
- src/Futhark/CodeGen/ImpGen/GPU/SegHist.hs +33/−33
- src/Futhark/CodeGen/ImpGen/GPU/SegMap.hs +2/−2
- src/Futhark/CodeGen/ImpGen/GPU/SegRed.hs +12/−12
- src/Futhark/CodeGen/ImpGen/GPU/SegScan.hs +1/−1
- src/Futhark/CodeGen/ImpGen/GPU/SegScan/SinglePass.hs +4/−4
- src/Futhark/CodeGen/ImpGen/GPU/SegScan/TwoPass.hs +7/−7
- src/Futhark/CodeGen/ImpGen/Multicore.hs +1/−1
- src/Futhark/CodeGen/ImpGen/Multicore/Base.hs +4/−4
- src/Futhark/CodeGen/ImpGen/Multicore/SegHist.hs +13/−13
- src/Futhark/CodeGen/ImpGen/Multicore/SegMap.hs +3/−3
- src/Futhark/CodeGen/ImpGen/Multicore/SegRed.hs +2/−2
- src/Futhark/CodeGen/ImpGen/Multicore/SegScan.hs +3/−3
- src/Futhark/CodeGen/RTS/C.hs +12/−0
- src/Futhark/Construct.hs +75/−28
- src/Futhark/IR/Aliases.hs +6/−1
- src/Futhark/IR/Mem.hs +35/−28
- src/Futhark/IR/Mem/IxFun.hs +104/−285
- src/Futhark/IR/Mem/Simplify.hs +10/−9
- src/Futhark/IR/Parse.hs +46/−28
- src/Futhark/IR/Pretty.hs +30/−13
- src/Futhark/IR/Prop.hs +3/−3
- src/Futhark/IR/Prop/Aliases.hs +11/−15
- src/Futhark/IR/Prop/Names.hs +5/−5
- src/Futhark/IR/Prop/Reshape.hs +7/−80
- src/Futhark/IR/Prop/Scope.hs +7/−7
- src/Futhark/IR/Prop/TypeOf.hs +4/−5
- src/Futhark/IR/SOACS.hs +2/−1
- src/Futhark/IR/SOACS/SOAC.hs +1/−1
- src/Futhark/IR/SOACS/Simplify.hs +13/−17
- src/Futhark/IR/SegOp.hs +2/−5
- src/Futhark/IR/Syntax.hs +43/−53
- src/Futhark/IR/Traversals.hs +18/−16
- src/Futhark/IR/TypeCheck.hs +23/−26
- src/Futhark/Internalise/Exps.hs +131/−141
- src/Futhark/LSP/Handlers.hs +0/−1
- src/Futhark/MonadFreshNames.hs +5/−5
- src/Futhark/Optimise/BlkRegTiling.hs +6/−4
- src/Futhark/Optimise/EntryPointMem.hs +81/−0
- src/Futhark/Optimise/Fusion.hs +7/−8
- src/Futhark/Optimise/Fusion/GraphRep.hs +11/−8
- src/Futhark/Optimise/Fusion/TryFusion.hs +7/−10
- src/Futhark/Optimise/GenRedOpt.hs +2/−2
- src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs +1/−2
- src/Futhark/Optimise/MemoryBlockMerging.hs +14/−25
- src/Futhark/Optimise/MergeGPUBodies.hs +8/−5
- src/Futhark/Optimise/ReduceDeviceSyncs.hs +42/−39
- src/Futhark/Optimise/ReduceDeviceSyncs/MigrationTable.hs +57/−59
- src/Futhark/Optimise/Simplify/Engine.hs +167/−144
- src/Futhark/Optimise/Simplify/Rep.hs +2/−2
- src/Futhark/Optimise/Simplify/Rule.hs +24/−23
- src/Futhark/Optimise/Simplify/Rules.hs +7/−222
- src/Futhark/Optimise/Simplify/Rules/BasicOp.hs +10/−14
- src/Futhark/Optimise/Simplify/Rules/ClosedForm.hs +8/−8
- src/Futhark/Optimise/Simplify/Rules/Index.hs +9/−12
- src/Futhark/Optimise/Simplify/Rules/Match.hs +251/−0
- src/Futhark/Optimise/Simplify/Rules/Simple.hs +21/−32
- src/Futhark/Optimise/Sink.hs +17/−11
- src/Futhark/Optimise/TileLoops.hs +3/−2
- src/Futhark/Optimise/TileLoops/Shared.hs +4/−4
- src/Futhark/Pass/ExpandAllocations.hs +17/−24
- src/Futhark/Pass/ExplicitAllocations.hs +306/−282
- src/Futhark/Pass/ExplicitAllocations/GPU.hs +1/−1
- src/Futhark/Pass/ExtractKernels.hs +16/−13
- src/Futhark/Pass/ExtractKernels/BlockedKernel.hs +1/−1
- src/Futhark/Pass/ExtractKernels/DistributeNests.hs +8/−11
- src/Futhark/Pass/ExtractKernels/Interchange.hs +8/−9
- src/Futhark/Pass/ExtractKernels/Intragroup.hs +5/−6
- src/Futhark/Pass/ExtractMulticore.hs +4/−2
- src/Futhark/Pass/KernelBabysitting.hs +15/−19
- src/Futhark/Passes.hs +5/−0
- src/Futhark/Tools.hs +3/−4
- src/Futhark/Transform/Substitute.hs +0/−3
- src/Futhark/Util/Log.hs +3/−3
- src/Language/Futhark/FreeVars.hs +0/−1
- src/Language/Futhark/Interpreter.hs +4/−2
- src/Language/Futhark/TypeChecker.hs +2/−1
- src/Language/Futhark/TypeChecker/Terms.hs +6/−1
- unittests/Futhark/IR/Mem/IxFun/Alg.hs +22/−24
- unittests/Futhark/IR/Mem/IxFunTests.hs +24/−161
- unittests/Futhark/IR/Mem/IxFunWrapper.hs +8/−10
- unittests/Futhark/IR/Prop/ReshapeTests.hs +14/−67
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: futhark-version: 0.21.13+version: 0.21.14 synopsis: An optimising compiler for a functional, array-oriented language. description: Futhark is a small programming language designed to be compiled to@@ -37,6 +37,8 @@ -- Cabal's recompilation tracking doesn't work when we use wildcards -- here, so for now we spell out every single file. rts/c/atomics.h+ rts/c/context.h+ rts/c/context_prototypes.h rts/c/lock.h rts/c/timing.h rts/c/errors.h@@ -116,7 +118,6 @@ Futhark.Analysis.Metrics.Type Futhark.Analysis.PrimExp Futhark.Analysis.PrimExp.Convert- Futhark.Analysis.PrimExp.Generalize Futhark.Analysis.PrimExp.Parse Futhark.Analysis.PrimExp.Simplify Futhark.Analysis.Rephrase@@ -157,8 +158,12 @@ Futhark.CodeGen.Backends.COpenCL.Boilerplate Futhark.CodeGen.Backends.GenericC Futhark.CodeGen.Backends.GenericC.CLI+ Futhark.CodeGen.Backends.GenericC.Code+ Futhark.CodeGen.Backends.GenericC.EntryPoints+ Futhark.CodeGen.Backends.GenericC.Monad Futhark.CodeGen.Backends.GenericC.Options Futhark.CodeGen.Backends.GenericC.Server+ Futhark.CodeGen.Backends.GenericC.Types Futhark.CodeGen.Backends.GenericPython Futhark.CodeGen.Backends.GenericPython.AST Futhark.CodeGen.Backends.GenericPython.Options@@ -271,6 +276,7 @@ Futhark.Optimise.BlkRegTiling Futhark.Optimise.CSE Futhark.Optimise.DoubleBuffer+ Futhark.Optimise.EntryPointMem Futhark.Optimise.Fusion Futhark.Optimise.Fusion.Composing Futhark.Optimise.Fusion.GraphRep@@ -296,6 +302,7 @@ Futhark.Optimise.Simplify.Rules.ClosedForm Futhark.Optimise.Simplify.Rules.Index Futhark.Optimise.Simplify.Rules.Loop+ Futhark.Optimise.Simplify.Rules.Match Futhark.Optimise.Simplify.Rules.Simple Futhark.Optimise.Sink Futhark.Optimise.TileLoops@@ -388,7 +395,7 @@ aeson >=2.0.0.0 , ansi-terminal >=0.6.3.1 , array >=0.4- , base >=4.13 && <5+ , base >=4.15 && <5 , base16-bytestring , binary >=0.8.3 , blaze-html >=0.9.0.1
prelude/array.fut view
@@ -77,7 +77,12 @@ -- -- For example, if `b==rotate r a`, then `b[x] = a[x+r]`. ----- **Complexity:** O(1).+-- **Work:** O(n).+--+-- **Span:** O(1).+--+-- Note: In most cases, `rotate` will be fused with subsequent+-- operations such as `map`, in which case it is free. def rotate [n] 't (r: i64) (xs: [n]t): [n]t = intrinsics.rotate (r, xs) -- | Construct an array of consecutive integers of the given length,
+ rts/c/context.h view
@@ -0,0 +1,41 @@+// Start of context.h++// Eventually it would be nice to move the context definition in here+// instead of generating it in the compiler. For now it defines+// various helper functions that must be available.++// Internal functions.++static void set_error(struct futhark_context* ctx, char *error) {+ lock_lock(&ctx->error_lock);+ if (ctx->error == NULL) {+ ctx->error = error;+ } else {+ free(error);+ }+ lock_unlock(&ctx->error_lock);+}++// XXX: should be static, but used in ispc_util.h+void lexical_realloc_error(struct futhark_context* ctx, size_t new_size) {+ set_error(ctx,+ msgprintf("Failed to allocate memory.\nAttempted allocation: %12lld bytes\n",+ (long long) new_size));+}++static int lexical_realloc(struct futhark_context *ctx,+ unsigned char **ptr,+ int64_t *old_size,+ int64_t new_size) {+ unsigned char *new = realloc(*ptr, (size_t)new_size);+ if (new == NULL) {+ lexical_realloc_error(ctx, new_size);+ return FUTHARK_OUT_OF_MEMORY;+ } else {+ *ptr = new;+ *old_size = new_size;+ return FUTHARK_SUCCESS;+ }+}++// End of context.h
+ rts/c/context_prototypes.h view
@@ -0,0 +1,11 @@+// Start of context_prototypes.h+//+// Prototypes for the functions in prototypes.h that need to be+// available very early.++struct futhark_context_config;+struct futhark_context;++static void set_error(struct futhark_context* ctx, char *error);++// End of of context_prototypes.h
rts/c/ispc_util.h view
@@ -164,16 +164,15 @@ } extern "C" unmasked uniform unsigned char * uniform realloc(uniform unsigned char * uniform ptr, uniform int64_t new_size);-extern "C" unmasked uniform char * uniform lexical_realloc_error(uniform int64_t new_size);-extern "C" unmasked uniform char * uniform * uniform futhark_get_error_ref(uniform struct futhark_context * uniform ctx);+extern "C" unmasked uniform char * uniform lexical_realloc_error(uniform struct futhark_context * uniform ctx, uniform int64_t new_size); -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context * uniform ctx, unsigned char uniform * uniform * uniform ptr, int64_t uniform * uniform old_size, uniform int64_t new_size) { uniform unsigned char * uniform memptr = realloc(*ptr, new_size); if (memptr == NULL) {- *error = lexical_realloc_error(new_size);+ lexical_realloc_error(ctx, new_size); return FUTHARK_OUT_OF_MEMORY; } else { *ptr = memptr;@@ -183,14 +182,14 @@ } -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context *ctx, unsigned char uniform * uniform * uniform ptr, int64_t uniform * uniform old_size, varying int64_t new_size) {- return lexical_realloc(error, ptr, old_size, reduce_max(new_size));+ return lexical_realloc(ctx, ptr, old_size, reduce_max(new_size)); } -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context * uniform ctx, unsigned char uniform * varying * uniform ptr, int64_t uniform * varying old_size, varying int64_t new_size) {@@ -198,7 +197,7 @@ foreach_active(i){ uniform unsigned char * uniform memptr = realloc(extract(*ptr,i), extract(new_size,i)); if (memptr == NULL) {- *error = lexical_realloc_error(extract(new_size,i));+ lexical_realloc_error(ctx, extract(new_size,i)); err = FUTHARK_OUT_OF_MEMORY; } else { *ptr = (uniform unsigned char * varying)insert((int64_t)*ptr, i, (uniform int64_t) memptr);@@ -208,7 +207,7 @@ return err; } -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context * uniform ctx, unsigned char uniform * varying * uniform ptr, int64_t varying * uniform old_size, varying int64_t new_size) {@@ -216,7 +215,7 @@ foreach_active(i){ uniform unsigned char * uniform memptr = realloc(extract(*ptr,i), extract(new_size,i)); if (memptr == NULL) {- *error = lexical_realloc_error(extract(new_size,i));+ lexical_realloc_error(ctx, extract(new_size,i)); err = FUTHARK_OUT_OF_MEMORY; } else { *ptr = (uniform unsigned char * varying)insert((int64_t)*ptr, i, (uniform int64_t) memptr);@@ -226,14 +225,14 @@ return err; } -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context * uniform ctx, unsigned char uniform * varying * uniform ptr, size_t varying * uniform old_size, varying int64_t new_size) {- return lexical_realloc(error, ptr, (varying int64_t * uniform)old_size, new_size);+ return lexical_realloc(ctx, ptr, (varying int64_t * uniform)old_size, new_size); } -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context * uniform ctx, unsigned char varying * uniform * uniform ptr, size_t varying * uniform old_size, uniform int64_t new_size) {@@ -241,7 +240,7 @@ uniform unsigned char * uniform memptr = realloc((uniform unsigned char * uniform )*ptr, new_size*programCount); if (memptr == NULL) {- *error = lexical_realloc_error(new_size);+ lexical_realloc_error(ctx, new_size); err = FUTHARK_OUT_OF_MEMORY; } else { *ptr = (varying unsigned char * uniform)memptr;@@ -251,11 +250,11 @@ return err; } -static inline uniform int lexical_realloc(uniform char * uniform * uniform error,+static inline uniform int lexical_realloc(uniform struct futhark_context * uniform ctx, unsigned char varying * uniform * uniform ptr, size_t varying * uniform old_size, varying int64_t new_size) {- return lexical_realloc(error, ptr, old_size, reduce_max(new_size));+ return lexical_realloc(ctx, ptr, old_size, reduce_max(new_size)); } extern "C" unmasked uniform int memblock_unref(uniform struct futhark_context * uniform ctx,
rts/c/util.h view
@@ -135,22 +135,4 @@ b->used += needed; } -char * lexical_realloc_error(size_t new_size) {- return msgprintf("Failed to allocate memory.\nAttempted allocation: %12lld bytes\n",- (long long) new_size);-}--static int lexical_realloc(char **error, unsigned char **ptr, int64_t *old_size, int64_t new_size) {- unsigned char *new = realloc(*ptr, (size_t)new_size);- if (new == NULL) {- *error = msgprintf("Failed to allocate memory.\nAttempted allocation: %12lld bytes\n",- (long long) new_size);- return FUTHARK_OUT_OF_MEMORY;- } else {- *ptr = new;- *old_size = new_size;- return FUTHARK_SUCCESS;- }-}- // End of util.h.
src/Futhark/AD/Fwd.hs view
@@ -253,9 +253,9 @@ addStm $ Let pat_tan aux $ BasicOp $ Replicate n x_tan Scratch t shape -> addStm $ Let pat_tan aux $ BasicOp $ Scratch t shape- Reshape reshape arr -> do+ Reshape k reshape arr -> do arr_tan <- tangent arr- addStm $ Let pat_tan aux $ BasicOp $ Reshape reshape arr_tan+ addStm $ Let pat_tan aux $ BasicOp $ Reshape k reshape arr_tan Rearrange perm arr -> do arr_tan <- tangent arr addStm $ Let pat_tan aux $ BasicOp $ Rearrange perm arr_tan@@ -422,12 +422,12 @@ e_t = primExpType e zipWithM_ (letBindNames . pure) (patNames pat_tan) =<< mapM toExp (zipWith (~*~) (map (convertTo ret) arg_tans) derivs)-fwdStm (Let pat aux (If cond t f (IfDec ret ifsort))) = do- t' <- slocal' $ fwdBody t- f' <- slocal' $ fwdBody f+fwdStm (Let pat aux (Match ses cases defbody (MatchDec ret ifsort))) = do+ cases' <- slocal' $ mapM (traverse fwdBody) cases+ defbody' <- slocal' $ fwdBody defbody pat' <- bundleNew pat ret' <- bundleTan ret- addStm $ Let pat' aux $ If cond t' f' $ IfDec ret' ifsort+ addStm $ Let pat' aux $ Match ses cases' defbody' $ MatchDec ret' ifsort fwdStm (Let pat aux (DoLoop val_pats loop@(WhileLoop v) body)) = do val_pats' <- bundleNew val_pats pat' <- bundleNew pat
src/Futhark/AD/Rev.hs view
@@ -57,11 +57,11 @@ case t of FloatType ft -> update <=< letExp "contrib" $- If- (Var pat_adj)- (resultBody [constant (floatValue ft (1 :: Int))])+ Match+ [Var pat_adj]+ [Case [Just $ BoolValue True] $ resultBody [constant (floatValue ft (1 :: Int))]] (resultBody [constant (floatValue ft (0 :: Int))])- (IfDec [Prim (FloatType ft)] IfNormal)+ (MatchDec [Prim (FloatType ft)] MatchNormal) IntType it -> update <=< letExp "contrib" $ BasicOp $ ConvOp (BToI it) (Var pat_adj) Bool ->@@ -130,13 +130,13 @@ (_pat_v, pat_adj) <- commonBasicOp pat aux e m returnSweepCode $ updateSubExpAdj se pat_adj --- Reshape _ arr -> do+ Reshape k _ arr -> do (_pat_v, pat_adj) <- commonBasicOp pat aux e m returnSweepCode $ do- arr_dims <- arrayDims <$> lookupType arr+ arr_shape <- arrayShape <$> lookupType arr void $ updateAdj arr <=< letExp "adj_reshape" . BasicOp $- Reshape (map DimNew arr_dims) pat_adj+ Reshape k arr_shape pat_adj -- Rearrange perm arr -> do (_pat_v, pat_adj) <- commonBasicOp pat aux e m@@ -163,7 +163,7 @@ n <- letSubExp "rep_size" =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) ns pat_adj_flat <- letExp (baseString pat_adj <> "_flat") . BasicOp $- Reshape (map DimNew $ n : arrayDims x_t) pat_adj+ Reshape ReshapeArbitrary (Shape $ n : arrayDims x_t) pat_adj reduce <- reduceSOAC [Reduce Commutative lam [ne]] updateSubExpAdj x =<< letExp "rep_contrib" (Op $ Screma n [pat_adj_flat] reduce)@@ -263,13 +263,13 @@ letExp "contrib" <=< toExp . convert ret argt $ pat_adj' ~*~ deriv zipWithM_ updateSubExpAdj (map fst args) contribs-diffStm stm@(Let pat _ (If cond tbody fbody _)) m = do+diffStm stm@(Let pat _ (Match ses cases defbody _)) m = do addStm stm m returnSweepCode $ do- let tbody_free = freeIn tbody- fbody_free = freeIn fbody- branches_free = namesToList $ tbody_free <> fbody_free+ let cases_free = map freeIn cases+ defbody_free = freeIn defbody+ branches_free = namesToList $ mconcat $ defbody_free : cases_free adjs <- mapM lookupAdj $ patNames pat @@ -278,10 +278,10 @@ <=< letTupExp "branch_adj" <=< renameExp )- =<< eIf- (eSubExp cond)- (diffBody adjs branches_free tbody)- (diffBody adjs branches_free fbody)+ =<< eMatch+ ses+ (map (fmap $ diffBody adjs branches_free) cases)+ (diffBody adjs branches_free defbody) zipWithM_ insAdj branches_free branches_free_adj diffStm (Let pat aux (Op soac)) m = vjpSOAC vjpOps pat aux soac m
src/Futhark/AD/Rev/Monad.hs view
@@ -105,8 +105,8 @@ OutOfBounds deriving (Eq, Ord, Show) --- | A symbolic representation of an array that is all zeroes, except at one--- index.+-- | A symbolic representation of an array that is all zeroes, except+-- at certain indexes. data Sparse = Sparse { -- | The shape of the array. sparseShape :: Shape,
src/Futhark/Analysis/Alias.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-} -- | Alias analysis of a full Futhark program. Takes as input a -- program with an arbitrary rep and produces one with aliases. This@@ -90,12 +91,12 @@ Exp (Aliases rep) -- Would be better to put this in a BranchType annotation, but that -- requires a lot of other work.-analyseExp aliases (If cond tb fb dec) =- let Body ((tb_als, tb_cons), tb_dec) tb_stms tb_res = analyseBody aliases tb- Body ((fb_als, fb_cons), fb_dec) fb_stms fb_res = analyseBody aliases fb- cons = tb_cons <> fb_cons+analyseExp aliases (Match cond cases defbody matchdec) =+ let cases' = map (fmap $ analyseBody aliases) cases+ defbody' = analyseBody aliases defbody+ all_cons = foldMap (snd . fst . bodyDec) $ defbody' : map caseBody cases' isConsumed v =- any (`nameIn` unAliases cons) $+ any (`nameIn` unAliases all_cons) $ v : namesToList (M.findWithDefault mempty v aliases) notConsumed = AliasDec@@ -103,11 +104,11 @@ . filter (not . isConsumed) . namesToList . unAliases- tb_als' = map notConsumed tb_als- fb_als' = map notConsumed fb_als- tb' = Body ((tb_als', tb_cons), tb_dec) tb_stms tb_res- fb' = Body ((fb_als', fb_cons), fb_dec) fb_stms fb_res- in If cond tb' fb' dec+ onBody (Body ((als, cons), dec) stms res) =+ Body ((map notConsumed als, cons), dec) stms res+ cases'' = map (fmap onBody) cases'+ defbody'' = onBody defbody'+ in Match cond cases'' defbody'' matchdec analyseExp aliases e = mapExp analyse e where analyse =
src/Futhark/Analysis/DataDependencies.hs view
@@ -8,6 +8,7 @@ ) where +import qualified Data.List as L import qualified Data.Map.Strict as M import Futhark.IR @@ -28,14 +29,15 @@ Dependencies dataDependencies' startdeps = foldl grow startdeps . bodyStms where- grow deps (Let pat _ (If c tb fb _)) =- let tdeps = dataDependencies' deps tb- fdeps = dataDependencies' deps fb- cdeps = depsOf deps c- comb (pe, SubExpRes _ tres, SubExpRes _ fres) =+ grow deps (Let pat _ (Match c cases defbody _)) =+ let cases_deps = map (dataDependencies' deps . caseBody) cases+ defbody_deps = dataDependencies' deps defbody+ cdeps = foldMap (depsOf deps) c+ comb (pe, se_cases_deps, se_defbody_deps) = ( patElemName pe, mconcat $- [freeIn pe, cdeps, depsOf tdeps tres, depsOf fdeps fres]+ se_cases_deps+ ++ [freeIn pe, cdeps, se_defbody_deps] ++ map (depsOfVar deps) (namesToList $ freeIn pe) ) branchdeps =@@ -43,9 +45,11 @@ map comb $ zip3 (patElems pat)- (bodyResult tb)- (bodyResult fb)- in M.unions [branchdeps, deps, tdeps, fdeps]+ ( L.transpose . zipWith (map . depsOf) cases_deps $+ map (map resSubExp . bodyResult . caseBody) cases+ )+ (map (depsOf defbody_deps . resSubExp) (bodyResult defbody))+ in M.unions $ [branchdeps, deps, defbody_deps] ++ cases_deps grow deps (Let pat _ e) = let free = freeIn pat <> freeIn e freeDeps = mconcat $ map (depsOfVar deps) $ namesToList free
src/Futhark/Analysis/HORep/SOAC.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} -- | High-level representation of SOACs. When performing@@ -80,7 +79,6 @@ Rearrange, Replicate, Reshape,- Var, typeOf, ) import qualified Futhark.IR as Futhark@@ -103,11 +101,11 @@ = -- | A permutation of an otherwise valid input. Rearrange Certs [Int] | -- | A reshaping of an otherwise valid input.- Reshape Certs (ShapeChange SubExp)+ Reshape Certs ReshapeKind Shape | -- | A reshaping of the outer dimension.- ReshapeOuter Certs (ShapeChange SubExp)+ ReshapeOuter Certs ReshapeKind Shape | -- | A reshaping of everything but the outer dimension.- ReshapeInner Certs (ShapeChange SubExp)+ ReshapeInner Certs ReshapeKind Shape | -- | Replicate the rows of the array a number of times. Replicate Certs Shape deriving (Show, Eq, Ord)@@ -115,12 +113,12 @@ instance Substitute ArrayTransform where substituteNames substs (Rearrange cs xs) = Rearrange (substituteNames substs cs) xs- substituteNames substs (Reshape cs ses) =- Reshape (substituteNames substs cs) (substituteNames substs ses)- substituteNames substs (ReshapeOuter cs ses) =- ReshapeOuter (substituteNames substs cs) (substituteNames substs ses)- substituteNames substs (ReshapeInner cs ses) =- ReshapeInner (substituteNames substs cs) (substituteNames substs ses)+ substituteNames substs (Reshape cs k ses) =+ Reshape (substituteNames substs cs) k (substituteNames substs ses)+ substituteNames substs (ReshapeOuter cs k ses) =+ ReshapeOuter (substituteNames substs cs) k (substituteNames substs ses)+ substituteNames substs (ReshapeInner cs k ses) =+ ReshapeInner (substituteNames substs cs) k (substituteNames substs ses) substituteNames substs (Replicate cs se) = Replicate (substituteNames substs cs) (substituteNames substs se) @@ -231,9 +229,9 @@ transformFromExp :: Certs -> Exp rep -> Maybe (VName, ArrayTransform) transformFromExp cs (BasicOp (Futhark.Rearrange perm v)) = Just (v, Rearrange cs perm)-transformFromExp cs (BasicOp (Futhark.Reshape shape v)) =- Just (v, Reshape cs shape)-transformFromExp cs (BasicOp (Futhark.Replicate shape (Futhark.Var v))) =+transformFromExp cs (BasicOp (Futhark.Reshape k shape v)) =+ Just (v, Reshape cs k shape)+transformFromExp cs (BasicOp (Futhark.Replicate shape (Var v))) = Just (v, Replicate cs shape) transformFromExp _ _ = Nothing @@ -268,12 +266,12 @@ isVarInput (Input ts v _) | nullTransforms ts = Just v isVarInput _ = Nothing --- | If the given input is a plain variable input, with no non-vacuous transforms,--- return the variable.+-- | If the given input is a plain variable input, with no non-vacuous+-- transforms, return the variable. isVarishInput :: Input -> Maybe VName isVarishInput (Input ts v t) | nullTransforms ts = Just v- | Reshape cs [DimCoercion _] :< ts' <- viewf ts,+ | Reshape cs ReshapeCoerce (Shape [_]) :< ts' <- viewf ts, cs == mempty = isVarishInput $ Input ts' v t isVarishInput _ = Nothing@@ -291,22 +289,22 @@ applyTransform :: MonadBuilder m => ArrayTransform -> VName -> m VName applyTransform (Replicate cs n) ia = certifying cs . letExp "repeat" . BasicOp $- Futhark.Replicate n (Futhark.Var ia)+ Futhark.Replicate n (Var ia) applyTransform (Rearrange cs perm) ia = do r <- arrayRank <$> lookupType ia certifying cs . letExp "rearrange" . BasicOp $ Futhark.Rearrange (perm ++ [length perm .. r - 1]) ia-applyTransform (Reshape cs shape) ia =+applyTransform (Reshape cs k shape) ia = certifying cs . letExp "reshape" . BasicOp $- Futhark.Reshape shape ia-applyTransform (ReshapeOuter cs shape) ia = do+ Futhark.Reshape k shape ia+applyTransform (ReshapeOuter cs k shape) ia = do shape' <- reshapeOuter shape 1 . arrayShape <$> lookupType ia certifying cs . letExp "reshape_outer" . BasicOp $- Futhark.Reshape shape' ia-applyTransform (ReshapeInner cs shape) ia = do+ Futhark.Reshape k shape' ia+applyTransform (ReshapeInner cs k shape) ia = do shape' <- reshapeInner shape 1 . arrayShape <$> lookupType ia certifying cs . letExp "reshape_inner" . BasicOp $- Futhark.Reshape shape' ia+ Futhark.Reshape k shape' ia applyTransforms :: MonadBuilder m => ArrayTransforms -> VName -> m VName applyTransforms (ArrayTransforms ts) a = foldlM (flip applyTransform) a ts@@ -337,14 +335,14 @@ arrayOfShape t shape transformType t (Rearrange _ perm) = rearrangeType perm t- transformType t (Reshape _ shape) =- t `setArrayShape` newShape shape- transformType t (ReshapeOuter _ shape) =+ transformType t (Reshape _ _ shape) =+ t `setArrayShape` shape+ transformType t (ReshapeOuter _ _ shape) = let Shape oldshape = arrayShape t- in t `setArrayShape` Shape (newDims shape ++ drop 1 oldshape)- transformType t (ReshapeInner _ shape) =+ in t `setArrayShape` Shape (shapeDims shape ++ drop 1 oldshape)+ transformType t (ReshapeInner _ _ shape) = let Shape oldshape = arrayShape t- in t `setArrayShape` Shape (take 1 oldshape ++ newDims shape)+ in t `setArrayShape` Shape (take 1 oldshape ++ shapeDims shape) -- | Return the row type of an input. Just a convenient alias. inputRowType :: Input -> Type@@ -362,8 +360,8 @@ where transformRows' inp (Rearrange cs perm) = addTransform (Rearrange cs (0 : map (+ 1) perm)) inp- transformRows' inp (Reshape cs shape) =- addTransform (ReshapeInner cs shape) inp+ transformRows' inp (Reshape cs k shape) =+ addTransform (ReshapeInner cs k shape) inp transformRows' inp (Replicate cs n) | inputRank inp == 1 = Rearrange mempty [1, 0]@@ -396,12 +394,18 @@ where f e (Rearrange cs perm) = text "rearrange" <> ppr cs <> PP.apply [PP.apply (map ppr perm), e]- f e (Reshape cs shape) =- text "reshape" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]- f e (ReshapeOuter cs shape) =- text "reshape_outer" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]- f e (ReshapeInner cs shape) =- text "reshape_inner" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]+ f e (Reshape cs ReshapeArbitrary shape) =+ text "reshape" <> ppr cs <> PP.apply [ppr shape, e]+ f e (ReshapeOuter cs ReshapeArbitrary shape) =+ text "reshape_outer" <> ppr cs <> PP.apply [ppr shape, e]+ f e (ReshapeInner cs ReshapeArbitrary shape) =+ text "reshape_inner" <> ppr cs <> PP.apply [ppr shape, e]+ f e (Reshape cs ReshapeCoerce shape) =+ text "coerce" <> ppr cs <> PP.apply [ppr shape, e]+ f e (ReshapeOuter cs ReshapeCoerce shape) =+ text "coerce_outer" <> ppr cs <> PP.apply [ppr shape, e]+ f e (ReshapeInner cs ReshapeCoerce shape) =+ text "coerce_inner" <> ppr cs <> PP.apply [ppr shape, e] f e (Replicate cs ne) = text "replicate" <> ppr cs <> PP.apply [ppr ne, e] @@ -526,12 +530,17 @@ -- Returns the Stream SOAC and the -- extra-accumulator body-result ident if any. soacToStream ::- (MonadFreshNames m, Buildable rep, Op rep ~ Futhark.SOAC rep) =>+ ( HasScope rep m,+ MonadFreshNames m,+ Buildable rep,+ BuilderOps rep,+ Op rep ~ Futhark.SOAC rep+ ) => SOAC rep -> m (SOAC rep, [Ident]) soacToStream soac = do chunk_param <- newParam "chunk" $ Prim int64- let chvar = Futhark.Var $ paramName chunk_param+ let chvar = Var $ paramName chunk_param (lam, inps) = (lambda soac, inputs soac) w = width soac lam' <- renameLambda lam@@ -554,7 +563,7 @@ Futhark.Screma chvar (map paramName strm_inpids) $ Futhark.mapSOAC lam' insstm = mkLet strm_resids $ Op insoac- strmbdy = mkBody (oneStm insstm) $ map (subExpRes . Futhark.Var . identName) strm_resids+ 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 []) []@@ -573,77 +582,51 @@ -- {acc', strm_resids, map_resids} -- the array and accumulator result types let scan_arr_ts = map (`arrayOfRow` chvar) $ lambdaReturnType scan_lam- map_arr_ts = drop (length nes) loutps accrtps = lambdaReturnType scan_lam - -- array result and input IDs of the stream's lambda- strm_resids <- mapM (newIdent "res") scan_arr_ts- scan0_ids <- mapM (newIdent "resarr0") scan_arr_ts- map_resids <- mapM (newIdent "map_res") map_arr_ts-- lastel_ids <- mapM (newIdent "lstel") accrtps- lastel_tmp_ids <- mapM (newIdent "lstel_tmp") accrtps- empty_arr <- newIdent "empty_arr" $ Prim Bool inpacc_ids <- mapM (newParam "inpacc") accrtps- outszm1id <- newIdent "szm1" $ Prim int64- -- 1. let (scan0_ids,map_resids) = scanomap(scan_lam,nes,map_lam,a_ch)- let insstm =- mkLet (scan0_ids ++ map_resids) . Op $- Futhark.Screma chvar (map paramName strm_inpids) $- Futhark.scanomapSOAC [Futhark.Scan scan_lam nes] lam'- -- 2. let outerszm1id = chunksize - 1- outszm1stm =- mkLet [outszm1id] . BasicOp $- BinOp- (Sub Int64 OverflowUndef)- (Futhark.Var $ paramName chunk_param)- (constant (1 :: Int64))- -- 3. let lasteel_ids = ...- empty_arr_stm =- mkLet [empty_arr] . BasicOp $- CmpOp- (CmpSlt Int64)- (Futhark.Var $ identName outszm1id)- (constant (0 :: Int64))- leltmpstms =- zipWith- ( \lid arrid ->- mkLet [lid] . BasicOp $- Index (identName arrid) $- fullSlice- (identType arrid)- [DimFix $ Futhark.Var $ identName outszm1id]- )- lastel_tmp_ids- scan0_ids- lelstm =- mkLet lastel_ids- $ If- (Futhark.Var $ identName empty_arr)- (mkBody mempty $ subExpsRes nes)- ( mkBody (stmsFromList leltmpstms) $- varsRes $- map identName lastel_tmp_ids- )- $ ifCommon- $ map identType lastel_tmp_ids- -- 4. let strm_resids = map (acc `+`,nes, scan0_ids)- maplam <- mkMapPlusAccLam (map (Futhark.Var . paramName) inpacc_ids) scan_lam- let mapstm =- mkLet strm_resids . Op $- Futhark.Screma chvar (map identName scan0_ids) (Futhark.mapSOAC maplam)- -- 5. let acc' = acc + lasteel_ids- addlelbdy <-- mkPlusBnds scan_lam $- map Futhark.Var $- map paramName inpacc_ids ++ map identName lastel_ids+ maplam <- mkMapPlusAccLam (map (Var . paramName) inpacc_ids) scan_lam -- Finally, construct the stream- let (addlelstm, addlelres) = (bodyStms addlelbdy, bodyResult addlelbdy)- strmbdy =- mkBody (stmsFromList [insstm, outszm1stm, empty_arr_stm, lelstm, mapstm] <> addlelstm) $- addlelres ++ map (subExpRes . Futhark.Var . identName) (strm_resids ++ map_resids)- strmpar = chunk_param : inpacc_ids ++ strm_inpids- strmlam = Lambda strmpar strmbdy (accrtps ++ loutps)+ let strmpar = chunk_param : inpacc_ids ++ strm_inpids+ strmlam <- fmap fst . runBuilder . mkLambda strmpar $ do+ -- 1. let (scan0_ids,map_resids) = scanomap(scan_lam,nes,map_lam,a_ch)+ (scan0_ids, map_resids) <-+ fmap (splitAt (length scan_arr_ts)) . letTupExp "scan" . Op $+ Futhark.Screma chvar (map paramName strm_inpids) $+ Futhark.scanomapSOAC [Futhark.Scan scan_lam nes] lam'+ -- 2. let outerszm1id = chunksize - 1+ outszm1id <-+ letSubExp "outszm1" . BasicOp $+ BinOp+ (Sub Int64 OverflowUndef)+ (Var $ paramName chunk_param)+ (constant (1 :: Int64))+ empty_arr <-+ letExp "empty_arr" . BasicOp $+ CmpOp+ (CmpSlt Int64)+ outszm1id+ (constant (0 :: Int64))+ -- 3. let lasteel_ids = ...+ let indexLast arr = do+ arr_t <- lookupType arr+ pure $ BasicOp . Index arr $ fullSlice arr_t [DimFix outszm1id]+ lastel_ids <-+ letTupExp "lastel"+ =<< eIf+ (eSubExp $ Var empty_arr)+ (resultBodyM nes)+ (eBody $ map indexLast scan0_ids)+ addlelbdy <-+ mkPlusBnds scan_lam $ map Var $ map paramName inpacc_ids ++ lastel_ids+ let (addlelstm, addlelres) = (bodyStms addlelbdy, bodyResult addlelbdy)+ -- 4. let strm_resids = map (acc `+`,nes, scan0_ids)+ strm_resids <-+ letTupExp "strm_res" . Op $+ Futhark.Screma chvar scan0_ids (Futhark.mapSOAC maplam)+ -- 5. let acc' = acc + lasteel_ids+ addStms addlelstm+ pure $ addlelres ++ map (subExpRes . Var) (strm_resids ++ map_resids) pure ( Stream w Sequential strmlam nes inps, map paramIdent inpacc_ids@@ -674,13 +657,13 @@ -- 2. let acc' = acc + acc0_ids in addaccbdy <- mkPlusBnds lamin $- map Futhark.Var $+ map Var $ map paramName inpacc_ids ++ map identName acc0_ids -- Construct the stream let (addaccstm, addaccres) = (bodyStms addaccbdy, bodyResult addaccbdy) strmbdy = mkBody (oneStm insstm <> addaccstm) $- addaccres ++ map (subExpRes . Futhark.Var . identName) strm_resids+ addaccres ++ map (subExpRes . Var . identName) strm_resids strmpar = chunk_param : inpacc_ids ++ strm_inpids strmlam = Lambda strmpar strmbdy (accrtps ++ loutps') lam0 <- renameLambda lamin
src/Futhark/Analysis/Interference.hs view
@@ -117,10 +117,10 @@ m (InUse, LastUsed, Graph VName) analyseExp lumap inuse_outside expr = case expr of- If _ then_body else_body _ -> do- res1 <- analyseBody lumap inuse_outside then_body- res2 <- analyseBody lumap inuse_outside else_body- pure $ res1 <> res2+ Match _ cases defbody _ ->+ fmap mconcat $+ mapM (analyseBody lumap inuse_outside) $+ defbody : map caseBody cases DoLoop merge _ body -> analyseLoopParams merge <$> analyseBody lumap inuse_outside body Op (Inner (SegOp segop)) -> do@@ -287,10 +287,8 @@ <$> mapM memSizesStm $ stmsToList $ kernelBodyStms body- memSizesExp (If _ then_body else_body _) = do- then_res <- memSizes $ bodyStms then_body- else_res <- memSizes $ bodyStms else_body- pure $ then_res <> else_res+ memSizesExp (Match _ cases defbody _) = do+ mconcat <$> mapM (memSizes . bodyStms) (defbody : map caseBody cases) memSizesExp (DoLoop _ _ body) = memSizes $ bodyStms body memSizesExp _ = pure mempty@@ -306,9 +304,8 @@ getSpacesStm (Let _ _ (Op (Alloc _ _))) = error "impossible" getSpacesStm (Let _ _ (Op (Inner (SegOp segop)))) = foldMap getSpacesStm $ kernelBodyStms $ segBody segop- getSpacesStm (Let _ _ (If _ then_body else_body _)) =- foldMap getSpacesStm (bodyStms then_body)- <> foldMap getSpacesStm (bodyStms else_body)+ getSpacesStm (Let _ _ (Match _ cases defbody _)) =+ foldMap (foldMap getSpacesStm . bodyStms) $ defbody : map caseBody cases getSpacesStm (Let _ _ (DoLoop _ _ body)) = foldMap getSpacesStm (bodyStms body) getSpacesStm _ = mempty@@ -327,11 +324,10 @@ m (InUse, LastUsed, Graph VName) helper stm@Let {stmExp = Op (Inner (SegOp segop))} = inScopeOf stm $ analyseSegOp lumap mempty segop- helper stm@Let {stmExp = If _ then_body else_body _} =- inScopeOf stm $ do- res1 <- analyseGPU' lumap (bodyStms then_body)- res2 <- analyseGPU' lumap (bodyStms else_body)- pure (res1 <> res2)+ helper stm@Let {stmExp = Match _ cases defbody _} =+ inScopeOf stm $+ mconcat+ <$> mapM (analyseGPU' lumap . bodyStms) (defbody : map caseBody cases) helper stm@Let {stmExp = DoLoop merge _ body} = fmap (analyseLoopParams merge) . inScopeOf stm $ analyseGPU' lumap $
src/Futhark/Analysis/LastUse.hs view
@@ -15,7 +15,7 @@ where import Control.Monad.Reader-import Data.Bifunctor (first)+import Data.Bifunctor (bimap, first) import Data.Foldable import Data.Function ((&)) import Data.Map (Map)@@ -148,12 +148,17 @@ analyseExp (lumap, used) (Apply _ args _ _) = do let nms = freeIn $ map fst args pure (insertNames pat_name nms lumap, used <> nms)- analyseExp (lumap, used) (If cse then_body else_body dec) = do- (lumap_then, used_then) <- analyseBody lumap used then_body- (lumap_else, used_else) <- analyseBody lumap used else_body- let used' = used_then <> used_else- nms = (freeIn cse <> freeIn dec) `namesSubtract` used'- pure (insertNames pat_name nms (lumap_then <> lumap_else), used' <> nms)+ analyseExp (lumap, used) (Match ses cases defbody dec) = do+ (lumap_cases, used_cases) <-+ bimap mconcat mconcat . unzip+ <$> mapM (analyseBody lumap used . caseBody) cases+ (lumap_defbody, used_defbody) <- analyseBody lumap used defbody+ let used' = used_cases <> used_defbody+ nms = (freeIn ses <> freeIn dec) `namesSubtract` used'+ pure+ ( insertNames pat_name nms (lumap_cases <> lumap_defbody),+ used' <> nms+ ) analyseExp (lumap, used) (DoLoop merge form body) = do (lumap', used') <- analyseBody lumap used body let nms = (freeIn merge <> freeIn form) `namesSubtract` used'
src/Futhark/Analysis/MemAlias.hs view
@@ -85,12 +85,10 @@ analyzeStm m (Let _ _ (Op (Inner inner))) = do on_inner <- asks onInner on_inner m inner-analyzeStm m (Let pat _ (If _ then_body else_body _)) = do- m' <-- analyzeStms (bodyStms then_body) m- >>= analyzeStms (bodyStms else_body)- zip (patNames pat) (map resSubExp $ bodyResult then_body)- <> zip (patNames pat) (map resSubExp $ bodyResult else_body)+analyzeStm m (Let pat _ (Match _ cases defbody _)) = do+ let bodies = defbody : map caseBody cases+ m' <- foldM (flip analyzeStms) m $ map bodyStms bodies+ foldMap (zip (patNames pat) . map resSubExp . bodyResult) bodies & mapMaybe (filterFun m') & foldr (uncurry addAlias) m' & pure
src/Futhark/Analysis/Metrics.hs view
@@ -107,10 +107,15 @@ inside "DoLoop" $ seen "ForLoop" >> bodyMetrics body expMetrics (DoLoop _ WhileLoop {} body) = inside "DoLoop" $ seen "WhileLoop" >> bodyMetrics body-expMetrics (If _ tb fb _) =+expMetrics (Match _ [Case [Just (BoolValue True)] tb] fb _) = inside "If" $ do inside "True" $ bodyMetrics tb inside "False" $ bodyMetrics fb+expMetrics (Match _ cases defbody _) =+ inside "Match" $ do+ forM_ (zip [0 ..] cases) $ \(i, c) ->+ inside (T.pack (show (i :: Int))) $ bodyMetrics $ caseBody c+ inside "default" $ bodyMetrics defbody expMetrics Apply {} = seen "Apply" expMetrics (WithAcc _ lam) =
src/Futhark/Analysis/PrimExp.hs view
@@ -67,6 +67,7 @@ (~/~), (~+~), (~-~),+ (~==~), ) where @@ -754,6 +755,14 @@ Bool -> LogOr Unit -> LogOr +-- | Equality of untyped 'PrimExp's, which must have the same type.+(~==~) :: PrimExp v -> PrimExp v -> PrimExp v+x ~==~ y = CmpOpExp (CmpEq t) x y+ where+ t = primExpType x+ infix 7 ~*~, ~/~ infix 6 ~+~, ~-~++infix 4 ~==~
− src/Futhark/Analysis/PrimExp/Generalize.hs
@@ -1,73 +0,0 @@--- | Generalization (anti-unification) of 'PrimExp's.-module Futhark.Analysis.PrimExp.Generalize- ( leastGeneralGeneralization,- )-where--import Data.List (elemIndex)-import Futhark.Analysis.PrimExp-import Futhark.IR.Syntax.Core (Ext (..))---- | Generalize two 'PrimExp's of the the same type.-leastGeneralGeneralization ::- (Eq v) =>- [(PrimExp v, PrimExp v)] ->- PrimExp v ->- PrimExp v ->- (PrimExp (Ext v), [(PrimExp v, PrimExp v)])-leastGeneralGeneralization m exp1@(LeafExp v1 t1) exp2@(LeafExp v2 _) =- if v1 == v2- then (LeafExp (Free v1) t1, m)- else generalize m exp1 exp2-leastGeneralGeneralization m exp1@(ValueExp v1) exp2@(ValueExp v2) =- if v1 == v2- then (ValueExp v1, m)- else generalize m exp1 exp2-leastGeneralGeneralization m exp1@(BinOpExp op1 e11 e12) exp2@(BinOpExp op2 e21 e22) =- if op1 == op2- then- let (e1, m1) = leastGeneralGeneralization m e11 e21- (e2, m2) = leastGeneralGeneralization m1 e12 e22- in (BinOpExp op1 e1 e2, m2)- else generalize m exp1 exp2-leastGeneralGeneralization m exp1@(CmpOpExp op1 e11 e12) exp2@(CmpOpExp op2 e21 e22) =- if op1 == op2- then- let (e1, m1) = leastGeneralGeneralization m e11 e21- (e2, m2) = leastGeneralGeneralization m1 e12 e22- in (CmpOpExp op1 e1 e2, m2)- else generalize m exp1 exp2-leastGeneralGeneralization m exp1@(UnOpExp op1 e1) exp2@(UnOpExp op2 e2) =- if op1 == op2- then- let (e, m1) = leastGeneralGeneralization m e1 e2- in (UnOpExp op1 e, m1)- else generalize m exp1 exp2-leastGeneralGeneralization m exp1@(ConvOpExp op1 e1) exp2@(ConvOpExp op2 e2) =- if op1 == op2- then- let (e, m1) = leastGeneralGeneralization m e1 e2- in (ConvOpExp op1 e, m1)- else generalize m exp1 exp2-leastGeneralGeneralization m exp1@(FunExp s1 args1 t1) exp2@(FunExp s2 args2 _) =- if s1 == s2 && length args1 == length args2- then- let (args, m') =- foldl- ( \(arg_acc, m_acc) (a1, a2) ->- let (a, m'') = leastGeneralGeneralization m_acc a1 a2- in (a : arg_acc, m'')- )- ([], m)- (zip args1 args2)- in (FunExp s1 (reverse args) t1, m')- else generalize m exp1 exp2-leastGeneralGeneralization m exp1 exp2 =- generalize m exp1 exp2--generalize :: Eq v => [(PrimExp v, PrimExp v)] -> PrimExp v -> PrimExp v -> (PrimExp (Ext v), [(PrimExp v, PrimExp v)])-generalize m exp1 exp2 =- let t = primExpType exp1- in case elemIndex (exp1, exp2) m of- Just i -> (LeafExp (Ext i) t, m)- Nothing -> (LeafExp (Ext $ length m) t, m ++ [(exp1, exp2)])
src/Futhark/Analysis/SymbolTable.hs view
@@ -352,12 +352,13 @@ indexExp table (BasicOp (Replicate (Shape [_]) (Var v))) _ (_ : is) = do guard $ v `available` table index' v is table-indexExp table (BasicOp (Reshape newshape v)) _ is+indexExp table (BasicOp (Reshape _ newshape v)) _ is | Just oldshape <- arrayDims <$> lookupType v table =+ -- TODO: handle coercions more efficiently. let is' = reshapeIndex (map pe64 oldshape)- (map pe64 $ newDims newshape)+ (map pe64 $ shapeDims newshape) is in index' v is' table indexExp table (BasicOp (Index v slice)) _ is = do
src/Futhark/Analysis/UsageTable.hs view
@@ -179,8 +179,8 @@ ] usageInExp e@DoLoop {} = foldMap consumedUsage $ namesToList $ consumedInExp e-usageInExp (If _ tbranch fbranch _) =- usageInBody tbranch <> usageInBody fbranch+usageInExp (Match _ cases defbody _) =+ foldMap (usageInBody . caseBody) cases <> usageInBody defbody usageInExp (WithAcc inputs lam) = foldMap inputUsage inputs <> usageInBody (lambdaBody lam) where
src/Futhark/Builder.hs view
@@ -123,7 +123,7 @@ pure x instance- (ASTRep rep, MonadFreshNames m, BuilderOps rep) =>+ (MonadFreshNames m, BuilderOps rep) => MonadBuilder (BuilderT rep m) where type Rep (BuilderT rep m) = rep
src/Futhark/CLI/Dev.hs view
@@ -430,28 +430,28 @@ "Run this compiler backend on pipeline result.", Option []- ["compile-imperative"]+ ["compile-imp-seq"] ( NoArg $ Right $ \opts -> opts {futharkAction = SeqMemAction $ \_ _ _ -> impCodeGenAction} )- "Translate program into the imperative IL and write it on standard output.",+ "Translate pipeline result to ImpSequential and write it on stdout.", Option []- ["compile-imperative-kernels"]+ ["compile-imp-gpu"] ( NoArg $ Right $ \opts -> opts {futharkAction = GPUMemAction $ \_ _ _ -> kernelImpCodeGenAction} )- "Translate program into the imperative IL with kernels and write it on standard output.",+ "Translate pipeline result to ImpGPU and write it on stdout.", Option []- ["compile-imperative-multicore"]+ ["compile-imp-multicore"] ( NoArg $ Right $ \opts -> opts {futharkAction = MCMemAction $ \_ _ _ -> multicoreImpCodeGenAction} )- "Translate program into the imperative IL with kernels and write it on standard output.",+ "Translate pipeline result to ImpMC write it on stdout.", Option "p" ["print"]
src/Futhark/CLI/Main.hs view
@@ -78,7 +78,7 @@ ("literate", (Literate.main, "Process a literate Futhark program.")), ("lsp", (LSP.main, "Run LSP server.")), ("thanks", (Misc.mainThanks, "Express gratitude.")),- ("tokens", (Misc.mainTokens, "Express gratitude."))+ ("tokens", (Misc.mainTokens, "Print tokens from Futhark file.")) ] msg :: String
src/Futhark/CLI/Test.hs view
@@ -497,19 +497,6 @@ moveCursorToTableTop :: IO () moveCursorToTableTop = cursorUpLine tableLines -reportText :: TestStatus -> IO ()-reportText ts =- putStr $- "("- ++ show (testStatusFail ts)- ++ " failed, "- ++ show (testStatusPass ts)- ++ " passed, "- ++ show num_remain- ++ " to go).\n"- where- num_remain = length $ testStatusRemain ts- runTests :: TestConfig -> [FilePath] -> IO () runTests config paths = do -- We force line buffering to ensure that we produce running output.@@ -533,10 +520,10 @@ report | fancy = reportTable- | otherwise = reportText+ | otherwise = const (pure ()) clear | fancy = clearFromCursorToScreenEnd- | otherwise = putStr "\n"+ | otherwise = pure () numTestCases tc = case testAction $ testCaseTest tc of@@ -552,10 +539,7 @@ report ts msg <- takeMVar reportmvar case msg of- TestStarted test -> do- unless fancy $- putStr $- "Started testing " <> testCaseProgram test <> " "+ TestStarted test -> getResults $ ts {testStatusRun = test : testStatusRun ts} TestDone test res -> do let ts' =@@ -573,9 +557,6 @@ { testStatusRunPass = testStatusRunPass ts' + numTestCases test }- unless fancy $- putStr $- "Finished testing " <> testCaseProgram test <> " " getResults $ ts'' {testStatusPass = testStatusPass ts + 1} Failure s -> do when fancy moveCursorToTableTop
src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs view
@@ -336,6 +336,7 @@ int logging; typename lock_t lock; char *error;+ typename lock_t error_lock; typename FILE *log; $sdecls:fields $sdecls:kernel_fields@@ -374,6 +375,7 @@ ctx->profiling_paused = 0; ctx->logging = cfg->cu_cfg.logging; ctx->error = NULL;+ create_lock(&ctx->error_lock); ctx->log = stderr; ctx->cuda.profiling_records_capacity = 200; ctx->cuda.profiling_records_used = 0;
src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs view
@@ -319,6 +319,7 @@ int logging; typename lock_t lock; char *error;+ typename lock_t error_lock; typename FILE *log; $sdecls:fields $sdecls:ctx_opencl_fields@@ -342,6 +343,7 @@ ctx->profiling_paused = 0; ctx->logging = cfg->opencl.logging; ctx->error = NULL;+ create_lock(&ctx->error_lock); ctx->log = stderr; ctx->opencl.profiling_records_capacity = 200; ctx->opencl.profiling_records_used = 0;
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -5,2539 +5,746 @@ {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TupleSections #-} --- | C code generator framework.-module Futhark.CodeGen.Backends.GenericC- ( compileProg,- compileProg',- CParts (..),- asLibrary,- asExecutable,- asServer,-- -- * Pluggable compiler- Operations (..),- defaultOperations,- OpCompiler,- ErrorCompiler,- CallCompiler,- PointerQuals,- MemoryType,- WriteScalar,- writeScalarPointerWithQuals,- ReadScalar,- readScalarPointerWithQuals,- Allocate,- Deallocate,- CopyBarrier (..),- Copy,- StaticArray,-- -- * Monadic compiler interface- CompilerM,- CompilerState (compUserState, compNameSrc, compDeclaredMem),- CompilerEnv (envCachedMem),- getUserState,- modifyUserState,- contextContents,- contextFinalInits,- runCompilerM,- inNewFunction,- cachingMemory,- compileFun,- compileCode,- compileExp,- compilePrimExp,- compileExpToName,- rawMem,- item,- items,- stm,- stms,- decl,- atInit,- headerDecl,- publicDef,- publicDef_,- profileReport,- onClear,- HeaderSection (..),- libDecl,- earlyDecl,- publicName,- contextField,- contextFieldDyn,- memToCType,- cacheMem,- fatMemory,- rawMemCType,- cproduct,- fatMemType,- declAllocatedMem,- freeAllocatedMem,- collect,-- -- * Building Blocks- primTypeToCType,- intTypeToCType,- copyMemoryDefaultSpace,- linearCode,- derefPointer,- fatMemAlloc,- fatMemSet,- fatMemUnRef,- errorMsgString,- )-where--import Control.Monad.Identity-import Control.Monad.Reader-import Control.Monad.State-import Data.Bifunctor (first)-import Data.Char (isAlpha, isAlphaNum, isDigit)-import qualified Data.DList as DL-import Data.List (unzip4)-import Data.Loc-import qualified Data.Map.Strict as M-import Data.Maybe-import qualified Data.Text as T-import Futhark.CodeGen.Backends.GenericC.CLI (cliDefs)-import Futhark.CodeGen.Backends.GenericC.Options-import Futhark.CodeGen.Backends.GenericC.Server (serverDefs)-import Futhark.CodeGen.Backends.SimpleRep-import Futhark.CodeGen.ImpCode-import Futhark.CodeGen.RTS.C (cacheH, errorsH, halfH, lockH, timingH, utilH)-import Futhark.IR.Prop (isBuiltInFunction)-import qualified Futhark.Manifest as Manifest-import Futhark.MonadFreshNames-import Futhark.Util (chunks, mapAccumLM, zEncodeString)-import Futhark.Util.Pretty (prettyText)-import qualified Language.C.Quote.OpenCL as C-import qualified Language.C.Syntax as C-import NeatInterpolation (untrimming)---- How public an array type definition sould be. Public types show up--- in the generated API, while private types are used only to--- implement the members of opaques.-data Publicness = Private | Public- deriving (Eq, Ord, Show)--type ArrayType = (Signedness, PrimType, Int)--data CompilerState s = CompilerState- { compArrayTypes :: M.Map ArrayType Publicness,- compEarlyDecls :: DL.DList C.Definition,- compInit :: [C.Stm],- compNameSrc :: VNameSource,- compUserState :: s,- compHeaderDecls :: M.Map HeaderSection (DL.DList C.Definition),- compLibDecls :: DL.DList C.Definition,- compCtxFields :: DL.DList (C.Id, C.Type, Maybe C.Exp, Maybe C.Stm),- compProfileItems :: DL.DList C.BlockItem,- compClearItems :: DL.DList C.BlockItem,- compDeclaredMem :: [(VName, Space)],- compItems :: DL.DList C.BlockItem- }--newCompilerState :: VNameSource -> s -> CompilerState s-newCompilerState src s =- CompilerState- { compArrayTypes = mempty,- compEarlyDecls = mempty,- compInit = [],- compNameSrc = src,- compUserState = s,- compHeaderDecls = mempty,- compLibDecls = mempty,- compCtxFields = mempty,- compProfileItems = mempty,- compClearItems = mempty,- compDeclaredMem = mempty,- compItems = mempty- }---- | In which part of the header file we put the declaration. This is--- to ensure that the header file remains structured and readable.-data HeaderSection- = ArrayDecl String- | OpaqueTypeDecl String- | OpaqueDecl String- | EntryDecl- | MiscDecl- | InitDecl- deriving (Eq, Ord)---- | A substitute expression compiler, tried before the main--- compilation function.-type OpCompiler op s = op -> CompilerM op s ()--type ErrorCompiler op s = ErrorMsg Exp -> String -> CompilerM op s ()---- | The address space qualifiers for a pointer of the given type with--- the given annotation.-type PointerQuals op s = String -> CompilerM op s [C.TypeQual]---- | The type of a memory block in the given memory space.-type MemoryType op s = SpaceId -> CompilerM op s C.Type---- | Write a scalar to the given memory block with the given element--- index and in the given memory space.-type WriteScalar op s =- C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> C.Exp -> CompilerM op s ()---- | Read a scalar from the given memory block with the given element--- index and in the given memory space.-type ReadScalar op s =- C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> CompilerM op s C.Exp---- | Allocate a memory block of the given size and with the given tag--- in the given memory space, saving a reference in the given variable--- name.-type Allocate op s =- C.Exp ->- C.Exp ->- C.Exp ->- SpaceId ->- CompilerM op s ()---- | De-allocate the given memory block with the given tag, which is--- in the given memory space.-type Deallocate op s = C.Exp -> C.Exp -> SpaceId -> CompilerM op s ()---- | Create a static array of values - initialised at load time.-type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()---- | Whether a copying operation should implicitly function as a--- barrier regarding further operations on the source. This is a--- rather subtle detail and is mostly useful for letting some--- device/GPU copies be asynchronous (#1664).-data CopyBarrier- = CopyBarrier- | -- | Explicit context synchronisation should be done- -- before the source or target is used.- CopyNoBarrier- deriving (Eq, Show)---- | Copy from one memory block to another.-type Copy op s =- CopyBarrier ->- C.Exp ->- C.Exp ->- Space ->- C.Exp ->- C.Exp ->- Space ->- C.Exp ->- CompilerM op s ()---- | Call a function.-type CallCompiler op s = [VName] -> Name -> [C.Exp] -> CompilerM op s ()--data Operations op s = Operations- { opsWriteScalar :: WriteScalar op s,- opsReadScalar :: ReadScalar op s,- opsAllocate :: Allocate op s,- opsDeallocate :: Deallocate op s,- opsCopy :: Copy op s,- opsStaticArray :: StaticArray op s,- opsMemoryType :: MemoryType op s,- opsCompiler :: OpCompiler op s,- opsError :: ErrorCompiler op s,- opsCall :: CallCompiler op s,- -- | If true, use reference counting. Otherwise, bare- -- pointers.- opsFatMemory :: Bool,- -- | Code to bracket critical sections.- opsCritical :: ([C.BlockItem], [C.BlockItem])- }--errorMsgString :: ErrorMsg Exp -> CompilerM op s (String, [C.Exp])-errorMsgString (ErrorMsg parts) = do- let boolStr e = [C.cexp|($exp:e) ? "true" : "false"|]- asLongLong e = [C.cexp|(long long int)$exp:e|]- asDouble e = [C.cexp|(double)$exp:e|]- onPart (ErrorString s) = pure ("%s", [C.cexp|$string:s|])- onPart (ErrorVal Bool x) = ("%s",) . boolStr <$> compileExp x- onPart (ErrorVal Unit _) = pure ("%s", [C.cexp|"()"|])- onPart (ErrorVal (IntType Int8) x) = ("%hhd",) <$> compileExp x- onPart (ErrorVal (IntType Int16) x) = ("%hd",) <$> compileExp x- onPart (ErrorVal (IntType Int32) x) = ("%d",) <$> compileExp x- onPart (ErrorVal (IntType Int64) x) = ("%lld",) . asLongLong <$> compileExp x- onPart (ErrorVal (FloatType Float16) x) = ("%f",) . asDouble <$> compileExp x- onPart (ErrorVal (FloatType Float32) x) = ("%f",) . asDouble <$> compileExp x- onPart (ErrorVal (FloatType Float64) x) = ("%f",) <$> compileExp x- (formatstrs, formatargs) <- unzip <$> mapM onPart parts- pure (mconcat formatstrs, formatargs)--freeAllocatedMem :: CompilerM op s [C.BlockItem]-freeAllocatedMem = collect $ mapM_ (uncurry unRefMem) =<< gets compDeclaredMem--declAllocatedMem :: CompilerM op s [C.BlockItem]-declAllocatedMem = collect $ mapM_ f =<< gets compDeclaredMem- where- f (name, space) = do- ty <- memToCType name space- decl [C.cdecl|$ty:ty $id:name;|]- resetMem name space--defError :: ErrorCompiler op s-defError msg stacktrace = do- (formatstr, formatargs) <- errorMsgString msg- let formatstr' = "Error: " <> formatstr <> "\n\nBacktrace:\n%s"- items- [C.citems|if (ctx->error == NULL)- ctx->error = msgprintf($string:formatstr', $args:formatargs, $string:stacktrace);- err = FUTHARK_PROGRAM_ERROR;- goto cleanup;|]--defCall :: CallCompiler op s-defCall dests fname args = do- let out_args = [[C.cexp|&$id:d|] | d <- dests]- args'- | isBuiltInFunction fname = args- | otherwise = [C.cexp|ctx|] : out_args ++ args- case dests of- [dest]- | isBuiltInFunction fname ->- stm [C.cstm|$id:dest = $id:(funName fname)($args:args');|]- _ ->- item [C.citem|if ($id:(funName fname)($args:args') != 0) { err = 1; goto cleanup; }|]---- | A set of operations that fail for every operation involving--- non-default memory spaces. Uses plain pointers and @malloc@ for--- memory management.-defaultOperations :: Operations op s-defaultOperations =- Operations- { opsWriteScalar = defWriteScalar,- opsReadScalar = defReadScalar,- opsAllocate = defAllocate,- opsDeallocate = defDeallocate,- opsCopy = defCopy,- opsStaticArray = defStaticArray,- opsMemoryType = defMemoryType,- opsCompiler = defCompiler,- opsFatMemory = True,- opsError = defError,- opsCall = defCall,- opsCritical = mempty- }- where- defWriteScalar _ _ _ _ _ =- error "Cannot write to non-default memory space because I am dumb"- defReadScalar _ _ _ _ =- error "Cannot read from non-default memory space"- defAllocate _ _ _ =- error "Cannot allocate in non-default memory space"- defDeallocate _ _ =- error "Cannot deallocate in non-default memory space"- defCopy _ destmem destoffset DefaultSpace srcmem srcoffset DefaultSpace size =- copyMemoryDefaultSpace destmem destoffset srcmem srcoffset size- defCopy _ _ _ _ _ _ _ _ =- error "Cannot copy to or from non-default memory space"- defStaticArray _ _ _ _ =- error "Cannot create static array in non-default memory space"- defMemoryType _ =- error "Has no type for non-default memory space"- defCompiler _ =- error "The default compiler cannot compile extended operations"--data CompilerEnv op s = CompilerEnv- { envOperations :: Operations op s,- -- | Mapping memory blocks to sizes. These memory blocks are CPU- -- memory that we know are used in particularly simple ways (no- -- reference counting necessary). To cut down on allocator- -- pressure, we keep these allocations around for a long time, and- -- record their sizes so we can reuse them if possible (and- -- realloc() when needed).- envCachedMem :: M.Map C.Exp VName- }--envOpCompiler :: CompilerEnv op s -> OpCompiler op s-envOpCompiler = opsCompiler . envOperations--envMemoryType :: CompilerEnv op s -> MemoryType op s-envMemoryType = opsMemoryType . envOperations--envReadScalar :: CompilerEnv op s -> ReadScalar op s-envReadScalar = opsReadScalar . envOperations--envWriteScalar :: CompilerEnv op s -> WriteScalar op s-envWriteScalar = opsWriteScalar . envOperations--envAllocate :: CompilerEnv op s -> Allocate op s-envAllocate = opsAllocate . envOperations--envDeallocate :: CompilerEnv op s -> Deallocate op s-envDeallocate = opsDeallocate . envOperations--envCopy :: CompilerEnv op s -> Copy op s-envCopy = opsCopy . envOperations--envStaticArray :: CompilerEnv op s -> StaticArray op s-envStaticArray = opsStaticArray . envOperations--envFatMemory :: CompilerEnv op s -> Bool-envFatMemory = opsFatMemory . envOperations--declsCode :: (HeaderSection -> Bool) -> CompilerState s -> T.Text-declsCode p =- T.unlines- . map prettyText- . concatMap (DL.toList . snd)- . filter (p . fst)- . M.toList- . compHeaderDecls--initDecls, arrayDecls, opaqueDecls, opaqueTypeDecls, entryDecls, miscDecls :: CompilerState s -> T.Text-initDecls = declsCode (== InitDecl)-arrayDecls = declsCode isArrayDecl- where- isArrayDecl ArrayDecl {} = True- isArrayDecl _ = False-opaqueTypeDecls = declsCode isOpaqueTypeDecl- where- isOpaqueTypeDecl OpaqueTypeDecl {} = True- isOpaqueTypeDecl _ = False-opaqueDecls = declsCode isOpaqueDecl- where- isOpaqueDecl OpaqueDecl {} = True- isOpaqueDecl _ = False-entryDecls = declsCode (== EntryDecl)-miscDecls = declsCode (== MiscDecl)--contextContents :: CompilerM op s ([C.FieldGroup], [C.Stm], [C.Stm])-contextContents = do- (field_names, field_types, field_values, field_frees) <-- gets $ unzip4 . DL.toList . compCtxFields- let fields =- [ [C.csdecl|$ty:ty $id:name;|]- | (name, ty) <- zip field_names field_types- ]- init_fields =- [ [C.cstm|ctx->$id:name = $exp:e;|]- | (name, Just e) <- zip field_names field_values- ]- pure (fields, init_fields, catMaybes field_frees)--contextFinalInits :: CompilerM op s [C.Stm]-contextFinalInits = gets compInit--newtype CompilerM op s a- = CompilerM (ReaderT (CompilerEnv op s) (State (CompilerState s)) a)- deriving- ( Functor,- Applicative,- Monad,- MonadState (CompilerState s),- MonadReader (CompilerEnv op s)- )--instance MonadFreshNames (CompilerM op s) where- getNameSource = gets compNameSrc- putNameSource src = modify $ \s -> s {compNameSrc = src}--runCompilerM ::- Operations op s ->- VNameSource ->- s ->- CompilerM op s a ->- (a, CompilerState s)-runCompilerM ops src userstate (CompilerM m) =- runState- (runReaderT m (CompilerEnv ops mempty))- (newCompilerState src userstate)--getUserState :: CompilerM op s s-getUserState = gets compUserState--modifyUserState :: (s -> s) -> CompilerM op s ()-modifyUserState f = modify $ \compstate ->- compstate {compUserState = f $ compUserState compstate}--atInit :: C.Stm -> CompilerM op s ()-atInit x = modify $ \s ->- s {compInit = compInit s ++ [x]}--collect :: CompilerM op s () -> CompilerM op s [C.BlockItem]-collect m = snd <$> collect' m--collect' :: CompilerM op s a -> CompilerM op s (a, [C.BlockItem])-collect' m = do- old <- gets compItems- modify $ \s -> s {compItems = mempty}- x <- m- new <- gets compItems- modify $ \s -> s {compItems = old}- pure (x, DL.toList new)---- | Used when we, inside an existing 'CompilerM' action, want to--- generate code for a new function. Use this so that the compiler--- understands that previously declared memory doesn't need to be--- freed inside this action.-inNewFunction :: CompilerM op s a -> CompilerM op s a-inNewFunction m = do- old_mem <- gets compDeclaredMem- modify $ \s -> s {compDeclaredMem = mempty}- x <- local noCached m- modify $ \s -> s {compDeclaredMem = old_mem}- pure x- where- noCached env = env {envCachedMem = mempty}--item :: C.BlockItem -> CompilerM op s ()-item x = modify $ \s -> s {compItems = DL.snoc (compItems s) x}--items :: [C.BlockItem] -> CompilerM op s ()-items xs = modify $ \s -> s {compItems = DL.append (compItems s) (DL.fromList xs)}--fatMemory :: Space -> CompilerM op s Bool-fatMemory ScalarSpace {} = pure False-fatMemory _ = asks envFatMemory--cacheMem :: C.ToExp a => a -> CompilerM op s (Maybe VName)-cacheMem a = asks $ M.lookup (C.toExp a noLoc) . envCachedMem---- | Construct a publicly visible definition using the specified name--- as the template. The first returned definition is put in the--- header file, and the second is the implementation. Returns the public--- name.-publicDef ::- String ->- HeaderSection ->- (String -> (C.Definition, C.Definition)) ->- CompilerM op s String-publicDef s h f = do- s' <- publicName s- let (pub, priv) = f s'- headerDecl h pub- earlyDecl priv- pure s'---- | As 'publicDef', but ignores the public name.-publicDef_ ::- String ->- HeaderSection ->- (String -> (C.Definition, C.Definition)) ->- CompilerM op s ()-publicDef_ s h f = void $ publicDef s h f--headerDecl :: HeaderSection -> C.Definition -> CompilerM op s ()-headerDecl sec def = modify $ \s ->- s- { compHeaderDecls =- M.unionWith- (<>)- (compHeaderDecls s)- (M.singleton sec (DL.singleton def))- }--libDecl :: C.Definition -> CompilerM op s ()-libDecl def = modify $ \s ->- s {compLibDecls = compLibDecls s <> DL.singleton def}--earlyDecl :: C.Definition -> CompilerM op s ()-earlyDecl def = modify $ \s ->- s {compEarlyDecls = compEarlyDecls s <> DL.singleton def}--contextField :: C.Id -> C.Type -> Maybe C.Exp -> CompilerM op s ()-contextField name ty initial = modify $ \s ->- s {compCtxFields = compCtxFields s <> DL.singleton (name, ty, initial, Nothing)}--contextFieldDyn :: C.Id -> C.Type -> Maybe C.Exp -> C.Stm -> CompilerM op s ()-contextFieldDyn name ty initial free = modify $ \s ->- s {compCtxFields = compCtxFields s <> DL.singleton (name, ty, initial, Just free)}--profileReport :: C.BlockItem -> CompilerM op s ()-profileReport x = modify $ \s ->- s {compProfileItems = compProfileItems s <> DL.singleton x}--onClear :: C.BlockItem -> CompilerM op s ()-onClear x = modify $ \s ->- s {compClearItems = compClearItems s <> DL.singleton x}--stm :: C.Stm -> CompilerM op s ()-stm s = item [C.citem|$stm:s|]--stms :: [C.Stm] -> CompilerM op s ()-stms = mapM_ stm--decl :: C.InitGroup -> CompilerM op s ()-decl x = item [C.citem|$decl:x;|]---- | Public names must have a consitent prefix.-publicName :: String -> CompilerM op s String-publicName s = pure $ "futhark_" ++ s---- | The generated code must define a context struct with this name.-contextType :: CompilerM op s C.Type-contextType = do- name <- publicName "context"- pure [C.cty|struct $id:name|]---- | The generated code must define a configuration struct with this--- name.-configType :: CompilerM op s C.Type-configType = do- name <- publicName "context_config"- pure [C.cty|struct $id:name|]--memToCType :: VName -> Space -> CompilerM op s C.Type-memToCType v space = do- refcount <- fatMemory space- cached <- isJust <$> cacheMem v- if refcount && not cached- then pure $ fatMemType space- else rawMemCType space--rawMemCType :: Space -> CompilerM op s C.Type-rawMemCType DefaultSpace = pure defaultMemBlockType-rawMemCType (Space sid) = join $ asks envMemoryType <*> pure sid-rawMemCType (ScalarSpace [] t) =- pure [C.cty|$ty:(primTypeToCType t)[1]|]-rawMemCType (ScalarSpace ds t) =- pure [C.cty|$ty:(primTypeToCType t)[$exp:(cproduct ds')]|]- where- ds' = map (`C.toExp` noLoc) ds--fatMemType :: Space -> C.Type-fatMemType space =- [C.cty|struct $id:name|]- where- name = case space of- Space sid -> "memblock_" ++ sid- _ -> "memblock"--fatMemSet :: Space -> String-fatMemSet (Space sid) = "memblock_set_" ++ sid-fatMemSet _ = "memblock_set"--fatMemAlloc :: Space -> String-fatMemAlloc (Space sid) = "memblock_alloc_" ++ sid-fatMemAlloc _ = "memblock_alloc"--fatMemUnRef :: Space -> String-fatMemUnRef (Space sid) = "memblock_unref_" ++ sid-fatMemUnRef _ = "memblock_unref"--rawMem :: VName -> CompilerM op s C.Exp-rawMem v = rawMem' <$> fat <*> pure v- where- fat = asks ((&&) . envFatMemory) <*> (isNothing <$> cacheMem v)--rawMem' :: C.ToExp a => Bool -> a -> C.Exp-rawMem' True e = [C.cexp|$exp:e.mem|]-rawMem' False e = [C.cexp|$exp:e|]--allocRawMem ::- (C.ToExp a, C.ToExp b, C.ToExp c) =>- a ->- b ->- Space ->- c ->- CompilerM op s ()-allocRawMem dest size space desc = case space of- Space sid ->- join $- asks envAllocate- <*> pure [C.cexp|$exp:dest|]- <*> pure [C.cexp|$exp:size|]- <*> pure [C.cexp|$exp:desc|]- <*> pure sid- _ ->- stm [C.cstm|$exp:dest = (unsigned char*) malloc((size_t)$exp:size);|]--freeRawMem ::- (C.ToExp a, C.ToExp b) =>- a ->- Space ->- b ->- CompilerM op s ()-freeRawMem mem space desc =- case space of- Space sid -> do- free_mem <- asks envDeallocate- free_mem [C.cexp|$exp:mem|] [C.cexp|$exp:desc|] sid- _ -> item [C.citem|free($exp:mem);|]--defineMemorySpace :: Space -> CompilerM op s (C.Definition, [C.Definition], C.BlockItem)-defineMemorySpace space = do- rm <- rawMemCType space- let structdef =- [C.cedecl|struct $id:sname { int *references;- $ty:rm mem;- typename int64_t size;- const char *desc; };|]-- contextField peakname [C.cty|typename int64_t|] $ Just [C.cexp|0|]- contextField usagename [C.cty|typename int64_t|] $ Just [C.cexp|0|]-- -- Unreferencing a memory block consists of decreasing its reference- -- count and freeing the corresponding memory if the count reaches- -- zero.- free <- collect $ freeRawMem [C.cexp|block->mem|] space [C.cexp|desc|]- ctx_ty <- contextType- let unrefdef =- [C.cedecl|int $id:(fatMemUnRef space) ($ty:ctx_ty *ctx, $ty:mty *block, const char *desc) {- if (block->references != NULL) {- *(block->references) -= 1;- if (ctx->detail_memory) {- fprintf(ctx->log, "Unreferencing block %s (allocated as %s) in %s: %d references remaining.\n",- desc, block->desc, $string:spacedesc, *(block->references));- }- if (*(block->references) == 0) {- ctx->$id:usagename -= block->size;- $items:free- free(block->references);- if (ctx->detail_memory) {- fprintf(ctx->log, "%lld bytes freed (now allocated: %lld bytes)\n",- (long long) block->size, (long long) ctx->$id:usagename);- }- }- block->references = NULL;- }- return 0;-}|]-- -- When allocating a memory block we initialise the reference count to 1.- alloc <-- collect $- allocRawMem [C.cexp|block->mem|] [C.cexp|size|] space [C.cexp|desc|]- let allocdef =- [C.cedecl|int $id:(fatMemAlloc space) ($ty:ctx_ty *ctx, $ty:mty *block, typename int64_t size, const char *desc) {- if (size < 0) {- futhark_panic(1, "Negative allocation of %lld bytes attempted for %s in %s.\n",- (long long)size, desc, $string:spacedesc, ctx->$id:usagename);- }- int ret = $id:(fatMemUnRef space)(ctx, block, desc);-- if (ret != FUTHARK_SUCCESS) {- return ret;- }-- if (ctx->detail_memory) {- fprintf(ctx->log, "Allocating %lld bytes for %s in %s (then allocated: %lld bytes)",- (long long) size,- desc, $string:spacedesc,- (long long) ctx->$id:usagename + size);- }- if (ctx->$id:usagename > ctx->$id:peakname) {- ctx->$id:peakname = ctx->$id:usagename;- if (ctx->detail_memory) {- fprintf(ctx->log, " (new peak).\n");- }- } else if (ctx->detail_memory) {- fprintf(ctx->log, ".\n");- }-- $items:alloc-- if (ctx->error == NULL) {- block->references = (int*) malloc(sizeof(int));- *(block->references) = 1;- block->size = size;- block->desc = desc;- ctx->$id:usagename += size;- return FUTHARK_SUCCESS;- } else {- // We are naively assuming that any memory allocation error is due to OOM.- // We preserve the original error so that a savvy user can perhaps find- // glory despite our naiveté.-- char *old_error = ctx->error;- ctx->error = msgprintf("Failed to allocate memory in %s.\nAttempted allocation: %12lld bytes\nCurrently allocated: %12lld bytes\n%s",- $string:spacedesc,- (long long) size,- (long long) ctx->$id:usagename,- old_error);- free(old_error);- return FUTHARK_OUT_OF_MEMORY;- }- }|]-- -- Memory setting - unreference the destination and increase the- -- count of the source by one.- let setdef =- [C.cedecl|int $id:(fatMemSet space) ($ty:ctx_ty *ctx, $ty:mty *lhs, $ty:mty *rhs, const char *lhs_desc) {- int ret = $id:(fatMemUnRef space)(ctx, lhs, lhs_desc);- if (rhs->references != NULL) {- (*(rhs->references))++;- }- *lhs = *rhs;- return ret;-}-|]-- onClear [C.citem|ctx->$id:peakname = 0;|]-- let peakmsg = "Peak memory usage for " ++ spacedesc ++ ": %lld bytes.\n"- pure- ( structdef,- [unrefdef, allocdef, setdef],- -- Do not report memory usage for DefaultSpace (CPU memory),- -- because it would not be accurate anyway. This whole- -- tracking probably needs to be rethought.- if space == DefaultSpace- then [C.citem|{}|]- else [C.citem|str_builder(&builder, $string:peakmsg, (long long) ctx->$id:peakname);|]- )- where- mty = fatMemType space- (peakname, usagename, sname, spacedesc) = case space of- Space sid ->- ( C.toIdent ("peak_mem_usage_" ++ sid) noLoc,- C.toIdent ("cur_mem_usage_" ++ sid) noLoc,- C.toIdent ("memblock_" ++ sid) noLoc,- "space '" ++ sid ++ "'"- )- _ ->- ( "peak_mem_usage_default",- "cur_mem_usage_default",- "memblock",- "default space"- )--declMem :: VName -> Space -> CompilerM op s ()-declMem name space = do- cached <- isJust <$> cacheMem name- fat <- fatMemory space- unless cached $- if fat- then modify $ \s -> s {compDeclaredMem = (name, space) : compDeclaredMem s}- else do- ty <- memToCType name space- decl [C.cdecl|$ty:ty $id:name;|]--resetMem :: C.ToExp a => a -> Space -> CompilerM op s ()-resetMem mem space = do- refcount <- fatMemory space- cached <- isJust <$> cacheMem mem- if cached- then stm [C.cstm|$exp:mem = NULL;|]- else- when refcount $- stm [C.cstm|$exp:mem.references = NULL;|]--setMem :: (C.ToExp a, C.ToExp b) => a -> b -> Space -> CompilerM op s ()-setMem dest src space = do- refcount <- fatMemory space- let src_s = pretty $ C.toExp src noLoc- if refcount- then- stm- [C.cstm|if ($id:(fatMemSet space)(ctx, &$exp:dest, &$exp:src,- $string:src_s) != 0) {- return 1;- }|]- else case space of- ScalarSpace ds _ -> do- i' <- newVName "i"- let i = C.toIdent i'- it = primTypeToCType $ IntType Int32- ds' = map (`C.toExp` noLoc) ds- bound = cproduct ds'- stm- [C.cstm|for ($ty:it $id:i = 0; $id:i < $exp:bound; $id:i++) {- $exp:dest[$id:i] = $exp:src[$id:i];- }|]- _ -> stm [C.cstm|$exp:dest = $exp:src;|]--unRefMem :: C.ToExp a => a -> Space -> CompilerM op s ()-unRefMem mem space = do- refcount <- fatMemory space- cached <- isJust <$> cacheMem mem- let mem_s = pretty $ C.toExp mem noLoc- when (refcount && not cached) $- stm- [C.cstm|if ($id:(fatMemUnRef space)(ctx, &$exp:mem, $string:mem_s) != 0) {- return 1;- }|]--allocMem ::- (C.ToExp a, C.ToExp b) =>- a ->- b ->- Space ->- C.Stm ->- CompilerM op s ()-allocMem mem size space on_failure = do- refcount <- fatMemory space- let mem_s = pretty $ C.toExp mem noLoc- if refcount- then- stm- [C.cstm|if ($id:(fatMemAlloc space)(ctx, &$exp:mem, $exp:size,- $string:mem_s)) {- $stm:on_failure- }|]- else do- freeRawMem mem space mem_s- allocRawMem mem size space [C.cexp|desc|]--copyMemoryDefaultSpace ::- C.Exp ->- C.Exp ->- C.Exp ->- C.Exp ->- C.Exp ->- CompilerM op s ()-copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes =- stm- [C.cstm|if ($exp:nbytes > 0) {- memmove($exp:destmem + $exp:destidx,- $exp:srcmem + $exp:srcidx,- $exp:nbytes);- }|]----- Entry points.--criticalSection :: Operations op s -> [C.BlockItem] -> [C.BlockItem]-criticalSection ops x =- [C.citems|lock_lock(&ctx->lock);- $items:(fst (opsCritical ops))- $items:x- $items:(snd (opsCritical ops))- lock_unlock(&ctx->lock);- |]--arrayLibraryFunctions ::- Publicness ->- Space ->- PrimType ->- Signedness ->- Int ->- CompilerM op s Manifest.ArrayOps-arrayLibraryFunctions pub space pt signed rank = do- let pt' = primAPIType signed pt- name = arrayName pt signed rank- arr_name = "futhark_" ++ name- array_type = [C.cty|struct $id:arr_name|]-- new_array <- publicName $ "new_" ++ name- new_raw_array <- publicName $ "new_raw_" ++ name- free_array <- publicName $ "free_" ++ name- values_array <- publicName $ "values_" ++ name- values_raw_array <- publicName $ "values_raw_" ++ name- shape_array <- publicName $ "shape_" ++ name-- let shape_names = ["dim" ++ show i | i <- [0 .. rank - 1]]- shape_params = [[C.cparam|typename int64_t $id:k|] | k <- shape_names]- arr_size = cproduct [[C.cexp|$id:k|] | k <- shape_names]- arr_size_array = cproduct [[C.cexp|arr->shape[$int:i]|] | i <- [0 .. rank - 1]]- copy <- asks envCopy-- memty <- rawMemCType space-- let prepare_new = do- resetMem [C.cexp|arr->mem|] space- allocMem- [C.cexp|arr->mem|]- [C.cexp|$exp:arr_size * $int:(primByteSize pt::Int)|]- space- [C.cstm|return NULL;|]- forM_ [0 .. rank - 1] $ \i ->- let dim_s = "dim" ++ show i- in stm [C.cstm|arr->shape[$int:i] = $id:dim_s;|]-- new_body <- collect $ do- prepare_new- copy- CopyNoBarrier- [C.cexp|arr->mem.mem|]- [C.cexp|0|]- space- [C.cexp|data|]- [C.cexp|0|]- DefaultSpace- [C.cexp|((size_t)$exp:arr_size) * $int:(primByteSize pt::Int)|]-- new_raw_body <- collect $ do- prepare_new- copy- CopyNoBarrier- [C.cexp|arr->mem.mem|]- [C.cexp|0|]- space- [C.cexp|data|]- [C.cexp|offset|]- space- [C.cexp|((size_t)$exp:arr_size) * $int:(primByteSize pt::Int)|]-- free_body <- collect $ unRefMem [C.cexp|arr->mem|] space-- values_body <-- collect $- copy- CopyNoBarrier- [C.cexp|data|]- [C.cexp|0|]- DefaultSpace- [C.cexp|arr->mem.mem|]- [C.cexp|0|]- space- [C.cexp|((size_t)$exp:arr_size_array) * $int:(primByteSize pt::Int)|]-- ctx_ty <- contextType- ops <- asks envOperations-- let proto = case pub of- Public -> headerDecl (ArrayDecl name)- Private -> libDecl-- proto- [C.cedecl|struct $id:arr_name;|]- proto- [C.cedecl|$ty:array_type* $id:new_array($ty:ctx_ty *ctx, const $ty:pt' *data, $params:shape_params);|]- proto- [C.cedecl|$ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, const $ty:memty data, typename int64_t offset, $params:shape_params);|]- proto- [C.cedecl|int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]- proto- [C.cedecl|int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data);|]- proto- [C.cedecl|$ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]- proto- [C.cedecl|const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]-- mapM_- libDecl- [C.cunit|- $ty:array_type* $id:new_array($ty:ctx_ty *ctx, const $ty:pt' *data, $params:shape_params) {- $ty:array_type* bad = NULL;- $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));- if (arr == NULL) {- return bad;- }- $items:(criticalSection ops new_body)- return arr;- }-- $ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, const $ty:memty data, typename int64_t offset,- $params:shape_params) {- $ty:array_type* bad = NULL;- $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));- if (arr == NULL) {- return bad;- }- $items:(criticalSection ops new_raw_body)- return arr;- }-- int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr) {- $items:(criticalSection ops free_body)- free(arr);- return 0;- }-- int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data) {- $items:(criticalSection ops values_body)- return 0;- }-- $ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr) {- (void)ctx;- return arr->mem.mem;- }-- const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr) {- (void)ctx;- return arr->shape;- }- |]-- pure $- Manifest.ArrayOps- { Manifest.arrayFree = T.pack free_array,- Manifest.arrayShape = T.pack shape_array,- Manifest.arrayValues = T.pack values_array,- Manifest.arrayNew = T.pack new_array- }--lookupOpaqueType :: String -> OpaqueTypes -> OpaqueType-lookupOpaqueType v (OpaqueTypes types) =- case lookup v types of- Just t -> t- Nothing -> error $ "Unknown opaque type: " ++ show v--opaquePayload :: OpaqueTypes -> OpaqueType -> [ValueType]-opaquePayload _ (OpaqueType ts) = ts-opaquePayload types (OpaqueRecord fs) = concatMap f fs- where- f (_, TypeOpaque s) = opaquePayload types $ lookupOpaqueType s types- f (_, TypeTransparent v) = [v]--opaqueToCType :: String -> CompilerM op s C.Type-opaqueToCType desc = do- name <- publicName $ opaqueName desc- pure [C.cty|struct $id:name|]--valueTypeToCType :: Publicness -> ValueType -> CompilerM op s C.Type-valueTypeToCType _ (ValueType signed (Rank 0) pt) =- pure $ primAPIType signed pt-valueTypeToCType pub (ValueType signed (Rank rank) pt) = do- name <- publicName $ arrayName pt signed rank- let add = M.insertWith max (signed, pt, rank) pub- modify $ \s -> s {compArrayTypes = add $ compArrayTypes s}- pure [C.cty|struct $id:name|]--entryPointTypeToCType :: Publicness -> EntryPointType -> CompilerM op s C.Type-entryPointTypeToCType _ (TypeOpaque desc) = opaqueToCType desc-entryPointTypeToCType pub (TypeTransparent vt) = valueTypeToCType pub vt--entryTypeName :: EntryPointType -> Manifest.TypeName-entryTypeName (TypeOpaque desc) = T.pack desc-entryTypeName (TypeTransparent vt) = prettyText vt---- | Figure out which of the members of an opaque type corresponds to--- which fields.-recordFieldPayloads :: OpaqueTypes -> [EntryPointType] -> [a] -> [[a]]-recordFieldPayloads types = chunks . map typeLength- where- typeLength (TypeTransparent _) = 1- typeLength (TypeOpaque desc) =- length $ opaquePayload types $ lookupOpaqueType desc types--opaqueProjectFunctions ::- OpaqueTypes ->- String ->- [(Name, EntryPointType)] ->- [ValueType] ->- CompilerM op s [Manifest.RecordField]-opaqueProjectFunctions types desc fs vds = do- opaque_type <- opaqueToCType desc- ctx_ty <- contextType- ops <- asks envOperations- let mkProject (TypeTransparent (ValueType sign (Rank 0) pt)) [(i, _)] = do- pure- ( primAPIType sign pt,- [C.citems|v = obj->$id:(tupleField i);|]- )- mkProject (TypeTransparent vt) [(i, _)] = do- ct <- valueTypeToCType Public vt- pure- ( [C.cty|$ty:ct *|],- criticalSection- ops- [C.citems|v = malloc(sizeof($ty:ct));- memcpy(v, obj->$id:(tupleField i), sizeof($ty:ct));- (void)(*(v->mem.references))++;|]- )- mkProject (TypeTransparent _) rep =- error $ "mkProject: invalid representation of transparent type: " ++ show rep- mkProject (TypeOpaque f_desc) components = do- ct <- opaqueToCType f_desc- let setField j (i, ValueType _ (Rank r) _) =- if r == 0- then [C.citems|v->$id:(tupleField j) = obj->$id:(tupleField i);|]- else- [C.citems|v->$id:(tupleField j) = malloc(sizeof(*v->$id:(tupleField j)));- *v->$id:(tupleField j) = *obj->$id:(tupleField i);- (void)(*(v->$id:(tupleField j)->mem.references))++;|]- pure- ( [C.cty|$ty:ct *|],- criticalSection- ops- [C.citems|v = malloc(sizeof($ty:ct));- $items:(concat (zipWith setField [0..] components))|]- )- let onField ((f, et), elems) = do- project <- publicName $ "project_" ++ opaqueName desc ++ "_" ++ nameToString f- (et_ty, project_items) <- mkProject et elems- headerDecl- (OpaqueDecl desc)- [C.cedecl|int $id:project($ty:ctx_ty *ctx, $ty:et_ty *out, const $ty:opaque_type *obj);|]- libDecl- [C.cedecl|int $id:project($ty:ctx_ty *ctx, $ty:et_ty *out, const $ty:opaque_type *obj) {- (void)ctx;- $ty:et_ty v;- $items:project_items- *out = v;- return 0;- }|]- pure $ Manifest.RecordField (nameToText f) (entryTypeName et) (T.pack project)-- mapM onField . zip fs . recordFieldPayloads types (map snd fs) $- zip [0 ..] vds--opaqueNewFunctions ::- OpaqueTypes ->- String ->- [(Name, EntryPointType)] ->- [ValueType] ->- CompilerM op s Manifest.CFuncName-opaqueNewFunctions types desc fs vds = do- opaque_type <- opaqueToCType desc- ctx_ty <- contextType- ops <- asks envOperations- new <- publicName $ "new_" ++ opaqueName desc-- (params, new_stms) <-- fmap (unzip . snd)- . mapAccumLM onField 0- . zip fs- . recordFieldPayloads types (map snd fs)- $ vds-- headerDecl- (OpaqueDecl desc)- [C.cedecl|int $id:new($ty:ctx_ty *ctx, $ty:opaque_type** out, $params:params);|]- libDecl- [C.cedecl|int $id:new($ty:ctx_ty *ctx, $ty:opaque_type** out, $params:params) {- $ty:opaque_type* v = malloc(sizeof($ty:opaque_type));- $items:(criticalSection ops new_stms)- *out = v;- return 0;- }|]- pure $ T.pack new- where- onField offset ((f, et), f_vts) = do- let param_name =- if all isDigit (nameToString f)- then C.toIdent ("v" <> f) mempty- else C.toIdent f mempty- case et of- TypeTransparent (ValueType sign (Rank 0) pt) -> do- let ct = primAPIType sign pt- pure- ( offset + 1,- ( [C.cparam|const $ty:ct $id:param_name|],- [C.citem|v->$id:(tupleField offset) = $id:param_name;|]- )- )- TypeTransparent vt -> do- ct <- valueTypeToCType Public vt- pure- ( offset + 1,- ( [C.cparam|const $ty:ct* $id:param_name|],- [C.citem|{v->$id:(tupleField offset) = malloc(sizeof($ty:ct));- *v->$id:(tupleField offset) = *$id:param_name;- (void)(*(v->$id:(tupleField offset)->mem.references))++;}|]- )- )- TypeOpaque f_desc -> do- ct <- opaqueToCType f_desc- let param_fields = do- i <- [0 ..]- pure [C.cexp|$id:param_name->$id:(tupleField i)|]- pure- ( offset + length f_vts,- ( [C.cparam|const $ty:ct* $id:param_name|],- [C.citem|{$stms:(zipWith3 setFieldField [offset ..] param_fields f_vts)}|]- )- )-- setFieldField i e (ValueType _ (Rank r) _)- | r == 0 =- [C.cstm|v->$id:(tupleField i) = $exp:e;|]- | otherwise =- [C.cstm|{v->$id:(tupleField i) = malloc(sizeof(*$exp:e));- *v->$id:(tupleField i) = *$exp:e;- (void)(*(v->$id:(tupleField i)->mem.references))++;}|]--processOpaqueRecord ::- OpaqueTypes ->- String ->- OpaqueType ->- [ValueType] ->- CompilerM op s (Maybe Manifest.RecordOps)-processOpaqueRecord _ _ (OpaqueType _) _ = pure Nothing-processOpaqueRecord types desc (OpaqueRecord fs) vds =- Just- <$> ( Manifest.RecordOps- <$> opaqueProjectFunctions types desc fs vds- <*> opaqueNewFunctions types desc fs vds- )--opaqueLibraryFunctions ::- OpaqueTypes ->- String ->- OpaqueType ->- CompilerM op s (Manifest.OpaqueOps, Maybe Manifest.RecordOps)-opaqueLibraryFunctions types desc ot = do- name <- publicName $ opaqueName desc- free_opaque <- publicName $ "free_" ++ opaqueName desc- store_opaque <- publicName $ "store_" ++ opaqueName desc- restore_opaque <- publicName $ "restore_" ++ opaqueName desc-- let opaque_type = [C.cty|struct $id:name|]-- freeComponent i (ValueType signed (Rank rank) pt) = unless (rank == 0) $ do- let field = tupleField i- free_array <- publicName $ "free_" ++ arrayName pt signed rank- -- Protect against NULL here, because we also want to use this- -- to free partially loaded opaques.- stm- [C.cstm|if (obj->$id:field != NULL && (tmp = $id:free_array(ctx, obj->$id:field)) != 0) {- ret = tmp;- }|]-- storeComponent i (ValueType sign (Rank 0) pt) =- let field = tupleField i- in ( storageSize pt 0 [C.cexp|NULL|],- storeValueHeader sign pt 0 [C.cexp|NULL|] [C.cexp|out|]- ++ [C.cstms|memcpy(out, &obj->$id:field, sizeof(obj->$id:field));- out += sizeof(obj->$id:field);|]- )- storeComponent i (ValueType sign (Rank rank) pt) =- let arr_name = arrayName pt sign rank- field = tupleField i- shape_array = "futhark_shape_" ++ arr_name- values_array = "futhark_values_" ++ arr_name- shape' = [C.cexp|$id:shape_array(ctx, obj->$id:field)|]- num_elems = cproduct [[C.cexp|$exp:shape'[$int:j]|] | j <- [0 .. rank - 1]]- in ( storageSize pt rank shape',- storeValueHeader sign pt rank shape' [C.cexp|out|]- ++ [C.cstms|ret |= $id:values_array(ctx, obj->$id:field, (void*)out);- out += $exp:num_elems * $int:(primByteSize pt::Int);|]- )-- ctx_ty <- contextType-- let vds = opaquePayload types ot- free_body <- collect $ zipWithM_ freeComponent [0 ..] vds-- store_body <- collect $ do- let (sizes, stores) = unzip $ zipWith storeComponent [0 ..] vds- size_vars = map (("size_" ++) . show) [0 .. length sizes - 1]- size_sum = csum [[C.cexp|$id:size|] | size <- size_vars]- forM_ (zip size_vars sizes) $ \(v, e) ->- item [C.citem|typename int64_t $id:v = $exp:e;|]- stm [C.cstm|*n = $exp:size_sum;|]- stm [C.cstm|if (p != NULL && *p == NULL) { *p = malloc(*n); }|]- stm [C.cstm|if (p != NULL) { unsigned char *out = *p; $stms:(concat stores) }|]-- let restoreComponent i (ValueType sign (Rank 0) pt) = do- let field = tupleField i- dataptr = "data_" ++ show i- stms $ loadValueHeader sign pt 0 [C.cexp|NULL|] [C.cexp|src|]- item [C.citem|const void* $id:dataptr = src;|]- stm [C.cstm|src += sizeof(obj->$id:field);|]- pure [C.cstms|memcpy(&obj->$id:field, $id:dataptr, sizeof(obj->$id:field));|]- restoreComponent i (ValueType sign (Rank rank) pt) = do- let field = tupleField i- arr_name = arrayName pt sign rank- new_array = "futhark_new_" ++ arr_name- dataptr = "data_" ++ show i- shapearr = "shape_" ++ show i- dims = [[C.cexp|$id:shapearr[$int:j]|] | j <- [0 .. rank - 1]]- num_elems = cproduct dims- item [C.citem|typename int64_t $id:shapearr[$int:rank] = {0};|]- stms $ loadValueHeader sign pt rank [C.cexp|$id:shapearr|] [C.cexp|src|]- item [C.citem|const void* $id:dataptr = src;|]- stm [C.cstm|obj->$id:field = NULL;|]- stm [C.cstm|src += $exp:num_elems * $int:(primByteSize pt::Int);|]- pure- [C.cstms|- obj->$id:field = $id:new_array(ctx, $id:dataptr, $args:dims);- if (obj->$id:field == NULL) { err = 1; }|]-- load_body <- collect $ do- loads <- concat <$> zipWithM restoreComponent [0 ..] (opaquePayload types ot)- stm- [C.cstm|if (err == 0) {- $stms:loads- }|]-- headerDecl- (OpaqueTypeDecl desc)- [C.cedecl|struct $id:name;|]- headerDecl- (OpaqueDecl desc)- [C.cedecl|int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj);|]- headerDecl- (OpaqueDecl desc)- [C.cedecl|int $id:store_opaque($ty:ctx_ty *ctx, const $ty:opaque_type *obj, void **p, size_t *n);|]- headerDecl- (OpaqueDecl desc)- [C.cedecl|$ty:opaque_type* $id:restore_opaque($ty:ctx_ty *ctx, const void *p);|]-- record <- processOpaqueRecord types desc ot vds-- -- We do not need to enclose most bodies in a critical section,- -- because when we operate on the components of the opaque, we are- -- calling public API functions that do their own locking. The- -- exception is projection, where we fiddle with reference counts.- mapM_- libDecl- [C.cunit|- int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj) {- (void)ctx;- int ret = 0, tmp;- $items:free_body- free(obj);- return ret;- }-- int $id:store_opaque($ty:ctx_ty *ctx,- const $ty:opaque_type *obj, void **p, size_t *n) {- (void)ctx;- int ret = 0;- $items:store_body- return ret;- }-- $ty:opaque_type* $id:restore_opaque($ty:ctx_ty *ctx,- const void *p) {- int err = 0;- const unsigned char *src = p;- $ty:opaque_type* obj = malloc(sizeof($ty:opaque_type));- $items:load_body- if (err != 0) {- int ret = 0, tmp;- $items:free_body- free(obj);- obj = NULL;- }- return obj;- }- |]-- pure- ( Manifest.OpaqueOps- { Manifest.opaqueFree = T.pack free_opaque,- Manifest.opaqueStore = T.pack store_opaque,- Manifest.opaqueRestore = T.pack restore_opaque- },- record- )--valueDescToType :: ValueDesc -> ValueType-valueDescToType (ScalarValue pt signed _) =- ValueType signed (Rank 0) pt-valueDescToType (ArrayValue _ _ pt signed shape) =- ValueType signed (Rank (length shape)) pt--generateArray ::- Space ->- ((Signedness, PrimType, Int), Publicness) ->- CompilerM op s (Maybe (T.Text, Manifest.Type))-generateArray space ((signed, pt, rank), pub) = do- name <- publicName $ arrayName pt signed rank- let memty = fatMemType space- libDecl [C.cedecl|struct $id:name { $ty:memty mem; typename int64_t shape[$int:rank]; };|]- ops <- arrayLibraryFunctions pub space pt signed rank- let pt_name = T.pack $ prettySigned (signed == Unsigned) pt- pretty_name = mconcat (replicate rank "[]") <> pt_name- arr_type = [C.cty|struct $id:name*|]- case pub of- Public ->- pure $- Just- ( pretty_name,- Manifest.TypeArray (prettyText arr_type) pt_name rank ops- )- Private ->- pure Nothing--generateOpaque ::- OpaqueTypes ->- (String, OpaqueType) ->- CompilerM op s (T.Text, Manifest.Type)-generateOpaque types (desc, ot) = do- name <- publicName $ opaqueName desc- members <- zipWithM field (opaquePayload types ot) [(0 :: Int) ..]- libDecl [C.cedecl|struct $id:name { $sdecls:members };|]- (ops, record) <- opaqueLibraryFunctions types desc ot- let opaque_type = [C.cty|struct $id:name*|]- pure (T.pack desc, Manifest.TypeOpaque (prettyText opaque_type) ops record)- where- field vt@(ValueType _ (Rank r) _) i = do- ct <- valueTypeToCType Private vt- pure $- if r == 0- then [C.csdecl|$ty:ct $id:(tupleField i);|]- else [C.csdecl|$ty:ct *$id:(tupleField i);|]--generateAPITypes :: Space -> OpaqueTypes -> CompilerM op s (M.Map T.Text Manifest.Type)-generateAPITypes arr_space types@(OpaqueTypes opaques) = do- mapM_ (findNecessaryArrays . snd) opaques- array_ts <- mapM (generateArray arr_space) . M.toList =<< gets compArrayTypes- opaque_ts <- mapM (generateOpaque types) opaques- pure $ M.fromList $ catMaybes array_ts <> opaque_ts- where- -- Ensure that array types will be generated before the opaque- -- records that allow projection of them. This is because the- -- projection functions somewhat uglily directly poke around in- -- the innards to increment reference counts.- findNecessaryArrays (OpaqueType _) =- pure ()- findNecessaryArrays (OpaqueRecord fs) =- mapM_ (entryPointTypeToCType Public . snd) fs--allTrue :: [C.Exp] -> C.Exp-allTrue [] = [C.cexp|true|]-allTrue [x] = x-allTrue (x : xs) = [C.cexp|$exp:x && $exp:(allTrue xs)|]--prepareEntryInputs ::- [ExternalValue] ->- CompilerM op s ([(C.Param, Maybe C.Exp)], [C.BlockItem])-prepareEntryInputs args = collect' $ zipWithM prepare [(0 :: Int) ..] args- where- arg_names = namesFromList $ concatMap evNames args- evNames (OpaqueValue _ vds) = map vdName vds- evNames (TransparentValue vd) = [vdName vd]- vdName (ArrayValue v _ _ _ _) = v- vdName (ScalarValue _ _ v) = v-- prepare pno (TransparentValue vd) = do- let pname = "in" ++ show pno- (ty, check) <- prepareValue Public [C.cexp|$id:pname|] vd- pure- ( [C.cparam|const $ty:ty $id:pname|],- if null check then Nothing else Just $ allTrue check- )- prepare pno (OpaqueValue desc vds) = do- ty <- opaqueToCType desc- let pname = "in" ++ show pno- field i ScalarValue {} = [C.cexp|$id:pname->$id:(tupleField i)|]- field i ArrayValue {} = [C.cexp|$id:pname->$id:(tupleField i)|]- checks <- map snd <$> zipWithM (prepareValue Private) (zipWith field [0 ..] vds) vds- pure- ( [C.cparam|const $ty:ty *$id:pname|],- if all null checks- then Nothing- else Just $ allTrue $ concat checks- )-- prepareValue _ src (ScalarValue pt signed name) = do- let pt' = primAPIType signed pt- src' = fromStorage pt $ C.toExp src mempty- stm [C.cstm|$id:name = $exp:src';|]- pure (pt', [])- prepareValue pub src vd@(ArrayValue mem _ _ _ shape) = do- ty <- valueTypeToCType pub $ valueDescToType vd-- stm [C.cstm|$exp:mem = $exp:src->mem;|]-- let rank = length shape- maybeCopyDim (Var d) i- | d `notNameIn` arg_names =- ( Just [C.cstm|$id:d = $exp:src->shape[$int:i];|],- [C.cexp|$id:d == $exp:src->shape[$int:i]|]- )- maybeCopyDim x i =- ( Nothing,- [C.cexp|$exp:x == $exp:src->shape[$int:i]|]- )-- let (sets, checks) =- unzip $ zipWith maybeCopyDim shape [0 .. rank - 1]- stms $ catMaybes sets-- pure ([C.cty|$ty:ty*|], checks)--prepareEntryOutputs :: [ExternalValue] -> CompilerM op s ([C.Param], [C.BlockItem])-prepareEntryOutputs = collect' . zipWithM prepare [(0 :: Int) ..]- where- prepare pno (TransparentValue vd) = do- let pname = "out" ++ show pno- ty <- valueTypeToCType Public $ valueDescToType vd-- case vd of- ArrayValue {} -> do- stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]- prepareValue [C.cexp|*$id:pname|] vd- pure [C.cparam|$ty:ty **$id:pname|]- ScalarValue {} -> do- prepareValue [C.cexp|*$id:pname|] vd- pure [C.cparam|$ty:ty *$id:pname|]- prepare pno (OpaqueValue desc vds) = do- let pname = "out" ++ show pno- ty <- opaqueToCType desc- vd_ts <- mapM (valueTypeToCType Private . valueDescToType) vds-- stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]-- forM_ (zip3 [0 ..] vd_ts vds) $ \(i, ct, vd) -> do- let field = [C.cexp|((*$id:pname)->$id:(tupleField i))|]- case vd of- ScalarValue {} -> pure ()- ArrayValue {} -> do- stm [C.cstm|assert(($exp:field = ($ty:ct*) malloc(sizeof($ty:ct))) != NULL);|]- prepareValue field vd-- pure [C.cparam|$ty:ty **$id:pname|]-- prepareValue dest (ScalarValue t _ name) =- let name' = toStorage t $ C.toExp name mempty- in stm [C.cstm|$exp:dest = $exp:name';|]- prepareValue dest (ArrayValue mem _ _ _ shape) = do- stm [C.cstm|$exp:dest->mem = $id:mem;|]-- let rank = length shape- maybeCopyDim (Constant x) i =- [C.cstm|$exp:dest->shape[$int:i] = $exp:x;|]- maybeCopyDim (Var d) i =- [C.cstm|$exp:dest->shape[$int:i] = $id:d;|]- stms $ zipWith maybeCopyDim shape [0 .. rank - 1]--isValidCName :: Name -> Bool-isValidCName = check . nameToString- where- check [] = True -- academic- check (c : cs) = isAlpha c && all constituent cs- constituent c = isAlphaNum c || c == '_'--entryName :: Name -> String-entryName v- | isValidCName v = "entry_" <> nameToString v- | otherwise = "entry_" <> zEncodeString (nameToString v)--onEntryPoint ::- [C.BlockItem] ->- Name ->- Function op ->- CompilerM op s (Maybe (C.Definition, (T.Text, Manifest.EntryPoint)))-onEntryPoint _ _ (Function Nothing _ _ _) = pure Nothing-onEntryPoint get_consts fname (Function (Just (EntryPoint ename results args)) outputs inputs _) = inNewFunction $ do- let out_args = map (\p -> [C.cexp|&$id:(paramName p)|]) outputs- in_args = map (\p -> [C.cexp|$id:(paramName p)|]) inputs-- inputdecls <- collect $ mapM_ stubParam inputs- outputdecls <- collect $ mapM_ stubParam outputs- decl_mem <- declAllocatedMem-- entry_point_function_name <- publicName $ entryName ename-- (inputs', unpack_entry_inputs) <- prepareEntryInputs $ map snd args- let (entry_point_input_params, entry_point_input_checks) = unzip inputs'-- (entry_point_output_params, pack_entry_outputs) <-- prepareEntryOutputs $ map snd results-- ctx_ty <- contextType-- headerDecl- EntryDecl- [C.cedecl|int $id:entry_point_function_name- ($ty:ctx_ty *ctx,- $params:entry_point_output_params,- $params:entry_point_input_params);|]-- let checks = catMaybes entry_point_input_checks- check_input =- if null checks- then []- else- [C.citems|- if (!($exp:(allTrue (catMaybes entry_point_input_checks)))) {- ret = 1;- if (!ctx->error) {- ctx->error = msgprintf("Error: entry point arguments have invalid sizes.\n");- }- }|]-- critical =- [C.citems|- $items:decl_mem- $items:unpack_entry_inputs- $items:check_input- if (ret == 0) {- ret = $id:(funName fname)(ctx, $args:out_args, $args:in_args);- if (ret == 0) {- $items:get_consts-- $items:pack_entry_outputs- }- }- |]-- ops <- asks envOperations-- let cdef =- [C.cedecl|- int $id:entry_point_function_name- ($ty:ctx_ty *ctx,- $params:entry_point_output_params,- $params:entry_point_input_params) {- $items:inputdecls- $items:outputdecls-- int ret = 0;-- $items:(criticalSection ops critical)-- return ret;- }|]-- manifest =- Manifest.EntryPoint- { Manifest.entryPointCFun = T.pack entry_point_function_name,- -- Note that our convention about what is "input/output"- -- and what is "results/args" is different between the- -- manifest and ImpCode.- Manifest.entryPointOutputs = map outputManifest results,- Manifest.entryPointInputs = map inputManifest args- }-- pure $ Just (cdef, (nameToText ename, manifest))- where- stubParam (MemParam name space) =- declMem name space- stubParam (ScalarParam name ty) = do- let ty' = primTypeToCType ty- decl [C.cdecl|$ty:ty' $id:name;|]-- vdType (TransparentValue (ScalarValue pt signed _)) =- T.pack $ prettySigned (signed == Unsigned) pt- vdType (TransparentValue (ArrayValue _ _ pt signed shape)) =- T.pack $- mconcat (replicate (length shape) "[]")- <> prettySigned (signed == Unsigned) pt- vdType (OpaqueValue name _) =- T.pack name-- outputManifest (u, vd) =- Manifest.Output- { Manifest.outputType = vdType vd,- Manifest.outputUnique = u == Unique- }- inputManifest ((v, u), vd) =- Manifest.Input- { Manifest.inputName = nameToText v,- Manifest.inputType = vdType vd,- Manifest.inputUnique = u == Unique- }---- | The result of compilation to C is multiple parts, which can be--- put together in various ways. The obvious way is to concatenate--- all of them, which yields a CLI program. Another is to compile the--- library part by itself, and use the header file to call into it.-data CParts = CParts- { cHeader :: T.Text,- -- | Utility definitions that must be visible- -- to both CLI and library parts.- cUtils :: T.Text,- cCLI :: T.Text,- cServer :: T.Text,- cLib :: T.Text,- -- | The manifest, in JSON format.- cJsonManifest :: T.Text- }--gnuSource :: T.Text-gnuSource =- [untrimming|-// We need to define _GNU_SOURCE before-// _any_ headers files are imported to get-// the usage statistics of a thread (i.e. have RUSAGE_THREAD) on GNU/Linux-// https://manpages.courier-mta.org/htmlman2/getrusage.2.html-#ifndef _GNU_SOURCE // Avoid possible double-definition warning.-#define _GNU_SOURCE-#endif-|]---- We may generate variables that are never used (e.g. for--- certificates) or functions that are never called (e.g. unused--- intrinsics), and generated code may have other cosmetic issues that--- compilers warn about. We disable these warnings to not clutter the--- compilation logs.-disableWarnings :: T.Text-disableWarnings =- [untrimming|-#ifdef __clang__-#pragma clang diagnostic ignored "-Wunused-function"-#pragma clang diagnostic ignored "-Wunused-variable"-#pragma clang diagnostic ignored "-Wparentheses"-#pragma clang diagnostic ignored "-Wunused-label"-#elif __GNUC__-#pragma GCC diagnostic ignored "-Wunused-function"-#pragma GCC diagnostic ignored "-Wunused-variable"-#pragma GCC diagnostic ignored "-Wparentheses"-#pragma GCC diagnostic ignored "-Wunused-label"-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"-#endif-|]---- | Produce header, implementation, and manifest files.-asLibrary :: CParts -> (T.Text, T.Text, T.Text)-asLibrary parts =- ( "#pragma once\n\n" <> cHeader parts,- gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cLib parts,- cJsonManifest parts- )---- | As executable with command-line interface.-asExecutable :: CParts -> T.Text-asExecutable parts =- gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cCLI parts <> cLib parts---- | As server executable.-asServer :: CParts -> T.Text-asServer parts =- gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cServer parts <> cLib parts--compileProg' ::- MonadFreshNames m =>- T.Text ->- T.Text ->- Operations op s ->- s ->- CompilerM op s () ->- T.Text ->- (Space, [Space]) ->- [Option] ->- Definitions op ->- m (CParts, CompilerState s)-compileProg' backend version ops def extra header_extra (arr_space, spaces) options prog = do- src <- getNameSource- let ((prototypes, definitions, entry_point_decls, manifest), endstate) =- runCompilerM ops src def compileProgAction- initdecls = initDecls endstate- entrydecls = entryDecls endstate- arraydecls = arrayDecls endstate- opaquetypedecls = opaqueTypeDecls endstate- opaquedecls = opaqueDecls endstate- miscdecls = miscDecls endstate-- let headerdefs =- [untrimming|-// Headers-#include <stdint.h>-#include <stddef.h>-#include <stdbool.h>-#include <stdio.h>-#include <float.h>-$header_extra-#ifdef __cplusplus-extern "C" {-#endif--// Initialisation-$initdecls--// Arrays-$arraydecls--// Opaque values-$opaquetypedecls-$opaquedecls--// Entry points-$entrydecls--// Miscellaneous-$miscdecls-#define FUTHARK_BACKEND_$backend-$errorsH--#ifdef __cplusplus-}-#endif-|]-- let utildefs =- [untrimming|-#include <stdio.h>-#include <stdlib.h>-#include <stdbool.h>-#include <math.h>-#include <stdint.h>-// If NDEBUG is set, the assert() macro will do nothing. Since Futhark-// (unfortunately) makes use of assert() for error detection (and even some-// side effects), we want to avoid that.-#undef NDEBUG-#include <assert.h>-#include <stdarg.h>-$utilH-$cacheH-$halfH-$timingH-|]-- let early_decls = T.unlines $ map prettyText $ DL.toList $ compEarlyDecls endstate- lib_decls = T.unlines $ map prettyText $ DL.toList $ compLibDecls endstate- clidefs = cliDefs options manifest- serverdefs = serverDefs options manifest- libdefs =- [untrimming|-#ifdef _MSC_VER-#define inline __inline-#endif-#include <string.h>-#include <string.h>-#include <errno.h>-#include <assert.h>-#include <ctype.h>--$header_extra--$lockH--#define FUTHARK_F64_ENABLED--$cScalarDefs--$early_decls--$prototypes--$lib_decls--$definitions--$entry_point_decls- |]-- pure- ( CParts- { cHeader = headerdefs,- cUtils = utildefs,- cCLI = clidefs,- cServer = serverdefs,- cLib = libdefs,- cJsonManifest = Manifest.manifestToJSON manifest- },- endstate- )- where- Definitions types consts (Functions funs) = prog-- compileProgAction = do- (memstructs, memfuns, memreport) <- unzip3 <$> mapM defineMemorySpace spaces-- get_consts <- compileConstants consts-- ctx_ty <- contextType-- (prototypes, functions) <-- unzip <$> mapM (compileFun get_consts [[C.cparam|$ty:ctx_ty *ctx|]]) funs-- mapM_ earlyDecl memstructs- (entry_points, entry_points_manifest) <-- unzip . catMaybes <$> mapM (uncurry (onEntryPoint get_consts)) funs-- extra-- mapM_ earlyDecl $ concat memfuns-- type_funs <- generateAPITypes arr_space types- generateCommonLibFuns memreport-- pure- ( T.unlines $ map prettyText prototypes,- T.unlines $ map (prettyText . funcToDef) functions,- T.unlines $ map prettyText entry_points,- Manifest.Manifest (M.fromList entry_points_manifest) type_funs backend version- )-- funcToDef func = C.FuncDef func loc- where- loc = case func of- C.OldFunc _ _ _ _ _ _ l -> l- C.Func _ _ _ _ _ l -> l---- | Compile imperative program to a C program. Always uses the--- function named "main" as entry point, so make sure it is defined.-compileProg ::- MonadFreshNames m =>- T.Text ->- T.Text ->- Operations op () ->- CompilerM op () () ->- T.Text ->- (Space, [Space]) ->- [Option] ->- Definitions op ->- m CParts-compileProg backend version ops extra header_extra (arr_space, spaces) options prog =- fst <$> compileProg' backend version ops () extra header_extra (arr_space, spaces) options prog--generateCommonLibFuns :: [C.BlockItem] -> CompilerM op s ()-generateCommonLibFuns memreport = do- ctx <- contextType- cfg <- configType- ops <- asks envOperations- profilereport <- gets $ DL.toList . compProfileItems-- publicDef_ "context_config_set_cache_file" MiscDecl $ \s ->- ( [C.cedecl|void $id:s($ty:cfg* cfg, const char *f);|],- [C.cedecl|void $id:s($ty:cfg* cfg, const char *f) {- cfg->cache_fname = f;- }|]- )-- publicDef_ "get_tuning_param_count" InitDecl $ \s ->- ( [C.cedecl|int $id:s(void);|],- [C.cedecl|int $id:s(void) {- return sizeof(tuning_param_names)/sizeof(tuning_param_names[0]);- }|]- )-- publicDef_ "get_tuning_param_name" InitDecl $ \s ->- ( [C.cedecl|const char* $id:s(int);|],- [C.cedecl|const char* $id:s(int i) {- return tuning_param_names[i];- }|]- )-- publicDef_ "get_tuning_param_class" InitDecl $ \s ->- ( [C.cedecl|const char* $id:s(int);|],- [C.cedecl|const char* $id:s(int i) {- return tuning_param_classes[i];- }|]- )-- sync <- publicName "context_sync"- publicDef_ "context_report" MiscDecl $ \s ->- ( [C.cedecl|char* $id:s($ty:ctx *ctx);|],- [C.cedecl|char* $id:s($ty:ctx *ctx) {- if ($id:sync(ctx) != 0) {- return NULL;- }-- struct str_builder builder;- str_builder_init(&builder);- $items:memreport- if (ctx->profiling) {- $items:profilereport- }- return builder.str;- }|]- )-- publicDef_ "context_get_error" MiscDecl $ \s ->- ( [C.cedecl|char* $id:s($ty:ctx* ctx);|],- [C.cedecl|char* $id:s($ty:ctx* ctx) {- char* error = ctx->error;- ctx->error = NULL;- return error;- }|]- )-- publicDef_ "context_set_logging_file" MiscDecl $ \s ->- ( [C.cedecl|void $id:s($ty:ctx* ctx, typename FILE* f);|],- [C.cedecl|void $id:s($ty:ctx* ctx, typename FILE* f) {- ctx->log = f;- }|]- )-- publicDef_ "context_pause_profiling" MiscDecl $ \s ->- ( [C.cedecl|void $id:s($ty:ctx* ctx);|],- [C.cedecl|void $id:s($ty:ctx* ctx) {- ctx->profiling_paused = 1;- }|]- )-- publicDef_ "context_unpause_profiling" MiscDecl $ \s ->- ( [C.cedecl|void $id:s($ty:ctx* ctx);|],- [C.cedecl|void $id:s($ty:ctx* ctx) {- ctx->profiling_paused = 0;- }|]- )-- clears <- gets $ DL.toList . compClearItems- publicDef_ "context_clear_caches" MiscDecl $ \s ->- ( [C.cedecl|int $id:s($ty:ctx* ctx);|],- [C.cedecl|int $id:s($ty:ctx* ctx) {- $items:(criticalSection ops clears)- return ctx->error != NULL;- }|]- )--compileConstants :: Constants op -> CompilerM op s [C.BlockItem]-compileConstants (Constants ps init_consts) = do- ctx_ty <- contextType- const_fields <- mapM constParamField ps- -- Avoid an empty struct, as that is apparently undefined behaviour.- let const_fields'- | null const_fields = [[C.csdecl|int dummy;|]]- | otherwise = const_fields- contextField "constants" [C.cty|struct { $sdecls:const_fields' }|] Nothing- earlyDecl [C.cedecl|static int init_constants($ty:ctx_ty*);|]- earlyDecl [C.cedecl|static int free_constants($ty:ctx_ty*);|]-- inNewFunction $ do- -- We locally define macros for the constants, so that when we- -- generate assignments to local variables, we actually assign into- -- the constants struct. This is not needed for functions, because- -- they can only read constants, not write them.- let (defs, undefs) = unzip $ map constMacro ps- init_consts' <- collect $ do- mapM_ resetMemConst ps- compileCode init_consts- decl_mem <- declAllocatedMem- free_mem <- freeAllocatedMem- libDecl- [C.cedecl|static int init_constants($ty:ctx_ty *ctx) {- (void)ctx;- int err = 0;- $items:defs- $items:decl_mem- $items:init_consts'- $items:free_mem- $items:undefs- cleanup:- return err;- }|]-- inNewFunction $ do- free_consts <- collect $ mapM_ freeConst ps- libDecl- [C.cedecl|static int free_constants($ty:ctx_ty *ctx) {- (void)ctx;- $items:free_consts- return 0;- }|]-- mapM getConst ps- where- constParamField (ScalarParam name bt) = do- let ctp = primTypeToCType bt- pure [C.csdecl|$ty:ctp $id:name;|]- constParamField (MemParam name space) = do- ty <- memToCType name space- pure [C.csdecl|$ty:ty $id:name;|]-- constMacro p = ([C.citem|$escstm:def|], [C.citem|$escstm:undef|])- where- p' = pretty (C.toIdent (paramName p) mempty)- def = "#define " ++ p' ++ " (" ++ "ctx->constants." ++ p' ++ ")"- undef = "#undef " ++ p'-- resetMemConst ScalarParam {} = pure ()- resetMemConst (MemParam name space) = resetMem name space-- freeConst ScalarParam {} = pure ()- freeConst (MemParam name space) = unRefMem [C.cexp|ctx->constants.$id:name|] space-- getConst (ScalarParam name bt) = do- let ctp = primTypeToCType bt- pure [C.citem|$ty:ctp $id:name = ctx->constants.$id:name;|]- getConst (MemParam name space) = do- ty <- memToCType name space- pure [C.citem|$ty:ty $id:name = ctx->constants.$id:name;|]--cachingMemory ::- M.Map VName Space ->- ([C.BlockItem] -> [C.Stm] -> CompilerM op s a) ->- CompilerM op s a-cachingMemory lexical f = do- -- We only consider lexical 'DefaultSpace' memory blocks to be- -- cached. This is not a deep technical restriction, but merely a- -- heuristic based on GPU memory usually involving larger- -- allocations, that do not suffer from the overhead of reference- -- counting.- let cached = M.keys $ M.filter (== DefaultSpace) lexical-- cached' <- forM cached $ \mem -> do- size <- newVName $ pretty mem <> "_cached_size"- pure (mem, size)-- let lexMem env =- env- { envCachedMem =- M.fromList (map (first (`C.toExp` noLoc)) cached')- <> envCachedMem env- }-- declCached (mem, size) =- [ [C.citem|typename int64_t $id:size = 0;|],- [C.citem|$ty:defaultMemBlockType $id:mem = NULL;|]- ]-- freeCached (mem, _) =- [C.cstm|free($id:mem);|]-- local lexMem $ f (concatMap declCached cached') (map freeCached cached')--compileFun :: [C.BlockItem] -> [C.Param] -> (Name, Function op) -> CompilerM op s (C.Definition, C.Func)-compileFun get_constants extra (fname, func@(Function _ outputs inputs body)) = inNewFunction $ do- (outparams, out_ptrs) <- unzip <$> mapM compileOutput outputs- inparams <- mapM compileInput inputs-- cachingMemory (lexicalMemoryUsage func) $ \decl_cached free_cached -> do- body' <- collect $ compileFunBody out_ptrs outputs body- decl_mem <- declAllocatedMem- free_mem <- freeAllocatedMem-- pure- ( [C.cedecl|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams);|],- [C.cfun|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams) {- $stms:ignores- int err = 0;- $items:decl_cached- $items:decl_mem- $items:get_constants- $items:body'- cleanup:- {- $stms:free_cached- $items:free_mem- }- return err;- }|]- )- where- -- Ignore all the boilerplate parameters, just in case we don't- -- actually need to use them.- ignores = [[C.cstm|(void)$id:p;|] | C.Param (Just p) _ _ _ <- extra]-- compileInput (ScalarParam name bt) = do- let ctp = primTypeToCType bt- pure [C.cparam|$ty:ctp $id:name|]- compileInput (MemParam name space) = do- ty <- memToCType name space- pure [C.cparam|$ty:ty $id:name|]-- compileOutput (ScalarParam name bt) = do- let ctp = primTypeToCType bt- p_name <- newVName $ "out_" ++ baseString name- pure ([C.cparam|$ty:ctp *$id:p_name|], [C.cexp|$id:p_name|])- compileOutput (MemParam name space) = do- ty <- memToCType name space- p_name <- newVName $ baseString name ++ "_p"- pure ([C.cparam|$ty:ty *$id:p_name|], [C.cexp|$id:p_name|])--derefPointer :: C.Exp -> C.Exp -> C.Type -> C.Exp-derefPointer ptr i res_t =- [C.cexp|(($ty:res_t)$exp:ptr)[$exp:i]|]--volQuals :: Volatility -> [C.TypeQual]-volQuals Volatile = [C.ctyquals|volatile|]-volQuals Nonvolatile = []--writeScalarPointerWithQuals :: PointerQuals op s -> WriteScalar op s-writeScalarPointerWithQuals quals_f dest i elemtype space vol v = do- quals <- quals_f space- let quals' = volQuals vol ++ quals- deref =- derefPointer- dest- i- [C.cty|$tyquals:quals' $ty:elemtype*|]- stm [C.cstm|$exp:deref = $exp:v;|]--readScalarPointerWithQuals :: PointerQuals op s -> ReadScalar op s-readScalarPointerWithQuals quals_f dest i elemtype space vol = do- quals <- quals_f space- let quals' = volQuals vol ++ quals- pure $ derefPointer dest i [C.cty|$tyquals:quals' $ty:elemtype*|]--compileExpToName :: String -> PrimType -> Exp -> CompilerM op s VName-compileExpToName _ _ (LeafExp v _) =- pure v-compileExpToName desc t e = do- desc' <- newVName desc- e' <- compileExp e- decl [C.cdecl|$ty:(primTypeToCType t) $id:desc' = $e';|]- pure desc'--compileExp :: Exp -> CompilerM op s C.Exp-compileExp = compilePrimExp $ \v -> pure [C.cexp|$id:v|]---- | Tell me how to compile a @v@, and I'll Compile any @PrimExp v@ for you.-compilePrimExp :: Monad m => (v -> m C.Exp) -> PrimExp v -> m C.Exp-compilePrimExp _ (ValueExp val) =- pure $ C.toExp val mempty-compilePrimExp f (LeafExp v _) =- f v-compilePrimExp f (UnOpExp Complement {} x) = do- x' <- compilePrimExp f x- pure [C.cexp|~$exp:x'|]-compilePrimExp f (UnOpExp Not {} x) = do- x' <- compilePrimExp f x- pure [C.cexp|!$exp:x'|]-compilePrimExp f (UnOpExp (FAbs Float32) x) = do- x' <- compilePrimExp f x- pure [C.cexp|(float)fabs($exp:x')|]-compilePrimExp f (UnOpExp (FAbs Float64) x) = do- x' <- compilePrimExp f x- pure [C.cexp|fabs($exp:x')|]-compilePrimExp f (UnOpExp SSignum {} x) = do- x' <- compilePrimExp f x- pure [C.cexp|($exp:x' > 0 ? 1 : 0) - ($exp:x' < 0 ? 1 : 0)|]-compilePrimExp f (UnOpExp USignum {} x) = do- x' <- compilePrimExp f x- pure [C.cexp|($exp:x' > 0 ? 1 : 0) - ($exp:x' < 0 ? 1 : 0) != 0|]-compilePrimExp f (UnOpExp op x) = do- x' <- compilePrimExp f x- pure [C.cexp|$id:(pretty op)($exp:x')|]-compilePrimExp f (CmpOpExp cmp x y) = do- x' <- compilePrimExp f x- y' <- compilePrimExp f y- pure $ case cmp of- CmpEq {} -> [C.cexp|$exp:x' == $exp:y'|]- FCmpLt {} -> [C.cexp|$exp:x' < $exp:y'|]- FCmpLe {} -> [C.cexp|$exp:x' <= $exp:y'|]- CmpLlt {} -> [C.cexp|$exp:x' < $exp:y'|]- CmpLle {} -> [C.cexp|$exp:x' <= $exp:y'|]- _ -> [C.cexp|$id:(pretty cmp)($exp:x', $exp:y')|]-compilePrimExp f (ConvOpExp conv x) = do- x' <- compilePrimExp f x- pure [C.cexp|$id:(pretty conv)($exp:x')|]-compilePrimExp f (BinOpExp bop x y) = do- x' <- compilePrimExp f x- y' <- compilePrimExp f y- -- Note that integer addition, subtraction, and multiplication with- -- OverflowWrap are not handled by explicit operators, but rather by- -- functions. This is because we want to implicitly convert them to- -- unsigned numbers, so we can do overflow without invoking- -- undefined behaviour.- pure $ case bop of- Add _ OverflowUndef -> [C.cexp|$exp:x' + $exp:y'|]- Sub _ OverflowUndef -> [C.cexp|$exp:x' - $exp:y'|]- Mul _ OverflowUndef -> [C.cexp|$exp:x' * $exp:y'|]- FAdd {} -> [C.cexp|$exp:x' + $exp:y'|]- FSub {} -> [C.cexp|$exp:x' - $exp:y'|]- FMul {} -> [C.cexp|$exp:x' * $exp:y'|]- FDiv {} -> [C.cexp|$exp:x' / $exp:y'|]- Xor {} -> [C.cexp|$exp:x' ^ $exp:y'|]- And {} -> [C.cexp|$exp:x' & $exp:y'|]- Or {} -> [C.cexp|$exp:x' | $exp:y'|]- LogAnd {} -> [C.cexp|$exp:x' && $exp:y'|]- LogOr {} -> [C.cexp|$exp:x' || $exp:y'|]- _ -> [C.cexp|$id:(pretty bop)($exp:x', $exp:y')|]-compilePrimExp f (FunExp h args _) = do- args' <- mapM (compilePrimExp f) args- pure [C.cexp|$id:(funName (nameFromString h))($args:args')|]--linearCode :: Code op -> [Code op]-linearCode = reverse . go []- where- go acc (x :>>: y) =- go (go acc x) y- go acc x = x : acc--compileCode :: Code op -> CompilerM op s ()-compileCode (Op op) =- join $ asks envOpCompiler <*> pure op-compileCode Skip = pure ()-compileCode (Comment s code) = do- xs <- collect $ compileCode code- let comment = "// " ++ s- stm- [C.cstm|$comment:comment- { $items:xs }- |]-compileCode (TracePrint msg) = do- (formatstr, formatargs) <- errorMsgString msg- stm [C.cstm|fprintf(ctx->log, $string:formatstr, $args:formatargs);|]-compileCode (DebugPrint s (Just e)) = do- e' <- compileExp e- stm- [C.cstm|if (ctx->debugging) {- fprintf(ctx->log, $string:fmtstr, $exp:s, ($ty:ety)$exp:e', '\n');- }|]- where- (fmt, ety) = case primExpType e of- IntType _ -> ("llu", [C.cty|long long int|])- FloatType _ -> ("f", [C.cty|double|])- _ -> ("d", [C.cty|int|])- fmtstr = "%s: %" ++ fmt ++ "%c"-compileCode (DebugPrint s Nothing) =- stm- [C.cstm|if (ctx->debugging) {- fprintf(ctx->log, "%s\n", $exp:s);- }|]--- :>>: is treated in a special way to detect declare-set pairs in--- order to generate prettier code.-compileCode (c1 :>>: c2) = go (linearCode (c1 :>>: c2))- where- go (DeclareScalar name vol t : SetScalar dest e : code)- | name == dest = do- let ct = primTypeToCType t- e' <- compileExp e- item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $exp:e';|]- go code- go (x : xs) = compileCode x >> go xs- go [] = pure ()-compileCode (Assert e msg (loc, locs)) = do- e' <- compileExp e- err <-- collect . join $- asks (opsError . envOperations) <*> pure msg <*> pure stacktrace- stm [C.cstm|if (!$exp:e') { $items:err }|]- where- stacktrace = prettyStacktrace 0 $ map locStr $ loc : locs-compileCode (Allocate _ _ ScalarSpace {}) =- -- Handled by the declaration of the memory block, which is- -- translated to an actual array.- pure ()-compileCode (Allocate name (Count (TPrimExp e)) space) = do- size <- compileExp e- cached <- cacheMem name- case cached of- Just cur_size ->- stm- [C.cstm|if ($exp:cur_size < $exp:size) {- err = lexical_realloc(&ctx->error, &$exp:name, &$exp:cur_size, $exp:size);- if (err != FUTHARK_SUCCESS) {- goto cleanup;- }- }|]- _ ->- allocMem name size space [C.cstm|{err = 1; goto cleanup;}|]-compileCode (Free name space) = do- cached <- isJust <$> cacheMem name- unless cached $ unRefMem name space-compileCode (For i bound body) = do- let i' = C.toIdent i- t = primTypeToCType $ primExpType bound- bound' <- compileExp bound- body' <- collect $ compileCode body- stm- [C.cstm|for ($ty:t $id:i' = 0; $id:i' < $exp:bound'; $id:i'++) {- $items:body'- }|]-compileCode (While cond body) = do- cond' <- compileExp $ untyped cond- body' <- collect $ compileCode body- stm- [C.cstm|while ($exp:cond') {- $items:body'- }|]-compileCode (If cond tbranch fbranch) = do- cond' <- compileExp $ untyped cond- tbranch' <- collect $ compileCode tbranch- fbranch' <- collect $ compileCode fbranch- stm $ case (tbranch', fbranch') of- (_, []) ->- [C.cstm|if ($exp:cond') { $items:tbranch' }|]- ([], _) ->- [C.cstm|if (!($exp:cond')) { $items:fbranch' }|]- _ ->- [C.cstm|if ($exp:cond') { $items:tbranch' } else { $items:fbranch' }|]-compileCode (Copy _ dest (Count destoffset) DefaultSpace src (Count srcoffset) DefaultSpace (Count size)) =- join $- copyMemoryDefaultSpace- <$> rawMem dest- <*> compileExp (untyped destoffset)- <*> rawMem src- <*> compileExp (untyped srcoffset)- <*> compileExp (untyped size)-compileCode (Copy _ dest (Count destoffset) destspace src (Count srcoffset) srcspace (Count size)) = do- copy <- asks envCopy- join $- copy CopyBarrier- <$> rawMem dest- <*> compileExp (untyped destoffset)- <*> pure destspace- <*> rawMem src- <*> compileExp (untyped srcoffset)- <*> pure srcspace- <*> compileExp (untyped size)-compileCode (Write _ _ Unit _ _ _) = pure ()-compileCode (Write dest (Count idx) elemtype DefaultSpace vol elemexp) = do- dest' <- rawMem dest- deref <-- derefPointer dest'- <$> compileExp (untyped idx)- <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primStorageType elemtype)*|]- elemexp' <- toStorage elemtype <$> compileExp elemexp- stm [C.cstm|$exp:deref = $exp:elemexp';|]-compileCode (Write dest (Count idx) _ ScalarSpace {} _ elemexp) = do- idx' <- compileExp (untyped idx)- elemexp' <- compileExp elemexp- stm [C.cstm|$id:dest[$exp:idx'] = $exp:elemexp';|]-compileCode (Write dest (Count idx) elemtype (Space space) vol elemexp) =- join $- asks envWriteScalar- <*> rawMem dest- <*> compileExp (untyped idx)- <*> pure (primStorageType elemtype)- <*> 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 envReadScalar- <*> rawMem src- <*> compileExp (untyped iexp)- <*> pure (primStorageType restype)- <*> pure space- <*> pure 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- let ct = primTypeToCType t- decl [C.cdecl|$tyquals:(volQuals vol) $ty:ct $id:name;|]-compileCode (DeclareArray name ScalarSpace {} _ _) =- error $ "Cannot declare array " ++ pretty name ++ " in scalar space."-compileCode (DeclareArray name DefaultSpace t vs) = do- name_realtype <- newVName $ baseString name ++ "_realtype"- let ct = primTypeToCType t- case vs of- ArrayValues vs' -> do- let vs'' = [[C.cinit|$exp:v|] | v <- vs']- earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs')] = {$inits:vs''};|]- ArrayZeros n ->- earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|]- -- Fake a memory block.- contextField- (C.toIdent name noLoc)- [C.cty|struct memblock|]- $ Just- [C.cexp|(struct memblock){NULL,- (unsigned char*)$id:name_realtype,- 0,- $string:(pretty name)}|]- item [C.citem|struct memblock $id:name = ctx->$id:name;|]-compileCode (DeclareArray name (Space space) t vs) =- join $- asks envStaticArray- <*> pure name- <*> pure space- <*> pure t- <*> pure vs--- For assignments of the form 'x = x OP e', we generate C assignment--- operators to make the resulting code slightly nicer. This has no--- effect on performance.-compileCode (SetScalar dest (BinOpExp op (LeafExp x _) y))- | dest == x,- Just f <- assignmentOperator op = do- y' <- compileExp y- stm [C.cstm|$exp:(f dest y');|]-compileCode (SetScalar dest src) = do- src' <- compileExp src- stm [C.cstm|$id:dest = $exp:src';|]-compileCode (SetMem dest src space) =- setMem dest src space-compileCode (Call dests fname args) =- join $- asks (opsCall . envOperations)- <*> pure dests- <*> pure fname- <*> mapM compileArg args- where- compileArg (MemArg m) = pure [C.cexp|$exp:m|]- compileArg (ExpArg e) = compileExp e--compileFunBody :: [C.Exp] -> [Param] -> Code op -> CompilerM op s ()-compileFunBody output_ptrs outputs code = do- mapM_ declareOutput outputs- compileCode code- zipWithM_ setRetVal' output_ptrs outputs- where- declareOutput (MemParam name space) =- declMem name space- declareOutput (ScalarParam name pt) = do- let ctp = primTypeToCType pt- decl [C.cdecl|$ty:ctp $id:name;|]-- setRetVal' p (MemParam name space) = do- resetMem [C.cexp|*$exp:p|] space- setMem [C.cexp|*$exp:p|] name space- setRetVal' p (ScalarParam name _) =- stm [C.cstm|*$exp:p = $id:name;|]--assignmentOperator :: BinOp -> Maybe (VName -> C.Exp -> C.Exp)-assignmentOperator Add {} = Just $ \d e -> [C.cexp|$id:d += $exp:e|]-assignmentOperator Sub {} = Just $ \d e -> [C.cexp|$id:d -= $exp:e|]-assignmentOperator Mul {} = Just $ \d e -> [C.cexp|$id:d *= $exp:e|]-assignmentOperator _ = Nothing+-- | C code generation for whole programs, built on+-- "Futhark.CodeGen.Backends.GenericC.Monad". Most of this module is+-- concerned with constructing the C API.+module Futhark.CodeGen.Backends.GenericC+ ( compileProg,+ compileProg',+ compileFun,+ defaultOperations,+ CParts (..),+ asLibrary,+ asExecutable,+ asServer,+ module Futhark.CodeGen.Backends.GenericC.Monad,+ module Futhark.CodeGen.Backends.GenericC.Code,+ )+where++import Control.Monad.Reader+import Control.Monad.State+import qualified Data.DList as DL+import Data.Loc+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Text as T+import Futhark.CodeGen.Backends.GenericC.CLI (cliDefs)+import Futhark.CodeGen.Backends.GenericC.Code+import Futhark.CodeGen.Backends.GenericC.EntryPoints+import Futhark.CodeGen.Backends.GenericC.Monad+import Futhark.CodeGen.Backends.GenericC.Options+import Futhark.CodeGen.Backends.GenericC.Server (serverDefs)+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)+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C+import NeatInterpolation (untrimming)++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; }|]++defError :: ErrorCompiler op s+defError msg stacktrace = do+ (formatstr, formatargs) <- errorMsgString msg+ let formatstr' = "Error: " <> formatstr <> "\n\nBacktrace:\n%s"+ items+ [C.citems|set_error(ctx, msgprintf($string:formatstr', $args:formatargs, $string:stacktrace));+ err = FUTHARK_PROGRAM_ERROR;+ goto cleanup;|]++-- | A set of operations that fail for every operation involving+-- non-default memory spaces. Uses plain pointers and @malloc@ for+-- memory management.+defaultOperations :: Operations op s+defaultOperations =+ Operations+ { opsWriteScalar = defWriteScalar,+ opsReadScalar = defReadScalar,+ opsAllocate = defAllocate,+ opsDeallocate = defDeallocate,+ opsCopy = defCopy,+ opsStaticArray = defStaticArray,+ opsMemoryType = defMemoryType,+ opsCompiler = defCompiler,+ opsFatMemory = True,+ opsError = defError,+ opsCall = defCall,+ opsCritical = mempty+ }+ where+ defWriteScalar _ _ _ _ _ =+ error "Cannot write to non-default memory space because I am dumb"+ defReadScalar _ _ _ _ =+ error "Cannot read from non-default memory space"+ defAllocate _ _ _ =+ error "Cannot allocate in non-default memory space"+ defDeallocate _ _ =+ error "Cannot deallocate in non-default memory space"+ defCopy _ destmem destoffset DefaultSpace srcmem srcoffset DefaultSpace size =+ copyMemoryDefaultSpace destmem destoffset srcmem srcoffset size+ defCopy _ _ _ _ _ _ _ _ =+ error "Cannot copy to or from non-default memory space"+ defStaticArray _ _ _ _ =+ error "Cannot create static array in non-default memory space"+ defMemoryType _ =+ error "Has no type for non-default memory space"+ defCompiler _ =+ error "The default compiler cannot compile extended operations"++compileFunBody :: [C.Exp] -> [Param] -> Code op -> CompilerM op s ()+compileFunBody output_ptrs outputs code = do+ mapM_ declareOutput outputs+ compileCode code+ zipWithM_ setRetVal' output_ptrs outputs+ where+ declareOutput (MemParam name space) =+ declMem name space+ declareOutput (ScalarParam name pt) = do+ let ctp = primTypeToCType pt+ decl [C.cdecl|$ty:ctp $id:name;|]++ setRetVal' p (MemParam name space) = do+ resetMem [C.cexp|*$exp:p|] space+ setMem [C.cexp|*$exp:p|] name space+ setRetVal' p (ScalarParam name _) =+ stm [C.cstm|*$exp:p = $id:name;|]++compileFun :: [C.BlockItem] -> [C.Param] -> (Name, Function op) -> CompilerM op s (C.Definition, C.Func)+compileFun get_constants extra (fname, func@(Function _ outputs inputs body)) = inNewFunction $ do+ (outparams, out_ptrs) <- unzip <$> mapM compileOutput outputs+ inparams <- mapM compileInput inputs++ cachingMemory (lexicalMemoryUsage func) $ \decl_cached free_cached -> do+ body' <- collect $ compileFunBody out_ptrs outputs body+ decl_mem <- declAllocatedMem+ free_mem <- freeAllocatedMem++ pure+ ( [C.cedecl|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams);|],+ [C.cfun|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams) {+ $stms:ignores+ int err = 0;+ $items:decl_cached+ $items:decl_mem+ $items:get_constants+ $items:body'+ cleanup:+ {+ $stms:free_cached+ $items:free_mem+ }+ return err;+ }|]+ )+ where+ -- Ignore all the boilerplate parameters, just in case we don't+ -- actually need to use them.+ ignores = [[C.cstm|(void)$id:p;|] | C.Param (Just p) _ _ _ <- extra]++ compileInput (ScalarParam name bt) = do+ let ctp = primTypeToCType bt+ pure [C.cparam|$ty:ctp $id:name|]+ compileInput (MemParam name space) = do+ ty <- memToCType name space+ pure [C.cparam|$ty:ty $id:name|]++ compileOutput (ScalarParam name bt) = do+ let ctp = primTypeToCType bt+ p_name <- newVName $ "out_" ++ baseString name+ pure ([C.cparam|$ty:ctp *$id:p_name|], [C.cexp|$id:p_name|])+ compileOutput (MemParam name space) = do+ ty <- memToCType name space+ p_name <- newVName $ baseString name ++ "_p"+ pure ([C.cparam|$ty:ty *$id:p_name|], [C.cexp|$id:p_name|])++declsCode :: (HeaderSection -> Bool) -> CompilerState s -> T.Text+declsCode p =+ T.unlines+ . map prettyText+ . concatMap (DL.toList . snd)+ . filter (p . fst)+ . M.toList+ . compHeaderDecls++initDecls, arrayDecls, opaqueDecls, opaqueTypeDecls, entryDecls, miscDecls :: CompilerState s -> T.Text+initDecls = declsCode (== InitDecl)+arrayDecls = declsCode isArrayDecl+ where+ isArrayDecl ArrayDecl {} = True+ isArrayDecl _ = False+opaqueTypeDecls = declsCode isOpaqueTypeDecl+ where+ isOpaqueTypeDecl OpaqueTypeDecl {} = True+ isOpaqueTypeDecl _ = False+opaqueDecls = declsCode isOpaqueDecl+ where+ isOpaqueDecl OpaqueDecl {} = True+ isOpaqueDecl _ = False+entryDecls = declsCode (== EntryDecl)+miscDecls = declsCode (== MiscDecl)++defineMemorySpace :: Space -> CompilerM op s (C.Definition, [C.Definition], C.BlockItem)+defineMemorySpace space = do+ rm <- rawMemCType space+ let structdef =+ [C.cedecl|struct $id:sname { int *references;+ $ty:rm mem;+ typename int64_t size;+ const char *desc; };|]++ contextField peakname [C.cty|typename int64_t|] $ Just [C.cexp|0|]+ contextField usagename [C.cty|typename int64_t|] $ Just [C.cexp|0|]++ -- Unreferencing a memory block consists of decreasing its reference+ -- count and freeing the corresponding memory if the count reaches+ -- zero.+ free <- collect $ freeRawMem [C.cexp|block->mem|] space [C.cexp|desc|]+ ctx_ty <- contextType+ let unrefdef =+ [C.cedecl|int $id:(fatMemUnRef space) ($ty:ctx_ty *ctx, $ty:mty *block, const char *desc) {+ if (block->references != NULL) {+ *(block->references) -= 1;+ if (ctx->detail_memory) {+ fprintf(ctx->log, "Unreferencing block %s (allocated as %s) in %s: %d references remaining.\n",+ desc, block->desc, $string:spacedesc, *(block->references));+ }+ if (*(block->references) == 0) {+ ctx->$id:usagename -= block->size;+ $items:free+ free(block->references);+ if (ctx->detail_memory) {+ fprintf(ctx->log, "%lld bytes freed (now allocated: %lld bytes)\n",+ (long long) block->size, (long long) ctx->$id:usagename);+ }+ }+ block->references = NULL;+ }+ return 0;+}|]++ -- When allocating a memory block we initialise the reference count to 1.+ alloc <-+ collect $+ allocRawMem [C.cexp|block->mem|] [C.cexp|size|] space [C.cexp|desc|]+ let allocdef =+ [C.cedecl|int $id:(fatMemAlloc space) ($ty:ctx_ty *ctx, $ty:mty *block, typename int64_t size, const char *desc) {+ if (size < 0) {+ futhark_panic(1, "Negative allocation of %lld bytes attempted for %s in %s.\n",+ (long long)size, desc, $string:spacedesc, ctx->$id:usagename);+ }+ int ret = $id:(fatMemUnRef space)(ctx, block, desc);++ if (ret != FUTHARK_SUCCESS) {+ return ret;+ }++ if (ctx->detail_memory) {+ fprintf(ctx->log, "Allocating %lld bytes for %s in %s (then allocated: %lld bytes)",+ (long long) size,+ desc, $string:spacedesc,+ (long long) ctx->$id:usagename + size);+ }+ if (ctx->$id:usagename > ctx->$id:peakname) {+ ctx->$id:peakname = ctx->$id:usagename;+ if (ctx->detail_memory) {+ fprintf(ctx->log, " (new peak).\n");+ }+ } else if (ctx->detail_memory) {+ fprintf(ctx->log, ".\n");+ }++ $items:alloc++ if (ctx->error == NULL) {+ block->references = (int*) malloc(sizeof(int));+ *(block->references) = 1;+ block->size = size;+ block->desc = desc;+ ctx->$id:usagename += size;+ return FUTHARK_SUCCESS;+ } else {+ // We are naively assuming that any memory allocation error is due to OOM.+ // We preserve the original error so that a savvy user can perhaps find+ // glory despite our naiveté.++ char *old_error = ctx->error;+ set_error(ctx, msgprintf("Failed to allocate memory in %s.\nAttempted allocation: %12lld bytes\nCurrently allocated: %12lld bytes\n%s",+ $string:spacedesc,+ (long long) size,+ (long long) ctx->$id:usagename,+ old_error));+ free(old_error);+ return FUTHARK_OUT_OF_MEMORY;+ }+ }|]++ -- Memory setting - unreference the destination and increase the+ -- count of the source by one.+ let setdef =+ [C.cedecl|int $id:(fatMemSet space) ($ty:ctx_ty *ctx, $ty:mty *lhs, $ty:mty *rhs, const char *lhs_desc) {+ int ret = $id:(fatMemUnRef space)(ctx, lhs, lhs_desc);+ if (rhs->references != NULL) {+ (*(rhs->references))++;+ }+ *lhs = *rhs;+ return ret;+}+|]++ onClear [C.citem|ctx->$id:peakname = 0;|]++ let peakmsg = "Peak memory usage for " ++ spacedesc ++ ": %lld bytes.\n"+ pure+ ( structdef,+ [unrefdef, allocdef, setdef],+ -- Do not report memory usage for DefaultSpace (CPU memory),+ -- because it would not be accurate anyway. This whole+ -- tracking probably needs to be rethought.+ if space == DefaultSpace+ then [C.citem|{}|]+ else [C.citem|str_builder(&builder, $string:peakmsg, (long long) ctx->$id:peakname);|]+ )+ where+ mty = fatMemType space+ (peakname, usagename, sname, spacedesc) = case space of+ Space sid ->+ ( C.toIdent ("peak_mem_usage_" ++ sid) noLoc,+ C.toIdent ("cur_mem_usage_" ++ sid) noLoc,+ C.toIdent ("memblock_" ++ sid) noLoc,+ "space '" ++ sid ++ "'"+ )+ _ ->+ ( "peak_mem_usage_default",+ "cur_mem_usage_default",+ "memblock",+ "default space"+ )++-- | The result of compilation to C is multiple parts, which can be+-- put together in various ways. The obvious way is to concatenate+-- all of them, which yields a CLI program. Another is to compile the+-- library part by itself, and use the header file to call into it.+data CParts = CParts+ { cHeader :: T.Text,+ -- | Utility definitions that must be visible+ -- to both CLI and library parts.+ cUtils :: T.Text,+ cCLI :: T.Text,+ cServer :: T.Text,+ cLib :: T.Text,+ -- | The manifest, in JSON format.+ cJsonManifest :: T.Text+ }++gnuSource :: T.Text+gnuSource =+ [untrimming|+// We need to define _GNU_SOURCE before+// _any_ headers files are imported to get+// the usage statistics of a thread (i.e. have RUSAGE_THREAD) on GNU/Linux+// https://manpages.courier-mta.org/htmlman2/getrusage.2.html+#ifndef _GNU_SOURCE // Avoid possible double-definition warning.+#define _GNU_SOURCE+#endif+|]++-- We may generate variables that are never used (e.g. for+-- certificates) or functions that are never called (e.g. unused+-- intrinsics), and generated code may have other cosmetic issues that+-- compilers warn about. We disable these warnings to not clutter the+-- compilation logs.+disableWarnings :: T.Text+disableWarnings =+ [untrimming|+#ifdef __clang__+#pragma clang diagnostic ignored "-Wunused-function"+#pragma clang diagnostic ignored "-Wunused-variable"+#pragma clang diagnostic ignored "-Wparentheses"+#pragma clang diagnostic ignored "-Wunused-label"+#elif __GNUC__+#pragma GCC diagnostic ignored "-Wunused-function"+#pragma GCC diagnostic ignored "-Wunused-variable"+#pragma GCC diagnostic ignored "-Wparentheses"+#pragma GCC diagnostic ignored "-Wunused-label"+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"+#endif+|]++-- | Produce header, implementation, and manifest files.+asLibrary :: CParts -> (T.Text, T.Text, T.Text)+asLibrary parts =+ ( "#pragma once\n\n" <> cHeader parts,+ gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cLib parts,+ cJsonManifest parts+ )++-- | As executable with command-line interface.+asExecutable :: CParts -> T.Text+asExecutable parts =+ gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cCLI parts <> cLib parts++-- | As server executable.+asServer :: CParts -> T.Text+asServer parts =+ gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cServer parts <> cLib parts++compileProg' ::+ MonadFreshNames m =>+ T.Text ->+ T.Text ->+ Operations op s ->+ s ->+ CompilerM op s () ->+ T.Text ->+ (Space, [Space]) ->+ [Option] ->+ Definitions op ->+ m (CParts, CompilerState s)+compileProg' backend version ops def extra header_extra (arr_space, spaces) options prog = do+ src <- getNameSource+ let ((prototypes, definitions, entry_point_decls, manifest), endstate) =+ runCompilerM ops src def compileProgAction+ initdecls = initDecls endstate+ entrydecls = entryDecls endstate+ arraydecls = arrayDecls endstate+ opaquetypedecls = opaqueTypeDecls endstate+ opaquedecls = opaqueDecls endstate+ miscdecls = miscDecls endstate++ let headerdefs =+ [untrimming|+// Headers+#include <stdint.h>+#include <stddef.h>+#include <stdbool.h>+#include <stdio.h>+#include <float.h>+$header_extra+#ifdef __cplusplus+extern "C" {+#endif++// Initialisation+$initdecls++// Arrays+$arraydecls++// Opaque values+$opaquetypedecls+$opaquedecls++// Entry points+$entrydecls++// Miscellaneous+$miscdecls+#define FUTHARK_BACKEND_$backend+$errorsH++#ifdef __cplusplus+}+#endif+|]++ let utildefs =+ [untrimming|+#include <stdio.h>+#include <stdlib.h>+#include <stdbool.h>+#include <math.h>+#include <stdint.h>+// If NDEBUG is set, the assert() macro will do nothing. Since Futhark+// (unfortunately) makes use of assert() for error detection (and even some+// side effects), we want to avoid that.+#undef NDEBUG+#include <assert.h>+#include <stdarg.h>+$utilH+$cacheH+$halfH+$timingH+|]++ let early_decls = T.unlines $ map prettyText $ DL.toList $ compEarlyDecls endstate+ lib_decls = T.unlines $ map prettyText $ DL.toList $ compLibDecls endstate+ clidefs = cliDefs options manifest+ serverdefs = serverDefs options manifest+ libdefs =+ [untrimming|+#ifdef _MSC_VER+#define inline __inline+#endif+#include <string.h>+#include <string.h>+#include <errno.h>+#include <assert.h>+#include <ctype.h>++$header_extra++$lockH++#define FUTHARK_F64_ENABLED++$cScalarDefs++$contextPrototypesH++$early_decls++$contextH++$prototypes++$lib_decls++$definitions++$entry_point_decls+ |]++ pure+ ( CParts+ { cHeader = headerdefs,+ cUtils = utildefs,+ cCLI = clidefs,+ cServer = serverdefs,+ cLib = libdefs,+ cJsonManifest = Manifest.manifestToJSON manifest+ },+ endstate+ )+ where+ Definitions types consts (Functions funs) = prog++ compileProgAction = do+ (memstructs, memfuns, memreport) <- unzip3 <$> mapM defineMemorySpace spaces++ get_consts <- compileConstants consts++ ctx_ty <- contextType++ (prototypes, functions) <-+ unzip <$> mapM (compileFun get_consts [[C.cparam|$ty:ctx_ty *ctx|]]) funs++ mapM_ earlyDecl memstructs+ (entry_points, entry_points_manifest) <-+ unzip . catMaybes <$> mapM (uncurry (onEntryPoint get_consts)) funs++ extra++ mapM_ earlyDecl $ concat memfuns++ type_funs <- generateAPITypes arr_space types+ generateCommonLibFuns memreport++ pure+ ( T.unlines $ map prettyText prototypes,+ T.unlines $ map (prettyText . funcToDef) functions,+ T.unlines $ map prettyText entry_points,+ Manifest.Manifest (M.fromList entry_points_manifest) type_funs backend version+ )++ funcToDef func = C.FuncDef func loc+ where+ loc = case func of+ C.OldFunc _ _ _ _ _ _ l -> l+ C.Func _ _ _ _ _ l -> l++-- | Compile imperative program to a C program. Always uses the+-- function named "main" as entry point, so make sure it is defined.+compileProg ::+ MonadFreshNames m =>+ T.Text ->+ T.Text ->+ Operations op () ->+ CompilerM op () () ->+ T.Text ->+ (Space, [Space]) ->+ [Option] ->+ Definitions op ->+ m CParts+compileProg backend version ops extra header_extra (arr_space, spaces) options prog =+ fst <$> compileProg' backend version ops () extra header_extra (arr_space, spaces) options prog++generateCommonLibFuns :: [C.BlockItem] -> CompilerM op s ()+generateCommonLibFuns memreport = do+ ctx <- contextType+ cfg <- configType+ ops <- asks envOperations+ profilereport <- gets $ DL.toList . compProfileItems++ publicDef_ "context_config_set_cache_file" MiscDecl $ \s ->+ ( [C.cedecl|void $id:s($ty:cfg* cfg, const char *f);|],+ [C.cedecl|void $id:s($ty:cfg* cfg, const char *f) {+ cfg->cache_fname = f;+ }|]+ )++ publicDef_ "get_tuning_param_count" InitDecl $ \s ->+ ( [C.cedecl|int $id:s(void);|],+ [C.cedecl|int $id:s(void) {+ return sizeof(tuning_param_names)/sizeof(tuning_param_names[0]);+ }|]+ )++ publicDef_ "get_tuning_param_name" InitDecl $ \s ->+ ( [C.cedecl|const char* $id:s(int);|],+ [C.cedecl|const char* $id:s(int i) {+ return tuning_param_names[i];+ }|]+ )++ publicDef_ "get_tuning_param_class" InitDecl $ \s ->+ ( [C.cedecl|const char* $id:s(int);|],+ [C.cedecl|const char* $id:s(int i) {+ return tuning_param_classes[i];+ }|]+ )++ sync <- publicName "context_sync"+ publicDef_ "context_report" MiscDecl $ \s ->+ ( [C.cedecl|char* $id:s($ty:ctx *ctx);|],+ [C.cedecl|char* $id:s($ty:ctx *ctx) {+ if ($id:sync(ctx) != 0) {+ return NULL;+ }++ struct str_builder builder;+ str_builder_init(&builder);+ $items:memreport+ if (ctx->profiling) {+ $items:profilereport+ }+ return builder.str;+ }|]+ )++ publicDef_ "context_get_error" MiscDecl $ \s ->+ ( [C.cedecl|char* $id:s($ty:ctx* ctx);|],+ [C.cedecl|char* $id:s($ty:ctx* ctx) {+ char* error = ctx->error;+ ctx->error = NULL;+ return error;+ }|]+ )++ publicDef_ "context_set_logging_file" MiscDecl $ \s ->+ ( [C.cedecl|void $id:s($ty:ctx* ctx, typename FILE* f);|],+ [C.cedecl|void $id:s($ty:ctx* ctx, typename FILE* f) {+ ctx->log = f;+ }|]+ )++ publicDef_ "context_pause_profiling" MiscDecl $ \s ->+ ( [C.cedecl|void $id:s($ty:ctx* ctx);|],+ [C.cedecl|void $id:s($ty:ctx* ctx) {+ ctx->profiling_paused = 1;+ }|]+ )++ publicDef_ "context_unpause_profiling" MiscDecl $ \s ->+ ( [C.cedecl|void $id:s($ty:ctx* ctx);|],+ [C.cedecl|void $id:s($ty:ctx* ctx) {+ ctx->profiling_paused = 0;+ }|]+ )++ clears <- gets $ DL.toList . compClearItems+ publicDef_ "context_clear_caches" MiscDecl $ \s ->+ ( [C.cedecl|int $id:s($ty:ctx* ctx);|],+ [C.cedecl|int $id:s($ty:ctx* ctx) {+ $items:(criticalSection ops clears)+ return ctx->error != NULL;+ }|]+ )++compileConstants :: Constants op -> CompilerM op s [C.BlockItem]+compileConstants (Constants ps init_consts) = do+ ctx_ty <- contextType+ const_fields <- mapM constParamField ps+ -- Avoid an empty struct, as that is apparently undefined behaviour.+ let const_fields'+ | null const_fields = [[C.csdecl|int dummy;|]]+ | otherwise = const_fields+ contextField "constants" [C.cty|struct { $sdecls:const_fields' }|] Nothing+ earlyDecl [C.cedecl|static int init_constants($ty:ctx_ty*);|]+ earlyDecl [C.cedecl|static int free_constants($ty:ctx_ty*);|]++ inNewFunction $ do+ -- We locally define macros for the constants, so that when we+ -- generate assignments to local variables, we actually assign into+ -- the constants struct. This is not needed for functions, because+ -- they can only read constants, not write them.+ let (defs, undefs) = unzip $ map constMacro ps+ init_consts' <- collect $ do+ mapM_ resetMemConst ps+ compileCode init_consts+ decl_mem <- declAllocatedMem+ free_mem <- freeAllocatedMem+ libDecl+ [C.cedecl|static int init_constants($ty:ctx_ty *ctx) {+ (void)ctx;+ int err = 0;+ $items:defs+ $items:decl_mem+ $items:init_consts'+ $items:free_mem+ $items:undefs+ cleanup:+ return err;+ }|]++ inNewFunction $ do+ free_consts <- collect $ mapM_ freeConst ps+ libDecl+ [C.cedecl|static int free_constants($ty:ctx_ty *ctx) {+ (void)ctx;+ $items:free_consts+ return 0;+ }|]++ mapM getConst ps+ where+ constParamField (ScalarParam name bt) = do+ let ctp = primTypeToCType bt+ pure [C.csdecl|$ty:ctp $id:name;|]+ constParamField (MemParam name space) = do+ ty <- memToCType name space+ pure [C.csdecl|$ty:ty $id:name;|]++ constMacro p = ([C.citem|$escstm:def|], [C.citem|$escstm:undef|])+ where+ p' = pretty (C.toIdent (paramName p) mempty)+ def = "#define " ++ p' ++ " (" ++ "ctx->constants." ++ p' ++ ")"+ undef = "#undef " ++ p'++ resetMemConst ScalarParam {} = pure ()+ resetMemConst (MemParam name space) = resetMem name space++ freeConst ScalarParam {} = pure ()+ freeConst (MemParam name space) = unRefMem [C.cexp|ctx->constants.$id:name|] space++ getConst (ScalarParam name bt) = do+ let ctp = primTypeToCType bt+ pure [C.citem|$ty:ctp $id:name = ctx->constants.$id:name;|]+ getConst (MemParam name space) = do+ ty <- memToCType name space+ pure [C.citem|$ty:ty $id:name = ctx->constants.$id:name;|]
+ src/Futhark/CodeGen/Backends/GenericC/Code.hs view
@@ -0,0 +1,356 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}++-- | Translation of ImpCode Exp and Code to C.+module Futhark.CodeGen.Backends.GenericC.Code+ ( compilePrimExp,+ compileExp,+ compileExpToName,+ compileCode,+ errorMsgString,+ linearCode,+ )+where++import Control.Monad.Reader+import Data.Loc+import Data.Maybe+import Futhark.CodeGen.Backends.GenericC.Monad+import Futhark.CodeGen.ImpCode+import Futhark.MonadFreshNames+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C++errorMsgString :: ErrorMsg Exp -> CompilerM op s (String, [C.Exp])+errorMsgString (ErrorMsg parts) = do+ let boolStr e = [C.cexp|($exp:e) ? "true" : "false"|]+ asLongLong e = [C.cexp|(long long int)$exp:e|]+ asDouble e = [C.cexp|(double)$exp:e|]+ onPart (ErrorString s) = pure ("%s", [C.cexp|$string:s|])+ onPart (ErrorVal Bool x) = ("%s",) . boolStr <$> compileExp x+ onPart (ErrorVal Unit _) = pure ("%s", [C.cexp|"()"|])+ onPart (ErrorVal (IntType Int8) x) = ("%hhd",) <$> compileExp x+ onPart (ErrorVal (IntType Int16) x) = ("%hd",) <$> compileExp x+ onPart (ErrorVal (IntType Int32) x) = ("%d",) <$> compileExp x+ onPart (ErrorVal (IntType Int64) x) = ("%lld",) . asLongLong <$> compileExp x+ onPart (ErrorVal (FloatType Float16) x) = ("%f",) . asDouble <$> compileExp x+ onPart (ErrorVal (FloatType Float32) x) = ("%f",) . asDouble <$> compileExp x+ onPart (ErrorVal (FloatType Float64) x) = ("%f",) <$> compileExp x+ (formatstrs, formatargs) <- unzip <$> mapM onPart parts+ pure (mconcat formatstrs, formatargs)++compileExpToName :: String -> PrimType -> Exp -> CompilerM op s VName+compileExpToName _ _ (LeafExp v _) =+ pure v+compileExpToName desc t e = do+ desc' <- newVName desc+ e' <- compileExp e+ decl [C.cdecl|$ty:(primTypeToCType t) $id:desc' = $e';|]+ pure desc'++compileExp :: Exp -> CompilerM op s C.Exp+compileExp = compilePrimExp $ \v -> pure [C.cexp|$id:v|]++-- | Tell me how to compile a @v@, and I'll Compile any @PrimExp v@ for you.+compilePrimExp :: Monad m => (v -> m C.Exp) -> PrimExp v -> m C.Exp+compilePrimExp _ (ValueExp val) =+ pure $ C.toExp val mempty+compilePrimExp f (LeafExp v _) =+ f v+compilePrimExp f (UnOpExp Complement {} x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|~$exp:x'|]+compilePrimExp f (UnOpExp Not {} x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|!$exp:x'|]+compilePrimExp f (UnOpExp (FAbs Float32) x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|(float)fabs($exp:x')|]+compilePrimExp f (UnOpExp (FAbs Float64) x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|fabs($exp:x')|]+compilePrimExp f (UnOpExp SSignum {} x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|($exp:x' > 0 ? 1 : 0) - ($exp:x' < 0 ? 1 : 0)|]+compilePrimExp f (UnOpExp USignum {} x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|($exp:x' > 0 ? 1 : 0) - ($exp:x' < 0 ? 1 : 0) != 0|]+compilePrimExp f (UnOpExp op x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|$id:(pretty op)($exp:x')|]+compilePrimExp f (CmpOpExp cmp x y) = do+ x' <- compilePrimExp f x+ y' <- compilePrimExp f y+ pure $ case cmp of+ CmpEq {} -> [C.cexp|$exp:x' == $exp:y'|]+ FCmpLt {} -> [C.cexp|$exp:x' < $exp:y'|]+ FCmpLe {} -> [C.cexp|$exp:x' <= $exp:y'|]+ CmpLlt {} -> [C.cexp|$exp:x' < $exp:y'|]+ CmpLle {} -> [C.cexp|$exp:x' <= $exp:y'|]+ _ -> [C.cexp|$id:(pretty cmp)($exp:x', $exp:y')|]+compilePrimExp f (ConvOpExp conv x) = do+ x' <- compilePrimExp f x+ pure [C.cexp|$id:(pretty conv)($exp:x')|]+compilePrimExp f (BinOpExp bop x y) = do+ x' <- compilePrimExp f x+ y' <- compilePrimExp f y+ -- Note that integer addition, subtraction, and multiplication with+ -- OverflowWrap are not handled by explicit operators, but rather by+ -- functions. This is because we want to implicitly convert them to+ -- unsigned numbers, so we can do overflow without invoking+ -- undefined behaviour.+ pure $ case bop of+ Add _ OverflowUndef -> [C.cexp|$exp:x' + $exp:y'|]+ Sub _ OverflowUndef -> [C.cexp|$exp:x' - $exp:y'|]+ Mul _ OverflowUndef -> [C.cexp|$exp:x' * $exp:y'|]+ FAdd {} -> [C.cexp|$exp:x' + $exp:y'|]+ FSub {} -> [C.cexp|$exp:x' - $exp:y'|]+ FMul {} -> [C.cexp|$exp:x' * $exp:y'|]+ FDiv {} -> [C.cexp|$exp:x' / $exp:y'|]+ Xor {} -> [C.cexp|$exp:x' ^ $exp:y'|]+ And {} -> [C.cexp|$exp:x' & $exp:y'|]+ Or {} -> [C.cexp|$exp:x' | $exp:y'|]+ LogAnd {} -> [C.cexp|$exp:x' && $exp:y'|]+ LogOr {} -> [C.cexp|$exp:x' || $exp:y'|]+ _ -> [C.cexp|$id:(pretty bop)($exp:x', $exp:y')|]+compilePrimExp f (FunExp h args _) = do+ args' <- mapM (compilePrimExp f) args+ pure [C.cexp|$id:(funName (nameFromString h))($args:args')|]++linearCode :: Code op -> [Code op]+linearCode = reverse . go []+ where+ go acc (x :>>: y) =+ go (go acc x) y+ go acc x = x : acc++assignmentOperator :: BinOp -> Maybe (VName -> C.Exp -> C.Exp)+assignmentOperator Add {} = Just $ \d e -> [C.cexp|$id:d += $exp:e|]+assignmentOperator Sub {} = Just $ \d e -> [C.cexp|$id:d -= $exp:e|]+assignmentOperator Mul {} = Just $ \d e -> [C.cexp|$id:d *= $exp:e|]+assignmentOperator _ = Nothing++compileCode :: Code op -> CompilerM op s ()+compileCode (Op op) =+ join $ asks (opsCompiler . envOperations) <*> pure op+compileCode Skip = pure ()+compileCode (Comment s code) = do+ xs <- collect $ compileCode code+ let comment = "// " ++ s+ stm+ [C.cstm|$comment:comment+ { $items:xs }+ |]+compileCode (TracePrint msg) = do+ (formatstr, formatargs) <- errorMsgString msg+ stm [C.cstm|fprintf(ctx->log, $string:formatstr, $args:formatargs);|]+compileCode (DebugPrint s (Just e)) = do+ e' <- compileExp e+ stm+ [C.cstm|if (ctx->debugging) {+ fprintf(ctx->log, $string:fmtstr, $exp:s, ($ty:ety)$exp:e', '\n');+ }|]+ where+ (fmt, ety) = case primExpType e of+ IntType _ -> ("llu", [C.cty|long long int|])+ FloatType _ -> ("f", [C.cty|double|])+ _ -> ("d", [C.cty|int|])+ fmtstr = "%s: %" ++ fmt ++ "%c"+compileCode (DebugPrint s Nothing) =+ stm+ [C.cstm|if (ctx->debugging) {+ fprintf(ctx->log, "%s\n", $exp:s);+ }|]+-- :>>: is treated in a special way to detect declare-set pairs in+-- order to generate prettier code.+compileCode (c1 :>>: c2) = go (linearCode (c1 :>>: c2))+ where+ go (DeclareScalar name vol t : SetScalar dest e : code)+ | name == dest = do+ let ct = primTypeToCType t+ e' <- compileExp e+ item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $exp:e';|]+ go code+ go (x : xs) = compileCode x >> go xs+ go [] = pure ()+compileCode (Assert e msg (loc, locs)) = do+ e' <- compileExp e+ err <-+ collect . join $+ asks (opsError . envOperations) <*> pure msg <*> pure stacktrace+ stm [C.cstm|if (!$exp:e') { $items:err }|]+ where+ stacktrace = prettyStacktrace 0 $ map locStr $ loc : locs+compileCode (Allocate _ _ ScalarSpace {}) =+ -- Handled by the declaration of the memory block, which is+ -- translated to an actual array.+ pure ()+compileCode (Allocate name (Count (TPrimExp e)) space) = do+ size <- compileExp e+ cached <- cacheMem name+ case cached of+ Just cur_size ->+ stm+ [C.cstm|if ($exp:cur_size < $exp:size) {+ err = lexical_realloc(ctx, &$exp:name, &$exp:cur_size, $exp:size);+ if (err != FUTHARK_SUCCESS) {+ goto cleanup;+ }+ }|]+ _ ->+ allocMem name size space [C.cstm|{err = 1; goto cleanup;}|]+compileCode (Free name space) = do+ cached <- isJust <$> cacheMem name+ unless cached $ unRefMem name space+compileCode (For i bound body) = do+ let i' = C.toIdent i+ t = primTypeToCType $ primExpType bound+ bound' <- compileExp bound+ body' <- collect $ compileCode body+ stm+ [C.cstm|for ($ty:t $id:i' = 0; $id:i' < $exp:bound'; $id:i'++) {+ $items:body'+ }|]+compileCode (While cond body) = do+ cond' <- compileExp $ untyped cond+ body' <- collect $ compileCode body+ stm+ [C.cstm|while ($exp:cond') {+ $items:body'+ }|]+compileCode (If cond tbranch fbranch) = do+ cond' <- compileExp $ untyped cond+ tbranch' <- collect $ compileCode tbranch+ fbranch' <- collect $ compileCode fbranch+ stm $ case (tbranch', fbranch') of+ (_, []) ->+ [C.cstm|if ($exp:cond') { $items:tbranch' }|]+ ([], _) ->+ [C.cstm|if (!($exp:cond')) { $items:fbranch' }|]+ (_, [C.BlockStm x@C.If {}]) ->+ [C.cstm|if ($exp:cond') { $items:tbranch' } else $stm:x|]+ _ ->+ [C.cstm|if ($exp:cond') { $items:tbranch' } else { $items:fbranch' }|]+compileCode (Copy _ dest (Count destoffset) DefaultSpace src (Count srcoffset) DefaultSpace (Count size)) =+ join $+ copyMemoryDefaultSpace+ <$> rawMem dest+ <*> compileExp (untyped destoffset)+ <*> rawMem src+ <*> compileExp (untyped srcoffset)+ <*> compileExp (untyped size)+compileCode (Copy _ dest (Count destoffset) destspace src (Count srcoffset) srcspace (Count size)) = do+ copy <- asks $ opsCopy . envOperations+ join $+ copy CopyBarrier+ <$> rawMem dest+ <*> compileExp (untyped destoffset)+ <*> pure destspace+ <*> rawMem src+ <*> compileExp (untyped srcoffset)+ <*> pure srcspace+ <*> compileExp (untyped size)+compileCode (Write _ _ Unit _ _ _) = pure ()+compileCode (Write dest (Count idx) elemtype DefaultSpace vol elemexp) = do+ dest' <- rawMem dest+ deref <-+ derefPointer dest'+ <$> compileExp (untyped idx)+ <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primStorageType elemtype)*|]+ elemexp' <- toStorage elemtype <$> compileExp elemexp+ stm [C.cstm|$exp:deref = $exp:elemexp';|]+compileCode (Write dest (Count idx) _ ScalarSpace {} _ elemexp) = do+ idx' <- compileExp (untyped idx)+ elemexp' <- compileExp elemexp+ stm [C.cstm|$id:dest[$exp:idx'] = $exp:elemexp';|]+compileCode (Write dest (Count idx) elemtype (Space space) vol elemexp) =+ join $+ asks (opsWriteScalar . envOperations)+ <*> rawMem dest+ <*> compileExp (untyped idx)+ <*> pure (primStorageType elemtype)+ <*> 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+ 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+ let ct = primTypeToCType t+ decl [C.cdecl|$tyquals:(volQuals vol) $ty:ct $id:name;|]+compileCode (DeclareArray name ScalarSpace {} _ _) =+ error $ "Cannot declare array " ++ pretty name ++ " in scalar space."+compileCode (DeclareArray name DefaultSpace t vs) = do+ name_realtype <- newVName $ baseString name ++ "_realtype"+ let ct = primTypeToCType t+ case vs of+ ArrayValues vs' -> do+ let vs'' = [[C.cinit|$exp:v|] | v <- vs']+ earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs')] = {$inits:vs''};|]+ ArrayZeros n ->+ earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|]+ -- Fake a memory block.+ contextField+ (C.toIdent name noLoc)+ [C.cty|struct memblock|]+ $ Just+ [C.cexp|(struct memblock){NULL,+ (unsigned char*)$id:name_realtype,+ 0,+ $string:(pretty name)}|]+ item [C.citem|struct memblock $id:name = ctx->$id:name;|]+compileCode (DeclareArray name (Space space) t vs) =+ join $+ asks (opsStaticArray . envOperations)+ <*> pure name+ <*> pure space+ <*> pure t+ <*> pure vs+-- For assignments of the form 'x = x OP e', we generate C assignment+-- operators to make the resulting code slightly nicer. This has no+-- effect on performance.+compileCode (SetScalar dest (BinOpExp op (LeafExp x _) y))+ | dest == x,+ Just f <- assignmentOperator op = do+ y' <- compileExp y+ stm [C.cstm|$exp:(f dest y');|]+compileCode (SetScalar dest src) = do+ src' <- compileExp src+ stm [C.cstm|$id:dest = $exp:src';|]+compileCode (SetMem dest src space) =+ setMem dest src space+compileCode (Call dests fname args) =+ join $+ asks (opsCall . envOperations)+ <*> pure dests+ <*> pure fname+ <*> mapM compileArg args+ where+ compileArg (MemArg m) = pure [C.cexp|$exp:m|]+ compileArg (ExpArg e) = compileExp e
+ src/Futhark/CodeGen/Backends/GenericC/EntryPoints.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}++-- | Generate the entry point packing/unpacking code.+module Futhark.CodeGen.Backends.GenericC.EntryPoints+ ( onEntryPoint,+ )+where++import Control.Monad.Reader+import Data.Char (isAlpha, isAlphaNum)+import Data.Maybe+import qualified Data.Text as T+import Futhark.CodeGen.Backends.GenericC.Monad+import Futhark.CodeGen.Backends.GenericC.Types (opaqueToCType, valueTypeToCType)+import Futhark.CodeGen.ImpCode+import qualified Futhark.Manifest as Manifest+import Futhark.Util (zEncodeString)+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C++valueDescToType :: ValueDesc -> ValueType+valueDescToType (ScalarValue pt signed _) =+ ValueType signed (Rank 0) pt+valueDescToType (ArrayValue _ _ pt signed shape) =+ ValueType signed (Rank (length shape)) pt++allTrue :: [C.Exp] -> C.Exp+allTrue [] = [C.cexp|true|]+allTrue [x] = x+allTrue (x : xs) = [C.cexp|$exp:x && $exp:(allTrue xs)|]++prepareEntryInputs ::+ [ExternalValue] ->+ CompilerM op s ([(C.Param, Maybe C.Exp)], [C.BlockItem])+prepareEntryInputs args = collect' $ zipWithM prepare [(0 :: Int) ..] args+ where+ arg_names = namesFromList $ concatMap evNames args+ evNames (OpaqueValue _ vds) = map vdName vds+ evNames (TransparentValue vd) = [vdName vd]+ vdName (ArrayValue v _ _ _ _) = v+ vdName (ScalarValue _ _ v) = v++ prepare pno (TransparentValue vd) = do+ let pname = "in" ++ show pno+ (ty, check) <- prepareValue Public [C.cexp|$id:pname|] vd+ pure+ ( [C.cparam|const $ty:ty $id:pname|],+ if null check then Nothing else Just $ allTrue check+ )+ prepare pno (OpaqueValue desc vds) = do+ ty <- opaqueToCType desc+ let pname = "in" ++ show pno+ field i ScalarValue {} = [C.cexp|$id:pname->$id:(tupleField i)|]+ field i ArrayValue {} = [C.cexp|$id:pname->$id:(tupleField i)|]+ checks <- map snd <$> zipWithM (prepareValue Private) (zipWith field [0 ..] vds) vds+ pure+ ( [C.cparam|const $ty:ty *$id:pname|],+ if all null checks+ then Nothing+ else Just $ allTrue $ concat checks+ )++ prepareValue _ src (ScalarValue pt signed name) = do+ let pt' = primAPIType signed pt+ src' = fromStorage pt $ C.toExp src mempty+ stm [C.cstm|$id:name = $exp:src';|]+ pure (pt', [])+ prepareValue pub src vd@(ArrayValue mem _ _ _ shape) = do+ ty <- valueTypeToCType pub $ valueDescToType vd++ stm [C.cstm|$exp:mem = $exp:src->mem;|]++ let rank = length shape+ maybeCopyDim (Var d) i+ | d `notNameIn` arg_names =+ ( Just [C.cstm|$id:d = $exp:src->shape[$int:i];|],+ [C.cexp|$id:d == $exp:src->shape[$int:i]|]+ )+ maybeCopyDim x i =+ ( Nothing,+ [C.cexp|$exp:x == $exp:src->shape[$int:i]|]+ )++ let (sets, checks) =+ unzip $ zipWith maybeCopyDim shape [0 .. rank - 1]+ stms $ catMaybes sets++ pure ([C.cty|$ty:ty*|], checks)++prepareEntryOutputs :: [ExternalValue] -> CompilerM op s ([C.Param], [C.BlockItem])+prepareEntryOutputs = collect' . zipWithM prepare [(0 :: Int) ..]+ where+ prepare pno (TransparentValue vd) = do+ let pname = "out" ++ show pno+ ty <- valueTypeToCType Public $ valueDescToType vd++ case vd of+ ArrayValue {} -> do+ stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]+ prepareValue [C.cexp|*$id:pname|] vd+ pure [C.cparam|$ty:ty **$id:pname|]+ ScalarValue {} -> do+ prepareValue [C.cexp|*$id:pname|] vd+ pure [C.cparam|$ty:ty *$id:pname|]+ prepare pno (OpaqueValue desc vds) = do+ let pname = "out" ++ show pno+ ty <- opaqueToCType desc+ vd_ts <- mapM (valueTypeToCType Private . valueDescToType) vds++ stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]++ forM_ (zip3 [0 ..] vd_ts vds) $ \(i, ct, vd) -> do+ let field = [C.cexp|((*$id:pname)->$id:(tupleField i))|]+ case vd of+ ScalarValue {} -> pure ()+ ArrayValue {} -> do+ stm [C.cstm|assert(($exp:field = ($ty:ct*) malloc(sizeof($ty:ct))) != NULL);|]+ prepareValue field vd++ pure [C.cparam|$ty:ty **$id:pname|]++ prepareValue dest (ScalarValue t _ name) =+ let name' = toStorage t $ C.toExp name mempty+ in stm [C.cstm|$exp:dest = $exp:name';|]+ prepareValue dest (ArrayValue mem _ _ _ shape) = do+ stm [C.cstm|$exp:dest->mem = $id:mem;|]++ let rank = length shape+ maybeCopyDim (Constant x) i =+ [C.cstm|$exp:dest->shape[$int:i] = $exp:x;|]+ maybeCopyDim (Var d) i =+ [C.cstm|$exp:dest->shape[$int:i] = $id:d;|]+ stms $ zipWith maybeCopyDim shape [0 .. rank - 1]++isValidCName :: Name -> Bool+isValidCName = check . nameToString+ where+ check [] = True -- academic+ check (c : cs) = isAlpha c && all constituent cs+ constituent c = isAlphaNum c || c == '_'++entryName :: Name -> String+entryName v+ | isValidCName v = "entry_" <> nameToString v+ | otherwise = "entry_" <> zEncodeString (nameToString v)++onEntryPoint ::+ [C.BlockItem] ->+ Name ->+ Function op ->+ CompilerM op s (Maybe (C.Definition, (T.Text, Manifest.EntryPoint)))+onEntryPoint _ _ (Function Nothing _ _ _) = pure Nothing+onEntryPoint get_consts fname (Function (Just (EntryPoint ename results args)) outputs inputs _) = inNewFunction $ do+ let out_args = map (\p -> [C.cexp|&$id:(paramName p)|]) outputs+ in_args = map (\p -> [C.cexp|$id:(paramName p)|]) inputs++ inputdecls <- collect $ mapM_ stubParam inputs+ outputdecls <- collect $ mapM_ stubParam outputs+ decl_mem <- declAllocatedMem++ entry_point_function_name <- publicName $ entryName ename++ (inputs', unpack_entry_inputs) <- prepareEntryInputs $ map snd args+ let (entry_point_input_params, entry_point_input_checks) = unzip inputs'++ (entry_point_output_params, pack_entry_outputs) <-+ prepareEntryOutputs $ map snd results++ ctx_ty <- contextType++ headerDecl+ EntryDecl+ [C.cedecl|int $id:entry_point_function_name+ ($ty:ctx_ty *ctx,+ $params:entry_point_output_params,+ $params:entry_point_input_params);|]++ let checks = catMaybes entry_point_input_checks+ check_input =+ if null checks+ then []+ else+ [C.citems|+ if (!($exp:(allTrue (catMaybes entry_point_input_checks)))) {+ ret = 1;+ set_error(ctx, msgprintf("Error: entry point arguments have invalid sizes.\n"));+ }|]++ critical =+ [C.citems|+ $items:decl_mem+ $items:unpack_entry_inputs+ $items:check_input+ if (ret == 0) {+ ret = $id:(funName fname)(ctx, $args:out_args, $args:in_args);+ if (ret == 0) {+ $items:get_consts++ $items:pack_entry_outputs+ }+ }+ |]++ ops <- asks envOperations++ let cdef =+ [C.cedecl|+ int $id:entry_point_function_name+ ($ty:ctx_ty *ctx,+ $params:entry_point_output_params,+ $params:entry_point_input_params) {+ $items:inputdecls+ $items:outputdecls++ int ret = 0;++ $items:(criticalSection ops critical)++ return ret;+ }|]++ manifest =+ Manifest.EntryPoint+ { Manifest.entryPointCFun = T.pack entry_point_function_name,+ -- Note that our convention about what is "input/output"+ -- and what is "results/args" is different between the+ -- manifest and ImpCode.+ Manifest.entryPointOutputs = map outputManifest results,+ Manifest.entryPointInputs = map inputManifest args+ }++ pure $ Just (cdef, (nameToText ename, manifest))+ where+ stubParam (MemParam name space) =+ declMem name space+ stubParam (ScalarParam name ty) = do+ let ty' = primTypeToCType ty+ decl [C.cdecl|$ty:ty' $id:name;|]++ vdType (TransparentValue (ScalarValue pt signed _)) =+ T.pack $ prettySigned (signed == Unsigned) pt+ vdType (TransparentValue (ArrayValue _ _ pt signed shape)) =+ T.pack $+ mconcat (replicate (length shape) "[]")+ <> prettySigned (signed == Unsigned) pt+ vdType (OpaqueValue name _) =+ T.pack name++ outputManifest (u, vd) =+ Manifest.Output+ { Manifest.outputType = vdType vd,+ Manifest.outputUnique = u == Unique+ }+ inputManifest ((v, u), vd) =+ Manifest.Input+ { Manifest.inputName = nameToText v,+ Manifest.inputType = vdType vd,+ Manifest.inputUnique = u == Unique+ }
+ src/Futhark/CodeGen/Backends/GenericC/Monad.hs view
@@ -0,0 +1,671 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}++-- | C code generator framework.+module Futhark.CodeGen.Backends.GenericC.Monad+ ( -- * Pluggable compiler+ Operations (..),+ Publicness (..),+ OpCompiler,+ ErrorCompiler,+ CallCompiler,+ PointerQuals,+ MemoryType,+ WriteScalar,+ writeScalarPointerWithQuals,+ ReadScalar,+ readScalarPointerWithQuals,+ Allocate,+ Deallocate,+ CopyBarrier (..),+ Copy,+ StaticArray,++ -- * Monadic compiler interface+ CompilerM,+ CompilerState (..),+ CompilerEnv (..),+ getUserState,+ modifyUserState,+ contextContents,+ contextFinalInits,+ runCompilerM,+ inNewFunction,+ cachingMemory,+ volQuals,+ rawMem,+ item,+ items,+ stm,+ stms,+ decl,+ atInit,+ headerDecl,+ publicDef,+ publicDef_,+ profileReport,+ onClear,+ HeaderSection (..),+ libDecl,+ earlyDecl,+ publicName,+ contextField,+ contextFieldDyn,+ memToCType,+ cacheMem,+ fatMemory,+ rawMemCType,+ freeRawMem,+ allocRawMem,+ fatMemType,+ declAllocatedMem,+ freeAllocatedMem,+ collect,+ collect',+ contextType,+ configType,++ -- * Building Blocks+ copyMemoryDefaultSpace,+ derefPointer,+ setMem,+ allocMem,+ unRefMem,+ declMem,+ resetMem,+ fatMemAlloc,+ fatMemSet,+ fatMemUnRef,+ criticalSection,+ module Futhark.CodeGen.Backends.SimpleRep,+ )+where++import Control.Monad.Identity+import Control.Monad.Reader+import Control.Monad.State+import Data.Bifunctor (first)+import qualified Data.DList as DL+import Data.List (unzip4)+import Data.Loc+import qualified Data.Map.Strict as M+import Data.Maybe+import Futhark.CodeGen.Backends.SimpleRep+import Futhark.CodeGen.ImpCode+import Futhark.MonadFreshNames+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C++-- How public an array type definition sould be. Public types show up+-- in the generated API, while private types are used only to+-- implement the members of opaques.+data Publicness = Private | Public+ deriving (Eq, Ord, Show)++type ArrayType = (Signedness, PrimType, Int)++data CompilerState s = CompilerState+ { compArrayTypes :: M.Map ArrayType Publicness,+ compEarlyDecls :: DL.DList C.Definition,+ compInit :: [C.Stm],+ compNameSrc :: VNameSource,+ compUserState :: s,+ compHeaderDecls :: M.Map HeaderSection (DL.DList C.Definition),+ compLibDecls :: DL.DList C.Definition,+ compCtxFields :: DL.DList (C.Id, C.Type, Maybe C.Exp, Maybe C.Stm),+ compProfileItems :: DL.DList C.BlockItem,+ compClearItems :: DL.DList C.BlockItem,+ compDeclaredMem :: [(VName, Space)],+ compItems :: DL.DList C.BlockItem+ }++newCompilerState :: VNameSource -> s -> CompilerState s+newCompilerState src s =+ CompilerState+ { compArrayTypes = mempty,+ compEarlyDecls = mempty,+ compInit = [],+ compNameSrc = src,+ compUserState = s,+ compHeaderDecls = mempty,+ compLibDecls = mempty,+ compCtxFields = mempty,+ compProfileItems = mempty,+ compClearItems = mempty,+ compDeclaredMem = mempty,+ compItems = mempty+ }++-- | In which part of the header file we put the declaration. This is+-- to ensure that the header file remains structured and readable.+data HeaderSection+ = ArrayDecl String+ | OpaqueTypeDecl String+ | OpaqueDecl String+ | EntryDecl+ | MiscDecl+ | InitDecl+ deriving (Eq, Ord)++-- | A substitute expression compiler, tried before the main+-- compilation function.+type OpCompiler op s = op -> CompilerM op s ()++type ErrorCompiler op s = ErrorMsg Exp -> String -> CompilerM op s ()++-- | The address space qualifiers for a pointer of the given type with+-- the given annotation.+type PointerQuals op s = String -> CompilerM op s [C.TypeQual]++-- | The type of a memory block in the given memory space.+type MemoryType op s = SpaceId -> CompilerM op s C.Type++-- | Write a scalar to the given memory block with the given element+-- index and in the given memory space.+type WriteScalar op s =+ C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> C.Exp -> CompilerM op s ()++-- | Read a scalar from the given memory block with the given element+-- index and in the given memory space.+type ReadScalar op s =+ C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> CompilerM op s C.Exp++-- | Allocate a memory block of the given size and with the given tag+-- in the given memory space, saving a reference in the given variable+-- name.+type Allocate op s =+ C.Exp ->+ C.Exp ->+ C.Exp ->+ SpaceId ->+ CompilerM op s ()++-- | De-allocate the given memory block with the given tag, which is+-- in the given memory space.+type Deallocate op s = C.Exp -> C.Exp -> SpaceId -> CompilerM op s ()++-- | Create a static array of values - initialised at load time.+type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()++-- | Whether a copying operation should implicitly function as a+-- barrier regarding further operations on the source. This is a+-- rather subtle detail and is mostly useful for letting some+-- device/GPU copies be asynchronous (#1664).+data CopyBarrier+ = CopyBarrier+ | -- | Explicit context synchronisation should be done+ -- before the source or target is used.+ CopyNoBarrier+ deriving (Eq, Show)++-- | Copy from one memory block to another.+type Copy op s =+ CopyBarrier ->+ C.Exp ->+ C.Exp ->+ Space ->+ C.Exp ->+ C.Exp ->+ Space ->+ C.Exp ->+ CompilerM op s ()++-- | Call a function.+type CallCompiler op s = [VName] -> Name -> [C.Exp] -> CompilerM op s ()++data Operations op s = Operations+ { opsWriteScalar :: WriteScalar op s,+ opsReadScalar :: ReadScalar op s,+ opsAllocate :: Allocate op s,+ opsDeallocate :: Deallocate op s,+ opsCopy :: Copy op s,+ opsStaticArray :: StaticArray op s,+ opsMemoryType :: MemoryType op s,+ opsCompiler :: OpCompiler op s,+ opsError :: ErrorCompiler op s,+ opsCall :: CallCompiler op s,+ -- | If true, use reference counting. Otherwise, bare+ -- pointers.+ opsFatMemory :: Bool,+ -- | Code to bracket critical sections.+ opsCritical :: ([C.BlockItem], [C.BlockItem])+ }++freeAllocatedMem :: CompilerM op s [C.BlockItem]+freeAllocatedMem = collect $ mapM_ (uncurry unRefMem) =<< gets compDeclaredMem++declAllocatedMem :: CompilerM op s [C.BlockItem]+declAllocatedMem = collect $ mapM_ f =<< gets compDeclaredMem+ where+ f (name, space) = do+ ty <- memToCType name space+ decl [C.cdecl|$ty:ty $id:name;|]+ resetMem name space++data CompilerEnv op s = CompilerEnv+ { envOperations :: Operations op s,+ -- | Mapping memory blocks to sizes. These memory blocks are CPU+ -- memory that we know are used in particularly simple ways (no+ -- reference counting necessary). To cut down on allocator+ -- pressure, we keep these allocations around for a long time, and+ -- record their sizes so we can reuse them if possible (and+ -- realloc() when needed).+ envCachedMem :: M.Map C.Exp VName+ }++contextContents :: CompilerM op s ([C.FieldGroup], [C.Stm], [C.Stm])+contextContents = do+ (field_names, field_types, field_values, field_frees) <-+ gets $ unzip4 . DL.toList . compCtxFields+ let fields =+ [ [C.csdecl|$ty:ty $id:name;|]+ | (name, ty) <- zip field_names field_types+ ]+ init_fields =+ [ [C.cstm|ctx->$id:name = $exp:e;|]+ | (name, Just e) <- zip field_names field_values+ ]+ pure (fields, init_fields, catMaybes field_frees)++contextFinalInits :: CompilerM op s [C.Stm]+contextFinalInits = gets compInit++newtype CompilerM op s a+ = CompilerM (ReaderT (CompilerEnv op s) (State (CompilerState s)) a)+ deriving+ ( Functor,+ Applicative,+ Monad,+ MonadState (CompilerState s),+ MonadReader (CompilerEnv op s)+ )++instance MonadFreshNames (CompilerM op s) where+ getNameSource = gets compNameSrc+ putNameSource src = modify $ \s -> s {compNameSrc = src}++runCompilerM ::+ Operations op s ->+ VNameSource ->+ s ->+ CompilerM op s a ->+ (a, CompilerState s)+runCompilerM ops src userstate (CompilerM m) =+ runState+ (runReaderT m (CompilerEnv ops mempty))+ (newCompilerState src userstate)++getUserState :: CompilerM op s s+getUserState = gets compUserState++modifyUserState :: (s -> s) -> CompilerM op s ()+modifyUserState f = modify $ \compstate ->+ compstate {compUserState = f $ compUserState compstate}++atInit :: C.Stm -> CompilerM op s ()+atInit x = modify $ \s ->+ s {compInit = compInit s ++ [x]}++collect :: CompilerM op s () -> CompilerM op s [C.BlockItem]+collect m = snd <$> collect' m++collect' :: CompilerM op s a -> CompilerM op s (a, [C.BlockItem])+collect' m = do+ old <- gets compItems+ modify $ \s -> s {compItems = mempty}+ x <- m+ new <- gets compItems+ modify $ \s -> s {compItems = old}+ pure (x, DL.toList new)++-- | Used when we, inside an existing 'CompilerM' action, want to+-- generate code for a new function. Use this so that the compiler+-- understands that previously declared memory doesn't need to be+-- freed inside this action.+inNewFunction :: CompilerM op s a -> CompilerM op s a+inNewFunction m = do+ old_mem <- gets compDeclaredMem+ modify $ \s -> s {compDeclaredMem = mempty}+ x <- local noCached m+ modify $ \s -> s {compDeclaredMem = old_mem}+ pure x+ where+ noCached env = env {envCachedMem = mempty}++item :: C.BlockItem -> CompilerM op s ()+item x = modify $ \s -> s {compItems = DL.snoc (compItems s) x}++items :: [C.BlockItem] -> CompilerM op s ()+items xs = modify $ \s -> s {compItems = DL.append (compItems s) (DL.fromList xs)}++fatMemory :: Space -> CompilerM op s Bool+fatMemory ScalarSpace {} = pure False+fatMemory _ = asks $ opsFatMemory . envOperations++cacheMem :: C.ToExp a => a -> CompilerM op s (Maybe VName)+cacheMem a = asks $ M.lookup (C.toExp a noLoc) . envCachedMem++-- | Construct a publicly visible definition using the specified name+-- as the template. The first returned definition is put in the+-- header file, and the second is the implementation. Returns the public+-- name.+publicDef ::+ String ->+ HeaderSection ->+ (String -> (C.Definition, C.Definition)) ->+ CompilerM op s String+publicDef s h f = do+ s' <- publicName s+ let (pub, priv) = f s'+ headerDecl h pub+ earlyDecl priv+ pure s'++-- | As 'publicDef', but ignores the public name.+publicDef_ ::+ String ->+ HeaderSection ->+ (String -> (C.Definition, C.Definition)) ->+ CompilerM op s ()+publicDef_ s h f = void $ publicDef s h f++headerDecl :: HeaderSection -> C.Definition -> CompilerM op s ()+headerDecl sec def = modify $ \s ->+ s+ { compHeaderDecls =+ M.unionWith+ (<>)+ (compHeaderDecls s)+ (M.singleton sec (DL.singleton def))+ }++libDecl :: C.Definition -> CompilerM op s ()+libDecl def = modify $ \s ->+ s {compLibDecls = compLibDecls s <> DL.singleton def}++earlyDecl :: C.Definition -> CompilerM op s ()+earlyDecl def = modify $ \s ->+ s {compEarlyDecls = compEarlyDecls s <> DL.singleton def}++contextField :: C.Id -> C.Type -> Maybe C.Exp -> CompilerM op s ()+contextField name ty initial = modify $ \s ->+ s {compCtxFields = compCtxFields s <> DL.singleton (name, ty, initial, Nothing)}++contextFieldDyn :: C.Id -> C.Type -> Maybe C.Exp -> C.Stm -> CompilerM op s ()+contextFieldDyn name ty initial free = modify $ \s ->+ s {compCtxFields = compCtxFields s <> DL.singleton (name, ty, initial, Just free)}++profileReport :: C.BlockItem -> CompilerM op s ()+profileReport x = modify $ \s ->+ s {compProfileItems = compProfileItems s <> DL.singleton x}++onClear :: C.BlockItem -> CompilerM op s ()+onClear x = modify $ \s ->+ s {compClearItems = compClearItems s <> DL.singleton x}++stm :: C.Stm -> CompilerM op s ()+stm s = item [C.citem|$stm:s|]++stms :: [C.Stm] -> CompilerM op s ()+stms = mapM_ stm++decl :: C.InitGroup -> CompilerM op s ()+decl x = item [C.citem|$decl:x;|]++-- | Public names must have a consitent prefix.+publicName :: String -> CompilerM op s String+publicName s = pure $ "futhark_" ++ s++memToCType :: VName -> Space -> CompilerM op s C.Type+memToCType v space = do+ refcount <- fatMemory space+ cached <- isJust <$> cacheMem v+ if refcount && not cached+ then pure $ fatMemType space+ else rawMemCType space++rawMemCType :: Space -> CompilerM op s C.Type+rawMemCType DefaultSpace = pure defaultMemBlockType+rawMemCType (Space sid) = join $ asks (opsMemoryType . envOperations) <*> pure sid+rawMemCType (ScalarSpace [] t) =+ pure [C.cty|$ty:(primTypeToCType t)[1]|]+rawMemCType (ScalarSpace ds t) =+ pure [C.cty|$ty:(primTypeToCType t)[$exp:(cproduct ds')]|]+ where+ ds' = map (`C.toExp` noLoc) ds++fatMemType :: Space -> C.Type+fatMemType space =+ [C.cty|struct $id:name|]+ where+ name = case space of+ Space sid -> "memblock_" ++ sid+ _ -> "memblock"++fatMemSet :: Space -> String+fatMemSet (Space sid) = "memblock_set_" ++ sid+fatMemSet _ = "memblock_set"++fatMemAlloc :: Space -> String+fatMemAlloc (Space sid) = "memblock_alloc_" ++ sid+fatMemAlloc _ = "memblock_alloc"++fatMemUnRef :: Space -> String+fatMemUnRef (Space sid) = "memblock_unref_" ++ sid+fatMemUnRef _ = "memblock_unref"++rawMem :: VName -> CompilerM op s C.Exp+rawMem v = rawMem' <$> fat <*> pure v+ where+ fat = asks ((&&) . opsFatMemory . envOperations) <*> (isNothing <$> cacheMem v)++rawMem' :: C.ToExp a => Bool -> a -> C.Exp+rawMem' True e = [C.cexp|$exp:e.mem|]+rawMem' False e = [C.cexp|$exp:e|]++allocRawMem ::+ (C.ToExp a, C.ToExp b, C.ToExp c) =>+ a ->+ b ->+ Space ->+ c ->+ CompilerM op s ()+allocRawMem dest size space desc = case space of+ Space sid ->+ join $+ asks (opsAllocate . envOperations)+ <*> pure [C.cexp|$exp:dest|]+ <*> pure [C.cexp|$exp:size|]+ <*> pure [C.cexp|$exp:desc|]+ <*> pure sid+ _ ->+ stm [C.cstm|$exp:dest = (unsigned char*) malloc((size_t)$exp:size);|]++freeRawMem ::+ (C.ToExp a, C.ToExp b) =>+ a ->+ Space ->+ b ->+ CompilerM op s ()+freeRawMem mem space desc =+ case space of+ Space sid -> do+ free_mem <- asks (opsDeallocate . envOperations)+ free_mem [C.cexp|$exp:mem|] [C.cexp|$exp:desc|] sid+ _ -> item [C.citem|free($exp:mem);|]++declMem :: VName -> Space -> CompilerM op s ()+declMem name space = do+ cached <- isJust <$> cacheMem name+ fat <- fatMemory space+ unless cached $+ if fat+ then modify $ \s -> s {compDeclaredMem = (name, space) : compDeclaredMem s}+ else do+ ty <- memToCType name space+ decl [C.cdecl|$ty:ty $id:name;|]++resetMem :: C.ToExp a => a -> Space -> CompilerM op s ()+resetMem mem space = do+ refcount <- fatMemory space+ cached <- isJust <$> cacheMem mem+ if cached+ then stm [C.cstm|$exp:mem = NULL;|]+ else+ when refcount $+ stm [C.cstm|$exp:mem.references = NULL;|]++setMem :: (C.ToExp a, C.ToExp b) => a -> b -> Space -> CompilerM op s ()+setMem dest src space = do+ refcount <- fatMemory space+ let src_s = pretty $ C.toExp src noLoc+ if refcount+ then+ stm+ [C.cstm|if ($id:(fatMemSet space)(ctx, &$exp:dest, &$exp:src,+ $string:src_s) != 0) {+ return 1;+ }|]+ else case space of+ ScalarSpace ds _ -> do+ i' <- newVName "i"+ let i = C.toIdent i'+ it = primTypeToCType $ IntType Int32+ ds' = map (`C.toExp` noLoc) ds+ bound = cproduct ds'+ stm+ [C.cstm|for ($ty:it $id:i = 0; $id:i < $exp:bound; $id:i++) {+ $exp:dest[$id:i] = $exp:src[$id:i];+ }|]+ _ -> stm [C.cstm|$exp:dest = $exp:src;|]++unRefMem :: C.ToExp a => a -> Space -> CompilerM op s ()+unRefMem mem space = do+ refcount <- fatMemory space+ cached <- isJust <$> cacheMem mem+ let mem_s = pretty $ C.toExp mem noLoc+ when (refcount && not cached) $+ stm+ [C.cstm|if ($id:(fatMemUnRef space)(ctx, &$exp:mem, $string:mem_s) != 0) {+ return 1;+ }|]++allocMem ::+ (C.ToExp a, C.ToExp b) =>+ a ->+ b ->+ Space ->+ C.Stm ->+ CompilerM op s ()+allocMem mem size space on_failure = do+ refcount <- fatMemory space+ let mem_s = pretty $ C.toExp mem noLoc+ if refcount+ then+ stm+ [C.cstm|if ($id:(fatMemAlloc space)(ctx, &$exp:mem, $exp:size,+ $string:mem_s)) {+ $stm:on_failure+ }|]+ else do+ freeRawMem mem space mem_s+ allocRawMem mem size space [C.cexp|desc|]++copyMemoryDefaultSpace ::+ C.Exp ->+ C.Exp ->+ C.Exp ->+ C.Exp ->+ C.Exp ->+ CompilerM op s ()+copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes =+ stm+ [C.cstm|if ($exp:nbytes > 0) {+ memmove($exp:destmem + $exp:destidx,+ $exp:srcmem + $exp:srcidx,+ $exp:nbytes);+ }|]++cachingMemory ::+ M.Map VName Space ->+ ([C.BlockItem] -> [C.Stm] -> CompilerM op s a) ->+ CompilerM op s a+cachingMemory lexical f = do+ -- We only consider lexical 'DefaultSpace' memory blocks to be+ -- cached. This is not a deep technical restriction, but merely a+ -- heuristic based on GPU memory usually involving larger+ -- allocations, that do not suffer from the overhead of reference+ -- counting.+ let cached = M.keys $ M.filter (== DefaultSpace) lexical++ cached' <- forM cached $ \mem -> do+ size <- newVName $ pretty mem <> "_cached_size"+ pure (mem, size)++ let lexMem env =+ env+ { envCachedMem =+ M.fromList (map (first (`C.toExp` noLoc)) cached')+ <> envCachedMem env+ }++ declCached (mem, size) =+ [ [C.citem|typename int64_t $id:size = 0;|],+ [C.citem|$ty:defaultMemBlockType $id:mem = NULL;|]+ ]++ freeCached (mem, _) =+ [C.cstm|free($id:mem);|]++ local lexMem $ f (concatMap declCached cached') (map freeCached cached')++derefPointer :: C.Exp -> C.Exp -> C.Type -> C.Exp+derefPointer ptr i res_t =+ [C.cexp|(($ty:res_t)$exp:ptr)[$exp:i]|]++volQuals :: Volatility -> [C.TypeQual]+volQuals Volatile = [C.ctyquals|volatile|]+volQuals Nonvolatile = []++writeScalarPointerWithQuals :: PointerQuals op s -> WriteScalar op s+writeScalarPointerWithQuals quals_f dest i elemtype space vol v = do+ quals <- quals_f space+ let quals' = volQuals vol ++ quals+ deref =+ derefPointer+ dest+ i+ [C.cty|$tyquals:quals' $ty:elemtype*|]+ stm [C.cstm|$exp:deref = $exp:v;|]++readScalarPointerWithQuals :: PointerQuals op s -> ReadScalar op s+readScalarPointerWithQuals quals_f dest i elemtype space vol = do+ quals <- quals_f space+ let quals' = volQuals vol ++ quals+ pure $ derefPointer dest i [C.cty|$tyquals:quals' $ty:elemtype*|]++criticalSection :: Operations op s -> [C.BlockItem] -> [C.BlockItem]+criticalSection ops x =+ [C.citems|lock_lock(&ctx->lock);+ $items:(fst (opsCritical ops))+ $items:x+ $items:(snd (opsCritical ops))+ lock_unlock(&ctx->lock);+ |]++-- | The generated code must define a context struct with this name.+contextType :: CompilerM op s C.Type+contextType = do+ name <- publicName "context"+ pure [C.cty|struct $id:name|]++-- | The generated code must define a configuration struct with this+-- name.+configType :: CompilerM op s C.Type+configType = do+ name <- publicName "context_config"+ pure [C.cty|struct $id:name|]
+ src/Futhark/CodeGen/Backends/GenericC/Types.hs view
@@ -0,0 +1,586 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}++-- | Code generation for public API types.+module Futhark.CodeGen.Backends.GenericC.Types+ ( generateAPITypes,+ valueTypeToCType,+ opaqueToCType,+ )+where++import Control.Monad.Reader+import Control.Monad.State+import Data.Char (isDigit)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Text as T+import Futhark.CodeGen.Backends.GenericC.Monad+import Futhark.CodeGen.ImpCode+import qualified Futhark.Manifest as Manifest+import Futhark.Util (chunks, mapAccumLM)+import Futhark.Util.Pretty (prettyText)+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C++opaqueToCType :: String -> CompilerM op s C.Type+opaqueToCType desc = do+ name <- publicName $ opaqueName desc+ pure [C.cty|struct $id:name|]++valueTypeToCType :: Publicness -> ValueType -> CompilerM op s C.Type+valueTypeToCType _ (ValueType signed (Rank 0) pt) =+ pure $ primAPIType signed pt+valueTypeToCType pub (ValueType signed (Rank rank) pt) = do+ name <- publicName $ arrayName pt signed rank+ let add = M.insertWith max (signed, pt, rank) pub+ modify $ \s -> s {compArrayTypes = add $ compArrayTypes s}+ pure [C.cty|struct $id:name|]++arrayLibraryFunctions ::+ Publicness ->+ Space ->+ PrimType ->+ Signedness ->+ Int ->+ CompilerM op s Manifest.ArrayOps+arrayLibraryFunctions pub space pt signed rank = do+ let pt' = primAPIType signed pt+ name = arrayName pt signed rank+ arr_name = "futhark_" ++ name+ array_type = [C.cty|struct $id:arr_name|]++ new_array <- publicName $ "new_" ++ name+ new_raw_array <- publicName $ "new_raw_" ++ name+ free_array <- publicName $ "free_" ++ name+ values_array <- publicName $ "values_" ++ name+ values_raw_array <- publicName $ "values_raw_" ++ name+ shape_array <- publicName $ "shape_" ++ name++ let shape_names = ["dim" ++ show i | i <- [0 .. rank - 1]]+ shape_params = [[C.cparam|typename int64_t $id:k|] | k <- shape_names]+ arr_size = cproduct [[C.cexp|$id:k|] | k <- shape_names]+ arr_size_array = cproduct [[C.cexp|arr->shape[$int:i]|] | i <- [0 .. rank - 1]]+ copy <- asks $ opsCopy . envOperations++ memty <- rawMemCType space++ let prepare_new = do+ resetMem [C.cexp|arr->mem|] space+ allocMem+ [C.cexp|arr->mem|]+ [C.cexp|$exp:arr_size * $int:(primByteSize pt::Int)|]+ space+ [C.cstm|return NULL;|]+ forM_ [0 .. rank - 1] $ \i ->+ let dim_s = "dim" ++ show i+ in stm [C.cstm|arr->shape[$int:i] = $id:dim_s;|]++ new_body <- collect $ do+ prepare_new+ copy+ CopyNoBarrier+ [C.cexp|arr->mem.mem|]+ [C.cexp|0|]+ space+ [C.cexp|data|]+ [C.cexp|0|]+ DefaultSpace+ [C.cexp|((size_t)$exp:arr_size) * $int:(primByteSize pt::Int)|]++ new_raw_body <- collect $ do+ prepare_new+ copy+ CopyNoBarrier+ [C.cexp|arr->mem.mem|]+ [C.cexp|0|]+ space+ [C.cexp|data|]+ [C.cexp|offset|]+ space+ [C.cexp|((size_t)$exp:arr_size) * $int:(primByteSize pt::Int)|]++ free_body <- collect $ unRefMem [C.cexp|arr->mem|] space++ values_body <-+ collect $+ copy+ CopyNoBarrier+ [C.cexp|data|]+ [C.cexp|0|]+ DefaultSpace+ [C.cexp|arr->mem.mem|]+ [C.cexp|0|]+ space+ [C.cexp|((size_t)$exp:arr_size_array) * $int:(primByteSize pt::Int)|]++ ctx_ty <- contextType+ ops <- asks envOperations++ let proto = case pub of+ Public -> headerDecl (ArrayDecl name)+ Private -> libDecl++ proto+ [C.cedecl|struct $id:arr_name;|]+ proto+ [C.cedecl|$ty:array_type* $id:new_array($ty:ctx_ty *ctx, const $ty:pt' *data, $params:shape_params);|]+ proto+ [C.cedecl|$ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, const $ty:memty data, typename int64_t offset, $params:shape_params);|]+ proto+ [C.cedecl|int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]+ proto+ [C.cedecl|int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data);|]+ proto+ [C.cedecl|$ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]+ proto+ [C.cedecl|const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]++ mapM_+ libDecl+ [C.cunit|+ $ty:array_type* $id:new_array($ty:ctx_ty *ctx, const $ty:pt' *data, $params:shape_params) {+ $ty:array_type* bad = NULL;+ $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));+ if (arr == NULL) {+ return bad;+ }+ $items:(criticalSection ops new_body)+ return arr;+ }++ $ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, const $ty:memty data, typename int64_t offset,+ $params:shape_params) {+ $ty:array_type* bad = NULL;+ $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));+ if (arr == NULL) {+ return bad;+ }+ $items:(criticalSection ops new_raw_body)+ return arr;+ }++ int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr) {+ $items:(criticalSection ops free_body)+ free(arr);+ return 0;+ }++ int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data) {+ $items:(criticalSection ops values_body)+ return 0;+ }++ $ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr) {+ (void)ctx;+ return arr->mem.mem;+ }++ const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr) {+ (void)ctx;+ return arr->shape;+ }+ |]++ pure $+ Manifest.ArrayOps+ { Manifest.arrayFree = T.pack free_array,+ Manifest.arrayShape = T.pack shape_array,+ Manifest.arrayValues = T.pack values_array,+ Manifest.arrayNew = T.pack new_array+ }++lookupOpaqueType :: String -> OpaqueTypes -> OpaqueType+lookupOpaqueType v (OpaqueTypes types) =+ case lookup v types of+ Just t -> t+ Nothing -> error $ "Unknown opaque type: " ++ show v++opaquePayload :: OpaqueTypes -> OpaqueType -> [ValueType]+opaquePayload _ (OpaqueType ts) = ts+opaquePayload types (OpaqueRecord fs) = concatMap f fs+ where+ f (_, TypeOpaque s) = opaquePayload types $ lookupOpaqueType s types+ f (_, TypeTransparent v) = [v]++entryPointTypeToCType :: Publicness -> EntryPointType -> CompilerM op s C.Type+entryPointTypeToCType _ (TypeOpaque desc) = opaqueToCType desc+entryPointTypeToCType pub (TypeTransparent vt) = valueTypeToCType pub vt++entryTypeName :: EntryPointType -> Manifest.TypeName+entryTypeName (TypeOpaque desc) = T.pack desc+entryTypeName (TypeTransparent vt) = prettyText vt++-- | Figure out which of the members of an opaque type corresponds to+-- which fields.+recordFieldPayloads :: OpaqueTypes -> [EntryPointType] -> [a] -> [[a]]+recordFieldPayloads types = chunks . map typeLength+ where+ typeLength (TypeTransparent _) = 1+ typeLength (TypeOpaque desc) =+ length $ opaquePayload types $ lookupOpaqueType desc types++opaqueProjectFunctions ::+ OpaqueTypes ->+ String ->+ [(Name, EntryPointType)] ->+ [ValueType] ->+ CompilerM op s [Manifest.RecordField]+opaqueProjectFunctions types desc fs vds = do+ opaque_type <- opaqueToCType desc+ ctx_ty <- contextType+ ops <- asks envOperations+ let mkProject (TypeTransparent (ValueType sign (Rank 0) pt)) [(i, _)] = do+ pure+ ( primAPIType sign pt,+ [C.citems|v = obj->$id:(tupleField i);|]+ )+ mkProject (TypeTransparent vt) [(i, _)] = do+ ct <- valueTypeToCType Public vt+ pure+ ( [C.cty|$ty:ct *|],+ criticalSection+ ops+ [C.citems|v = malloc(sizeof($ty:ct));+ memcpy(v, obj->$id:(tupleField i), sizeof($ty:ct));+ (void)(*(v->mem.references))++;|]+ )+ mkProject (TypeTransparent _) rep =+ error $ "mkProject: invalid representation of transparent type: " ++ show rep+ mkProject (TypeOpaque f_desc) components = do+ ct <- opaqueToCType f_desc+ let setField j (i, ValueType _ (Rank r) _) =+ if r == 0+ then [C.citems|v->$id:(tupleField j) = obj->$id:(tupleField i);|]+ else+ [C.citems|v->$id:(tupleField j) = malloc(sizeof(*v->$id:(tupleField j)));+ *v->$id:(tupleField j) = *obj->$id:(tupleField i);+ (void)(*(v->$id:(tupleField j)->mem.references))++;|]+ pure+ ( [C.cty|$ty:ct *|],+ criticalSection+ ops+ [C.citems|v = malloc(sizeof($ty:ct));+ $items:(concat (zipWith setField [0..] components))|]+ )+ let onField ((f, et), elems) = do+ project <- publicName $ "project_" ++ opaqueName desc ++ "_" ++ nameToString f+ (et_ty, project_items) <- mkProject et elems+ headerDecl+ (OpaqueDecl desc)+ [C.cedecl|int $id:project($ty:ctx_ty *ctx, $ty:et_ty *out, const $ty:opaque_type *obj);|]+ libDecl+ [C.cedecl|int $id:project($ty:ctx_ty *ctx, $ty:et_ty *out, const $ty:opaque_type *obj) {+ (void)ctx;+ $ty:et_ty v;+ $items:project_items+ *out = v;+ return 0;+ }|]+ pure $ Manifest.RecordField (nameToText f) (entryTypeName et) (T.pack project)++ mapM onField . zip fs . recordFieldPayloads types (map snd fs) $+ zip [0 ..] vds++opaqueNewFunctions ::+ OpaqueTypes ->+ String ->+ [(Name, EntryPointType)] ->+ [ValueType] ->+ CompilerM op s Manifest.CFuncName+opaqueNewFunctions types desc fs vds = do+ opaque_type <- opaqueToCType desc+ ctx_ty <- contextType+ ops <- asks envOperations+ new <- publicName $ "new_" ++ opaqueName desc++ (params, new_stms) <-+ fmap (unzip . snd)+ . mapAccumLM onField 0+ . zip fs+ . recordFieldPayloads types (map snd fs)+ $ vds++ headerDecl+ (OpaqueDecl desc)+ [C.cedecl|int $id:new($ty:ctx_ty *ctx, $ty:opaque_type** out, $params:params);|]+ libDecl+ [C.cedecl|int $id:new($ty:ctx_ty *ctx, $ty:opaque_type** out, $params:params) {+ $ty:opaque_type* v = malloc(sizeof($ty:opaque_type));+ $items:(criticalSection ops new_stms)+ *out = v;+ return 0;+ }|]+ pure $ T.pack new+ where+ onField offset ((f, et), f_vts) = do+ let param_name =+ if all isDigit (nameToString f)+ then C.toIdent ("v" <> f) mempty+ else C.toIdent f mempty+ case et of+ TypeTransparent (ValueType sign (Rank 0) pt) -> do+ let ct = primAPIType sign pt+ pure+ ( offset + 1,+ ( [C.cparam|const $ty:ct $id:param_name|],+ [C.citem|v->$id:(tupleField offset) = $id:param_name;|]+ )+ )+ TypeTransparent vt -> do+ ct <- valueTypeToCType Public vt+ pure+ ( offset + 1,+ ( [C.cparam|const $ty:ct* $id:param_name|],+ [C.citem|{v->$id:(tupleField offset) = malloc(sizeof($ty:ct));+ *v->$id:(tupleField offset) = *$id:param_name;+ (void)(*(v->$id:(tupleField offset)->mem.references))++;}|]+ )+ )+ TypeOpaque f_desc -> do+ ct <- opaqueToCType f_desc+ let param_fields = do+ i <- [0 ..]+ pure [C.cexp|$id:param_name->$id:(tupleField i)|]+ pure+ ( offset + length f_vts,+ ( [C.cparam|const $ty:ct* $id:param_name|],+ [C.citem|{$stms:(zipWith3 setFieldField [offset ..] param_fields f_vts)}|]+ )+ )++ setFieldField i e (ValueType _ (Rank r) _)+ | r == 0 =+ [C.cstm|v->$id:(tupleField i) = $exp:e;|]+ | otherwise =+ [C.cstm|{v->$id:(tupleField i) = malloc(sizeof(*$exp:e));+ *v->$id:(tupleField i) = *$exp:e;+ (void)(*(v->$id:(tupleField i)->mem.references))++;}|]++processOpaqueRecord ::+ OpaqueTypes ->+ String ->+ OpaqueType ->+ [ValueType] ->+ CompilerM op s (Maybe Manifest.RecordOps)+processOpaqueRecord _ _ (OpaqueType _) _ = pure Nothing+processOpaqueRecord types desc (OpaqueRecord fs) vds =+ Just+ <$> ( Manifest.RecordOps+ <$> opaqueProjectFunctions types desc fs vds+ <*> opaqueNewFunctions types desc fs vds+ )++opaqueLibraryFunctions ::+ OpaqueTypes ->+ String ->+ OpaqueType ->+ CompilerM op s (Manifest.OpaqueOps, Maybe Manifest.RecordOps)+opaqueLibraryFunctions types desc ot = do+ name <- publicName $ opaqueName desc+ free_opaque <- publicName $ "free_" ++ opaqueName desc+ store_opaque <- publicName $ "store_" ++ opaqueName desc+ restore_opaque <- publicName $ "restore_" ++ opaqueName desc++ let opaque_type = [C.cty|struct $id:name|]++ freeComponent i (ValueType signed (Rank rank) pt) = unless (rank == 0) $ do+ let field = tupleField i+ free_array <- publicName $ "free_" ++ arrayName pt signed rank+ -- Protect against NULL here, because we also want to use this+ -- to free partially loaded opaques.+ stm+ [C.cstm|if (obj->$id:field != NULL && (tmp = $id:free_array(ctx, obj->$id:field)) != 0) {+ ret = tmp;+ }|]++ storeComponent i (ValueType sign (Rank 0) pt) =+ let field = tupleField i+ in ( storageSize pt 0 [C.cexp|NULL|],+ storeValueHeader sign pt 0 [C.cexp|NULL|] [C.cexp|out|]+ ++ [C.cstms|memcpy(out, &obj->$id:field, sizeof(obj->$id:field));+ out += sizeof(obj->$id:field);|]+ )+ storeComponent i (ValueType sign (Rank rank) pt) =+ let arr_name = arrayName pt sign rank+ field = tupleField i+ shape_array = "futhark_shape_" ++ arr_name+ values_array = "futhark_values_" ++ arr_name+ shape' = [C.cexp|$id:shape_array(ctx, obj->$id:field)|]+ num_elems = cproduct [[C.cexp|$exp:shape'[$int:j]|] | j <- [0 .. rank - 1]]+ in ( storageSize pt rank shape',+ storeValueHeader sign pt rank shape' [C.cexp|out|]+ ++ [C.cstms|ret |= $id:values_array(ctx, obj->$id:field, (void*)out);+ out += $exp:num_elems * $int:(primByteSize pt::Int);|]+ )++ ctx_ty <- contextType++ let vds = opaquePayload types ot+ free_body <- collect $ zipWithM_ freeComponent [0 ..] vds++ store_body <- collect $ do+ let (sizes, stores) = unzip $ zipWith storeComponent [0 ..] vds+ size_vars = map (("size_" ++) . show) [0 .. length sizes - 1]+ size_sum = csum [[C.cexp|$id:size|] | size <- size_vars]+ forM_ (zip size_vars sizes) $ \(v, e) ->+ item [C.citem|typename int64_t $id:v = $exp:e;|]+ stm [C.cstm|*n = $exp:size_sum;|]+ stm [C.cstm|if (p != NULL && *p == NULL) { *p = malloc(*n); }|]+ stm [C.cstm|if (p != NULL) { unsigned char *out = *p; $stms:(concat stores) }|]++ let restoreComponent i (ValueType sign (Rank 0) pt) = do+ let field = tupleField i+ dataptr = "data_" ++ show i+ stms $ loadValueHeader sign pt 0 [C.cexp|NULL|] [C.cexp|src|]+ item [C.citem|const void* $id:dataptr = src;|]+ stm [C.cstm|src += sizeof(obj->$id:field);|]+ pure [C.cstms|memcpy(&obj->$id:field, $id:dataptr, sizeof(obj->$id:field));|]+ restoreComponent i (ValueType sign (Rank rank) pt) = do+ let field = tupleField i+ arr_name = arrayName pt sign rank+ new_array = "futhark_new_" ++ arr_name+ dataptr = "data_" ++ show i+ shapearr = "shape_" ++ show i+ dims = [[C.cexp|$id:shapearr[$int:j]|] | j <- [0 .. rank - 1]]+ num_elems = cproduct dims+ item [C.citem|typename int64_t $id:shapearr[$int:rank] = {0};|]+ stms $ loadValueHeader sign pt rank [C.cexp|$id:shapearr|] [C.cexp|src|]+ item [C.citem|const void* $id:dataptr = src;|]+ stm [C.cstm|obj->$id:field = NULL;|]+ stm [C.cstm|src += $exp:num_elems * $int:(primByteSize pt::Int);|]+ pure+ [C.cstms|+ obj->$id:field = $id:new_array(ctx, $id:dataptr, $args:dims);+ if (obj->$id:field == NULL) { err = 1; }|]++ load_body <- collect $ do+ loads <- concat <$> zipWithM restoreComponent [0 ..] (opaquePayload types ot)+ stm+ [C.cstm|if (err == 0) {+ $stms:loads+ }|]++ headerDecl+ (OpaqueTypeDecl desc)+ [C.cedecl|struct $id:name;|]+ headerDecl+ (OpaqueDecl desc)+ [C.cedecl|int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj);|]+ headerDecl+ (OpaqueDecl desc)+ [C.cedecl|int $id:store_opaque($ty:ctx_ty *ctx, const $ty:opaque_type *obj, void **p, size_t *n);|]+ headerDecl+ (OpaqueDecl desc)+ [C.cedecl|$ty:opaque_type* $id:restore_opaque($ty:ctx_ty *ctx, const void *p);|]++ record <- processOpaqueRecord types desc ot vds++ -- We do not need to enclose most bodies in a critical section,+ -- because when we operate on the components of the opaque, we are+ -- calling public API functions that do their own locking. The+ -- exception is projection, where we fiddle with reference counts.+ mapM_+ libDecl+ [C.cunit|+ int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj) {+ (void)ctx;+ int ret = 0, tmp;+ $items:free_body+ free(obj);+ return ret;+ }++ int $id:store_opaque($ty:ctx_ty *ctx,+ const $ty:opaque_type *obj, void **p, size_t *n) {+ (void)ctx;+ int ret = 0;+ $items:store_body+ return ret;+ }++ $ty:opaque_type* $id:restore_opaque($ty:ctx_ty *ctx,+ const void *p) {+ int err = 0;+ const unsigned char *src = p;+ $ty:opaque_type* obj = malloc(sizeof($ty:opaque_type));+ $items:load_body+ if (err != 0) {+ int ret = 0, tmp;+ $items:free_body+ free(obj);+ obj = NULL;+ }+ return obj;+ }+ |]++ pure+ ( Manifest.OpaqueOps+ { Manifest.opaqueFree = T.pack free_opaque,+ Manifest.opaqueStore = T.pack store_opaque,+ Manifest.opaqueRestore = T.pack restore_opaque+ },+ record+ )++generateArray ::+ Space ->+ ((Signedness, PrimType, Int), Publicness) ->+ CompilerM op s (Maybe (T.Text, Manifest.Type))+generateArray space ((signed, pt, rank), pub) = do+ name <- publicName $ arrayName pt signed rank+ let memty = fatMemType space+ libDecl [C.cedecl|struct $id:name { $ty:memty mem; typename int64_t shape[$int:rank]; };|]+ ops <- arrayLibraryFunctions pub space pt signed rank+ let pt_name = T.pack $ prettySigned (signed == Unsigned) pt+ pretty_name = mconcat (replicate rank "[]") <> pt_name+ arr_type = [C.cty|struct $id:name*|]+ case pub of+ Public ->+ pure $+ Just+ ( pretty_name,+ Manifest.TypeArray (prettyText arr_type) pt_name rank ops+ )+ Private ->+ pure Nothing++generateOpaque ::+ OpaqueTypes ->+ (String, OpaqueType) ->+ CompilerM op s (T.Text, Manifest.Type)+generateOpaque types (desc, ot) = do+ name <- publicName $ opaqueName desc+ members <- zipWithM field (opaquePayload types ot) [(0 :: Int) ..]+ libDecl [C.cedecl|struct $id:name { $sdecls:members };|]+ (ops, record) <- opaqueLibraryFunctions types desc ot+ let opaque_type = [C.cty|struct $id:name*|]+ pure (T.pack desc, Manifest.TypeOpaque (prettyText opaque_type) ops record)+ where+ field vt@(ValueType _ (Rank r) _) i = do+ ct <- valueTypeToCType Private vt+ pure $+ if r == 0+ then [C.csdecl|$ty:ct $id:(tupleField i);|]+ else [C.csdecl|$ty:ct *$id:(tupleField i);|]++generateAPITypes :: Space -> OpaqueTypes -> CompilerM op s (M.Map T.Text Manifest.Type)+generateAPITypes arr_space types@(OpaqueTypes opaques) = do+ mapM_ (findNecessaryArrays . snd) opaques+ array_ts <- mapM (generateArray arr_space) . M.toList =<< gets compArrayTypes+ opaque_ts <- mapM (generateOpaque types) opaques+ pure $ M.fromList $ catMaybes array_ts <> opaque_ts+ where+ -- Ensure that array types will be generated before the opaque+ -- records that allow projection of them. This is because the+ -- projection functions somewhat uglily directly poke around in+ -- the innards to increment reference counts.+ findNecessaryArrays (OpaqueType _) =+ pure ()+ findNecessaryArrays (OpaqueRecord fs) =+ mapM_ (entryPointTypeToCType Public . snd) fs
src/Futhark/CodeGen/Backends/MulticoreC.hs view
@@ -144,6 +144,7 @@ int logging; typename lock_t lock; char *error;+ typename lock_t error_lock; typename FILE *log; int total_runs; long int total_runtime;@@ -174,6 +175,7 @@ ctx->profiling_paused = 0; ctx->logging = 0; ctx->error = NULL;+ create_lock(&ctx->error_lock); ctx->log = stderr; create_lock(&ctx->lock);
src/Futhark/CodeGen/Backends/MulticoreISPC.hs view
@@ -76,7 +76,6 @@ operations (ISPCState mempty mempty) ( do- GC.libDecl [C.cedecl|char** futhark_get_error_ref(struct futhark_context* ctx) { return &ctx->error; }|] MC.generateContext mapM_ compileBuiltinFun funs )@@ -379,8 +378,7 @@ shim <- MC.multicoreDef "assert_shim" $ \s -> do pure [C.cedecl|void $id:s(struct futhark_context* ctx, $params:params) {- if (ctx->error == NULL)- ctx->error = msgprintf($string:formatstr', $args:formatargs', $string:stacktrace);+ set_error(ctx, msgprintf($string:formatstr', $args:formatargs', $string:stacktrace)); }|] ispcDecl [C.cedecl|extern "C" $tyqual:unmasked void $id:shim($tyqual:uniform struct futhark_context* $tyqual:uniform, $params:params_uni);|]@@ -544,7 +542,7 @@ Just cur_size -> GC.stm [C.cstm|if ($exp:cur_size < $exp:size) {- err = lexical_realloc(futhark_get_error_ref(ctx), &$exp:name, &$exp:cur_size, $exp:size);+ err = lexical_realloc(ctx, &$exp:name, &$exp:cur_size, $exp:size); if (err != FUTHARK_SUCCESS) { $escstm:("unmasked { return err; }") }
src/Futhark/CodeGen/Backends/SequentialC/Boilerplate.hs view
@@ -73,6 +73,7 @@ int logging; typename lock_t lock; char *error;+ typename lock_t error_lock; typename FILE *log; int profiling_paused; $sdecls:fields@@ -95,6 +96,7 @@ ctx->profiling = cfg->debugging; ctx->logging = cfg->debugging; ctx->error = NULL;+ create_lock(&ctx->error_lock); ctx->log = stderr; create_lock(&ctx->lock); $stms:init_fields
src/Futhark/CodeGen/ImpCode.hs view
@@ -572,9 +572,11 @@ <+> ppr cond <+> "then {" </> indent 2 (ppr tbranch)- </> "} else {"- </> indent 2 (ppr fbranch)- </> "}"+ </> "} else"+ <+> case fbranch of+ If {} -> ppr fbranch+ _ ->+ "{" </> indent 2 (ppr fbranch) </> "}" ppr (Call dests fname args) = commasep (map ppr dests) <+> "<-"
src/Futhark/CodeGen/ImpGen.hs view
@@ -82,6 +82,7 @@ typeSize, inBounds, isMapTransposeCopy,+ caseMatch, -- * Constructing code. dLParams,@@ -97,6 +98,7 @@ dPrimVE, dIndexSpace, dIndexSpace',+ rotateIndex, sFor, sWhile, sComment,@@ -155,7 +157,7 @@ import Futhark.Util.IntegralExp import Futhark.Util.Loc (noLoc) import Language.Futhark.Warnings-import Prelude hiding (quot)+import Prelude hiding (mod, quot) -- | How to compile an t'Op'. type OpCompiler rep r op = Pat (LetDec rep) -> Op rep -> ImpM rep r op ()@@ -795,13 +797,23 @@ ec <- asks envExpCompiler ec pat e +-- | Generate an expression that is true if the subexpressions match+-- the case pasttern.+caseMatch :: [SubExp] -> [Maybe PrimValue] -> Imp.TExp Bool+caseMatch ses vs = foldl (.&&.) true (zipWith cmp ses vs)+ where+ cmp se (Just v) = isBool $ toExp' (primValueType v) se ~==~ ValueExp v+ cmp _ Nothing = true+ defCompileExp :: (Mem rep inner) => Pat (LetDec rep) -> Exp rep -> ImpM rep r op ()-defCompileExp pat (If cond tbranch fbranch _) =- sIf (toBoolExp cond) (compileBody pat tbranch) (compileBody pat fbranch)+defCompileExp pat (Match ses cases defbody _) =+ foldl f (compileBody pat defbody) cases+ where+ f rest (Case vs body) = sIf (caseMatch ses vs) (compileBody pat body) rest defCompileExp pat (Apply fname args _ _) = do dest <- destinationFromPat pat targets <- funcallTargets dest@@ -920,7 +932,7 @@ uncurry warn loc "Safety check required at run-time." defCompileBasicOp (Pat [pe]) (Index src slice) | Just idxs <- sliceIndices slice =- copyDWIM (patElemName pe) [] (Var src) $ map (DimFix . toInt64Exp) idxs+ copyDWIM (patElemName pe) [] (Var src) $ map (DimFix . pe64) idxs defCompileBasicOp _ Index {} = pure () defCompileBasicOp (Pat [pe]) (Update safety _ slice se) =@@ -928,8 +940,8 @@ Unsafe -> write Safe -> sWhen (inBounds slice' dims) write where- slice' = fmap toInt64Exp slice- dims = map toInt64Exp $ arrayDims $ patElemType pe+ slice' = fmap pe64 slice+ dims = map pe64 $ arrayDims $ patElemType pe write = sUpdate (patElemName pe) slice' se defCompileBasicOp _ FlatIndex {} = pure ()@@ -938,7 +950,7 @@ v_loc <- entryArrayLoc <$> lookupArray v copy (elemType (patElemType pe)) (flatSliceMemLoc pe_loc slice') v_loc where- slice' = fmap toInt64Exp slice+ slice' = fmap pe64 slice defCompileBasicOp (Pat [pe]) (Replicate (Shape ds) se) | Acc {} <- patElemType pe = pure () | otherwise = do@@ -951,7 +963,7 @@ defCompileBasicOp (Pat [pe]) (Iota n e s it) = do e' <- toExp e s' <- toExp s- sFor "i" (toInt64Exp n) $ \i -> do+ sFor "i" (pe64 n) $ \i -> do let i' = sExt it $ untyped i x <- dPrimV "x" . TPrimExp $@@ -969,10 +981,10 @@ y_dims <- arrayDims <$> lookupType y let rows = case drop i y_dims of [] -> error $ "defCompileBasicOp Concat: empty array shape for " ++ pretty y- r : _ -> toInt64Exp r+ r : _ -> pe64 r skip_dims = take i y_dims sliceAllDim d = DimSlice 0 d 1- skip_slices = map (sliceAllDim . toInt64Exp) skip_dims+ skip_slices = map (sliceAllDim . pe64) skip_dims destslice = skip_slices ++ [DimSlice (tvExp offs_glb) rows 1] copyDWIM (patElemName pe) destslice (Var y) [] offs_glb <-- tvExp offs_glb + rows@@ -997,8 +1009,13 @@ isLiteral _ = Nothing defCompileBasicOp _ Rearrange {} = pure ()-defCompileBasicOp _ Rotate {} =- pure ()+defCompileBasicOp (Pat [pe]) (Rotate rs arr) = do+ shape <- arrayShape <$> lookupType arr+ sLoopNest shape $ \is -> do+ is' <- sequence $ zipWith3 rotate (shapeDims shape) rs is+ copyDWIMFix (patElemName pe) is (Var arr) is'+ where+ rotate d r i = dPrimVE "rot_i" $ rotateIndex (pe64 d) (pe64 r) i defCompileBasicOp _ Reshape {} = pure () defCompileBasicOp _ (UpdateAcc acc is vs) = sComment "UpdateAcc" $ do@@ -1007,7 +1024,7 @@ -- we might otherwise end up declaring lambda parameters (if any) -- multiple times, as they are duplicated every time we do an -- UpdateAcc for the same accumulator.- let is' = map toInt64Exp is+ let is' = map pe64 is -- We need to figure out whether we are updating a scatter-like -- accumulator or a generalised reduction. This also binds the@@ -1227,19 +1244,13 @@ -- | Compile things to 'Imp.Exp'. class ToExp a where- -- | Compile to an 'Imp.Exp', where the type (must must still be a+ -- | Compile to an 'Imp.Exp', where the type (which must still be a -- primitive) is deduced monadically. toExp :: a -> ImpM rep r op Imp.Exp -- | Compile where we know the type in advance. toExp' :: PrimType -> a -> Imp.Exp - toInt64Exp :: a -> Imp.TExp Int64- toInt64Exp = TPrimExp . toExp' int64-- toBoolExp :: a -> Imp.TExp Bool- toBoolExp = TPrimExp . toExp' Bool- instance ToExp SubExp where toExp (Constant v) = pure $ Imp.ValueExp v@@ -1368,12 +1379,12 @@ ( acc, space, arrs,- map toInt64Exp (shapeDims ispace),+ map pe64 (shapeDims ispace), Just op {lambdaParams = ps} ) Just (arrs@(arr : _), Nothing) -> do space <- lookupArraySpace arr- pure (acc, space, arrs, map toInt64Exp (shapeDims ispace), Nothing)+ pure (acc, space, arrs, map pe64 (shapeDims ispace), Nothing) Nothing -> error $ "ImpGen.lookupAcc: unlisted accumulator: " ++ pretty name _ -> error $ "ImpGen.lookupAcc: not an accumulator: " ++ pretty name@@ -1588,8 +1599,8 @@ emit $ Imp.Read tmp srcmem srcoffset bt srcspace vol emit $ Imp.Write targetmem targetoffset bt destspace vol $ Imp.var tmp bt | otherwise = do- let destslice' = fullSliceNum (map toInt64Exp destshape) destslice- srcslice' = fullSliceNum (map toInt64Exp srcshape) srcslice+ let destslice' = fullSliceNum (map pe64 destshape) destslice+ srcslice' = fullSliceNum (map pe64 srcshape) srcslice destrank = length $ sliceDims destslice' srcrank = length $ sliceDims srcslice' destlocation' = sliceMemLoc destlocation destslice'@@ -1749,7 +1760,7 @@ compileAlloc :: Mem rep inner => Pat (LetDec rep) -> SubExp -> Space -> ImpM rep r op () compileAlloc (Pat [mem]) e space = do- let e' = Imp.bytes $ toInt64Exp e+ let e' = Imp.bytes $ pe64 e allocator <- asks $ M.lookup space . envAllocCompilers case allocator of Nothing -> emit $ Imp.Allocate (patElemName mem) e' space@@ -1761,7 +1772,7 @@ -- straightforward contiguous format, as an t'Int64' expression. typeSize :: Type -> Count Bytes (Imp.TExp Int64) typeSize t =- Imp.bytes $ primByteSize (elemType t) * product (map toInt64Exp (arrayDims t))+ Imp.bytes $ primByteSize (elemType t) * product (map pe64 (arrayDims t)) -- | Is this indexing in-bounds for an array of the given shape? This -- is useful for things like scatter, which ignores out-of-bounds@@ -1776,6 +1787,13 @@ --- Building blocks for constructing code. +rotateIndex ::+ Imp.TExp Int64 ->+ Imp.TExp Int64 ->+ Imp.TExp Int64 ->+ Imp.TExp Int64+rotateIndex d r i = (i + r) `mod` d+ sFor' :: VName -> Imp.Exp -> ImpM rep r op () -> ImpM rep r op () sFor' i bound body = do let it = case primExpType bound of@@ -1913,7 +1931,7 @@ Shape -> ([Imp.TExp Int64] -> ImpM rep r op ()) -> ImpM rep r op ()-sLoopNest = sLoopSpace . map toInt64Exp . shapeDims+sLoopNest = sLoopSpace . map pe64 . shapeDims -- | Untyped assignment. (<~~) :: VName -> Imp.Exp -> ImpM rep r op ()
src/Futhark/CodeGen/ImpGen/GPU.hs view
@@ -135,7 +135,7 @@ -- The calculations are done with 64-bit integers to avoid overflow -- issues. let num_groups_maybe_zero =- sMin64 (toInt64Exp w64 `divUp` toInt64Exp group_size) $+ sMin64 (pe64 w64 `divUp` pe64 group_size) $ sExt64 (tvExp max_num_groups) -- We also don't want zero groups. let num_groups = sMax64 1 num_groups_maybe_zero@@ -247,29 +247,34 @@ x' <- toExp x s' <- toExp s - sIota (patElemName pe) (toInt64Exp n) x' s' et+ sIota (patElemName pe) (pe64 n) x' s' et expCompiler (Pat [pe]) (BasicOp (Replicate _ se)) | Acc {} <- patElemType pe = pure () | otherwise = sReplicate (patElemName pe) se+expCompiler (Pat [pe]) (BasicOp (Rotate rs arr))+ | Acc {} <- patElemType pe = pure ()+ | otherwise =+ sRotateKernel (patElemName pe) (map pe64 rs) arr -- Allocation in the "local" space is just a placeholder. expCompiler _ (Op (Alloc _ (Space "local"))) = pure () expCompiler pat (WithAcc inputs lam) = withAcc pat inputs lam--- This is a multi-versioning If created by incremental flattening.+-- This is a multi-versioning Match created by incremental flattening. -- We need to augment the conditional with a check that any local -- memory requirements in tbranch are compatible with the hardware.--- We do not check anything for fbranch, as we assume that it will+-- We do not check anything for defbody, as we assume that it will -- always be safe (and what would we do if none of the branches would -- work?).-expCompiler dest (If cond tbranch fbranch (IfDec _ IfEquiv)) = do- tcode <- collect $ compileBody dest tbranch- fcode <- collect $ compileBody dest fbranch+expCompiler dest (Match cond (first_case : cases) defbranch sort@(MatchDec _ MatchEquiv)) = do+ tcode <- collect $ compileBody dest $ caseBody first_case+ fcode <- collect $ expCompiler dest $ Match cond cases defbranch sort check <- checkLocalMemoryReqs tcode+ let matches = caseMatch cond (casePat first_case) emit $ case check of Nothing -> fcode- Just ok -> Imp.If (ok .&&. toBoolExp cond) tcode fcode+ Just ok -> Imp.If (matches .&&. ok) tcode fcode expCompiler dest e = defCompileExp dest e @@ -293,7 +298,7 @@ | bt_size <- primByteSize bt, Just destoffset <- IxFun.linearWithOffset destIxFun bt_size, Just srcoffset <- IxFun.linearWithOffset srcIxFun bt_size = do- let num_elems = Imp.elements $ product $ map toInt64Exp srcshape+ let num_elems = Imp.elements $ product $ map pe64 srcshape srcspace <- entryMemSpace <$> lookupMemory srcmem destspace <- entryMemSpace <$> lookupMemory destmem emit
src/Futhark/CodeGen/ImpGen/GPU/Base.hs view
@@ -21,6 +21,7 @@ defKernelAttrs, sReplicate, sIota,+ sRotateKernel, sCopy, compileThreadResult, compileGroupResult,@@ -97,7 +98,6 @@ kernelGroupSize :: Imp.TExp Int64, kernelNumThreads :: Imp.TExp Int32, kernelWaveSize :: Imp.TExp Int32,- kernelThreadActive :: Imp.TExp Bool, -- | A mapping from dimensions of nested SegOps to already -- computed local thread IDs. Only valid in non-virtualised case. kernelLocalIdMap :: M.Map [SubExp] [Imp.TExp Int32],@@ -121,8 +121,8 @@ _ -> S.singleton $ map snd $ unSegSpace $ segSpace op onExp (BasicOp (Replicate shape _)) = S.singleton $ shapeDims shape- onExp (If _ tbranch fbranch _) =- onStms (bodyStms tbranch) <> onStms (bodyStms fbranch)+ onExp (Match _ cases defbody _) =+ foldMap (onStms . bodyStms . caseBody) cases <> onStms (bodyStms defbody) onExp (DoLoop _ _ body) = onStms (bodyStms body) onExp _ = mempty@@ -161,7 +161,7 @@ localEnv f m where mkMap ltid dims = do- let dims' = map toInt64Exp dims+ let dims' = map pe64 dims ids' <- dIndexSpace' "ltid_pre" dims' (sExt64 ltid) pure (dims, map sExt32 ids') @@ -183,23 +183,22 @@ -- translated to an actual scalar variable during C code generation. pure () kernelAlloc (Pat [mem]) size (Space "local") =- allocLocal (patElemName mem) $ Imp.bytes $ toInt64Exp size+ 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 ::- (ToExp w, ToExp i, ToExp elems_per_thread) => Pat LetDecMem -> SplitOrdering ->- w ->- i ->- elems_per_thread ->+ 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' = toInt64Exp i+ 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 _ _ _ _ =@@ -208,7 +207,7 @@ updateAcc :: VName -> [SubExp] -> [SubExp] -> InKernelGen () updateAcc acc is vs = sComment "UpdateAcc" $ do -- See the ImpGen implementation of UpdateAcc for general notes.- let is' = map toInt64Exp is+ let is' = map pe64 is (c, space, arrs, dims, op) <- lookupAcc acc is' sWhen (inBounds (Slice (map DimFix is')) dims) $ case op of@@ -308,7 +307,7 @@ localThreadIDs :: [SubExp] -> InKernelGen [Imp.TExp Int64] localThreadIDs dims = do ltid <- sExt64 . kernelLocalThreadId . kernelConstants <$> askEnv- let dims' = map toInt64Exp dims+ let dims' = map pe64 dims maybe (dIndexSpace' "ltid" dims' ltid) (pure . map sExt64) . M.lookup dims . kernelLocalIdMap@@ -374,6 +373,14 @@ 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@@ -401,8 +408,8 @@ Safe -> sWhen (inBounds slice' dims) write sOp $ Imp.Barrier Imp.FenceLocal where- slice' = fmap toInt64Exp slice- dims = map toInt64Exp $ arrayDims $ patElemType pe+ slice' = fmap pe64 slice+ dims = map pe64 $ arrayDims $ patElemType pe write = copyDWIM (patElemName pe) (unSlice slice') se [] compileGroupExp dest e = defCompileExp dest e@@ -439,8 +446,8 @@ (Nothing, AtomicLocking f) -> do locks <- newVName "locks" - let num_locks = toInt64Exp $ unCount group_size- dims = map toInt64Exp $ shapeDims (histOpShape op <> histShape op)+ 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 @@ -512,7 +519,7 @@ 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 (DimNew . pe64) $+ map pe64 $ shapeDims flat_shape -- | @applyLambda lam dests args@ emits code that:@@ -608,7 +615,7 @@ compileFlatId lvl space let (ltids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims groupCoverSegSpace (segVirt lvl) space $ compileStms mempty (kernelBodyStms body) $@@ -654,7 +661,7 @@ compileGroupOp pat (Inner (SegOp (SegRed lvl space ops _ body))) = do compileFlatId lvl space - let dims' = map toInt64Exp dims+ let dims' = map pe64 dims mkTempArr t = sAllocArray "red_arr" (elemType t) (Shape dims <> arrayShape t) $ Space "local" @@ -792,8 +799,8 @@ forM_ (zip4 red_is vs_per_op ops' ops) $ \(bin, op_vs, do_op, HistOp dest_shape _ _ _ shape lam) -> do- let bin' = toInt64Exp bin- dest_shape' = map toInt64Exp $ shapeDims dest_shape+ 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@@ -1106,7 +1113,7 @@ chunk_var <-- sMin64 (Imp.unCount elements_per_thread)- ((Imp.unCount num_elements - thread_index) `divUp` toInt64Exp stride)+ ((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" $@@ -1160,7 +1167,6 @@ kernelGroupSize = group_size', kernelNumThreads = sExt32 (group_size' * num_groups'), kernelWaveSize = Imp.le32 wave_size,- kernelThreadActive = true, kernelLocalIdMap = mempty, kernelChunkItersMap = mempty }@@ -1185,7 +1191,7 @@ x : xs -> foldl (.&&.) x xs where (is, ws) = unzip limit- actives = zipWith active is $ map toInt64Exp ws+ actives = zipWith active is $ map pe64 ws active i = (Imp.le64 i .<.) -- | Change every memory block to be in the global address space,@@ -1553,28 +1559,61 @@ | otherwise = copyDWIM (paramName y) [] (Var $ paramName x) [] -computeMapKernelGroups ::+simpleKernelGroups :: Imp.TExp Int64 ->- CallKernelGen (Count NumGroups SubExp, Count GroupSize SubExp)-computeMapKernelGroups kernel_size = do+ 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- num_groups <- dPrimV "num_groups" $ kernel_size `divUp` tvExp group_size- pure (Count $ tvSize num_groups, Count $ tvSize group_size)+ 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 (KernelConstants, InKernelGen ())+ 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"- (num_groups, group_size) <- computeMapKernelGroups kernel_size- let set_constants = do+ (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@@ -1582,29 +1621,14 @@ 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- group_size' = Imp.pe64 $ unCount group_size- num_groups' = Imp.pe64 $ unCount num_groups+ 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- ( 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,- kernelThreadActive = Imp.le64 thread_gtid .<. kernel_size,- kernelLocalIdMap = mempty,- kernelChunkItersMap = mempty- },- set_constants- )+ 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@@ -1792,9 +1816,9 @@ t <- subExpType se ds <- dropLast (arrayRank t) . arrayDims <$> lookupType arr - let dims = map toInt64Exp $ ds ++ arrayDims t- (constants, set_constants) <-- simpleKernelConstants (product $ map sExt64 dims) "replicate"+ let dims = map pe64 $ ds ++ arrayDims t+ n <- dPrimVE "replicate_n" $ product $ map sExt64 dims+ (virtualise, constants) <- simpleKernelConstants n "replicate" fname <- askFunction let name =@@ -1802,12 +1826,12 @@ nameFromString $ "replicate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants) - sKernelFailureTolerant True threadOperations constants name $ do- set_constants- is' <- dIndexSpace' "rep_i" dims $ sExt64 $ kernelGlobalThreadId constants- sWhen (kernelThreadActive constants) $- copyDWIMFix arr is' se $- drop (length ds) is'+ 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@@ -1824,16 +1848,13 @@ let params = [ Imp.MemParam mem (Space "device"),- Imp.ScalarParam num_elems int32,+ 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+ sArray "arr" bt shape mem $ IxFun.iota $ map pe64 $ shapeDims shape sReplicateKernel arr $ Var val pure fname@@ -1852,7 +1873,7 @@ [] fname [ Imp.MemArg arr_mem,- Imp.ExpArg $ untyped $ product $ map toInt64Exp arr_shape,+ Imp.ExpArg $ untyped $ product $ map pe64 arr_shape, Imp.ExpArg $ toExp' v_t' v ] _ -> pure Nothing@@ -1878,7 +1899,7 @@ CallKernelGen () sIotaKernel arr n x s et = do destloc <- entryArrayLoc <$> lookupArray arr- (constants, set_constants) <- simpleKernelConstants n "iota"+ (virtualise, constants) <- simpleKernelConstants n "iota" fname <- askFunction let name =@@ -1889,18 +1910,17 @@ ++ "_" ++ show (baseTag $ kernelGlobalThreadIdVar constants) - sKernelFailureTolerant True threadOperations constants name $ do- set_constants- let gtid = sExt64 $ kernelGlobalThreadId constants- sWhen (kernelThreadActive constants) $ do- (destmem, destspace, destidx) <- fullyIndexArray' destloc [gtid]+ 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+ 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@@ -1961,10 +1981,10 @@ 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 toInt64Exp srcdims+ let shape = map pe64 srcdims kernel_size = product shape - (constants, set_constants) <- simpleKernelConstants kernel_size "copy"+ (virtualise, constants) <- simpleKernelConstants kernel_size "copy" fname <- askFunction let name =@@ -1972,48 +1992,68 @@ nameFromString $ "copy_" ++ show (baseTag $ kernelGlobalThreadIdVar constants) - sKernelFailureTolerant True threadOperations constants name $ do- set_constants+ sKernelFailureTolerant True threadOperations constants name $+ virtualise $ \gtid -> do+ is <- dIndexSpace' "copy_i" shape gtid - let gtid = sExt64 $ kernelGlobalThreadId constants- is <- dIndexSpace' "copy_i" shape gtid+ (_, destspace, destidx) <- fullyIndexArray' destloc is+ (_, srcspace, srcidx) <- fullyIndexArray' srcloc is - (_, 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 - 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 <- toInt64Exp . arraySize 0 <$> lookupType what+ n <- pe64 . arraySize 0 <$> lookupType what constants <- kernelConstants <$> askEnv let ltid = sExt64 $ kernelLocalThreadId constants offset =- toInt64Exp per_group_elems+ 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 toInt64Exp per_group_elems == kernelGroupSize constants+ if pe64 per_group_elems == kernelGroupSize constants then- sWhen (ltid + offset .<. toInt64Exp w) $+ 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 .<. toInt64Exp w) $+ 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 (toInt64Exp . snd) dims+ 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 <-@@ -2028,8 +2068,8 @@ let gids = map fst $ unSegSpace space (dims, group_tiles, reg_tiles) = unzip3 dims_n_tiles- group_tiles' = map toInt64Exp group_tiles- reg_tiles' = map toInt64Exp reg_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@@ -2057,7 +2097,7 @@ 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 toInt64Exp dims) $+ 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@@ -2092,20 +2132,19 @@ compileThreadResult _ pe (ConcatReturns _ SplitContiguous _ per_thread_elems what) = do constants <- kernelConstants <$> askEnv let offset =- toInt64Exp per_thread_elems+ pe64 per_thread_elems * sExt64 (kernelGlobalThreadId constants)- n <- toInt64Exp . arraySize 0 <$> lookupType what+ 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 <- toInt64Exp . arraySize 0 <$> lookupType what- copyDWIM (patElemName pe) [DimSlice offset n $ toInt64Exp stride] (Var what) []+ n <- pe64 . arraySize 0 <$> lookupType what+ copyDWIM (patElemName pe) [DimSlice offset n $ pe64 stride] (Var what) [] compileThreadResult _ pe (WriteReturns _ (Shape rws) _arr dests) = do- constants <- kernelConstants <$> askEnv- let rws' = map toInt64Exp rws+ let rws' = map pe64 rws forM_ dests $ \(slice, e) -> do- let slice' = fmap toInt64Exp slice- write = kernelThreadActive constants .&&. inBounds slice' rws'+ 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/SegHist.hs view
@@ -76,7 +76,7 @@ histOp op histSize :: HistOp GPUMem -> Imp.TExp Int64-histSize = product . map toInt64Exp . shapeDims . histShape+histSize = product . map pe64 . shapeDims . histShape histRank :: HistOp GPUMem -> Int histRank = shapeRank . histShape@@ -127,7 +127,7 @@ Space "device" multiHistoCase = do- let num_elems = product $ map toInt64Exp $ shapeDims subhistos_shape+ let num_elems = product $ map pe64 $ shapeDims subhistos_shape subhistos_mem_size = Imp.bytes $ Imp.unCount (Imp.elements num_elems `Imp.withElemType` elemType dest_t)@@ -136,15 +136,15 @@ sReplicate subhistos ne subhistos_t <- lookupType subhistos let slice =- fullSliceNum (map toInt64Exp $ arrayDims subhistos_t) $- map (unitSlice 0 . toInt64Exp . snd) segment_dims+ fullSliceNum (map pe64 $ arrayDims subhistos_t) $+ map (unitSlice 0 . pe64 . snd) segment_dims ++ [DimFix 0] sUpdate subhistos slice $ Var dest sIf (tvExp num_subhistos .==. 1) unitHistoCase multiHistoCase let h = histSpaceUsage op- segmented_h = h * product (map (Imp.bytes . toInt64Exp) $ init $ segSpaceDims space)+ segmented_h = h * product (map (Imp.bytes . pe64) $ init $ segSpaceDims space) atomics <- hostAtomics <$> askEnv @@ -181,7 +181,7 @@ -- algorithm to ensure good distribution of locks. let num_locks = 100151 dims =- map toInt64Exp $+ map pe64 $ shapeDims (histOpShape (slugOp slug)) ++ [tvSize (slugNumSubhistos slug)] ++ shapeDims (histShape (slugOp slug))@@ -225,7 +225,7 @@ hist_RF <- dPrimVE "hist_RF" $- sum (map (r64 . toInt64Exp . histRaceFactor . slugOp) slugs)+ sum (map (r64 . pe64 . histRaceFactor . slugOp) slugs) / genericLength slugs hist_el_size <- dPrimVE "hist_el_size" $ sum $ map slugElAvgSize slugs@@ -399,7 +399,7 @@ CallKernelGen () histKernelGlobalPass map_pes num_groups group_size space slugs kbody histograms hist_S chk_i = do let (space_is, space_sizes) = unzip $ unSegSpace space- space_sizes_64 = map (sExt64 . toInt64Exp) space_sizes+ space_sizes_64 = map (sExt64 . pe64) space_sizes total_w_64 = product space_sizes_64 hist_H_chks <- forM (map (histSize . slugOp) slugs) $ \w ->@@ -456,8 +456,8 @@ hist_H_chk ) -> do let chk_beg = sExt64 chk_i * hist_H_chk- bucket' = map toInt64Exp bucket- dest_shape' = map toInt64Exp $ shapeDims dest_shape+ bucket' = map pe64 bucket+ dest_shape' = map pe64 $ shapeDims dest_shape flat_bucket = flattenIndex dest_shape' bucket' bucket_in_bounds = chk_beg .<=. flat_bucket@@ -485,8 +485,8 @@ KernelBody GPUMem -> CallKernelGen () histKernelGlobal map_pes num_groups group_size space slugs kbody = do- let num_groups' = fmap toInt64Exp num_groups- group_size' = fmap toInt64Exp group_size+ let num_groups' = fmap pe64 num_groups+ group_size' = fmap pe64 group_size let (_space_is, space_sizes) = unzip $ unSegSpace space num_threads = sExt32 $ unCount num_groups' * unCount group_size' @@ -496,7 +496,7 @@ prepareIntermediateArraysGlobal (bodyPassage kbody) num_threads- (toInt64Exp $ last space_sizes)+ (pe64 $ last space_sizes) slugs sFor "chk_i" hist_S $ \chk_i ->@@ -549,7 +549,7 @@ : shapeDims (histOpShape op) ++ [hist_H_chk] - let dims = map toInt64Exp $ shapeDims lock_shape+ let dims = map pe64 $ shapeDims lock_shape locks <- sAllocArray "locks" int32 lock_shape $ Space "local" @@ -614,12 +614,12 @@ segment_dims = init space_sizes (i_in_segment, segment_size) = last $ unSegSpace space num_subhistos_per_group = tvExp num_subhistos_per_group_var- segment_size' = toInt64Exp segment_size+ segment_size' = pe64 segment_size num_segments <- dPrimVE "num_segments" $ product $- map toInt64Exp segment_dims+ map pe64 segment_dims hist_H_chks <- forM (map slugOp slugs) $ \op -> dPrimV "hist_H_chk" $ histSize op `divUp` sExt64 hist_S@@ -627,7 +627,7 @@ histo_sizes <- forM (zip slugs hist_H_chks) $ \(slug, hist_H_chk) -> do let histo_dims = tvExp hist_H_chk- : map toInt64Exp (shapeDims (histOpShape (slugOp slug)))+ : map pe64 (shapeDims (histOpShape (slugOp slug))) histo_size <- dPrimVE "histo_size" $ product histo_dims let group_hists_size =@@ -656,7 +656,7 @@ -- Set segment indices. zipWithM_ dPrimV_ segment_is $- unflattenIndex (map toInt64Exp segment_dims) $+ unflattenIndex (map pe64 segment_dims) $ sExt64 flat_segment_id histograms <- forM (zip init_histograms hist_H_chks) $@@ -694,7 +694,7 @@ local_subhisto_i <- dPrimVE "local_subhisto_i" $ j `quot` sExt32 histo_size let local_bucket_is = unflattenIndex histo_dims $ sExt64 $ j `rem` sExt32 histo_size nested_hist_size =- map toInt64Exp $ shapeDims $ histShape $ slugOp slug+ map pe64 $ shapeDims $ histShape $ slugOp slug global_bucket_is = unflattenIndex@@ -757,8 +757,8 @@ (bucket, vs') ) -> do let chk_beg = sExt64 chk_i * tvExp hist_H_chk- bucket' = map toInt64Exp bucket- dest_shape' = map toInt64Exp $ shapeDims dest_shape+ bucket' = map pe64 bucket+ dest_shape' = map pe64 $ shapeDims dest_shape flat_bucket = flattenIndex dest_shape' bucket' bucket_in_bounds = inBounds (Slice (map DimFix bucket')) dest_shape'@@ -785,7 +785,7 @@ histSize (slugOp slug) - sExt64 chk_i * head histo_dims let trunc_histo_dims = tvExp trunc_H- : map toInt64Exp (shapeDims (histOpShape (slugOp slug)))+ : map pe64 (shapeDims (histOpShape (slugOp slug))) trunc_histo_size <- dPrimVE "histo_size" $ sExt32 $ product trunc_histo_dims sFor "local_i" bins_per_thread $ \i -> do@@ -798,7 +798,7 @@ -- we immediately unflatten. let local_bucket_is = unflattenIndex histo_dims $ sExt64 j nested_hist_size =- map toInt64Exp $ shapeDims $ histShape $ slugOp slug+ map pe64 $ shapeDims $ histShape $ slugOp slug global_bucket_is = unflattenIndex nested_hist_size@@ -908,9 +908,9 @@ num_groups <- fmap (Imp.Count . tvSize) $ dPrimV "num_groups" $- sExt64 hist_T `divUp` toInt64Exp (unCount group_size)- let num_groups' = toInt64Exp <$> num_groups- group_size' = toInt64Exp <$> group_size+ sExt64 hist_T `divUp` pe64 (unCount group_size)+ let num_groups' = pe64 <$> num_groups+ group_size' = pe64 <$> group_size let r64 = isF64 . ConvOpExp (SIToFP Int64 Float64) . untyped t64 = isInt64 . ConvOpExp (FPToSI Float64 Int64) . untyped@@ -937,9 +937,9 @@ let q_small = 2 -- The number of segments/histograms produced..- hist_Nout <- dPrimVE "hist_Nout" $ product $ map toInt64Exp segment_dims+ hist_Nout <- dPrimVE "hist_Nout" $ product $ map pe64 segment_dims - hist_Nin <- dPrimVE "hist_Nin" $ toInt64Exp $ last space_sizes+ hist_Nin <- dPrimVE "hist_Nin" $ pe64 $ last space_sizes -- Maximum M for work efficiency. work_asymp_M_max <-@@ -1059,9 +1059,9 @@ -- rather figuring out whether to use a local or global memory -- strategy, as well as collapsing the subhistograms produced (which -- are always in global memory, but their number may vary).- let num_groups' = fmap toInt64Exp num_groups- group_size' = fmap toInt64Exp group_size- dims = map toInt64Exp $ segSpaceDims space+ let num_groups' = fmap pe64 num_groups+ group_size' = fmap pe64 group_size+ dims = map pe64 $ segSpaceDims space num_red_res = length ops + sum (map (length . histNeutral) ops) (all_red_pes, map_pes) = splitAt num_red_res pes@@ -1097,7 +1097,7 @@ hist_RF <- dPrimVE "hist_RF" $ sExt32 $- sum (map (toInt64Exp . histRaceFactor . slugOp) slugs)+ sum (map (pe64 . histRaceFactor . slugOp) slugs) `quot` genericLength slugs let hist_T = sExt32 $ unCount num_groups' * unCount group_size'@@ -1111,7 +1111,7 @@ Just $ untyped $ product $- map (toInt64Exp . snd) segment_dims+ map (pe64 . snd) segment_dims emit $ Imp.DebugPrint "Histogram element size (el_size)" $ Just $ untyped hist_el_size emit $ Imp.DebugPrint "Race factor (RF)" $ Just $ untyped hist_RF emit $ Imp.DebugPrint "Memory per set of subhistograms per segment" $ Just $ untyped h
src/Futhark/CodeGen/ImpGen/GPU/SegMap.hs view
@@ -24,8 +24,8 @@ CallKernelGen () compileSegMap pat lvl space kbody = do let (is, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims- group_size' = toInt64Exp <$> segGroupSize lvl+ dims' = map pe64 dims+ group_size' = pe64 <$> segGroupSize lvl attrs = defKernelAttrs (segNumGroups lvl) (segGroupSize lvl) emit $ Imp.DebugPrint "\n# SegMap" Nothing
src/Futhark/CodeGen/ImpGen/GPU/SegRed.hs view
@@ -109,8 +109,8 @@ | [(_, Constant (IntValue (Int64Value 1))), _] <- unSegSpace space = nonsegmentedReduction pat num_groups group_size space reds body | otherwise = do- let group_size' = toInt64Exp $ unCount group_size- segment_size = toInt64Exp $ last $ segSpaceDims space+ let group_size' = pe64 $ unCount group_size+ segment_size = pe64 $ last $ segSpaceDims space use_small_segments = segment_size * 2 .<. group_size' sIf use_small_segments@@ -182,9 +182,9 @@ CallKernelGen () nonsegmentedReduction segred_pat num_groups group_size space reds body = do let (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims- num_groups' = fmap toInt64Exp num_groups- group_size' = fmap toInt64Exp group_size+ dims' = map pe64 dims+ num_groups' = fmap pe64 num_groups+ group_size' = fmap pe64 group_size global_tid = Imp.le64 $ segFlat space w = last dims' @@ -266,15 +266,15 @@ CallKernelGen () smallSegmentsReduction (Pat segred_pes) num_groups group_size space reds body = do let (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims segment_size = last dims' -- Careful to avoid division by zero now. segment_size_nonzero <- dPrimVE "segment_size_nonzero" $ sMax64 1 segment_size - let num_groups' = fmap toInt64Exp num_groups- group_size' = fmap toInt64Exp group_size+ let num_groups' = fmap pe64 num_groups+ group_size' = fmap pe64 group_size num_threads <- dPrimV "num_threads" $ unCount num_groups' * unCount group_size' let num_segments = product $ init dims' segments_per_group = unCount group_size' `quot` segment_size_nonzero@@ -383,11 +383,11 @@ CallKernelGen () largeSegmentsReduction segred_pat num_groups group_size space reds body = do let (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims num_segments = product $ init dims' segment_size = last dims'- num_groups' = fmap toInt64Exp num_groups- group_size' = fmap toInt64Exp group_size+ num_groups' = fmap pe64 num_groups+ group_size' = fmap pe64 group_size (groups_per_segment, elems_per_thread) <- groupsPerSegmentAndElementsPerThread@@ -458,7 +458,7 @@ let first_group_for_segment = sExt64 flat_segment_id * groups_per_segment dIndexSpace (zip segment_gtids (init dims')) $ sExt64 flat_segment_id dPrim_ (last gtids) int64- let num_elements = Imp.elements $ toInt64Exp w+ let num_elements = Imp.elements $ pe64 w slugs <- mapM (segBinOpSlug local_tid group_id) $
src/Futhark/CodeGen/ImpGen/GPU/SegScan.hs view
@@ -64,4 +64,4 @@ SinglePass.compileSegScan pat lvl space scan' kbody _ -> TwoPass.compileSegScan pat lvl space scans kbody where- n = product $ map toInt64Exp $ segSpaceDims space+ n = product $ map pe64 $ segSpaceDims space
src/Futhark/CodeGen/ImpGen/GPU/SegScan/SinglePass.hs view
@@ -34,8 +34,8 @@ [PrimType] -> InKernelGen (VName, [VName], [VName], VName, [VName]) createLocalArrays (Count groupSize) m types = do- let groupSizeE = toInt64Exp groupSize- workSize = toInt64Exp m * groupSizeE+ let groupSizeE = pe64 groupSize+ workSize = pe64 m * groupSizeE prefixArraysSize = foldl (\acc tySize -> alignTo acc tySize + tySize * groupSizeE) 0 $ map primByteSize types@@ -55,7 +55,7 @@ byteOffsets <- mapM (fmap varTE . dPrimV "byte_offsets") $- scanl (\off tySize -> alignTo off tySize + toInt64Exp groupSize * tySize) 0 $+ scanl (\off tySize -> alignTo off tySize + pe64 groupSize * tySize) 0 $ map primByteSize types warpByteOffsets <-@@ -245,7 +245,7 @@ dPrimVE "num_threads" $ num_groups' * group_size' let (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims segmented = length dims' > 1 not_segmented_e = if segmented then false else true segment_size = last dims'
src/Futhark/CodeGen/ImpGen/GPU/SegScan/TwoPass.hs view
@@ -156,12 +156,12 @@ KernelBody GPUMem -> CallKernelGen (TV Int32, Imp.TExp Int64, CrossesSegment) scanStage1 (Pat all_pes) num_groups group_size space scans kbody = do- let num_groups' = fmap toInt64Exp num_groups- group_size' = fmap toInt64Exp group_size+ let num_groups' = fmap pe64 num_groups+ group_size' = fmap pe64 group_size num_threads <- dPrimV "num_threads" $ sExt32 $ unCount num_groups' * unCount group_size' let (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims let num_elements = product dims' elems_per_thread = num_elements `divUp` sExt64 (tvExp num_threads) elems_per_group = unCount group_size' * elems_per_thread@@ -332,7 +332,7 @@ CallKernelGen () scanStage2 (Pat all_pes) stage1_num_threads elems_per_group num_groups crossesSegment space scans = do let (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims -- Our group size is the number of groups for the stage 1 kernel. let group_size = Count $ unCount num_groups@@ -407,9 +407,9 @@ [SegBinOp GPUMem] -> CallKernelGen () scanStage3 (Pat all_pes) num_groups group_size elems_per_group crossesSegment space scans = do- let group_size' = fmap toInt64Exp group_size+ let group_size' = fmap pe64 group_size (gtids, dims) = unzip $ unSegSpace space- dims' = map toInt64Exp dims+ dims' = map pe64 dims required_groups <- dPrimVE "required_groups" $ sExt32 $@@ -502,7 +502,7 @@ fmap (Imp.Count . tvSize) $ dPrimV "stage1_num_groups" $ sMin64 (tvExp stage1_max_num_groups) $- toInt64Exp $+ pe64 $ Imp.unCount $ segNumGroups lvl
src/Futhark/CodeGen/ImpGen/Multicore.hs view
@@ -57,7 +57,7 @@ updateAcc :: VName -> [SubExp] -> [SubExp] -> MulticoreGen () updateAcc acc is vs = sComment "UpdateAcc" $ do -- See the ImpGen implementation of UpdateAcc for general notes.- let is' = map toInt64Exp is+ let is' = map pe64 is (c, _space, arrs, dims, op) <- lookupAcc acc is' sWhen (inBounds (Slice (map DimFix is')) dims) $ case op of
src/Futhark/CodeGen/ImpGen/Multicore/Base.hs view
@@ -96,11 +96,11 @@ getIterationDomain :: SegOp () MCMem -> SegSpace -> MulticoreGen (Imp.TExp Int64) getIterationDomain SegMap {} space = do let ns = map snd $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns pure $ product ns_64 getIterationDomain _ space = do let ns = map snd $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns case unSegSpace space of [_] -> pure $ product ns_64 -- A segmented SegOp is over the segments@@ -346,9 +346,9 @@ where sLoopNest' is [] f = f $ reverse is sLoopNest' is [d] f =- sForVectorized "nest_i" (toInt64Exp d) $ \i -> sLoopNest' (i : is) [] f+ sForVectorized "nest_i" (pe64 d) $ \i -> sLoopNest' (i : is) [] f sLoopNest' is (d : ds) f =- sFor "nest_i" (toInt64Exp d) $ \i -> sLoopNest' (i : is) ds f+ sFor "nest_i" (pe64 d) $ \i -> sLoopNest' (i : is) ds f ------------------------------- ------- SegHist helpers -------
src/Futhark/CodeGen/ImpGen/Multicore/SegHist.hs view
@@ -35,7 +35,7 @@ segHistOpChunks = chunks . map (length . histNeutral) histSize :: HistOp MCMem -> Imp.TExp Int64-histSize = product . map toInt64Exp . shapeDims . histShape+histSize = product . map pe64 . shapeDims . histShape genHistOpParams :: HistOp MCMem -> MulticoreGen () genHistOpParams histops =@@ -56,7 +56,7 @@ MulticoreGen Imp.MCCode nonsegmentedHist pat space histops kbody num_histos = do let ns = map snd $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns num_histos' = tvExp num_histos hist_width = histSize $ head histops use_subhistogram = sExt64 num_histos' * hist_width .<=. product ns_64@@ -94,7 +94,7 @@ -- Allocate a static array of locks -- as in the GPU backend let num_locks = 100151 -- This number is taken from the GPU backend- dims = map toInt64Exp $ shapeDims (histOpShape op <> histShape op)+ dims = map pe64 $ shapeDims (histOpShape op <> histShape op) locks <- sStaticArray "hist_locks" DefaultSpace int32 $ Imp.ArrayZeros num_locks@@ -109,7 +109,7 @@ MulticoreGen () atomicHistogram pat space histops kbody = do let (is, ns) = unzip $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns let num_red_res = length histops + sum (map (length . histNeutral) histops) (all_red_pes, map_pes) = splitAt num_red_res $ patElems pat @@ -129,8 +129,8 @@ forM_ (zip4 histops red_res_split atomicOps pes_per_op) $ \(HistOp dest_shape _ _ _ shape lam, (bucket, vs'), do_op, dest_res) -> do let (_is_params, vs_params) = splitAt (length vs') $ lambdaParams lam- dest_shape' = map toInt64Exp $ shapeDims dest_shape- bucket' = map toInt64Exp bucket+ dest_shape' = map pe64 $ shapeDims dest_shape+ bucket' = map pe64 bucket bucket_in_bounds = inBounds (Slice (map DimFix bucket')) dest_shape' sComment "save map-out results" $@@ -187,7 +187,7 @@ emit $ Imp.DebugPrint "subHistogram segHist" Nothing let (is, ns) = unzip $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns let pes = patElems pat num_red_res = length histops + sum (map (length . histNeutral) histops)@@ -247,8 +247,8 @@ ) -> do histop' <- renameHistop histop - let bucket' = map toInt64Exp bucket- dest_shape' = map toInt64Exp $ shapeDims dest_shape+ let bucket' = map pe64 bucket+ dest_shape' = map pe64 $ shapeDims dest_shape acc_params' = (lambdaParams . histOp) histop' vs_params' = takeLast (length vs') $ lambdaParams $ histOp histop' @@ -310,7 +310,7 @@ map fst segment_dims ++ [subhistogram_id] ++ bucket_ids ) - let ns_red = map (toInt64Exp . snd) $ unSegSpace segred_space+ let ns_red = map (pe64 . snd) $ unSegSpace segred_space iterations = product $ init ns_red -- The segmented reduction is sequential over the inner most dimension scheduler_info = Imp.SchedulerInfo (untyped iterations) Imp.Static red_task = Imp.ParallelTask red_code@@ -346,7 +346,7 @@ MulticoreGen Imp.MCCode compileSegHistBody pat space histops kbody = collect $ do let (is, ns) = unzip $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns let num_red_res = length histops + sum (map (length . histNeutral) histops) map_pes = drop num_red_res $ patElems pat@@ -369,8 +369,8 @@ forM_ (zip3 per_red_pes histops (splitHistResults histops red_res)) $ \(red_pes, HistOp dest_shape _ _ _ shape lam, (bucket, vs')) -> do let (is_params, vs_params) = splitAt (length vs') $ lambdaParams lam- bucket' = map toInt64Exp bucket- dest_shape' = map toInt64Exp $ shapeDims dest_shape+ bucket' = map pe64 bucket+ dest_shape' = map pe64 $ shapeDims dest_shape bucket_in_bounds = inBounds (Slice (map DimFix bucket')) dest_shape' sComment "save map-out results" $
src/Futhark/CodeGen/ImpGen/Multicore/SegMap.hs view
@@ -20,9 +20,9 @@ copyDWIMFix (patElemName pe) (map Imp.le64 is) se [] writeResult _ pe (WriteReturns _ (Shape rws) _ idx_vals) = do let (iss, vs) = unzip idx_vals- rws' = map toInt64Exp rws+ rws' = map pe64 rws forM_ (zip iss vs) $ \(slice, v) -> do- let slice' = fmap toInt64Exp slice+ let slice' = fmap pe64 slice sWhen (inBounds slice' rws') $ copyDWIM (patElemName pe) (unSlice slice') v [] writeResult _ _ res =@@ -35,7 +35,7 @@ MulticoreGen Imp.MCCode compileSegMapBody pat space (KernelBody _ kstms kres) = collect $ do let (is, ns) = unzip $ unSegSpace space- ns' = map toInt64Exp ns+ ns' = map pe64 ns dPrim_ (segFlat space) int64 sOp $ Imp.GetTaskId (segFlat space) kstms' <- mapM renameStm kstms
src/Futhark/CodeGen/ImpGen/Multicore/SegRed.hs view
@@ -210,7 +210,7 @@ MulticoreGen () genReductionLoop typ kbodymap slugs slug_local_accs space i = do let (is, ns) = unzip $ unSegSpace space- ns' = map toInt64Exp ns+ ns' = map pe64 ns zipWithM_ dPrimV_ is $ unflattenIndex ns' i kbodymap $ \all_red_res' -> do forM_ (zip3 all_red_res' slugs slug_local_accs) $ \(red_res, slug, local_accs) ->@@ -389,7 +389,7 @@ MulticoreGen Imp.MCCode compileSegRedBody pat space reds kbody = do let (is, ns) = unzip $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns inner_bound = last ns_64 dPrim_ (segFlat space) int64 sOp $ Imp.GetTaskId (segFlat space)
src/Futhark/CodeGen/ImpGen/Multicore/SegScan.hs view
@@ -142,7 +142,7 @@ per_scan_res = segBinOpChunks scan_ops all_scan_res per_scan_pes = segBinOpChunks scan_ops $ patElems pat let (is, ns) = unzip $ unSegSpace space- ns' = map toInt64Exp ns+ ns' = map pe64 ns zipWithM_ dPrimV_ is $ unflattenIndex ns' i compileStms mempty (kernelBodyStms kbody) $ do@@ -242,7 +242,7 @@ scanStage2 pat nsubtasks space scan_ops kbody = do emit $ Imp.DebugPrint "nonsegmentedScan stage 2" Nothing let (is, ns) = unzip $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns per_scan_pes = segBinOpChunks scan_ops $ patElems pat nsubtasks' = tvExp nsubtasks @@ -401,7 +401,7 @@ MulticoreGen Imp.MCCode compileSegScanBody pat space scan_ops kbody = collect $ do let (is, ns) = unzip $ unSegSpace space- ns_64 = map toInt64Exp ns+ ns_64 = map pe64 ns dPrim_ (segFlat space) int64 sOp $ Imp.GetTaskId (segFlat space)
src/Futhark/CodeGen/RTS/C.hs view
@@ -3,6 +3,8 @@ -- | Code snippets used by the C backends. module Futhark.CodeGen.RTS.C ( atomicsH,+ contextH,+ contextPrototypesH, cudaH, freeListH, halfH,@@ -118,3 +120,13 @@ cacheH :: T.Text cacheH = $(embedStringFile "rts/c/cache.h") {-# NOINLINE cacheH #-}++-- | @rts/c/context.h@+contextH :: T.Text+contextH = $(embedStringFile "rts/c/context.h")+{-# NOINLINE contextH #-}++-- | @rts/c/context_prototypes.h@+contextPrototypesH :: T.Text+contextPrototypesH = $(embedStringFile "rts/c/context_prototypes.h")+{-# NOINLINE contextPrototypesH #-}
src/Futhark/Construct.hs view
@@ -72,9 +72,12 @@ -- * Monadic expression builders eSubExp, eParam,+ eMatch',+ eMatch, eIf, eIf', eBinOp,+ eUnOp, eCmpOp, eConvOp, eSignum,@@ -85,6 +88,7 @@ eSliceArray, eBlank, eAll,+ eAny, eDimInBounds, eOutOfBounds, @@ -106,7 +110,6 @@ fullSliceNum, isFullSlice, sliceAt,- ifCommon, -- * Result types instantiateShapes,@@ -122,7 +125,7 @@ import Control.Monad.Identity import Control.Monad.State-import Data.List (sortOn)+import Data.List (foldl', sortOn, transpose) import qualified Data.Map.Strict as M import Futhark.Builder import Futhark.IR@@ -209,43 +212,76 @@ m (Exp (Rep m)) eParam = eSubExp . Var . paramName --- | Construct an 'If' expression from a monadic condition and monadic--- branches. 'eBody' might be convenient for constructing the--- branches.+removeRedundantScrutinees :: [SubExp] -> [Case b] -> ([SubExp], [Case b])+removeRedundantScrutinees ses cases =+ let (ses', vs) =+ unzip $ filter interesting $ zip ses $ transpose (map casePat cases)+ in (ses', zipWith Case (transpose vs) $ map caseBody cases)+ where+ interesting = any (/= Nothing) . snd++-- | As 'eMatch', but an 'MatchSort' can be given.+eMatch' ::+ (MonadBuilder m, BranchType (Rep m) ~ ExtType) =>+ [SubExp] ->+ [Case (m (Body (Rep m)))] ->+ m (Body (Rep m)) ->+ MatchSort ->+ m (Exp (Rep m))+eMatch' ses cases_m defbody_m sort = do+ cases <- mapM (traverse insertStmsM) cases_m+ defbody <- insertStmsM defbody_m+ ts <-+ foldl' generaliseExtTypes+ <$> bodyExtType defbody+ <*> mapM (bodyExtType . caseBody) cases+ cases' <- mapM (traverse $ addContextForBranch ts) cases+ defbody' <- addContextForBranch ts defbody+ let ts' = replicate (length (shapeContext ts)) (Prim int64) ++ ts+ (ses', cases'') = removeRedundantScrutinees ses cases'+ pure $ Match ses' cases'' defbody' $ MatchDec ts' sort+ where+ addContextForBranch ts (Body _ stms val_res) = do+ body_ts <- extendedScope (traverse subExpResType val_res) stmsscope+ let ctx_res =+ map snd $ sortOn fst $ M.toList $ shapeExtMapping ts body_ts+ mkBodyM stms $ subExpsRes ctx_res ++ val_res+ where+ stmsscope = scopeOf stms++-- | Construct a 'Match' expression. The main convenience here is+-- that the existential context of the return type is automatically+-- deduced, and the necessary elements added to the branches.+eMatch ::+ (MonadBuilder m, BranchType (Rep m) ~ ExtType) =>+ [SubExp] ->+ [Case (m (Body (Rep m)))] ->+ m (Body (Rep m)) ->+ m (Exp (Rep m))+eMatch ses cases_m defbody_m = eMatch' ses cases_m defbody_m MatchNormal++-- | Construct a 'Match' modelling an if-expression from a monadic+-- condition and monadic branches. 'eBody' might be convenient for+-- constructing the branches. eIf :: (MonadBuilder m, BranchType (Rep m) ~ ExtType) => m (Exp (Rep m)) -> m (Body (Rep m)) -> m (Body (Rep m)) -> m (Exp (Rep m))-eIf ce te fe = eIf' ce te fe IfNormal+eIf ce te fe = eIf' ce te fe MatchNormal --- | As 'eIf', but an 'IfSort' can be given.+-- | As 'eIf', but an 'MatchSort' can be given. eIf' :: (MonadBuilder m, BranchType (Rep m) ~ ExtType) => m (Exp (Rep m)) -> m (Body (Rep m)) -> m (Body (Rep m)) ->- IfSort ->+ MatchSort -> m (Exp (Rep m)) eIf' ce te fe if_sort = do ce' <- letSubExp "cond" =<< ce- te' <- insertStmsM te- fe' <- insertStmsM fe- -- We need to construct the context.- ts <- generaliseExtTypes <$> bodyExtType te' <*> bodyExtType fe'- te'' <- addContextForBranch ts te'- fe'' <- addContextForBranch ts fe'- let ts' = replicate (length (shapeContext ts)) (Prim int64) ++ ts- pure $ If ce' te'' fe'' $ IfDec ts' if_sort- where- addContextForBranch ts (Body _ stms val_res) = do- body_ts <- extendedScope (traverse subExpResType val_res) stmsscope- let ctx_res =- map snd $ sortOn fst $ M.toList $ shapeExtMapping ts body_ts- mkBodyM stms $ subExpsRes ctx_res ++ val_res- where- stmsscope = scopeOf stms+ eMatch' [ce'] [Case [Just $ BoolValue True] te] fe if_sort -- The type of a body. Watch out: this only works for the degenerate -- case where the body does not already return its context.@@ -268,6 +304,14 @@ y' <- letSubExp "y" =<< y pure $ BasicOp $ BinOp op x' y' +-- | Construct a v'UnOp' expression with the given operator.+eUnOp ::+ MonadBuilder m =>+ UnOp ->+ m (Exp (Rep m)) ->+ m (Exp (Rep m))+eUnOp op x = BasicOp . UnOp op <$> (letSubExp "x" =<< x)+ -- | Construct a v'CmpOp' expression with the given comparison. eCmpOp :: MonadBuilder m =>@@ -456,8 +500,15 @@ -- | True if all operands are true. eAll :: MonadBuilder m => [SubExp] -> m (Exp (Rep m)) eAll [] = pure $ BasicOp $ SubExp $ constant True+eAll [x] = eSubExp x eAll (x : xs) = foldBinOp LogAnd x xs +-- | True if any operand is true.+eAny :: MonadBuilder m => [SubExp] -> m (Exp (Rep m))+eAny [] = pure $ BasicOp $ SubExp $ constant False+eAny [x] = eSubExp x+eAny (x : xs) = foldBinOp LogOr x xs+ -- | Create a two-parameter lambda whose body applies the given binary -- operation to its arguments. It is assumed that both argument and -- result types are the same. (This assumption should be fixed at@@ -547,10 +598,6 @@ allOfIt (Constant v) DimFix {} = oneIsh v allOfIt d (DimSlice _ n _) = d == n allOfIt _ _ = False---- | Produce the common case of an 'IfDec'.-ifCommon :: [Type] -> IfDec ExtType-ifCommon ts = IfDec (staticShapes ts) IfNormal -- | Conveniently construct a body that contains no bindings. resultBody :: Buildable rep => [SubExp] -> Body rep
src/Futhark/IR/Aliases.hs view
@@ -393,4 +393,9 @@ let Body bodyrep _ _ = mkBody (fmap removeStmAliases stms) res in mkAliasedBody bodyrep stms res -instance (ASTRep (Aliases rep), Buildable (Aliases rep)) => BuilderOps (Aliases rep)+instance+ ( ASTRep rep,+ CanBeAliased (Op rep),+ Buildable (Aliases rep)+ ) =>+ BuilderOps (Aliases rep)
src/Futhark/IR/Mem.hs view
@@ -84,6 +84,7 @@ lookupMemInfo, subExpMemInfo, lookupArraySummary,+ lookupMemSpace, existentialiseIxFun, -- * Type checking parts@@ -778,12 +779,13 @@ ( length val_ts == length rt && and (zipWith (matches ctx_map_ids ctx_map_exts) val_ts rt) )- $ TC.bad- $ TC.TypeError- $ "Expression type:\n "- ++ prettyTuple rt- ++ "\ncannot match pattern type:\n "- ++ prettyTuple val_ts+ . TC.bad+ . TC.TypeError+ . pretty+ $ "Expression type:"+ </> indent 2 (ppTuple' rt)+ </> "cannot match pattern type:"+ </> indent 2 (ppTuple' val_ts) where matches _ _ (MemPrim x) (MemPrim y) = x == y matches _ _ (MemMem x_space) (MemMem y_space) =@@ -864,6 +866,22 @@ ++ " to be array but bound to:\n" ++ pretty summary +lookupMemSpace ::+ (Mem rep inner, HasScope rep m, Monad m) =>+ VName ->+ m Space+lookupMemSpace name = do+ summary <- lookupMemInfo name+ case summary of+ MemMem space ->+ pure space+ _ ->+ error $+ "Expected "+ ++ pretty name+ ++ " to be memory but bound to:\n"+ ++ pretty summary+ checkMemInfo :: TC.Checkable rep => VName ->@@ -996,26 +1014,25 @@ -- | The return information of an expression. This can be seen as the -- "return type with memory annotations" of the expression. expReturns ::- ( Monad m,- LocalScope rep m,- Mem rep inner- ) =>+ (LocalScope rep m, Mem rep inner) => Exp rep -> m [ExpReturns] expReturns (BasicOp (SubExp se)) = pure <$> subExpReturns se expReturns (BasicOp (Opaque _ (Var v))) = pure <$> varReturns v-expReturns (BasicOp (Reshape newshape v)) = do+expReturns (BasicOp (Reshape k newshape v)) = do (et, _, mem, ixfun) <- arrayVarReturns v pure- [ MemArray et (Shape $ map (Free . newDim) newshape) NoUniqueness $- Just $- ReturnsInBlock mem $- existentialiseIxFun [] $- IxFun.reshape ixfun $- map (fmap pe64) newshape+ [ MemArray et (fmap Free newshape) NoUniqueness $+ Just . ReturnsInBlock mem . existentialiseIxFun [] $+ reshaper ixfun $+ map pe64 (shapeDims newshape) ]+ where+ reshaper = case k of+ ReshapeArbitrary -> IxFun.reshape+ ReshapeCoerce -> IxFun.coerce expReturns (BasicOp (Rearrange perm v)) = do (et, Shape dims, mem, ixfun) <- arrayVarReturns v let ixfun' = IxFun.permute ixfun perm@@ -1026,16 +1043,6 @@ ReturnsInBlock mem $ existentialiseIxFun [] ixfun' ]-expReturns (BasicOp (Rotate offsets v)) = do- (et, Shape dims, mem, ixfun) <- arrayVarReturns v- let offsets' = map pe64 offsets- ixfun' = IxFun.rotate ixfun offsets'- pure- [ MemArray et (Shape $ map Free dims) NoUniqueness $- Just $- ReturnsInBlock mem $- existentialiseIxFun [] ixfun'- ] expReturns (BasicOp (Index v slice)) = do pure . varInfoToExpReturns <$> sliceInfo v slice expReturns (BasicOp (Update _ v _ _)) =@@ -1074,7 +1081,7 @@ mergevars = map fst merge expReturns (Apply _ _ ret _) = pure $ map funReturnsToExpReturns ret-expReturns (If _ _ _ (IfDec ret _)) =+expReturns (Match _ _ _ (MatchDec ret _)) = pure $ map bodyReturnsToExpReturns ret expReturns (Op op) = opReturns op
src/Futhark/IR/Mem/IxFun.hs view
@@ -6,15 +6,17 @@ -- linear-memory accessor descriptors; see Zhu, Hoeflinger and David work. module Futhark.IR.Mem.IxFun ( IxFun (..),+ Shape, LMAD (..), LMADDim (..), Monotonicity (..), index,+ mkExistential, iota, iotaOffset, permute,- rotate, reshape,+ coerce, slice, flatSlice, rebase,@@ -25,7 +27,6 @@ isDirect, isLinear, substituteInIxFun,- leastGeneralGeneralization, existentialize, closeEnough, equivalent,@@ -38,21 +39,18 @@ import Control.Monad.State import Control.Monad.Writer import Data.Function (on, (&))-import Data.List (sort, sortBy, zip4, zip5, zipWith5)+import Data.List (sort, sortBy, zip4, zipWith4) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M import Data.Maybe (isJust) import Futhark.Analysis.PrimExp import Futhark.Analysis.PrimExp.Convert (substituteInPrimExp)-import qualified Futhark.Analysis.PrimExp.Generalize as PEG import Futhark.IR.Prop import Futhark.IR.Syntax- ( DimChange (..),- DimIndex (..),+ ( DimIndex (..), FlatDimIndex (..), FlatSlice (..),- ShapeChange, Slice (..), dimFix, flatSliceDims,@@ -66,6 +64,7 @@ import Futhark.Util.Pretty import Prelude hiding (id, mod, (.)) +-- | The shape of an index function. type Shape num = [num] type Indices num = [num]@@ -86,7 +85,6 @@ -- | A single dimension in an 'LMAD'. data LMADDim num = LMADDim { ldStride :: num,- ldRotate :: num, ldShape :: num, ldPerm :: Int, ldMon :: Monotonicity@@ -94,7 +92,7 @@ deriving (Show, Eq) -- | LMAD's representation consists of a general offset and for each dimension a--- stride, rotate factor, number of elements (or shape), permutation, and+-- stride, number of elements (or shape), permutation, and -- monotonicity. Note that the permutation is not strictly necessary in that the -- permutation can be performed directly on LMAD dimensions, but then it is -- difficult to extract the permutation back from an LMAD.@@ -137,7 +135,7 @@ { ixfunLMADs :: NonEmpty (LMAD num), base :: Shape num, -- | ignoring permutations, is the index function contiguous?- ixfunContig :: Bool+ contiguous :: Bool } deriving (Show, Eq) @@ -150,7 +148,6 @@ semisep [ "offset: " <> oneLine (ppr offset), "strides: " <> p ldStride,- "rotates: " <> p ldRotate, "shape: " <> p ldShape, "permutation: " <> p ldPerm, "monotonicity: " <> p ldMon@@ -201,9 +198,10 @@ traverse f (LMAD offset dims) = LMAD <$> f offset <*> traverse f' dims where- f' (LMADDim s r n p m) =- LMADDim <$> f s <*> f r <*> f n <*> pure p <*> pure m+ f' (LMADDim s n p m) = LMADDim <$> f s <*> f n <*> pure p <*> pure m +-- It is important that the traversal order here is the same as in+-- mkExistential. instance Traversable IxFun where traverse f (IxFun lmads oshp cg) = IxFun <$> traverse (traverse f) lmads <*> traverse f oshp <*> pure cg@@ -241,10 +239,9 @@ let offset' = substituteInPrimExp tab offset dims' = map- ( \(LMADDim s r n p m) ->+ ( \(LMADDim s n p m) -> LMADDim (substituteInPrimExp tab s)- (substituteInPrimExp tab r) (substituteInPrimExp tab n) p m@@ -274,9 +271,7 @@ && length oshp == length dims && offset == 0 && all- ( \(LMADDim s r n p _, m, d, se) ->- s == se && r == 0 && n == d && p == m- )+ (\(LMADDim s n p _, m, d, se) -> s == se && n == d && p == m) (zip4 dims [0 .. length dims - 1] oshp strides_expected) isDirect _ = False @@ -330,20 +325,31 @@ sum $ zipWith flatOneDim- (map (\(LMADDim s r n _ _) -> (s, r, n)) dims)+ (map ldStride dims) (permuteInv (lmadPermutation lmad) inds) in off + prod -- | iota with offset. iotaOffset :: IntegralExp num => num -> Shape num -> IxFun num-iotaOffset o ns =- let rs = replicate (length ns) 0- in IxFun (makeRotIota Inc o (zip rs ns) :| []) ns True+iotaOffset o ns = IxFun (makeRotIota Inc o ns :| []) ns True -- | iota. iota :: IntegralExp num => Shape num -> IxFun num iota = iotaOffset 0 +-- | Create a contiguous single-LMAD index function that is+-- existential in everything, with the provided permutation,+-- monotonicity, and contiguousness.+mkExistential :: Int -> [(Int, Monotonicity)] -> Bool -> Int -> IxFun (Ext a)+mkExistential basis_rank perm contig start =+ IxFun (NE.singleton lmad) basis contig+ where+ basis = take basis_rank $ map Ext [start + 1 + dims_rank * 2 ..]+ dims_rank = length perm+ lmad = LMAD (Ext start) $ zipWith onDim perm [0 ..]+ onDim (p, mon) i =+ LMADDim (Ext (start + 1 + i * 2)) (Ext (start + 2 + i * 2)) p mon+ -- | Permute dimensions. permute :: IntegralExp num =>@@ -355,24 +361,6 @@ perm = map (perm_cur !!) perm_new in IxFun (setLMADPermutation perm lmad :| lmads) oshp cg --- | Rotate an index function.-rotate ::- (Eq num, IntegralExp num) =>- IxFun num ->- Indices num ->- IxFun num-rotate (IxFun (lmad@(LMAD off dims) :| lmads) oshp cg) offs =- let dims' =- zipWith- ( \(LMADDim s r n p f) o ->- if s == 0- then LMADDim 0 0 n p Unknown- else LMADDim s (r + o) n p f- )- dims- (permuteInv (lmadPermutation lmad) offs)- in IxFun (LMAD off dims' :| lmads) oshp cg- -- | Handle the case where a slice can stay within a single LMAD. sliceOneLMAD :: (Eq num, IntegralExp num) =>@@ -383,7 +371,6 @@ let perm = lmadPermutation lmad is' = permuteInv perm is cg' = cg && slicePreservesContiguous lmad (Slice is')- guard $ harmlessRotation lmad (Slice is') let lmad' = foldl sliceOne (LMAD (lmadOffset lmad) []) $ zip is' ldims -- need to remove the fixed dims from the permutation perm' =@@ -405,28 +392,6 @@ d = foldl f 0 inds in [p - d | d /= -1] - harmlessRotation' ::- (Eq num, IntegralExp num) =>- LMADDim num ->- DimIndex num ->- Bool- harmlessRotation' _ (DimFix _) = True- harmlessRotation' (LMADDim 0 _ _ _ _) _ = True- harmlessRotation' (LMADDim _ 0 _ _ _) _ = True- harmlessRotation' (LMADDim _ _ n _ _) dslc- | dslc == DimSlice (n - 1) n (-1)- || dslc == unitSlice 0 n =- True- harmlessRotation' _ _ = False-- harmlessRotation ::- (Eq num, IntegralExp num) =>- LMAD num ->- Slice num ->- Bool- harmlessRotation (LMAD _ dims) (Slice iss) =- and $ zipWith harmlessRotation' dims iss- -- XXX: TODO: what happens to r on a negative-stride slice; is there -- such a case? sliceOne ::@@ -434,26 +399,24 @@ LMAD num -> (DimIndex num, LMADDim num) -> LMAD num- sliceOne (LMAD off dims) (DimFix i, LMADDim s r n _ _) =- LMAD (off + flatOneDim (s, r, n) i) dims- sliceOne (LMAD off dims) (DimSlice _ ne _, LMADDim 0 _ _ p _) =- LMAD off (dims ++ [LMADDim 0 0 ne p Unknown])- sliceOne (LMAD off dims) (dmind, dim@(LMADDim _ _ n _ _))+ sliceOne (LMAD off dims) (DimFix i, LMADDim s _x _ _) =+ LMAD (off + flatOneDim s i) dims+ sliceOne (LMAD off dims) (DimSlice _ ne _, LMADDim 0 _ p _) =+ LMAD off (dims ++ [LMADDim 0 ne p Unknown])+ sliceOne (LMAD off dims) (dmind, dim@(LMADDim _ n _ _)) | dmind == unitSlice 0 n = LMAD off (dims ++ [dim])- sliceOne (LMAD off dims) (dmind, LMADDim s r n p m)+ sliceOne (LMAD off dims) (dmind, LMADDim s n p m) | dmind == DimSlice (n - 1) n (-1) =- let r' = if r == 0 then 0 else n - r- off' = off + flatOneDim (s, 0, n) (n - 1)- in LMAD off' (dims ++ [LMADDim (s * (-1)) r' n p (invertMonotonicity m)])- sliceOne (LMAD off dims) (DimSlice b ne 0, LMADDim s r n p _) =- LMAD (off + flatOneDim (s, r, n) b) (dims ++ [LMADDim 0 0 ne p Unknown])- sliceOne (LMAD off dims) (DimSlice bs ns ss, LMADDim s 0 _ p m) =+ let off' = off + flatOneDim s (n - 1)+ in LMAD off' (dims ++ [LMADDim (s * (-1)) n p (invertMonotonicity m)])+ sliceOne (LMAD off dims) (DimSlice b ne 0, LMADDim s _ p _) =+ LMAD (off + flatOneDim s b) (dims ++ [LMADDim 0 ne p Unknown])+ sliceOne (LMAD off dims) (DimSlice bs ns ss, LMADDim s _ p m) = let m' = case sgn ss of Just 1 -> m Just (-1) -> invertMonotonicity m _ -> Unknown- in LMAD (off + s * bs) (dims ++ [LMADDim (ss * s) 0 ns p m'])- sliceOne _ _ = error "slice: reached impossible case"+ in LMAD (off + s * bs) (dims ++ [LMADDim (ss * s) ns p m']) slicePreservesContiguous :: (Eq num, IntegralExp num) =>@@ -473,17 +436,17 @@ map normIndex slc -- Check that: -- 1. a clean split point exists between Fixed and Sliced dims- -- 2. the outermost sliced dim has +/- 1 stride AND is unrotated or full.+ -- 2. the outermost sliced dim has +/- 1 stride. -- 3. the rest of inner sliced dims are full. (_, success) = foldl- ( \(found, res) (slcdim, LMADDim _ r n _ _) ->+ ( \(found, res) (slcdim, LMADDim _ n _ _) -> case (slcdim, found) of (DimFix {}, True) -> (found, False) (DimFix {}, False) -> (found, res)- (DimSlice _ ne ds, False) ->+ (DimSlice _ _ ds, False) -> -- outermost sliced dim: +/-1 stride- let res' = (r == 0 || n == ne) && (ds == 1 || ds == -1)+ let res' = (ds == 1 || ds == -1) in (True, res && res') (DimSlice _ ne ds, True) -> -- inner sliced dim: needs to be full@@ -525,54 +488,27 @@ FlatSlice num -> IxFun num flatSlice ixfun@(IxFun (LMAD offset (dim : dims) :| lmads) oshp cg) (FlatSlice new_offset is)- | hasContiguousPerm ixfun,- ldRotate dim == 0 =+ | hasContiguousPerm ixfun = let lmad = LMAD (offset + new_offset * ldStride dim)- ( map (helper $ ldStride dim) is- <> dims- )+ (map (helper $ ldStride dim) is <> dims) & setLMADPermutation [0 ..] in IxFun (lmad :| lmads) oshp cg where helper s0 (FlatDimIndex n s) = let new_mon = if s0 * s == 1 then Inc else Unknown- in LMADDim (s0 * s) 0 n 0 new_mon+ in LMADDim (s0 * s) n 0 new_mon flatSlice (IxFun (lmad :| lmads) oshp cg) s@(FlatSlice new_offset _) = IxFun (LMAD (new_offset * base_stride) (new_dims <> tail_dims) :| lmad : lmads) oshp cg where tail_shapes = tail $ lmadShape lmad base_stride = product tail_shapes tail_strides = tail $ scanr (*) 1 tail_shapes- tail_dims = zipWith5 LMADDim tail_strides (repeat 0) tail_shapes [length new_shapes ..] (repeat Inc)+ tail_dims = zipWith4 LMADDim tail_strides tail_shapes [length new_shapes ..] (repeat Inc) new_shapes = flatSliceDims s new_strides = map (* base_stride) $ flatSliceStrides s- new_dims = zipWith5 LMADDim new_strides (repeat 0) new_shapes [0 ..] (repeat Inc)---- | Handle the simple case where all reshape dimensions are coercions.-reshapeCoercion ::- (Eq num, IntegralExp num) =>- IxFun num ->- ShapeChange num ->- Maybe (IxFun num)-reshapeCoercion (IxFun (lmad@(LMAD off dims) :| lmads) oldbase cg) newshape = do- let perm = lmadPermutation lmad- (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape- let hd_len = length head_coercions- num_coercions = hd_len + length tail_coercions- dims' = permuteFwd perm dims- mid_dims = take (length dims - num_coercions) $ drop hd_len dims'- num_rshps = length reshapes- guard (num_rshps == 0 || (num_rshps == 1 && length mid_dims == 1))- let dims'' =- permuteInv perm $- zipWith- (\ld n -> ld {ldShape = n})- dims'- (newDims newshape)- lmad' = LMAD off dims''- pure $ IxFun (lmad' :| lmads) oldbase cg+ new_dims = zipWith4 LMADDim new_strides new_shapes [0 ..] (repeat Inc) -- | Handle the case where a reshape operation can stay inside a single LMAD. --@@ -583,9 +519,7 @@ -- the LMAD dimensions that were *not* reshape coercions. -- (2) the repetition of dimensions of the underlying LMAD must -- refer only to the coerced-dimensions of the reshape operation.--- (3) similarly, the rotated dimensions must refer only to--- dimensions that are coerced by the reshape operation.--- (4) finally, the underlying memory is contiguous (and monotonous).+-- (3) finally, the underlying memory is contiguous (and monotonous). -- -- If any of these conditions do not hold, then the reshape operation will -- conservatively add a new LMAD to the list, leading to a representation that@@ -593,79 +527,51 @@ reshapeOneLMAD :: (Eq num, IntegralExp num) => IxFun num ->- ShapeChange num ->+ Shape num -> Maybe (IxFun num) reshapeOneLMAD ixfun@(IxFun (lmad@(LMAD off dims) :| lmads) oldbase cg) newshape = do let perm = lmadPermutation lmad- (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape- let hd_len = length head_coercions- num_coercions = hd_len + length tail_coercions dims_perm = permuteFwd perm dims- mid_dims = take (length dims - num_coercions) $ drop hd_len dims_perm- -- Ignore rotates, as we only care about not having rotates in the- -- dimensions that aren't coercions (@mid_dims@), which we check- -- separately.- mon = ixfunMonotonicityRots True ixfun+ mid_dims = take (length dims) dims_perm+ mon = ixfunMonotonicity ixfun guard $- -- checking conditions (2) and (3)- all (\(LMADDim s r _ _ _) -> s /= 0 && r == 0) mid_dims+ -- checking conditions (2)+ all (\(LMADDim s _ _ _) -> s /= 0) mid_dims && -- checking condition (1)- consecutive hd_len (map ldPerm mid_dims)+ consecutive 0 (map ldPerm mid_dims) &&- -- checking condition (4)+ -- checking condition (3) hasContiguousPerm ixfun && cg && (mon == Inc || mon == Dec) -- make new permutation- let rsh_len = length reshapes+ let rsh_len = length newshape diff = length newshape - length dims iota_shape = [0 .. length newshape - 1] perm' = map ( \i ->- let ind =- if i < hd_len- then i- else i - diff- in if (i >= hd_len) && (i < hd_len + rsh_len)+ let ind = i - diff+ in if (i >= 0) && (i < rsh_len) then i -- already checked mid_dims not affected- else- let p = ldPerm (dims !! ind)- in if p < hd_len- then p- else p + diff+ else ldPerm (dims !! ind) + diff ) iota_shape -- split the dimensions (support_inds, repeat_inds) = foldl- ( \(sup, rpt) (i, shpdim, ip) ->- case (i < hd_len, i >= hd_len + rsh_len, shpdim) of- (True, _, DimCoercion n) ->- case dims_perm !! i of- (LMADDim 0 _ _ _ _) -> (sup, (ip, n) : rpt)- (LMADDim _ r _ _ _) -> ((ip, (r, n)) : sup, rpt)- (_, True, DimCoercion n) ->- case dims_perm !! (i - diff) of- (LMADDim 0 _ _ _ _) -> (sup, (ip, n) : rpt)- (LMADDim _ r _ _ _) -> ((ip, (r, n)) : sup, rpt)- (False, False, _) ->- ((ip, (0, newDim shpdim)) : sup, rpt)- -- already checked that the reshaped- -- dims cannot be rotates- _ -> error "reshape: reached impossible case"- )+ (\(sup, rpt) (shpdim, ip) -> ((ip, shpdim) : sup, rpt)) ([], []) $ reverse- $ zip3 iota_shape newshape perm'+ $ zip newshape perm' (sup_inds, support) = unzip $ sortBy (compare `on` fst) support_inds (rpt_inds, repeats) = unzip repeat_inds LMAD off' dims_sup = makeRotIota mon off support- repeats' = map (\n -> LMADDim 0 0 n 0 Unknown) repeats+ repeats' = map (\n -> LMADDim 0 n 0 Unknown) repeats dims' = map snd $ sortBy (compare `on` fst) $@@ -677,33 +583,32 @@ consecutive i [p] = i == p consecutive i ps = and $ zipWith (==) ps [i, i + 1 ..] -splitCoercions ::- (Eq num, IntegralExp num) =>- ShapeChange num ->- Maybe (ShapeChange num, ShapeChange num, ShapeChange num)-splitCoercions newshape' = do- let (head_coercions, newshape'') = span isCoercion newshape'- (reshapes, tail_coercions) = break isCoercion newshape''- guard (all isCoercion tail_coercions)- pure (head_coercions, reshapes, tail_coercions)- where- isCoercion DimCoercion {} = True- isCoercion _ = False- -- | Reshape an index function. reshape :: (Eq num, IntegralExp num) => IxFun num ->- ShapeChange num ->+ Shape num -> IxFun num reshape ixfun new_shape- | Just ixfun' <- reshapeCoercion ixfun new_shape = ixfun' | Just ixfun' <- reshapeOneLMAD ixfun new_shape = ixfun' reshape (IxFun (lmad0 :| lmad0s) oshp cg) new_shape =- case iota (newDims new_shape) of+ case iota new_shape of IxFun (lmad :| []) _ _ -> IxFun (lmad :| lmad0 : lmad0s) oshp cg _ -> error "reshape: reached impossible case" +-- | Coerce an index function to look like it has a new shape.+-- Dynamically the shape must be the same.+coerce ::+ (Eq num, IntegralExp num) =>+ IxFun num ->+ Shape num ->+ IxFun num+coerce (IxFun (lmad :| lmads) oshp cg) new_shape =+ IxFun (onLMAD lmad :| lmads) oshp cg+ where+ onLMAD (LMAD offset dims) = LMAD offset $ zipWith onDim dims new_shape+ onDim ld d = ld {ldShape = d}+ -- | The number of dimensions in the domain of the input function. rank :: IntegralExp num =>@@ -788,16 +693,11 @@ (dims_base', offs_contrib) = unzip $ zipWith- ( \(LMADDim s1 r1 n1 p1 _) (LMADDim _ r2 _ _ m2) ->+ ( \(LMADDim s1 n1 p1 _) (LMADDim _ _ _ m2) -> let (s', off') | m2 == Inc = (s1, 0) | otherwise = (s1 * (-1), s1 * (n1 - 1))- r'- | m2 == Inc = if r2 == 0 then r1 else r1 + r2- | r1 == 0 = r2- | r2 == 0 = n1 - r1- | otherwise = n1 - r1 + r2- in (LMADDim s' r' n1 (p1 - n_fewer_dims) Inc, off')+ in (LMADDim s' n1 (p1 - n_fewer_dims) Inc, off') ) -- If @dims@ is morally a slice, it might have fewer dimensions than -- @dims_base@. Drop extraneous outer dimensions.@@ -835,13 +735,10 @@ if base ixfun == shape new_base then (lmads_base, shp_base) else- let IxFun lmads' shp_base'' _ = reshape new_base $ map DimCoercion shp+ let IxFun lmads' shp_base'' _ = reshape new_base shp in (lmads', shp_base'') in IxFun (lmads @++@ lmads_base') shp_base' (cg && cg_base) -ixfunMonotonicity :: (Eq num, IntegralExp num) => IxFun num -> Monotonicity-ixfunMonotonicity = ixfunMonotonicityRots False- -- | If the memory support of the index function is contiguous and row-major -- (i.e., no transpositions, repetitions, rotates, etc.), then this should -- return the offset from which the memory-support of this index function@@ -888,13 +785,12 @@ flatOneDim :: (Eq num, IntegralExp num) =>- (num, num, num) -> num ->+ num -> num-flatOneDim (s, r, n) i+flatOneDim s i | s == 0 = 0- | r == 0 = i * s- | otherwise = ((i + r) `mod` n) * s+ | otherwise = i * s -- | Generalised iota with user-specified offset and rotates. makeRotIota ::@@ -902,13 +798,12 @@ Monotonicity -> -- | Offset num ->- -- | Pairs of shape and rotation- [(num, num)] ->+ -- | Shape+ [num] -> LMAD num-makeRotIota mon off support+makeRotIota mon off ns | mon == Inc || mon == Dec =- let rk = length support- (rs, ns) = unzip support+ let rk = length ns ss0 = reverse $ take rk $ scanl (*) 1 $ reverse ns ss = if mon == Inc@@ -916,16 +811,15 @@ else map (* (-1)) ss0 ps = map fromIntegral [0 .. rk - 1] fi = replicate rk mon- in LMAD off $ zipWith5 LMADDim ss rs ns ps fi+ in LMAD off $ zipWith4 LMADDim ss ns ps fi | otherwise = error "makeRotIota: requires Inc or Dec" -- | Check monotonicity of an index function.-ixfunMonotonicityRots ::+ixfunMonotonicity :: (Eq num, IntegralExp num) =>- Bool -> IxFun num -> Monotonicity-ixfunMonotonicityRots ignore_rots (IxFun (lmad :| lmads) _ _) =+ixfunMonotonicity (IxFun (lmad :| lmads) _ _) = let mon0 = lmadMonotonicityRots lmad in if all ((== mon0) . lmadMonotonicityRots) lmads then mon0@@ -945,93 +839,19 @@ Monotonicity -> LMADDim num -> Bool- isMonDim mon (LMADDim s r _ _ ldmon) =- s == 0 || ((ignore_rots || r == 0) && mon == ldmon)---- | Generalization (anti-unification)------ Anti-unification of two index functions is supported under the following conditions:--- 0. Both index functions are represented by ONE lmad (assumed common case!)--- 1. The support array of the two indexfuns have the same dimensionality--- (we can relax this condition if we use a 1D support, as we probably should!)--- 2. The contiguous property and the per-dimension monotonicity are the same--- (otherwise we might loose important information; this can be relaxed!)--- 3. Most importantly, both index functions correspond to the same permutation--- (since the permutation is represented by INTs, this restriction cannot--- be relaxed, unless we move to a gated-LMAD representation!)-leastGeneralGeneralization ::- Eq v =>- IxFun (PrimExp v) ->- IxFun (PrimExp v) ->- Maybe (IxFun (PrimExp (Ext v)), [(PrimExp v, PrimExp v)])-leastGeneralGeneralization (IxFun (lmad1 :| []) oshp1 ctg1) (IxFun (lmad2 :| []) oshp2 ctg2) = do- guard $- length oshp1 == length oshp2- && ctg1 == ctg2- && map ldPerm (lmadDims lmad1) == map ldPerm (lmadDims lmad2)- && lmadDMon lmad1 == lmadDMon lmad2- let (ctg, dperm, dmon) = (ctg1, lmadPermutation lmad1, lmadDMon lmad1)- (dshp, m1) <- generalize [] (lmadDShp lmad1) (lmadDShp lmad2)- (oshp, m2) <- generalize m1 oshp1 oshp2- (dstd, m3) <- generalize m2 (lmadDSrd lmad1) (lmadDSrd lmad2)- (drot, m4) <- generalize m3 (lmadDRot lmad1) (lmadDRot lmad2)- let (offt, m5) = PEG.leastGeneralGeneralization m4 (lmadOffset lmad1) (lmadOffset lmad2)- let lmad_dims =- map (\(a, b, c, d, e) -> LMADDim a b c d e) $- zip5 dstd drot dshp dperm dmon- lmad = LMAD offt lmad_dims- pure (IxFun (lmad :| []) oshp ctg, m5)- where- lmadDMon = map ldMon . lmadDims- lmadDSrd = map ldStride . lmadDims- lmadDShp = map ldShape . lmadDims- lmadDRot = map ldRotate . lmadDims- generalize m l1 l2 =- foldM- ( \(l_acc, m') (pe1, pe2) -> do- let (e, m'') = PEG.leastGeneralGeneralization m' pe1 pe2- pure (l_acc ++ [e], m'')- )- ([], m)- (zip l1 l2)-leastGeneralGeneralization _ _ = Nothing--isSequential :: [Int] -> Bool-isSequential xs =- all (uncurry (==)) $ zip xs [0 ..]--existentializeExp :: TPrimExp t v -> State [TPrimExp t v] (TPrimExp t (Ext v))-existentializeExp e = do- i <- gets length- modify (++ [e])- let t = primExpType $ untyped e- pure $ TPrimExp $ LeafExp (Ext i) t+ isMonDim mon (LMADDim s _ _ ldmon) =+ s == 0 || mon == ldmon --- | Try to turn all the leaves of the index function into 'Ext's. We--- require that there's only one LMAD, that the index function is--- contiguous, and the base shape has only one dimension.+-- | Turn all the leaves of the index function into 'Ext's. existentialize ::- (IntExp t, Eq v, Pretty v) =>- IxFun (TPrimExp t v) ->- State [TPrimExp t v] (Maybe (IxFun (TPrimExp t (Ext v))))-existentialize (IxFun (lmad :| []) oshp True)- | all ((== 0) . ldRotate) (lmadDims lmad),- length (lmadShape lmad) == length oshp,- isSequential (map ldPerm $ lmadDims lmad) = do- oshp' <- mapM existentializeExp oshp- lmadOffset' <- existentializeExp $ lmadOffset lmad- lmadDims' <- mapM existentializeLMADDim $ lmadDims lmad- let lmad' = LMAD lmadOffset' lmadDims'- pure $ Just $ IxFun (lmad' :| []) oshp' True+ IxFun (TPrimExp Int64 a) ->+ IxFun (TPrimExp Int64 (Ext b))+existentialize ixfun = evalState (traverse (const mkExt) ixfun) 0 where- existentializeLMADDim ::- LMADDim (TPrimExp t v) ->- State [TPrimExp t v] (LMADDim (TPrimExp t (Ext v)))- existentializeLMADDim (LMADDim str rot shp perm mon) = do- stride' <- existentializeExp str- shape' <- existentializeExp shp- pure $ LMADDim stride' (fmap Free rot) shape' perm mon-existentialize _ = pure Nothing+ mkExt = do+ i <- get+ put $ i + 1+ pure $ TPrimExp $ LeafExp (Ext i) int64 -- | When comparing index functions as part of the type check in KernelsMem, -- we may run into problems caused by the simplifier. As index functions can be@@ -1048,6 +868,8 @@ (length (base ixf1) == length (base ixf2)) && (NE.length (ixfunLMADs ixf1) == NE.length (ixfunLMADs ixf2)) && all closeEnoughLMADs (NE.zip (ixfunLMADs ixf1) (ixfunLMADs ixf2))+ -- This treats ixf1 as the "declared type" that we are matching against.+ && (contiguous ixf1 <= contiguous ixf2) where closeEnoughLMADs :: (LMAD num, LMAD num) -> Bool closeEnoughLMADs (lmad1, lmad2) =@@ -1072,8 +894,6 @@ == lmadOffset lmad2 && map ldStride (lmadDims lmad1) == map ldStride (lmadDims lmad2)- && map ldRotate (lmadDims lmad1)- == map ldRotate (lmadDims lmad2) -- | Dynamically determine if two 'LMADDim' are equal. --@@ -1081,7 +901,6 @@ dynamicEqualsLMADDim :: Eq num => LMADDim (TPrimExp t num) -> LMADDim (TPrimExp t num) -> TPrimExp Bool num dynamicEqualsLMADDim dim1 dim2 = ldStride dim1 .==. ldStride dim2- .&&. ldRotate dim1 .==. ldRotate dim2 .&&. ldShape dim1 .==. ldShape dim2 .&&. fromBool (ldPerm dim1 == ldPerm dim2) .&&. fromBool (ldMon dim1 == ldMon dim2)
src/Futhark/IR/Mem/Simplify.hs view
@@ -101,6 +101,7 @@ BodyDec rep ~ (), CanBeWise (Op rep), BuilderOps (Wise rep),+ OpReturns (OpWithWisdom inner), Mem rep inner ) @@ -109,7 +110,7 @@ standardRules <> ruleBook [ RuleBasicOp copyCopyToCopy,- RuleIf unExistentialiseMemory,+ RuleMatch unExistentialiseMemory, RuleOp decertifySafeAlloc ] []@@ -118,8 +119,8 @@ -- the array is not existential, and the index function of the array -- does not refer to any names in the pattern, then we can create a -- block of the proper size and always return there.-unExistentialiseMemory :: SimplifyMemory rep inner => TopDownRuleIf (Wise rep)-unExistentialiseMemory vtable pat _ (cond, tbranch, fbranch, ifdec)+unExistentialiseMemory :: SimplifyMemory rep inner => TopDownRuleMatch (Wise rep)+unExistentialiseMemory vtable pat _ (cond, cases, defbody, ifdec) | ST.simplifyMemory vtable, fixable <- foldl hasConcretisableMemory mempty $ patElems pat, not $ null fixable = Simplify $ do@@ -149,9 +150,9 @@ pure $ SubExpRes cs $ Var mem updateResult _ se = pure se- tbranch' <- updateBody tbranch- fbranch' <- updateBody fbranch- letBind pat $ If cond tbranch' fbranch' ifdec+ cases' <- mapM (traverse updateBody) cases+ defbody' <- updateBody defbody+ letBind pat $ Match cond cases' defbody' ifdec where onlyUsedIn name here = not . any ((name `nameIn`) . freeIn) . filter ((/= here) . patElemName) $@@ -167,13 +168,13 @@ <$> find ((mem ==) . patElemName . snd) (zip [(0 :: Int) ..] $ patElems pat),- Just tse <- maybeNth j $ bodyResult tbranch,- Just fse <- maybeNth j $ bodyResult fbranch,+ Just cases_ses <- mapM (maybeNth j . bodyResult . caseBody) cases,+ Just defbody_se <- maybeNth j $ bodyResult defbody, mem `onlyUsedIn` patElemName pat_elem, length (IxFun.base ixfun) == shapeRank shape, -- See #1325 all knownSize (shapeDims shape), not $ freeIn ixfun `namesIntersect` namesFromList (patNames pat),- fse /= tse =+ any (defbody_se /=) cases_ses = let mem_size = untyped $ product $ primByteSize pt : map sExt64 (IxFun.base ixfun) in (pat_elem, mem_size, mem, space) : fixable
src/Futhark/IR/Parse.hs view
@@ -16,7 +16,7 @@ import Data.Char (isAlpha) import Data.Functor-import Data.List (zipWith5)+import Data.List (zipWith4) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE import qualified Data.Set as S@@ -238,15 +238,6 @@ pErrorLoc :: Parser (SrcLoc, [SrcLoc]) pErrorLoc = (,mempty) <$> pSrcLoc -pShapeChange :: Parser (ShapeChange SubExp)-pShapeChange = parens $ pDimChange `sepBy` pComma- where- pDimChange =- choice- [ "~" $> DimCoercion <*> pSubExp,- DimNew <$> pSubExp- ]- pIota :: Parser BasicOp pIota = choice $ map p allIntTypes@@ -286,7 +277,9 @@ keyword "replicate" *> parens (Replicate <$> pShape <* pComma <*> pSubExp), keyword "reshape"- *> parens (Reshape <$> pShapeChange <* pComma <*> pVName),+ *> parens (Reshape ReshapeArbitrary <$> pShape <* pComma <*> pVName),+ keyword "coerce"+ *> parens (Reshape ReshapeCoerce <$> pShape <* pComma <*> pVName), keyword "scratch" *> parens (Scratch <$> pPrimType <*> many (pComma *> pSubExp)), keyword "rearrange"@@ -415,30 +408,55 @@ pResult :: Parser Result pResult = braces $ pSubExpRes `sepBy` pComma +pMatchSort :: Parser MatchSort+pMatchSort =+ choice+ [ lexeme "<fallback>" $> MatchFallback,+ lexeme "<equiv>" $> MatchEquiv,+ pure MatchNormal+ ]++pBranchBody :: PR rep -> Parser (Body rep)+pBranchBody pr =+ choice+ [ try $ Body (pBodyDec pr) mempty <$> pResult,+ braces (pBody pr)+ ]+ pIf :: PR rep -> Parser (Exp rep) pIf pr = keyword "if" $> f- <*> pSort+ <*> pMatchSort <*> pSubExp- <*> (keyword "then" *> pBranchBody)- <*> (keyword "else" *> pBranchBody)+ <*> (keyword "then" *> pBranchBody pr)+ <*> (keyword "else" *> pBranchBody pr) <*> (lexeme ":" *> pBranchTypes pr) where- pSort =- choice- [ lexeme "<fallback>" $> IfFallback,- lexeme "<equiv>" $> IfEquiv,- pure IfNormal- ] f sort cond tbranch fbranch t =- If cond tbranch fbranch $ IfDec t sort- pBranchBody =- choice- [ try $ Body (pBodyDec pr) mempty <$> pResult,- braces (pBody pr)- ]+ Match [cond] [Case [Just $ BoolValue True] tbranch] fbranch $ MatchDec t sort +pMatch :: PR rep -> Parser (Exp rep)+pMatch pr =+ keyword "match"+ $> f+ <*> pMatchSort+ <*> braces (pSubExp `sepBy` pComma)+ <*> many pCase+ <*> (keyword "default" *> lexeme "->" *> pBranchBody pr)+ <*> (lexeme ":" *> pBranchTypes pr)+ where+ f sort cond cases defbody t =+ Match cond cases defbody $ MatchDec t sort+ pCase =+ keyword "case"+ $> Case+ <*> braces (pMaybeValue `sepBy` pComma)+ <* lexeme "->"+ <*> pBranchBody pr+ pMaybeValue =+ choice [lexeme "_" $> Nothing, Just <$> pPrimValue]+ pApply :: PR rep -> Parser (Exp rep) pApply pr = keyword "apply" *> (p =<< choice [lexeme "<unsafe>" $> Unsafe, pure Safe])@@ -534,6 +552,7 @@ pExp pr = choice [ pIf pr,+ pMatch pr, pApply pr, pLoop pr, pWithAcc pr,@@ -1012,11 +1031,10 @@ pLMAD = braces $ do offset <- pLab "offset" pNum <* pSemi strides <- pLab "strides" $ brackets (pNum `sepBy` pComma) <* pSemi- rotates <- pLab "rotates" $ brackets (pNum `sepBy` pComma) <* pSemi shape <- pLab "shape" $ brackets (pNum `sepBy` pComma) <* pSemi perm <- pLab "permutation" $ brackets (pInt `sepBy` pComma) <* pSemi mon <- pLab "monotonicity" $ brackets (pMon `sepBy` pComma)- pure $ IxFun.LMAD offset $ zipWith5 IxFun.LMADDim strides rotates shape perm mon+ pure $ IxFun.LMAD offset $ zipWith4 IxFun.LMADDim strides shape perm mon pPrimExpLeaf :: Parser VName pPrimExpLeaf = pVName
src/Futhark/IR/Pretty.hs view
@@ -217,8 +217,10 @@ text "replicate" <> apply [ppr ne, align (ppr ve)] ppr (Scratch t shape) = text "scratch" <> apply (ppr t : map ppr shape)- ppr (Reshape shape e) =- text "reshape" <> apply [apply (map ppr shape), ppr e]+ ppr (Reshape ReshapeArbitrary shape e) =+ text "reshape" <> apply [ppr shape, ppr e]+ ppr (Reshape ReshapeCoerce shape e) =+ text "coerce" <> apply [ppr shape, ppr e] ppr (Rearrange perm e) = text "rearrange" <> apply [apply (map ppr perm), ppr e] ppr (Rotate es e) =@@ -238,8 +240,17 @@ p (ErrorString s) = text $ show s p (ErrorVal t x) = ppr x <+> colon <+> ppr t +maybeNest :: PrettyRep rep => Body rep -> Doc+maybeNest b+ | null $ bodyStms b = ppr b+ | otherwise = nestedBlock "{" "}" $ ppr b++instance PrettyRep rep => Pretty (Case (Body rep)) where+ ppr (Case vs b) =+ "case" <+> ppTuple' (map (maybe "_" ppr) vs) <+> "->" <+> maybeNest b+ instance PrettyRep rep => Pretty (Exp rep) where- ppr (If c t f (IfDec ret ifsort)) =+ ppr (Match [c] [Case [Just (BoolValue True)] t] f (MatchDec ret ifsort)) = text "if" <+> info' <+> ppr c@@ -251,12 +262,22 @@ <+> ppTuple' ret where info' = case ifsort of- IfNormal -> mempty- IfFallback -> text "<fallback>"- IfEquiv -> text "<equiv>"- maybeNest b- | null $ bodyStms b = ppr b- | otherwise = nestedBlock "{" "}" $ ppr b+ MatchNormal -> mempty+ MatchFallback -> text "<fallback>"+ MatchEquiv -> text "<equiv>"+ ppr (Match ses cs defb (MatchDec ret ifsort)) =+ ("match" <+> info' <+> ppTuple' ses)+ </> stack (map ppr cs)+ </> "default"+ <+> "->"+ <+> maybeNest defb+ </> colon+ <+> ppTuple' ret+ where+ info' = case ifsort of+ MatchNormal -> mempty+ MatchFallback -> text "<fallback>"+ MatchEquiv -> text "<equiv>" ppr (BasicOp op) = ppr op ppr (Apply fname args ret (safety, _, _)) = applykw@@ -376,10 +397,6 @@ instance PrettyRep rep => Pretty (Prog rep) where ppr (Prog types consts funs) = stack $ punctuate line $ ppr types : ppr consts : map ppr funs--instance Pretty d => Pretty (DimChange d) where- ppr (DimCoercion se) = text "~" <> ppr se- ppr (DimNew se) = ppr se instance Pretty d => Pretty (DimIndex d) where ppr (DimFix i) = ppr i
src/Futhark/IR/Prop.hs view
@@ -126,9 +126,9 @@ safeExp (DoLoop _ _ body) = safeBody body safeExp (Apply fname _ _ _) = isBuiltInFunction fname-safeExp (If _ tbranch fbranch _) =- all (safeExp . stmExp) (bodyStms tbranch)- && all (safeExp . stmExp) (bodyStms fbranch)+safeExp (Match _ cases def_case _) =+ all (all (safeExp . stmExp) . bodyStms . caseBody) cases+ && all (safeExp . stmExp) (bodyStms def_case) safeExp WithAcc {} = True -- Although unlikely to matter. safeExp (Op op) = safeOp op
src/Futhark/IR/Prop/Aliases.hs view
@@ -33,7 +33,7 @@ import Data.Bifunctor (first, second) import qualified Data.Kind-import Data.List (find)+import Data.List (find, transpose) import qualified Data.Map as M import Futhark.IR.Prop (IsOp, NameInfo (..), Scope) import Futhark.IR.Prop.Names@@ -72,7 +72,7 @@ basicOpAliases Iota {} = [mempty] basicOpAliases Replicate {} = [mempty] basicOpAliases Scratch {} = [mempty]-basicOpAliases (Reshape _ e) = [vnameAliases e]+basicOpAliases (Reshape _ _ e) = [vnameAliases e] basicOpAliases (Rearrange _ e) = [vnameAliases e] basicOpAliases (Rotate _ e) = [vnameAliases e] basicOpAliases Concat {} = [mempty]@@ -81,11 +81,11 @@ basicOpAliases Assert {} = [mempty] basicOpAliases UpdateAcc {} = [mempty] -ifAliases :: ([Names], Names) -> ([Names], Names) -> [Names]-ifAliases (als1, cons1) (als2, cons2) =- map (`namesSubtract` cons) $ zipWith mappend als1 als2+matchAliases :: [([Names], Names)] -> [Names]+matchAliases l =+ map ((`namesSubtract` mconcat conses) . mconcat) $ transpose alses where- cons = cons1 <> cons2+ (alses, conses) = unzip l funcallAliases :: [(SubExp, Diet)] -> [TypeBase shape Uniqueness] -> [Names] funcallAliases args t =@@ -93,14 +93,10 @@ -- | The aliases of an expression, one per non-context value returned. expAliases :: (Aliased rep) => Exp rep -> [Names]-expAliases (If _ tb fb dec) =- drop (length all_aliases - length ts) all_aliases+expAliases (Match _ cases defbody _) =+ matchAliases $ onBody defbody : map (onBody . caseBody) cases where- ts = ifReturns dec- all_aliases =- ifAliases- (bodyAliases tb, consumedInBody tb)- (bodyAliases fb, consumedInBody fb)+ onBody body = (bodyAliases body, consumedInBody body) expAliases (BasicOp op) = basicOpAliases op expAliases (DoLoop merge _ loopbody) = do (p, als) <-@@ -160,8 +156,8 @@ where consumeArg (als, Consume) = als consumeArg _ = mempty-consumedInExp (If _ tb fb _) =- consumedInBody tb <> consumedInBody fb+consumedInExp (Match _ cases defbody _) =+ foldMap (consumedInBody . caseBody) cases <> consumedInBody defbody consumedInExp (DoLoop merge form body) = mconcat ( map (subExpAliases . snd) $
src/Futhark/IR/Prop/Names.hs view
@@ -306,6 +306,9 @@ instance FreeIn (Stm rep) => FreeIn (Stms rep) where freeIn' = foldMap freeIn' +instance FreeIn body => FreeIn (Case body) where+ freeIn' = freeIn' . caseBody+ instance FreeIn Names where freeIn' = fvNames @@ -356,9 +359,6 @@ freeIn' (ForLoop _ _ bound loop_vars) = freeIn' bound <> freeIn' loop_vars freeIn' (WhileLoop cond) = freeIn' cond -instance FreeIn d => FreeIn (DimChange d) where- freeIn' = Data.Foldable.foldMap freeIn'- instance FreeIn d => FreeIn (DimIndex d) where freeIn' = Data.Foldable.foldMap freeIn' @@ -389,8 +389,8 @@ instance FreeIn dec => FreeIn (StmAux dec) where freeIn' (StmAux cs attrs dec) = freeIn' cs <> freeIn' attrs <> freeIn' dec -instance FreeIn a => FreeIn (IfDec a) where- freeIn' (IfDec r _) = freeIn' r+instance FreeIn a => FreeIn (MatchDec a) where+ freeIn' (MatchDec r _) = freeIn' r -- | Either return precomputed free names stored in the attribute, or -- the freshly computed names. Relies on lazy evaluation to avoid the
src/Futhark/IR/Prop/Reshape.hs view
@@ -1,24 +1,14 @@ -- | Facilities for creating, inspecting, and simplifying reshape and -- coercion operations. module Futhark.IR.Prop.Reshape- ( -- * Basic tools- newDim,- newDims,- newShape,-- -- * Construction+ ( -- * Construction shapeCoerce, -- * Execution reshapeOuter, reshapeInner, - -- * Inspection- shapeCoercion,- -- * Simplification- fuseReshape,- informReshape, -- * Shape calculations reshapeIndex,@@ -33,86 +23,23 @@ import Futhark.Util.IntegralExp import Prelude hiding (product, quot, sum) --- | The new dimension.-newDim :: DimChange d -> d-newDim (DimCoercion se) = se-newDim (DimNew se) = se---- | The new dimensions resulting from a reshape operation.-newDims :: ShapeChange d -> [d]-newDims = map newDim---- | The new shape resulting from a reshape operation.-newShape :: ShapeChange SubExp -> Shape-newShape = Shape . newDims---- | Construct a 'Reshape' where all dimension changes are--- 'DimCoercion's.+-- | Construct a 'Reshape' that is a 'ReshapeCoerce'. shapeCoerce :: [SubExp] -> VName -> Exp rep shapeCoerce newdims arr =- BasicOp $ Reshape (map DimCoercion newdims) arr+ BasicOp $ Reshape ReshapeCoerce (Shape newdims) arr -- | @reshapeOuter newshape n oldshape@ returns a 'Reshape' expression -- that replaces the outer @n@ dimensions of @oldshape@ with @newshape@.-reshapeOuter :: ShapeChange SubExp -> Int -> Shape -> ShapeChange SubExp+reshapeOuter :: Shape -> Int -> Shape -> Shape reshapeOuter newshape n oldshape =- newshape ++ map coercion_or_new (drop n (shapeDims oldshape))- where- coercion_or_new- | length newshape == n = DimCoercion- | otherwise = DimNew+ newshape <> Shape (drop n (shapeDims oldshape)) -- | @reshapeInner newshape n oldshape@ returns a 'Reshape' expression -- that replaces the inner @m-n@ dimensions (where @m@ is the rank of -- @oldshape@) of @src@ with @newshape@.-reshapeInner :: ShapeChange SubExp -> Int -> Shape -> ShapeChange SubExp+reshapeInner :: Shape -> Int -> Shape -> Shape reshapeInner newshape n oldshape =- map coercion_or_new (take n (shapeDims oldshape)) ++ newshape- where- coercion_or_new- | length newshape == m - n = DimCoercion- | otherwise = DimNew- m = shapeRank oldshape---- | If the shape change is nothing but shape coercions, return the new dimensions. Otherwise, return--- 'Nothing'.-shapeCoercion :: ShapeChange d -> Maybe [d]-shapeCoercion = mapM dimCoercion- where- dimCoercion (DimCoercion d) = Just d- dimCoercion (DimNew _) = Nothing---- | @fuseReshape s1 s2@ creates a new 'ShapeChange' that is--- semantically the same as first applying @s1@ and then @s2@. This--- may take advantage of properties of 'DimCoercion' versus 'DimNew'--- to preserve information.-fuseReshape :: Eq d => ShapeChange d -> ShapeChange d -> ShapeChange d-fuseReshape s1 s2- | length s1 == length s2 =- zipWith comb s1 s2- where- comb (DimNew _) (DimCoercion d2) =- DimNew d2- comb (DimCoercion d1) (DimNew d2)- | d1 == d2 = DimCoercion d2- | otherwise = DimNew d2- comb _ d2 =- d2--- TODO: intelligently handle case where s1 is a prefix of s2.-fuseReshape _ s2 = s2---- | Given concrete information about the shape of the source array,--- convert some 'DimNew's into 'DimCoercion's.-informReshape :: Eq d => [d] -> ShapeChange d -> ShapeChange d-informReshape shape sc- | length shape == length sc =- zipWith inform shape sc- where- inform d1 (DimNew d2)- | d1 == d2 = DimCoercion d2- inform _ dc =- dc-informReshape _ sc = sc+ Shape (take n (shapeDims oldshape)) <> newshape -- | @reshapeIndex to_dims from_dims is@ transforms the index list -- @is@ (which is into an array of shape @from_dims@) into an index
src/Futhark/IR/Prop/Scope.hs view
@@ -102,7 +102,7 @@ asksScope f = f <$> askScope instance- (Applicative m, Monad m, RepTypes rep) =>+ (Monad m, RepTypes rep) => HasScope rep (ReaderT (Scope rep) m) where askScope = ask@@ -111,13 +111,13 @@ askScope = lift askScope instance- (Applicative m, Monad m, Monoid w, RepTypes rep) =>+ (Monad m, Monoid w, RepTypes rep) => HasScope rep (Control.Monad.RWS.Strict.RWST (Scope rep) w s m) where askScope = ask instance- (Applicative m, Monad m, Monoid w, RepTypes rep) =>+ (Monad m, Monoid w, RepTypes rep) => HasScope rep (Control.Monad.RWS.Lazy.RWST (Scope rep) w s m) where askScope = ask@@ -131,23 +131,23 @@ -- does not replace it. localScope :: Scope rep -> m a -> m a -instance (Monad m, LocalScope rep m) => LocalScope rep (ExceptT e m) where+instance (LocalScope rep m) => LocalScope rep (ExceptT e m) where localScope = mapExceptT . localScope instance- (Applicative m, Monad m, RepTypes rep) =>+ (Monad m, RepTypes rep) => LocalScope rep (ReaderT (Scope rep) m) where localScope = local . M.union instance- (Applicative m, Monad m, Monoid w, RepTypes rep) =>+ (Monad m, Monoid w, RepTypes rep) => LocalScope rep (Control.Monad.RWS.Strict.RWST (Scope rep) w s m) where localScope = local . M.union instance- (Applicative m, Monad m, Monoid w, RepTypes rep) =>+ (Monad m, Monoid w, RepTypes rep) => LocalScope rep (Control.Monad.RWS.Lazy.RWST (Scope rep) w s m) where localScope = local . M.union
src/Futhark/IR/Prop/TypeOf.hs view
@@ -37,7 +37,6 @@ import Data.List.NonEmpty (NonEmpty (..)) import Futhark.IR.Prop.Constants-import Futhark.IR.Prop.Reshape import Futhark.IR.Prop.Scope import Futhark.IR.Prop.Types import Futhark.IR.RetType@@ -102,14 +101,14 @@ pure . flip arrayOfShape shape <$> subExpType e basicOpType (Scratch t shape) = pure [arrayOf (Prim t) (Shape shape) NoUniqueness]-basicOpType (Reshape [] e) =+basicOpType (Reshape _ (Shape []) e) = result <$> lookupType e where result t = [Prim $ elemType t]-basicOpType (Reshape shape e) =+basicOpType (Reshape _ shape e) = result <$> lookupType e where- result t = [t `setArrayShape` newShape shape]+ result t = [t `setArrayShape` shape] basicOpType (Rearrange perm e) = result <$> lookupType e where@@ -135,7 +134,7 @@ Exp rep -> m [ExtType] expExtType (Apply _ _ rt _) = pure $ map (fromDecl . declExtTypeOf) rt-expExtType (If _ _ _ rt) = pure $ map extTypeOf $ ifReturns rt+expExtType (Match _ _ _ rt) = pure $ map extTypeOf $ matchReturns rt expExtType (DoLoop merge _ _) = pure $ loopExtType $ map fst merge expExtType (BasicOp op) = staticShapes <$> basicOpType op
src/Futhark/IR/SOACS.hs view
@@ -66,7 +66,8 @@ lamUsesAD lam || any (lamUsesAD . histOp) ops expUsesAD (Op (Scatter _ _ lam _)) = lamUsesAD lam- expUsesAD (If _ tbody fbody _) = bodyUsesAD tbody || bodyUsesAD fbody+ expUsesAD (Match _ cases def_case _) =+ any (bodyUsesAD . caseBody) cases || bodyUsesAD def_case expUsesAD (DoLoop _ _ body) = bodyUsesAD body expUsesAD (WithAcc _ lam) = lamUsesAD lam expUsesAD BasicOp {} = False
src/Futhark/IR/SOACS/SOAC.hs view
@@ -402,7 +402,7 @@ -- SOAC. The mapping does not descend recursively into subexpressions -- and is done left-to-right. mapSOACM ::- (Applicative m, Monad m) =>+ Monad m => SOACMapper frep trep m -> SOAC frep -> m (SOAC trep)
src/Futhark/IR/SOACS/Simplify.hs view
@@ -353,7 +353,7 @@ -- | Remove all arguments to the map that are simply replicates. -- These can be turned into free variables instead. removeReplicateMapping ::- (Aliased rep, Buildable rep, BuilderOps rep, HasSOAC rep) =>+ (Aliased rep, BuilderOps rep, HasSOAC rep) => TopDownRuleOp rep removeReplicateMapping vtable pat aux op | Just (Screma w arrs form) <- asSOAC op,@@ -484,15 +484,12 @@ -- Mapping some operations becomes an extension of that operation. mapOpToOp :: BottomUpRuleOp (Wise SOACS) mapOpToOp (_, used) pat aux1 e- | Just (map_pe, cs, w, BasicOp (Reshape newshape reshape_arr), [p], [arr]) <-+ | Just (map_pe, cs, w, BasicOp (Reshape k newshape reshape_arr), [p], [arr]) <- isMapWithOp pat e, paramName p == reshape_arr, not $ UT.isConsumed (patElemName map_pe) used = Simplify $ do- let redim- | isJust $ shapeCoercion newshape = DimCoercion w- | otherwise = DimNew w certifying (stmAuxCerts aux1 <> cs) . letBind pat . BasicOp $- Reshape (redim : newshape) arr+ Reshape k (Shape [w] <> newshape) arr | Just (_, cs, _, BasicOp (Concat d (arr :| arrs) dw), ps, outer_arr : outer_arrs) <- isMapWithOp pat e, (arr : arrs) == map paramName ps =@@ -628,8 +625,7 @@ y_ws <- mapM sizeOf ys guard $ all (x_w ==) y_ws pure (x_w, x : ys, cs)- Just (BasicOp (Reshape reshape arr), cs) -> do- guard $ isJust $ shapeCoercion reshape+ Just (BasicOp (Reshape ReshapeCoerce _ arr), cs) -> do (a, b, cs') <- isConcat arr pure (a, b, cs <> cs') _ -> Nothing@@ -710,7 +706,7 @@ = ArrayIndexing Certs VName (Slice SubExp) | ArrayRearrange Certs VName [Int] | ArrayRotate Certs VName [SubExp]- | ArrayReshape Certs VName (ShapeChange SubExp)+ | ArrayReshape Certs VName ReshapeKind Shape | ArrayCopy Certs VName | -- | Never constructed. ArrayVar Certs VName@@ -720,7 +716,7 @@ arrayOpArr (ArrayIndexing _ arr _) = arr arrayOpArr (ArrayRearrange _ arr _) = arr arrayOpArr (ArrayRotate _ arr _) = arr-arrayOpArr (ArrayReshape _ arr _) = arr+arrayOpArr (ArrayReshape _ arr _ _) = arr arrayOpArr (ArrayCopy _ arr) = arr arrayOpArr (ArrayVar _ arr) = arr @@ -728,7 +724,7 @@ arrayOpCerts (ArrayIndexing cs _ _) = cs arrayOpCerts (ArrayRearrange cs _ _) = cs arrayOpCerts (ArrayRotate cs _ _) = cs-arrayOpCerts (ArrayReshape cs _ _) = cs+arrayOpCerts (ArrayReshape cs _ _ _) = cs arrayOpCerts (ArrayCopy cs _) = cs arrayOpCerts (ArrayVar cs _) = cs @@ -739,8 +735,8 @@ Just $ ArrayRearrange cs arr perm isArrayOp cs (BasicOp (Rotate rots arr)) = Just $ ArrayRotate cs arr rots-isArrayOp cs (BasicOp (Reshape new_shape arr)) =- Just $ ArrayReshape cs arr new_shape+isArrayOp cs (BasicOp (Reshape k new_shape arr)) =+ Just $ ArrayReshape cs arr k new_shape isArrayOp cs (BasicOp (Copy arr)) = Just $ ArrayCopy cs arr isArrayOp _ _ =@@ -750,7 +746,7 @@ fromArrayOp (ArrayIndexing cs arr slice) = (cs, BasicOp $ Index arr slice) fromArrayOp (ArrayRearrange cs arr perm) = (cs, BasicOp $ Rearrange perm arr) fromArrayOp (ArrayRotate cs arr rots) = (cs, BasicOp $ Rotate rots arr)-fromArrayOp (ArrayReshape cs arr new_shape) = (cs, BasicOp $ Reshape new_shape arr)+fromArrayOp (ArrayReshape cs arr k new_shape) = (cs, BasicOp $ Reshape k new_shape arr) fromArrayOp (ArrayCopy cs arr) = (cs, BasicOp $ Copy arr) fromArrayOp (ArrayVar cs arr) = (cs, BasicOp $ SubExp $ Var arr) @@ -949,7 +945,7 @@ arrayIsMapParam (_, ArrayRotate cs arr rots) = arr `elem` map_param_names && all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn rots)- arrayIsMapParam (_, ArrayReshape cs arr new_shape) =+ arrayIsMapParam (_, ArrayReshape cs arr _ new_shape) = arr `elem` map_param_names && all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn new_shape) arrayIsMapParam (_, ArrayCopy cs arr) =@@ -972,8 +968,8 @@ BasicOp $ Rearrange (0 : map (+ 1) perm) arr ArrayRotate _ _ rots -> BasicOp $ Rotate (intConst Int64 0 : rots) arr- ArrayReshape _ _ new_shape ->- BasicOp $ Reshape (DimCoercion w : new_shape) arr+ ArrayReshape _ _ k new_shape ->+ BasicOp $ Reshape k (Shape [w] <> new_shape) arr ArrayCopy {} -> BasicOp $ Copy arr ArrayVar {} ->
src/Futhark/IR/SegOp.hs view
@@ -830,7 +830,7 @@ -- | Apply a 'SegOpMapper' to the given 'SegOp'. mapSegOpM ::- (Applicative m, Monad m) =>+ Monad m => SegOpMapper lvl frep trep m -> SegOp lvl frep -> m (SegOp lvl trep)@@ -922,10 +922,7 @@ where renamer = SegOpMapper rename rename rename rename rename -instance- (ASTRep rep, FreeIn (LParamInfo rep), FreeIn lvl) =>- FreeIn (SegOp lvl rep)- where+instance (ASTRep rep, FreeIn lvl) => FreeIn (SegOp lvl rep) where freeIn' e = fvBind (namesFromList $ M.keys $ scopeOfSegSpace (segSpace e)) $ flip execState mempty $
src/Futhark/IR/Syntax.hs view
@@ -132,13 +132,13 @@ CmpOp (..), ConvOp (..), OpaqueOp (..),- DimChange (..),- ShapeChange,+ ReshapeKind (..), WithAccInput, Exp (..),+ Case (..), LoopForm (..),- IfDec (..),- IfSort (..),+ MatchDec (..),+ MatchSort (..), Safety (..), Lambda (..), @@ -298,40 +298,6 @@ deriving instance RepTypes rep => Eq (Body rep) --- | The new dimension in a 'Reshape'-like operation. This allows us to--- disambiguate "real" reshapes, that change the actual shape of the--- array, from type coercions that are just present to make the types--- work out. The two constructors are considered equal for purposes of 'Eq'.-data DimChange d- = -- | The new dimension is guaranteed to be numerically- -- equal to the old one.- DimCoercion d- | -- | The new dimension is not necessarily numerically- -- equal to the old one.- DimNew d- deriving (Ord, Show)--instance Eq d => Eq (DimChange d) where- DimCoercion x == DimNew y = x == y- DimCoercion x == DimCoercion y = x == y- DimNew x == DimCoercion y = x == y- DimNew x == DimNew y = x == y--instance Functor DimChange where- fmap f (DimCoercion d) = DimCoercion $ f d- fmap f (DimNew d) = DimNew $ f d--instance Foldable DimChange where- foldMap f (DimCoercion d) = f d- foldMap f (DimNew d) = f d--instance Traversable DimChange where- traverse f (DimCoercion d) = DimCoercion <$> f d- traverse f (DimNew d) = DimNew <$> f d---- | A list of 'DimChange's, indicating the new dimensions of an array.-type ShapeChange d = [DimChange d]- -- | Apart from being Opaque, what else is going on here? data OpaqueOp = -- | No special operation.@@ -340,6 +306,14 @@ OpaqueTrace String deriving (Eq, Ord, Show) +-- | Which kind of reshape is this?+data ReshapeKind+ = -- | Any kind of reshaping.+ ReshapeCoerce+ | -- | New shape is dynamically same as original.+ ReshapeArbitrary+ deriving (Eq, Ord, Show)+ -- | A primitive operation that returns something of known size and -- does not itself contain any bindings. data BasicOp@@ -366,9 +340,7 @@ | -- | Turn a boolean into a certificate, halting the program with the -- given error message if the boolean is false. Assert SubExp (ErrorMsg SubExp) (SrcLoc, [SrcLoc])- | -- Primitive array operations-- -- | The certificates for bounds-checking are part of the 'Stm'.+ | -- | The certificates for bounds-checking are part of the 'Stm'. Index VName (Slice SubExp) | -- | An in-place update of the given array at the given position. -- Consumes the array. If 'Safe', perform a run-time bounds check@@ -401,10 +373,8 @@ Replicate Shape SubExp | -- | Create array of given type and shape, with undefined elements. Scratch PrimType [SubExp]- | -- Array index space transformation.-- -- | 1st arg is the new shape, 2nd arg is the input array.- Reshape (ShapeChange SubExp) VName+ | -- | 1st arg is the new shape, 2nd arg is the input array.+ Reshape ReshapeKind Shape VName | -- | Permute the dimensions of the input array. The list -- of integers is a list of dimensions (0-indexed), which -- must be a permutation of @[0,n-1]@, where @n@ is the@@ -425,6 +395,22 @@ type WithAccInput rep = (Shape, [VName], Maybe (Lambda rep, [SubExp])) +-- | A non-default case in a 'Match' statement. The number of+-- elements in the pattern must match the number of scrutinees. A+-- 'Nothing' value indicates that we don't care about it (i.e. a+-- wildcard).+data Case body = Case {casePat :: [Maybe PrimValue], caseBody :: body}+ deriving (Eq, Ord, Show)++instance Functor Case where+ fmap = fmapDefault++instance Foldable Case where+ foldMap = foldMapDefault++instance Traversable Case where+ traverse f (Case vs b) = Case vs <$> f b+ -- | The root Futhark expression type. The v'Op' constructor contains -- a rep-specific operation. Do-loops, branches and function calls -- are special. Everything else is a simple t'BasicOp'.@@ -432,7 +418,11 @@ = -- | A simple (non-recursive) operation. BasicOp BasicOp | Apply Name [(SubExp, Diet)] [RetType rep] (Safety, SrcLoc, [SrcLoc])- | If SubExp (Body rep) (Body rep) (IfDec (BranchType rep))+ | -- | A match statement picks a branch by comparing the given+ -- subexpressions (called the /scrutinee/) with the pattern in+ -- each of the cases. If none of the cases match, the /default+ -- body/ is picked.+ Match [SubExp] [Case (Body rep)] (Body rep) (MatchDec (BranchType rep)) | -- | @loop {a} = {v} (for i < n|while b) do b@. DoLoop [(FParam rep, SubExp)] (LoopForm rep) (Body rep) | -- | Create accumulators backed by the given arrays (which are@@ -463,29 +453,29 @@ deriving instance RepTypes rep => Ord (LoopForm rep) -- | Data associated with a branch.-data IfDec rt = IfDec- { ifReturns :: [rt],- ifSort :: IfSort+data MatchDec rt = MatchDec+ { matchReturns :: [rt],+ matchSort :: MatchSort } deriving (Eq, Show, Ord) -- | What kind of branch is this? This has no semantic meaning, but -- provides hints to simplifications.-data IfSort+data MatchSort = -- | An ordinary branch.- IfNormal+ MatchNormal | -- | A branch where the "true" case is what we are -- actually interested in, and the "false" case is only -- present as a fallback for when the true case cannot -- be safely evaluated. The compiler is permitted to -- optimise away the branch if the true case contains -- only safe statements.- IfFallback+ MatchFallback | -- | Both of these branches are semantically equivalent, -- and it is fine to eliminate one if it turns out to -- have problems (e.g. contain things we cannot generate -- code for).- IfEquiv+ MatchEquiv deriving (Eq, Show, Ord) -- | Anonymous function for use in a SOAC.
src/Futhark/IR/Traversals.hs view
@@ -83,7 +83,7 @@ -- expression. Importantly, the mapping does not descend recursively -- into subexpressions. The mapping is done left-to-right. mapExpM ::- (Applicative m, Monad m) =>+ Monad m => Mapper frep trep m -> Exp frep -> m (Exp trep)@@ -103,12 +103,14 @@ BasicOp <$> (ConvOp conv <$> mapOnSubExp tv x) mapExpM tv (BasicOp (UnOp op x)) = BasicOp <$> (UnOp op <$> mapOnSubExp tv x)-mapExpM tv (If c texp fexp (IfDec ts s)) =- If- <$> mapOnSubExp tv c- <*> mapOnBody tv mempty texp- <*> mapOnBody tv mempty fexp- <*> (IfDec <$> mapM (mapOnBranchType tv) ts <*> pure s)+mapExpM tv (Match ses cases defbody (MatchDec ts s)) =+ Match+ <$> mapM (mapOnSubExp tv) ses+ <*> mapM mapOnCase cases+ <*> mapOnBody tv mempty defbody+ <*> (MatchDec <$> mapM (mapOnBranchType tv) ts <*> pure s)+ where+ mapOnCase (Case vs body) = Case vs <$> mapOnBody tv mempty body mapExpM tv (Apply fname args ret loc) = do args' <- forM args $ \(arg, d) -> (,) <$> mapOnSubExp tv arg <*> pure d@@ -145,10 +147,10 @@ BasicOp <$> (Replicate <$> mapOnShape tv shape <*> mapOnSubExp tv vexp) mapExpM tv (BasicOp (Scratch t shape)) = BasicOp <$> (Scratch t <$> mapM (mapOnSubExp tv) shape)-mapExpM tv (BasicOp (Reshape shape arrexp)) =+mapExpM tv (BasicOp (Reshape kind shape arrexp)) = BasicOp- <$> ( Reshape- <$> mapM (traverse (mapOnSubExp tv)) shape+ <$> ( Reshape kind+ <$> mapM (mapOnSubExp tv) shape <*> mapOnVName tv arrexp ) mapExpM tv (BasicOp (Rearrange perm e)) =@@ -301,10 +303,10 @@ walkOnSubExp tv x walkExpM tv (BasicOp (UnOp _ x)) = walkOnSubExp tv x-walkExpM tv (If c texp fexp (IfDec ts _)) = do- walkOnSubExp tv c- walkOnBody tv mempty texp- walkOnBody tv mempty fexp+walkExpM tv (Match ses cases defbody (MatchDec ts _)) = do+ mapM_ (walkOnSubExp tv) ses+ mapM_ (walkOnBody tv mempty . caseBody) cases+ walkOnBody tv mempty defbody mapM_ (walkOnBranchType tv) ts walkExpM tv (Apply _ args ret _) = mapM_ (walkOnSubExp tv . fst) args >> mapM_ (walkOnRetType tv) ret@@ -326,8 +328,8 @@ walkOnShape tv shape >> walkOnSubExp tv vexp walkExpM tv (BasicOp (Scratch _ shape)) = mapM_ (walkOnSubExp tv) shape-walkExpM tv (BasicOp (Reshape shape arrexp)) =- mapM_ (traverse_ (walkOnSubExp tv)) shape >> walkOnVName tv arrexp+walkExpM tv (BasicOp (Reshape _ shape arrexp)) =+ mapM_ (walkOnSubExp tv) shape >> walkOnVName tv arrexp walkExpM tv (BasicOp (Rearrange _ e)) = walkOnVName tv e walkExpM tv (BasicOp (Rotate es e)) =
src/Futhark/IR/TypeCheck.hs view
@@ -904,27 +904,15 @@ void $ checkSubExp valexp checkBasicOp (Scratch _ shape) = mapM_ checkSubExp shape-checkBasicOp (Reshape newshape arrexp) = do+checkBasicOp (Reshape k newshape arrexp) = do rank <- shapeRank . fst <$> checkArrIdent arrexp- mapM_ (require [Prim int64] . newDim) newshape- zipWithM_ (checkDimChange rank) newshape [0 ..]- where- checkDimChange _ (DimNew _) _ =+ mapM_ (require [Prim int64]) $ shapeDims newshape+ case k of+ ReshapeCoerce ->+ when (shapeRank newshape /= rank) . bad $+ TypeError "Coercion changes rank of array."+ ReshapeArbitrary -> pure ()- checkDimChange rank (DimCoercion se) i- | i >= rank =- bad . TypeError $- "Asked to coerce dimension "- ++ show i- ++ " to "- ++ pretty se- ++ ", but array "- ++ pretty arrexp- ++ " has only "- ++ pretty rank- ++ " dimensions"- | otherwise =- pure () checkBasicOp (Rearrange perm arr) = do arrt <- lookupType arr let rank = arrayRank arrt@@ -1014,13 +1002,22 @@ Exp (Aliases rep) -> TypeM rep () checkExp (BasicOp op) = checkBasicOp op-checkExp (If e1 e2 e3 info) = do- require [Prim Bool] e1- _ <-- context "in true branch" (checkBody e2)- `alternative` context "in false branch" (checkBody e3)- context "in true branch" $ matchBranchType (ifReturns info) e2- context "in false branch" $ matchBranchType (ifReturns info) e3+checkExp (Match ses cases def_case info) = do+ ses_ts <- mapM checkSubExp ses+ mapM_ (checkCase ses_ts) cases+ checkCaseBody def_case+ where+ checkVal t (Just v) = Prim (primValueType v) == t+ checkVal _ Nothing = True+ checkCase ses_ts (Case vs body) = do+ let ok = length vs == length ses_ts && and (zipWith checkVal ses_ts vs)+ unless ok . bad . TypeError . pretty $+ "Scrutinee"+ </> indent 2 (ppTuple' ses)+ </> "cannot match pattern"+ </> indent 2 (ppTuple' vs)+ context ("in body of case " <> prettyTuple vs) $ checkCaseBody body+ checkCaseBody = matchBranchType (matchReturns info) checkExp (Apply fname args rettype_annot _) = do (rettype_derived, paramtypes) <- lookupFun fname $ map fst args argflows <- mapM (checkArg . fst) args
src/Futhark/Internalise/Exps.hs view
@@ -9,7 +9,7 @@ module Futhark.Internalise.Exps (transformProg) where import Control.Monad.Reader-import Data.List (find, intercalate, intersperse, transpose)+import Data.List (elemIndex, find, intercalate, intersperse, transpose) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M@@ -160,11 +160,6 @@ letValExp' _ (BasicOp (SubExp se)) = pure [se] letValExp' name ses = map I.Var <$> letValExp name ses -eValBody :: [InternaliseM (I.Exp SOACS)] -> InternaliseM (I.Body SOACS)-eValBody es = buildBody_ $ do- es' <- sequence es- varsRes . concat <$> mapM (letValExp "x") es'- internaliseAppExp :: String -> E.AppRes -> E.AppExp -> InternaliseM [I.SubExp] internaliseAppExp desc _ (E.Index e idxs loc) = do vs <- internaliseExpToVars "indexed" e@@ -279,18 +274,16 @@ bounds_invalid <- letSubExp "bounds_invalid"- $ I.If- downwards- (resultBody [bounds_invalid_downwards])- (resultBody [bounds_invalid_upwards])- $ ifCommon [I.Prim I.Bool]+ =<< eIf+ (eSubExp downwards)+ (resultBodyM [bounds_invalid_downwards])+ (resultBodyM [bounds_invalid_upwards]) distance_exclusive <- letSubExp "distance_exclusive"- $ I.If- downwards- (resultBody [distance_downwards_exclusive])- (resultBody [distance_upwards_exclusive])- $ ifCommon [I.Prim $ IntType it]+ =<< eIf+ (eSubExp downwards)+ (resultBodyM [distance_downwards_exclusive])+ (resultBodyM [distance_upwards_exclusive]) distance_exclusive_i64 <- asIntS Int64 distance_exclusive distance <- letSubExp "distance" $@@ -374,7 +367,7 @@ args' <- concat . reverse <$> mapM (internaliseArg arg_desc) (reverse args) fst <$> funcall desc qfname args' loc internaliseAppExp desc _ (E.LetPat sizes pat e body _) =- internalisePat desc sizes pat e body (internaliseExp desc)+ internalisePat desc sizes pat e $ internaliseExp desc body internaliseAppExp _ _ (E.LetFun ofname _ _ _) = error $ "Unexpected LetFun " ++ pretty ofname internaliseAppExp desc _ (E.DoLoop sparams mergepat mergeexp form loopbody loc) = do@@ -489,7 +482,7 @@ case se of I.Var v | not $ primType $ paramType p ->- Reshape (map DimCoercion $ arrayDims $ paramType p) v+ Reshape I.ReshapeCoerce (I.arrayShape $ paramType p) v _ -> SubExp se internaliseExp1 "loop_cond" cond @@ -515,7 +508,7 @@ case se of I.Var v | not $ primType $ paramType p ->- Reshape (map DimCoercion $ arrayDims $ paramType p) v+ Reshape I.ReshapeCoerce (I.arrayShape $ paramType p) v _ -> SubExp se subExpsRes <$> internaliseExp "loop_cond" cond loop_end_cond <- bodyBind loop_end_cond_body@@ -536,21 +529,20 @@ E.AppExp (E.LetPat [] pat e body loc) (Info (AppRes (E.typeOf body) mempty))-internaliseAppExp desc _ (E.Match e cs _) = do+internaliseAppExp desc _ (E.Match e orig_cs _) = do ses <- internaliseExp (desc ++ "_scrutinee") e+ cs <- mapM (onCase ses) orig_cs case NE.uncons cs of- (CasePat pCase eCase _, Nothing) -> do- (_, pertinent) <- generateCond pCase ses- internalisePat' [] pCase pertinent eCase (internaliseExp desc)- (c, Just cs') -> do- let CasePat pLast eLast _ = NE.last cs'- bFalse <- do- (_, pertinent) <- generateCond pLast ses- eLast' <- internalisePat' [] pLast pertinent eLast (internaliseBody desc)- foldM (\bf c' -> eValBody $ pure $ generateCaseIf ses c' bf) eLast' $- reverse $- NE.init cs'- letValExp' desc =<< generateCaseIf ses c bFalse+ (I.Case _ body, Nothing) ->+ fmap (map resSubExp) $ bodyBind =<< body+ _ -> do+ letValExp' desc =<< eMatch ses (NE.init cs) (I.caseBody $ NE.last cs)+ where+ onCase ses (E.CasePat p case_e _) = do+ (cmps, pertinent) <- generateCond p ses+ pure . I.Case cmps $+ internalisePat' [] p pertinent $+ internaliseBody "case" case_e internaliseAppExp desc _ (E.If ce te fe _) = letValExp' desc =<< eIf@@ -625,10 +617,10 @@ flat_arr_t <- lookupType flat_arr let new_shape' = reshapeOuter- (map (DimNew . intConst Int64 . toInteger) new_shape)+ (I.Shape $ map (intConst Int64 . toInteger) new_shape) 1 $ I.arrayShape flat_arr_t- letSubExp desc $ I.BasicOp $ I.Reshape new_shape' flat_arr+ letSubExp desc $ I.BasicOp $ I.Reshape I.ReshapeArbitrary new_shape' flat_arr | otherwise = do es' <- mapM (internaliseExp "arr_elem") es let arr_t_ext = internaliseType $ E.toStruct arr_t@@ -836,33 +828,45 @@ _ -> pure () pure arg' -subExpPrimType :: I.SubExp -> InternaliseM I.PrimType-subExpPrimType = fmap I.elemType . subExpType+internalisePatLit :: E.PatLit -> E.PatType -> I.PrimValue+internalisePatLit (E.PatLitPrim v) _ =+ internalisePrimValue v+internalisePatLit (E.PatLitInt x) (E.Scalar (E.Prim (E.Signed it))) =+ I.IntValue $ intValue it x+internalisePatLit (E.PatLitInt x) (E.Scalar (E.Prim (E.Unsigned it))) =+ I.IntValue $ intValue it x+internalisePatLit (E.PatLitFloat x) (E.Scalar (E.Prim (E.FloatType ft))) =+ I.FloatValue $ floatValue ft x+internalisePatLit l t =+ error $ "Nonsensical pattern and type: " ++ show (l, t) -generateCond :: E.Pat -> [I.SubExp] -> InternaliseM (I.SubExp, [I.SubExp])+generateCond ::+ E.Pat ->+ [I.SubExp] ->+ InternaliseM ([Maybe I.PrimValue], [I.SubExp]) generateCond orig_p orig_ses = do (cmps, pertinent, _) <- compares orig_p orig_ses- cmp <- letSubExp "matches" =<< eAll cmps- pure (cmp, pertinent)+ pure (cmps, pertinent) where- -- Literals are always primitive values.- compares (E.PatLit l t _) (se : ses) = do- e' <- case l of- PatLitPrim v -> pure $ constant $ internalisePrimValue v- PatLitInt x -> internaliseExp1 "constant" $ E.IntLit x t mempty- PatLitFloat x -> internaliseExp1 "constant" $ E.FloatLit x t mempty- t' <- subExpPrimType se- cmp <- letSubExp "match_lit" $ I.BasicOp $ I.CmpOp (I.CmpEq t') e' se- pure ([cmp], [se], ses)- compares (E.PatConstr c (Info (E.Scalar (E.Sum fs))) pats _) (se : ses) = do+ compares (E.PatLit l (Info t) _) (se : ses) =+ pure ([Just $ internalisePatLit l t], [se], ses)+ compares (E.PatConstr c (Info (E.Scalar (E.Sum fs))) pats _) (_ : ses) = do (payload_ts, m) <- internaliseSumType $ M.map (map toStruct) fs case M.lookup c m of- Just (i, payload_is) -> do- let i' = intConst Int8 $ toInteger i+ Just (tag, payload_is) -> do let (payload_ses, ses') = splitAt (length payload_ts) ses- cmp <- letSubExp "match_constr" $ I.BasicOp $ I.CmpOp (I.CmpEq int8) i' se- (cmps, pertinent, _) <- comparesMany pats $ map (payload_ses !!) payload_is- pure (cmp : cmps, pertinent, ses')+ (cmps, pertinent, _) <-+ comparesMany pats $ map (payload_ses !!) payload_is+ let missingCmps i _ =+ case i `elemIndex` payload_is of+ Just j -> cmps !! j+ Nothing -> Nothing+ pure+ ( Just (I.IntValue $ intValue Int8 $ toInteger tag)+ : zipWith missingCmps [0 ..] payload_ses,+ pertinent,+ ses'+ ) Nothing -> error "generateCond: missing constructor" compares (E.PatConstr _ (Info t) _ _) _ =@@ -871,7 +875,7 @@ compares (E.Wildcard t loc) ses compares (E.Wildcard (Info t) _) ses = do let (id_ses, rest_ses) = splitAt (internalisedTypeSize $ E.toStruct t) ses- pure ([], id_ses, rest_ses)+ pure (map (const Nothing) id_ses, id_ses, rest_ses) compares (E.PatParens pat _) ses = compares pat ses compares (E.PatAttr _ pat _) ses =@@ -900,23 +904,16 @@ ses'' ) -generateCaseIf :: [I.SubExp] -> Case -> I.Body SOACS -> InternaliseM (I.Exp SOACS)-generateCaseIf ses (CasePat p eCase _) bFail = do- (cond, pertinent) <- generateCond p ses- eCase' <- internalisePat' [] p pertinent eCase (internaliseBody "case")- eIf (eSubExp cond) (pure eCase') (pure bFail)- internalisePat :: String -> [E.SizeBinder VName] -> E.Pat -> E.Exp ->- E.Exp ->- (E.Exp -> InternaliseM a) ->+ InternaliseM a -> InternaliseM a-internalisePat desc sizes p e body m = do+internalisePat desc sizes p e m = do ses <- internaliseExp desc' e- internalisePat' sizes p ses body m+ internalisePat' sizes p ses m where desc' = case S.toList $ E.patIdents p of [v] -> baseString $ E.identName v@@ -926,16 +923,15 @@ [E.SizeBinder VName] -> E.Pat -> [I.SubExp] ->- E.Exp ->- (E.Exp -> InternaliseM a) ->+ InternaliseM a -> InternaliseM a-internalisePat' sizes p ses body m = do+internalisePat' sizes p ses m = do ses_ts <- mapM subExpType ses stmPat p ses_ts $ \pat_names -> do bindExtSizes (AppRes (E.patternType p) (map E.sizeName sizes)) ses forM_ (zip pat_names ses) $ \(v, se) -> letBindNames [v] $ I.BasicOp $ I.SubExp se- m body+ m internaliseSlice :: SrcLoc ->@@ -994,18 +990,16 @@ w_minus_1 <- letSubExp "w_minus_1" $ BasicOp $ I.BinOp (Sub Int64 I.OverflowWrap) w one let i_def = letSubExp "i_def"- $ I.If- backwards- (resultBody [w_minus_1])- (resultBody [zero])- $ ifCommon [I.Prim int64]+ =<< eIf+ (eSubExp backwards)+ (resultBodyM [w_minus_1])+ (resultBodyM [zero]) j_def = letSubExp "j_def"- $ I.If- backwards- (resultBody [negone])- (resultBody [w])- $ ifCommon [I.Prim int64]+ =<< eIf+ (eSubExp backwards)+ (resultBodyM [negone])+ (resultBodyM [w]) i' <- maybe i_def (fmap fst . internaliseSizeExp "i") i j' <- maybe j_def (fmap fst . internaliseSizeExp "j") j j_m_i <- letSubExp "j_m_i" $ BasicOp $ I.BinOp (Sub Int64 I.OverflowWrap) j' i'@@ -1062,11 +1056,10 @@ slice_ok <- letSubExp "slice_ok"- $ I.If- backwards- (resultBody [backwards_ok])- (resultBody [forwards_ok])- $ ifCommon [I.Prim I.Bool]+ =<< eIf+ (eSubExp backwards)+ (resultBodyM [backwards_ok])+ (resultBodyM [forwards_ok]) ok_or_empty <- letSubExp "ok_or_empty" $@@ -1634,32 +1627,33 @@ dims_match <- forM (zip x_dims y_dims) $ \(x_dim, y_dim) -> letSubExp "dim_eq" $ I.BasicOp $ I.CmpOp (I.CmpEq int64) x_dim y_dim shapes_match <- letSubExp "shapes_match" =<< eAll dims_match- compare_elems_body <- runBodyBuilder $ do- -- Flatten both x and y.- x_num_elems <-- letSubExp "x_num_elems"- =<< foldBinOp (I.Mul Int64 I.OverflowUndef) (constant (1 :: Int64)) x_dims- x' <- letExp "x" $ I.BasicOp $ I.SubExp x- y' <- letExp "x" $ I.BasicOp $ I.SubExp y- x_flat <- letExp "x_flat" $ I.BasicOp $ I.Reshape [I.DimNew x_num_elems] x'- y_flat <- letExp "y_flat" $ I.BasicOp $ I.Reshape [I.DimNew x_num_elems] y'+ let compare_elems_body = runBodyBuilder $ do+ -- Flatten both x and y.+ x_num_elems <-+ letSubExp "x_num_elems"+ =<< foldBinOp (I.Mul Int64 I.OverflowUndef) (constant (1 :: Int64)) x_dims+ x' <- letExp "x" $ I.BasicOp $ I.SubExp x+ y' <- letExp "x" $ I.BasicOp $ I.SubExp y+ x_flat <-+ letExp "x_flat" $ I.BasicOp $ I.Reshape I.ReshapeArbitrary (I.Shape [x_num_elems]) x'+ y_flat <-+ letExp "y_flat" $ I.BasicOp $ I.Reshape I.ReshapeArbitrary (I.Shape [x_num_elems]) y' - -- Compare the elements.- cmp_lam <- cmpOpLambda $ I.CmpEq (elemType x_t)- cmps <-- letExp "cmps" $- I.Op $- I.Screma x_num_elems [x_flat, y_flat] (I.mapSOAC cmp_lam)+ -- Compare the elements.+ cmp_lam <- cmpOpLambda $ I.CmpEq (elemType x_t)+ cmps <-+ letExp "cmps" $+ I.Op $+ I.Screma x_num_elems [x_flat, y_flat] (I.mapSOAC cmp_lam) - -- Check that all were equal.- and_lam <- binOpLambda I.LogAnd I.Bool- reduce <- I.reduceSOAC [Reduce Commutative and_lam [constant True]]- all_equal <- letSubExp "all_equal" $ I.Op $ I.Screma x_num_elems [cmps] reduce- pure $ resultBody [all_equal]+ -- Check that all were equal.+ and_lam <- binOpLambda I.LogAnd I.Bool+ reduce <- I.reduceSOAC [Reduce Commutative and_lam [constant True]]+ all_equal <- letSubExp "all_equal" $ I.Op $ I.Screma x_num_elems [cmps] reduce+ pure $ resultBody [all_equal] - letSubExp "arrays_equal" $- I.If shapes_match compare_elems_body (resultBody [constant False]) $- ifCommon [I.Prim I.Bool]+ letSubExp "arrays_equal"+ =<< eIf (eSubExp shapes_match) compare_elems_body (resultBodyM [constant False]) handleOps [x, y] name | Just bop <- find ((name ==) . pretty) [minBound .. maxBound :: E.BinOp] = Just $ \desc -> do@@ -1776,9 +1770,11 @@ certifying dim_ok_cert $ forM arrs $ \arr' -> do arr_t <- lookupType arr'- letSubExp desc $- I.BasicOp $- I.Reshape (reshapeOuter [DimNew n', DimNew m'] 1 $ I.arrayShape arr_t) arr'+ letSubExp desc . I.BasicOp $+ I.Reshape+ I.ReshapeArbitrary+ (reshapeOuter (I.Shape [n', m']) 1 $ I.arrayShape arr_t)+ arr' handleRest [arr] "flatten" = Just $ \desc -> do arrs <- internaliseExpToVars "flatten_arr" arr forM arrs $ \arr' -> do@@ -1786,9 +1782,11 @@ let n = arraySize 0 arr_t m = arraySize 1 arr_t k <- letSubExp "flat_dim" $ I.BasicOp $ I.BinOp (Mul Int64 I.OverflowUndef) n m- letSubExp desc $- I.BasicOp $- I.Reshape (reshapeOuter [DimNew k] 2 $ I.arrayShape arr_t) arr'+ letSubExp desc . I.BasicOp $+ I.Reshape+ I.ReshapeArbitrary+ (reshapeOuter (I.Shape [k]) 2 $ I.arrayShape arr_t)+ arr' handleRest [TupLit [x, y] _] "concat" = Just $ \desc -> do xs <- internaliseExpToVars "concat_x" x ys <- internaliseExpToVars "concat_y" y@@ -1841,11 +1839,10 @@ case E.typeOf e of E.Scalar (E.Prim E.Bool) -> letTupExp' desc- $ I.If- e'- (resultBody [intConst int_to 1])- (resultBody [intConst int_to 0])- $ ifCommon [I.Prim $ I.IntType int_to]+ =<< eIf+ (eSubExp e')+ (resultBodyM [intConst int_to 1])+ (resultBodyM [intConst int_to 0]) E.Scalar (E.Prim (E.Signed int_from)) -> letTupExp' desc $ I.BasicOp $ I.ConvOp (I.SExt int_from int_to) e' E.Scalar (E.Prim (E.Unsigned int_from)) ->@@ -1859,11 +1856,10 @@ case E.typeOf e of E.Scalar (E.Prim E.Bool) -> letTupExp' desc- $ I.If- e'- (resultBody [intConst int_to 1])- (resultBody [intConst int_to 0])- $ ifCommon [I.Prim $ I.IntType int_to]+ =<< eIf+ (eSubExp e')+ (resultBodyM [intConst int_to 1])+ (resultBodyM [intConst int_to 0]) E.Scalar (E.Prim (E.Signed int_from)) -> letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e' E.Scalar (E.Prim (E.Unsigned int_from)) ->@@ -1897,9 +1893,8 @@ "length of index and value array does not match" loc certifying c $- letExp (baseString sv ++ "_write_sv") $- I.BasicOp $- I.Reshape (reshapeOuter [DimCoercion si_w] 1 sv_shape) sv+ letExp (baseString sv ++ "_write_sv") . I.BasicOp $+ I.Reshape I.ReshapeCoerce (reshapeOuter (I.Shape [si_w]) 1 sv_shape) sv indexType <- fmap rowType <$> mapM lookupType si' indexName <- mapM (\_ -> newVName "write_index") indexType@@ -2139,18 +2134,14 @@ -- the total sizes, which are the last elements in the offests. We -- just have to be careful in case the array is empty. last_index <- letSubExp "last_index" $ I.BasicOp $ I.BinOp (I.Sub Int64 OverflowUndef) w $ constant (1 :: Int64)- nonempty_body <- runBodyBuilder $- fmap resultBody $- forM all_offsets $ \offset_array ->- letSubExp "last_offset" $ I.BasicOp $ I.Index offset_array $ Slice [I.DimFix last_index]- let empty_body = resultBody $ replicate k $ constant (0 :: Int64)+ let nonempty_body = runBodyBuilder $+ fmap resultBody $+ forM all_offsets $ \offset_array ->+ letSubExp "last_offset" $ I.BasicOp $ I.Index offset_array $ Slice [I.DimFix last_index]+ empty_body = resultBodyM $ replicate k $ constant (0 :: Int64) is_empty <- letSubExp "is_empty" $ I.BasicOp $ I.CmpOp (CmpEq int64) w $ constant (0 :: Int64) sizes <-- letTupExp "partition_size" $- I.If is_empty empty_body nonempty_body $- ifCommon $- replicate k $- I.Prim int64+ letTupExp "partition_size" =<< eIf (eSubExp is_empty) empty_body nonempty_body -- The total size of all partitions must necessarily be equal to the -- size of the input array.@@ -2218,11 +2209,10 @@ (constant (-1 :: Int64)) (I.Var (I.paramName p) : take i sizes) letSubExp "total_res"- $ I.If- is_this_one- (resultBody [this_one])- (resultBody [next_one])- $ ifCommon [I.Prim int64]+ =<< eIf+ (eSubExp is_this_one)+ (resultBodyM [this_one])+ (resultBodyM [next_one]) typeExpForError :: E.TypeExp VName -> InternaliseM [ErrorMsgPart SubExp] typeExpForError (E.TEVar qn _) =
src/Futhark/LSP/Handlers.hs view
@@ -49,7 +49,6 @@ logStringStderr <& "Got custom request: onFocusTextDocument" let NotificationMessage _ _ (Array vector_param) = msg String focused_uri = V.head vector_param -- only one parameter passed from the client- logStringStderr <& show focused_uri tryReCompile state_mvar (uriToFilePath (Uri focused_uri)) goToDefinitionHandler :: IORef State -> Handlers (LspM ())
src/Futhark/MonadFreshNames.hs view
@@ -41,27 +41,27 @@ -- getNameSource = get -- putNameSource = put -- @-class (Applicative m, Monad m) => MonadFreshNames m where+class Monad m => MonadFreshNames m where getNameSource :: m VNameSource putNameSource :: VNameSource -> m () -instance (Applicative im, Monad im) => MonadFreshNames (Control.Monad.State.Lazy.StateT VNameSource im) where+instance Monad im => MonadFreshNames (Control.Monad.State.Lazy.StateT VNameSource im) where getNameSource = Control.Monad.State.Lazy.get putNameSource = Control.Monad.State.Lazy.put -instance (Applicative im, Monad im) => MonadFreshNames (Control.Monad.State.Strict.StateT VNameSource im) where+instance Monad im => MonadFreshNames (Control.Monad.State.Strict.StateT VNameSource im) where getNameSource = Control.Monad.State.Strict.get putNameSource = Control.Monad.State.Strict.put instance- (Applicative im, Monad im, Monoid w) =>+ (Monad im, Monoid w) => MonadFreshNames (Control.Monad.RWS.Lazy.RWST r w VNameSource im) where getNameSource = Control.Monad.RWS.Lazy.get putNameSource = Control.Monad.RWS.Lazy.put instance- (Applicative im, Monad im, Monoid w) =>+ (Monad im, Monoid w) => MonadFreshNames (Control.Monad.RWS.Strict.RWST r w VNameSource im) where getNameSource = Control.Monad.RWS.Strict.get
src/Futhark/Optimise/BlkRegTiling.hs view
@@ -634,8 +634,9 @@ epilogue_t <- lookupType epilogue_res let (block_dims, rest_dims) = splitAt 2 $ arrayDims epilogue_t ones = map (const $ intConst Int64 1) rem_outer_dims- new_shape = concat [ones, block_dims, ones, rest_dims]- letExp "res_reshaped" $ BasicOp $ Reshape (map DimNew new_shape) epilogue_res+ new_shape = Shape $ concat [ones, block_dims, ones, rest_dims]+ letExp "res_reshaped" . BasicOp $+ Reshape ReshapeArbitrary new_shape epilogue_res pure [RegTileReturns mempty regtile_ret_dims epilogue_res'] mmBlkRegTilingNrm _ _ = pure Nothing @@ -1279,8 +1280,9 @@ res_tp' <- lookupType res let (block_dims, rest_dims) = splitAt 2 $ arrayDims res_tp' ones = map (const se1) rem_outer_dims- new_shape = concat [ones, block_dims, ones, rest_dims]- letExp "res_reshaped" $ BasicOp $ Reshape (map DimNew new_shape) res+ new_shape = Shape $ concat [ones, block_dims, ones, rest_dims]+ letExp "res_reshaped" . BasicOp $+ Reshape ReshapeArbitrary new_shape res pure $ map (RegTileReturns mempty regtile_ret_dims) epilogue_res' -- END (ret_seggroup, stms_seggroup) <- runBuilder $ do
+ src/Futhark/Optimise/EntryPointMem.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}++-- | We require that entry points return arrays with zero offset in+-- row-major order. "Futhark.Pass.ExplicitAllocations" is+-- conservative and inserts copies to ensure this is the case. After+-- simplification, it may turn out that those copies are redundant.+-- This pass removes them. It's a pretty simple pass, as it only has+-- to look at the top level of entry points.+module Futhark.Optimise.EntryPointMem+ ( entryPointMemGPU,+ entryPointMemMC,+ entryPointMemSeq,+ )+where++import Data.List (find)+import qualified Data.Map.Strict as M+import Futhark.IR.GPUMem (GPUMem)+import Futhark.IR.MCMem (MCMem)+import Futhark.IR.Mem+import Futhark.IR.SeqMem (SeqMem)+import Futhark.Pass+import Futhark.Pass.ExplicitAllocations.GPU ()+import Futhark.Transform.Substitute++type Table rep = M.Map VName (Stm rep)++mkTable :: Stms rep -> Table rep+mkTable = foldMap f+ where+ f stm = M.fromList $ zip (patNames (stmPat stm)) $ repeat stm++varInfo :: Mem rep inner => VName -> Table rep -> Maybe (LetDecMem, Exp rep)+varInfo v table = do+ Let pat _ e <- M.lookup v table+ PatElem _ info <- find ((== v) . patElemName) (patElems pat)+ Just (letDecMem info, e)++optimiseFun :: Mem rep inner => Table rep -> FunDef rep -> FunDef rep+optimiseFun consts_table fd =+ fd {funDefBody = onBody $ funDefBody fd}+ where+ table = consts_table <> mkTable (bodyStms (funDefBody fd))+ mkSubst (Var v0)+ | Just (MemArray _ _ _ (ArrayIn mem0 ixfun0), BasicOp (Manifest _ v1)) <-+ varInfo v0 table,+ Just (MemArray _ _ _ (ArrayIn mem1 ixfun1), _) <-+ varInfo v1 table,+ ixfun0 == ixfun1 =+ M.fromList [(mem0, mem1), (v0, v1)]+ mkSubst _ = mempty+ onBody (Body dec stms res) =+ let substs = mconcat $ map (mkSubst . resSubExp) res+ in Body dec stms $ substituteNames substs res++entryPointMem :: Mem rep inner => Pass rep rep+entryPointMem =+ Pass+ { passName = "Entry point memory optimisation",+ passDescription = "Remove redundant copies of entry point results.",+ passFunction = intraproceduralTransformationWithConsts pure onFun+ }+ where+ onFun consts fd = pure $ optimiseFun (mkTable consts) fd++-- | The pass for GPU representation.+entryPointMemGPU :: Pass GPUMem GPUMem+entryPointMemGPU = entryPointMem++-- | The pass for MC representation.+entryPointMemMC :: Pass MCMem MCMem+entryPointMemMC = entryPointMem++-- | The pass for Seq representation.+entryPointMemSeq :: Pass SeqMem SeqMem+entryPointMemSeq = entryPointMem
src/Futhark/Optimise/Fusion.hs view
@@ -98,7 +98,7 @@ DoNode stm lst -> do lst' <- mapM (finalizeNode . fst) lst pure $ mconcat lst' <> oneStm stm- IfNode stm lst -> do+ MatchNode stm lst -> do lst' <- mapM (finalizeNode . fst) lst pure $ mconcat lst' <> oneStm stm FinalNode stms1 nt' stms2 -> do@@ -253,10 +253,10 @@ -- First node is producer, second is consumer. vFuseNodeT :: [EdgeT] -> [VName] -> (NodeT, [EdgeT], [EdgeT]) -> (NodeT, [EdgeT]) -> FusionM (Maybe NodeT)-vFuseNodeT _ infusible (s1, _, e1s) (IfNode stm2 dfused, _)+vFuseNodeT _ infusible (s1, _, e1s) (MatchNode stm2 dfused, _) | isRealNode s1, null infusible =- pure $ Just $ IfNode stm2 $ (s1, e1s) : dfused+ pure $ Just $ MatchNode stm2 $ (s1, e1s) : dfused vFuseNodeT _ infusible (StmNode stm1, _, _) (SoacNode ots2 pats2 soac2 aux2, _) | null infusible, [stm1_out] <- patNames $ stmPat stm1,@@ -398,11 +398,10 @@ doFuseScans . localScope (scopeOfFParams (map fst params) <> scopeOf form) $ do b <- doFusionWithDelayed body to_fuse pure (incoming, node, DoNode (Let pat aux (DoLoop params form b)) [], outgoing)- IfNode (Let pat aux (If sz b1 b2 dec)) to_fuse -> doFuseScans $ do- b1' <- doFusionWithDelayed b1 to_fuse- b2' <- doFusionWithDelayed b2 to_fuse- rb2' <- renameBody b2'- pure (incoming, node, IfNode (Let pat aux (If sz b1' rb2' dec)) [], outgoing)+ MatchNode (Let pat aux (Match cond cases defbody dec)) to_fuse -> doFuseScans $ do+ cases' <- mapM (traverse $ renameBody <=< (`doFusionWithDelayed` to_fuse)) cases+ defbody' <- doFusionWithDelayed defbody to_fuse+ pure (incoming, node, MatchNode (Let pat aux (Match cond cases' defbody' dec)) [], outgoing) StmNode (Let pat aux (Op (Futhark.VJP lam args vec))) -> doFuseScans $ do lam' <- doFusionLambda lam pure (incoming, node, StmNode (Let pat aux (Op (Futhark.VJP lam' args vec))), outgoing)
src/Futhark/Optimise/Fusion/GraphRep.hs view
@@ -3,7 +3,8 @@ -- | A graph representation of a sequence of Futhark statements -- (i.e. a 'Body'), built to handle fusion. Could perhaps be made -- more general. An important property is that it does not handle--- "nested bodies" (e.g. 'If'); these are represented as single nodes.+-- "nested bodies" (e.g. 'Match'); these are represented as single+-- nodes. -- -- This is all implemented on top of the graph representation provided -- by the @fgl@ package ("Data.Graph.Inductive"). The graph provided@@ -83,7 +84,7 @@ -- Unclear whether we actually need these. FreeNode VName | FinalNode (Stms SOACS) NodeT (Stms SOACS)- | IfNode (Stm SOACS) [(NodeT, [EdgeT])]+ | MatchNode (Stm SOACS) [(NodeT, [EdgeT])] | DoNode (Stm SOACS) [(NodeT, [EdgeT])] deriving (Eq) @@ -101,7 +102,7 @@ show (FinalNode _ nt _) = show nt show (ResNode name) = pretty $ "Res: " ++ pretty name show (FreeNode name) = pretty $ "Input: " ++ pretty name- show (IfNode stm _) = "If: " ++ L.intercalate ", " (map pretty $ stmNames stm)+ show (MatchNode stm _) = "Match: " ++ L.intercalate ", " (map pretty $ stmNames stm) show (DoNode stm _) = "Do: " ++ L.intercalate ", " (map pretty $ stmNames stm) -- | The name that this edge depends on.@@ -290,8 +291,8 @@ Left H.NotSOAC -> pure n DoLoop {} -> pure $ DoNode s []- If {} ->- pure $ IfNode s []+ Match {} ->+ pure $ MatchNode s [] _ -> pure n nodeToSoacNode n = pure n @@ -375,8 +376,10 @@ bodyInputs (Body _ stms res) = foldMap stmInputs stms <> freeClassifications res expInputs :: Exp SOACS -> Classifications-expInputs (If cond b1 b2 attr) =- bodyInputs b1 <> bodyInputs b2 <> freeClassifications (cond, attr)+expInputs (Match cond cases defbody attr) =+ foldMap (bodyInputs . caseBody) cases+ <> bodyInputs defbody+ <> freeClassifications (cond, attr) expInputs (DoLoop params form b1) = freeClassifications (params, form) <> bodyInputs b1 expInputs (Op soac) = case soac of@@ -407,7 +410,7 @@ (StmNode stm) -> stmNames stm (ResNode _) -> [] (FreeNode name) -> [name]- (IfNode stm _) -> stmNames stm+ (MatchNode stm _) -> stmNames stm (DoNode stm _) -> stmNames stm FinalNode {} -> error "Final nodes cannot generate edges" (SoacNode _ pat _ _) -> patNames pat
src/Futhark/Optimise/Fusion/TryFusion.hs view
@@ -746,16 +746,16 @@ pullReshape :: SOAC -> SOAC.ArrayTransforms -> TryFusion (SOAC, SOAC.ArrayTransforms) pullReshape (SOAC.Screma _ form inps) ots | Just maplam <- Futhark.isMapSOAC form,- SOAC.Reshape cs shape SOAC.:< ots' <- SOAC.viewf ots,+ SOAC.Reshape cs k shape SOAC.:< ots' <- SOAC.viewf ots, all primType $ lambdaReturnType maplam = do- let mapw' = case reverse $ newDims shape of+ let mapw' = case reverse $ shapeDims shape of [] -> intConst Int64 0 d : _ -> d trInput inp | arrayRank (SOAC.inputType inp) == 1 =- SOAC.addTransform (SOAC.Reshape cs shape) inp+ SOAC.addTransform (SOAC.Reshape cs k shape) inp | otherwise =- SOAC.addTransform (SOAC.ReshapeOuter cs shape) inp+ SOAC.addTransform (SOAC.ReshapeOuter cs k shape) inp inputs' = map trInput inps inputTypes = map SOAC.inputType inputs' @@ -784,12 +784,9 @@ op' <- foldM outersoac (SOAC.Screma mapw' $ Futhark.mapSOAC maplam) $- zip (drop 1 $ reverse $ newDims shape) $- drop 1 $- reverse $- drop 1 $- tails $- newDims shape+ zip (drop 1 $ reverse $ shapeDims shape) $+ drop 1 . reverse . drop 1 . tails $+ shapeDims shape pure (op' inputs', ots') pullReshape _ _ = fail "Cannot pull reshape"
src/Futhark/Optimise/GenRedOpt.hs view
@@ -414,8 +414,8 @@ costRedundantStmt (Let _ _ DoLoop {}) = Big costRedundantStmt (Let _ _ Apply {}) = Big costRedundantStmt (Let _ _ WithAcc {}) = Big-costRedundantStmt (Let _ _ (If _cond b_then b_else _)) =- maxCost (costBody b_then) (costBody b_else)+costRedundantStmt (Let _ _ (Match _ cases defbody _)) =+ L.foldl' maxCost (costBody defbody) $ map (costBody . caseBody) cases costRedundantStmt (Let _ _ (BasicOp (ArrayLit _ Array {}))) = Big costRedundantStmt (Let _ _ (BasicOp (ArrayLit _ _))) = Small 1 costRedundantStmt (Let _ _ (BasicOp (Index _ slc))) =
src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs view
@@ -37,8 +37,7 @@ ( MonadFreshNames m, BuilderOps rep, Buildable rep,- Aliased rep,- LParamInfo rep ~ Type+ Aliased rep ) => IndexSubstitutions -> Stms rep ->
src/Futhark/Optimise/MemoryBlockMerging.hs view
@@ -30,9 +30,8 @@ getAllocsStm (Let (Pat [PatElem name _]) _ (Op (Alloc se sp))) = M.singleton name (se, sp) getAllocsStm (Let _ _ (Op (Alloc _ _))) = error "impossible"-getAllocsStm (Let _ _ (If _ then_body else_body _)) =- foldMap getAllocsStm (bodyStms then_body)- <> foldMap getAllocsStm (bodyStms else_body)+getAllocsStm (Let _ _ (Match _ cases defbody _)) =+ foldMap (foldMap getAllocsStm . bodyStms) $ defbody : map caseBody cases getAllocsStm (Let _ _ (DoLoop _ _ body)) = foldMap getAllocsStm (bodyStms body) getAllocsStm _ = mempty@@ -54,15 +53,10 @@ setAllocsStm _ stm@(Let _ _ (Op (Alloc _ _))) = stm setAllocsStm m stm@(Let _ _ (Op (Inner (SegOp segop)))) = stm {stmExp = Op $ Inner $ SegOp $ setAllocsSegOp m segop}-setAllocsStm m stm@(Let _ _ (If cse then_body else_body dec)) =- stm- { stmExp =- If- cse- (then_body {bodyStms = setAllocsStm m <$> bodyStms then_body})- (else_body {bodyStms = setAllocsStm m <$> bodyStms else_body})- dec- }+setAllocsStm m stm@(Let _ _ (Match cond cases defbody dec)) =+ stm {stmExp = Match cond (map (fmap onBody) cases) (onBody defbody) dec}+ where+ onBody (Body () stms res) = Body () (setAllocsStm m <$> stms) res setAllocsStm m stm@(Let _ _ (DoLoop merge form body)) = stm { stmExp =@@ -172,23 +166,18 @@ (SegOp SegLevel GPUMem -> m (SegOp SegLevel GPUMem)) -> Stms GPUMem -> m (Stms GPUMem)-onKernels f stms = inScopeOf stms $ mapM helper stms+onKernels f orig_stms = inScopeOf orig_stms $ mapM helper orig_stms where helper stm@Let {stmExp = Op (Inner (SegOp segop))} = do exp' <- f segop pure $ stm {stmExp = Op $ Inner $ SegOp exp'}- helper stm@Let {stmExp = If c then_body else_body dec} = do- then_body_stms <- f `onKernels` bodyStms then_body- else_body_stms <- f `onKernels` bodyStms else_body- pure $- stm- { stmExp =- If- c- (then_body {bodyStms = then_body_stms})- (else_body {bodyStms = else_body_stms})- dec- }+ helper stm@Let {stmExp = Match c cases defbody dec} = do+ cases' <- mapM (traverse onBody) cases+ defbody' <- onBody defbody+ pure $ stm {stmExp = Match c cases' defbody' dec}+ where+ onBody (Body () stms res) =+ Body () <$> f `onKernels` stms <*> pure res helper stm@Let {stmExp = DoLoop merge form body} = do body_stms <- f `onKernels` bodyStms body pure $ stm {stmExp = DoLoop merge form (body {bodyStms = body_stms})}
src/Futhark/Optimise/MergeGPUBodies.hs view
@@ -12,6 +12,7 @@ import Control.Monad import Control.Monad.Trans.Class import Control.Monad.Trans.State.Strict hiding (State)+import Data.Bifunctor (first) import Data.Foldable import qualified Data.IntMap as IM import Data.IntSet ((\\))@@ -173,11 +174,13 @@ case e of BasicOp {} -> pure (removeExpAliases e, depsOf e) Apply {} -> pure (removeExpAliases e, depsOf e)- If c tbody fbody dec -> do- (tbody', t_deps) <- transformBody aliases tbody- (fbody', f_deps) <- transformBody aliases fbody- let deps = depsOf c <> t_deps <> f_deps <> depsOf dec- pure (If c tbody' fbody' dec, deps)+ Match ses cases defbody dec -> do+ let transformCase (Case vs body) =+ first (Case vs) <$> transformBody aliases body+ (cases', cases_deps) <- unzip <$> mapM transformCase cases+ (defbody', defbody_deps) <- transformBody aliases defbody+ let deps = depsOf ses <> mconcat cases_deps <> defbody_deps <> depsOf dec+ pure (Match ses cases' defbody' dec, deps) DoLoop merge lform body -> do -- What merge and lform aliases outside the loop is irrelevant as those -- cannot be consumed within the loop.
src/Futhark/Optimise/ReduceDeviceSyncs.hs view
@@ -15,11 +15,11 @@ import Data.Bifunctor (second) import Data.Foldable import qualified Data.IntMap.Strict as IM-import Data.List (unzip4, zip4)+import Data.List (transpose) import qualified Data.Map.Strict as M import Data.Sequence ((<|), (><), (|>)) import qualified Data.Text as T-import Futhark.Construct (fullSlice, sliceDim)+import Futhark.Construct (fullSlice, mkBody, sliceDim) import Futhark.Error import Futhark.IR.GPU import Futhark.MonadFreshNames@@ -149,50 +149,53 @@ pure (out |> stm) Apply {} -> pure (out |> stm)- If cond (Body _ tstms0 tres) (Body _ fstms0 fres) (IfDec btypes sort) ->- do- -- Rewrite branches.- tstms1 <- optimizeStms tstms0- fstms1 <- optimizeStms fstms0+ Match ses cases defbody (MatchDec btypes sort) -> do+ -- Rewrite branches.+ cases_stms <- mapM (optimizeStms . bodyStms . caseBody) cases+ let cases_res = map (bodyResult . caseBody) cases+ defbody_stms <- optimizeStms $ bodyStms defbody+ let defbody_res = bodyResult defbody - -- Ensure return values and types match if one or both branches- -- return a result that now reside on device.- let bmerge (res, tstms, fstms) (pe, tr, fr, bt) =- do- let onHost (Var v) = (v ==) <$> resolveName v- onHost _ = pure True+ -- Ensure return values and types match if one or both branches+ -- return a result that now reside on device.+ let bmerge (acc, all_stms) (pe, reses, bt) = do+ let onHost (Var v) = (v ==) <$> resolveName v+ onHost _ = pure True - tr_on_host <- onHost (resSubExp tr)- fr_on_host <- onHost (resSubExp fr)+ on_host <- and <$> mapM (onHost . resSubExp) reses - if tr_on_host && fr_on_host- then -- No result resides on device ==> nothing to do.- pure ((pe, tr, fr, bt) : res, tstms, fstms)- else -- Otherwise, ensure both results are migrated.- do- let t = patElemType pe- (tstms', tarr) <- storeScalar tstms (resSubExp tr) t- (fstms', farr) <- storeScalar fstms (resSubExp fr) t+ if on_host+ then -- No result resides on device ==> nothing to do.+ pure ((pe, reses, bt) : acc, all_stms)+ 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) - pe' <- arrayizePatElem pe- let bt' = staticShapes1 (patElemType pe')- let tr' = tr {resSubExp = Var tarr}- let fr' = fr {resSubExp = Var farr}- pure ((pe', tr', fr', bt') : res, tstms', fstms')+ pe' <- arrayizePatElem pe+ let bt' = staticShapes1 (patElemType pe')+ reses' = zipWith SubExpRes (map resCerts reses) (map Var arrs)+ pure ((pe', reses', bt') : acc, all_stms') - let pes = patElems (stmPat stm)- let zipped = zip4 pes tres fres btypes- (zipped', tstms2, fstms2) <- foldM bmerge ([], tstms1, fstms1) zipped- let (pes', tres', fres', btypes') = unzip4 (reverse zipped')+ pes = patElems (stmPat stm)+ (acc, ~(defbody_stms' : cases_stms')) <-+ foldM bmerge ([], defbody_stms : cases_stms) $+ zip3 pes (transpose $ defbody_res : cases_res) btypes+ let (pes', reses, btypes') = unzip3 (reverse acc) - -- Rewrite statement.- let tbranch' = Body () tstms2 tres'- let fbranch' = Body () fstms2 fres'- let e' = If cond tbranch' fbranch' (IfDec btypes' sort)- let stm' = Let (Pat pes') (stmAux stm) e'+ -- Rewrite statement.+ let cases' =+ zipWith Case (map casePat cases) $+ zipWith mkBody cases_stms' $+ drop 1 $+ transpose reses+ defbody' = mkBody defbody_stms' $ map head reses+ e' = Match ses cases' defbody' (MatchDec btypes' sort)+ stm' = Let (Pat pes') (stmAux stm) e' - -- Read migrated scalars that are used on host.- foldM addRead (out |> stm') (zip pes pes')+ -- Read migrated scalars that are used on host.+ foldM addRead (out |> stm') (zip pes pes') DoLoop ps lf b -> do -- Enable the migration of for-in loop variables. (params, lform, body) <- rewriteForIn (ps, lf, b)
src/Futhark/Optimise/ReduceDeviceSyncs/MigrationTable.hs view
@@ -62,6 +62,7 @@ import Data.Foldable import qualified Data.IntMap.Strict as IM import qualified Data.IntSet as IS+import qualified Data.List as L import qualified Data.Map.Strict as M import Data.Maybe (fromJust, fromMaybe, isJust, isNothing) import qualified Data.Sequence as SQ@@ -124,8 +125,8 @@ statusOf n mt /= StayOnHost shouldMoveStm (Let (Pat ((PatElem n _) : _)) _ Apply {}) mt = statusOf n mt /= StayOnHost-shouldMoveStm (Let _ _ (If (Var n) _ _ _)) mt =- statusOf n mt == MoveToDevice+shouldMoveStm (Let _ _ (Match cond _ _ _)) mt =+ all ((== MoveToDevice) . (`statusOf` mt)) $ subExpVars cond shouldMoveStm (Let _ _ (DoLoop _ (ForLoop _ _ (Var n) _) _)) mt = statusOf n mt == MoveToDevice shouldMoveStm (Let _ _ (DoLoop _ (WhileLoop n) _)) mt =@@ -213,15 +214,13 @@ checkExp (WithAcc _ _) = hostOnly checkExp (Op _) = hostOnly checkExp (Apply fn _ _ _) = Just (S.singleton fn)- checkExp (If _ tbranch fbranch _) = do- calls1 <- checkBody tbranch- calls2 <- checkBody fbranch- pure (calls1 <> calls2)+ checkExp (Match _ cases defbody _) =+ mconcat <$> mapM checkBody (defbody : map caseBody cases) checkExp (DoLoop params lform body) = do checkLParams params checkLoopForm lform checkBody body- checkExp _ = Just S.empty+ checkExp BasicOp {} = Just S.empty -------------------------------------------------------------------------------- -- MIGRATION ANALYSIS --@@ -467,8 +466,8 @@ -- Can be replaced with 'graphHostOnly e' to disable migration. -- A fix can be verified by enabling tests/migration/reuse4_scratch.fut graphInefficientReturn s e- BasicOp (Reshape s arr) -> do- graphInefficientReturn (newDims s) e+ BasicOp (Reshape _ s arr) -> do+ graphInefficientReturn (shapeDims s) e one bs `reuses` arr BasicOp (Rearrange _ arr) -> do graphInefficientReturn [] e@@ -515,8 +514,8 @@ graphUpdateAcc (one bs) e Apply fn _ _ _ -> graphApply fn bs e- If cond tbody fbody _ ->- graphIf bs cond tbody fbody+ Match ses cases defbody _ ->+ graphMatch bs ses cases defbody DoLoop params lform body -> graphLoop bs params lform body WithAcc inputs f ->@@ -609,28 +608,29 @@ then graphHostOnly e else graphSimple bs e --- | Graph an if statement.-graphIf :: [Binding] -> SubExp -> Body GPU -> Body GPU -> Grapher ()-graphIf bs cond tbody fbody = do+-- | Graph a Match statement.+graphMatch :: [Binding] -> [SubExp] -> [Case (Body GPU)] -> Body GPU -> Grapher ()+graphMatch bs ses cases defbody = do body_host_only <-- incForkDepthFor- ( do- tstats <- captureBodyStats (graphBody tbody)- fstats <- captureBodyStats (graphBody fbody)- pure $ bodyHostOnly tstats || bodyHostOnly fstats- )+ incForkDepthFor $+ any bodyHostOnly+ <$> mapM (captureBodyStats . graphBody) (defbody : map caseBody cases) + let branch_results = results defbody : map (results . caseBody) cases+ -- Record aliases for copyable memory backing returned arrays.- may_copy_results <- reusesBranches bs (results tbody) (results fbody)+ may_copy_results <- reusesBranches bs branch_results let may_migrate = not body_host_only && may_copy_results - cond_id <- case (may_migrate, cond) of- (False, Var n) ->- -- The migration status of the condition is what determines whether the- -- statement may be migrated as a whole or not. See 'shouldMoveStm'.- connectToSink (nameToId n) >> pure IS.empty- (True, Var n) -> onlyGraphedScalar n- (_, _) -> pure IS.empty+ cond_id <-+ if may_migrate+ then onlyGraphedScalars $ subExpVars ses+ else do+ -- The migration status of the condition is what determines+ -- whether the statement may be migrated as a whole or+ -- not. See 'shouldMoveStm'.+ mapM_ (connectToSink . nameToId) (subExpVars ses)+ pure IS.empty tellOperands cond_id @@ -659,15 +659,12 @@ -- of host-device reads it means that some reads may needlessly be delayed -- out of branches. The overhead as measured on futhark-benchmarks appears -- to be neglible though.- ret <- zipWithM (comb cond_id) (bodyResult tbody) (bodyResult fbody)+ ret <- mapM (comb cond_id) $ L.transpose branch_results mapM_ (uncurry createNode) (zip bs ret) where results = map resSubExp . bodyResult - comb ci a b = (ci <>) <$> onlyGraphedScalars (toSet a <> toSet b)-- toSet (SubExpRes _ (Var n)) = S.singleton n- toSet _ = S.empty+ comb ci a = (ci <>) <$> onlyGraphedScalars (S.fromList $ subExpVars a) ----------------------------------------------------- -- These type aliases are only used by 'graphLoop' --@@ -702,7 +699,7 @@ -- Does the loop return any arrays which prevent it from being migrated? let args = map snd params let results = map resSubExp (bodyResult body)- may_copy_results <- reusesBranches (b : bs) args results+ 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@@ -1017,8 +1014,10 @@ collectBasic b collect (Apply _ params _ _) = mapM_ (collectSubExp . fst) params- collect (If cond tbranch fbranch _) =- collectSubExp cond >> collectBody tbranch >> collectBody fbranch+ collect (Match ses cases defbody _) = do+ mapM_ collectSubExp ses+ mapM_ (collectBody . caseBody) cases+ collectBody defbody collect (DoLoop params lform body) = do mapM_ (collectSubExp . snd) params collectLForm lform@@ -1257,19 +1256,21 @@ | otherwise = pure onlyCopyable --- @reusesBranches bs b1 b2@ records each array binding in @bs@ as reusing--- copyable memory if each corresponding return value in the lists @b1@ and @b2@--- are backed by copyable memory.+-- @reusesBranches bs seses@ records each array binding in @bs@ as+-- reusing copyable memory if each corresponding return value in the+-- lists in @ses@ are backed by copyable memory. Each list is the+-- result of a branch body (i.e. for 'if' the list has two elements). ----- If every array binding is registered as being backed by copyable memory then--- the function returns @True@, otherwise it returns @False@.-reusesBranches :: [Binding] -> [SubExp] -> [SubExp] -> Grapher Bool-reusesBranches bs b1 b2 = do+-- If every array binding is registered as being backed by copyable+-- memory then the function returns @True@, otherwise it returns+-- @False@.+reusesBranches :: [Binding] -> [[SubExp]] -> Grapher Bool+reusesBranches bs seses = do body_depth <- metaBodyDepth <$> getMeta- foldM (reuse body_depth) True $ zip3 bs b1 b2+ foldM (reuse body_depth) True $ zip bs $ L.transpose seses where- reuse :: Int -> Bool -> (Binding, SubExp, SubExp) -> Grapher Bool- reuse body_depth onlyCopyable (b, se1, se2)+ reuse :: Int -> Bool -> (Binding, [SubExp]) -> Grapher Bool+ reuse body_depth onlyCopyable (b, ses) | all (== intConst Int64 1) (arrayDims $ snd b) = -- Single element arrays are immediately recognizable as copyable so -- don't bother recording those. Note that this case also matches@@ -1277,19 +1278,16 @@ pure onlyCopyable | (i, t) <- b, isArray t,- Var n1 <- se1,- Var n2 <- se2 =- do- body_depth_1 <- outermostCopyableArray n1- body_depth_2 <- outermostCopyableArray n2- case (body_depth_1, body_depth_2) of- (Just bd1, Just bd2) -> do- let inner = min bd1 bd2- recordCopyableMemory i (min body_depth inner)- let returns_free_var = inner <= body_depth- pure (onlyCopyable && not returns_free_var)- _ ->- pure False+ Just ns <- mapM subExpVar ses = do+ body_depths <- mapM outermostCopyableArray ns+ case sequence body_depths of+ Just bds -> do+ let inner = minimum bds+ recordCopyableMemory i (min body_depth inner)+ let returns_free_var = inner <= body_depth+ pure (onlyCopyable && not returns_free_var)+ _ ->+ pure False | otherwise = pure onlyCopyable
src/Futhark/Optimise/Simplify/Engine.hs view
@@ -72,7 +72,7 @@ import Control.Monad.Reader import Control.Monad.State.Strict import Data.Either-import Data.List (find, foldl', mapAccumL)+import Data.List (find, foldl', inits, mapAccumL) import qualified Data.Map as M import Data.Maybe import qualified Futhark.Analysis.SymbolTable as ST@@ -117,7 +117,7 @@ } -- | A function that protects a hoisted operation (if possible). The--- first operand is the condition of the 'If' we have hoisted out of+-- first operand is the condition of the 'Case' we have hoisted out of -- (or equivalently, a boolean indicating whether a loop has nonzero -- trip count). type Protect m = SubExp -> Pat (LetDec (Rep m)) -> Op (Rep m) -> Maybe (m ())@@ -140,6 +140,10 @@ -- actually be used. protectHoistedOpS :: Protect (Builder (Wise rep)), opUsageS :: Op (Wise rep) -> UT.UsageTable,+ simplifyPatFromExpS ::+ Pat (LetDec rep) ->+ Exp (Wise rep) ->+ SimpleM rep (Pat (LetDec rep)), simplifyOpS :: SimplifyOp rep (Op (Wise rep)) } @@ -148,11 +152,12 @@ SimplifyOp rep (Op (Wise rep)) -> SimpleOps rep bindableSimpleOps =- SimpleOps mkExpDecS' mkBodyS' protectHoistedOpS' (const mempty)+ SimpleOps mkExpDecS' mkBodyS' protectHoistedOpS' (const mempty) simplifyPatFromExp where mkExpDecS' _ pat e = pure $ mkExpDec pat e mkBodyS' _ stms res = pure $ mkBody stms res protectHoistedOpS' _ _ _ = Nothing+ simplifyPatFromExp pat _ = traverse simplify pat newtype SimpleM rep a = SimpleM@@ -262,37 +267,80 @@ bindLoopVar var it bound = localVtable $ ST.insertLoopVar var it bound --- | We are willing to hoist potentially unsafe statements out of--- branches, but they most be protected by adding a branch on top of--- them. (This means such hoisting is not worth it unless they are in--- turn hoisted out of a loop somewhere.)-protectIfHoisted ::- SimplifiableRep rep =>- -- | Branch condition.- SubExp ->- -- | Which side of the branch are we- -- protecting here?- Bool ->- SimpleM rep (Stms (Wise rep), a) ->- SimpleM rep (Stms (Wise rep), a)-protectIfHoisted cond side m = do- (hoisted, x) <- m- ops <- asks $ protectHoistedOpS . fst- hoisted' <- runBuilder_ $ do- if not $ all (safeExp . stmExp) hoisted- then do- cond' <-- if side- then pure cond- else letSubExp "cond_neg" $ BasicOp $ UnOp Not cond- mapM_ (protectIf ops unsafeOrCostly cond') hoisted- else addStms hoisted- pure (hoisted', x)+makeSafe :: Exp rep -> Maybe (Exp rep)+makeSafe (BasicOp (BinOp (SDiv t _) x y)) =+ Just $ BasicOp (BinOp (SDiv t Safe) x y)+makeSafe (BasicOp (BinOp (SDivUp t _) x y)) =+ Just $ BasicOp (BinOp (SDivUp t Safe) x y)+makeSafe (BasicOp (BinOp (SQuot t _) x y)) =+ Just $ BasicOp (BinOp (SQuot t Safe) x y)+makeSafe (BasicOp (BinOp (UDiv t _) x y)) =+ Just $ BasicOp (BinOp (UDiv t Safe) x y)+makeSafe (BasicOp (BinOp (UDivUp t _) x y)) =+ Just $ BasicOp (BinOp (UDivUp t Safe) x y)+makeSafe (BasicOp (BinOp (SMod t _) x y)) =+ Just $ BasicOp (BinOp (SMod t Safe) x y)+makeSafe (BasicOp (BinOp (SRem t _) x y)) =+ Just $ BasicOp (BinOp (SRem t Safe) x y)+makeSafe (BasicOp (BinOp (UMod t _) x y)) =+ Just $ BasicOp (BinOp (UMod t Safe) x y)+makeSafe _ =+ Nothing++emptyOfType :: MonadBuilder m => [VName] -> Type -> m (Exp (Rep m))+emptyOfType _ Mem {} =+ error "emptyOfType: Cannot hoist non-existential memory."+emptyOfType _ Acc {} =+ error "emptyOfType: Cannot hoist accumulator."+emptyOfType _ (Prim pt) =+ pure $ BasicOp $ SubExp $ Constant $ blankPrimValue pt+emptyOfType ctx_names (Array et shape _) = do+ let dims = map zeroIfContext $ shapeDims shape+ pure $ BasicOp $ Scratch et dims where- unsafeOrCostly e = not (safeExp e) || not (cheapExp e)+ zeroIfContext (Var v) | v `elem` ctx_names = intConst Int64 0+ zeroIfContext se = se +protectIf ::+ MonadBuilder m =>+ Protect m ->+ (Exp (Rep m) -> Bool) ->+ SubExp ->+ Stm (Rep m) ->+ m ()+protectIf _ _ taken (Let pat aux (Match [cond] [Case [Just (BoolValue True)] taken_body] untaken_body (MatchDec if_ts MatchFallback))) = do+ cond' <- letSubExp "protect_cond_conj" $ BasicOp $ BinOp LogAnd taken cond+ auxing aux . letBind pat $+ Match [cond'] [Case [Just (BoolValue True)] taken_body] untaken_body $+ MatchDec if_ts MatchFallback+protectIf _ _ taken (Let pat aux (BasicOp (Assert cond msg loc))) = do+ not_taken <- letSubExp "loop_not_taken" $ BasicOp $ UnOp Not taken+ cond' <- letSubExp "protect_assert_disj" $ BasicOp $ BinOp LogOr not_taken cond+ auxing aux $ letBind pat $ BasicOp $ Assert cond' msg loc+protectIf protect _ taken (Let pat aux (Op op))+ | Just m <- protect taken pat op =+ auxing aux m+protectIf _ f taken (Let pat aux e)+ | f e =+ case makeSafe e of+ Just e' ->+ auxing aux $ letBind pat e'+ Nothing -> do+ taken_body <- eBody [pure e]+ untaken_body <-+ eBody $ map (emptyOfType $ patNames pat) (patTypes pat)+ if_ts <- expTypesFromPat pat+ auxing aux . letBind pat+ $ Match+ [taken]+ [Case [Just $ BoolValue True] taken_body]+ untaken_body+ $ MatchDec if_ts MatchFallback+protectIf _ _ _ stm =+ addStm stm+ -- | We are willing to hoist potentially unsafe statements out of--- loops, but they most be protected by adding a branch on top of+-- loops, but they must be protected by adding a branch on top of -- them. protectLoopHoisted :: SimplifiableRep rep =>@@ -323,74 +371,61 @@ BasicOp $ CmpOp (CmpSlt it) (intConst it 0) bound -protectIf ::- MonadBuilder m =>- Protect m ->- (Exp (Rep m) -> Bool) ->- SubExp ->- Stm (Rep m) ->- m ()-protectIf _ _ taken (Let pat aux (If cond taken_body untaken_body (IfDec if_ts IfFallback))) = do- cond' <- letSubExp "protect_cond_conj" $ BasicOp $ BinOp LogAnd taken cond- auxing aux . letBind pat $- If cond' taken_body untaken_body $- IfDec if_ts IfFallback-protectIf _ _ taken (Let pat aux (BasicOp (Assert cond msg loc))) = do- not_taken <- letSubExp "loop_not_taken" $ BasicOp $ UnOp Not taken- cond' <- letSubExp "protect_assert_disj" $ BasicOp $ BinOp LogOr not_taken cond- auxing aux $ letBind pat $ BasicOp $ Assert cond' msg loc-protectIf protect _ taken (Let pat aux (Op op))- | Just m <- protect taken pat op =- auxing aux m-protectIf _ f taken (Let pat aux e)- | f e =- case makeSafe e of- Just e' ->- auxing aux $ letBind pat e'- Nothing -> do- taken_body <- eBody [pure e]- untaken_body <-- eBody $ map (emptyOfType $ patNames pat) (patTypes pat)- if_ts <- expTypesFromPat pat- auxing aux . letBind pat $- If taken taken_body untaken_body $- IfDec if_ts IfFallback-protectIf _ _ _ stm =- addStm stm+-- Produces a true subexpression if the pattern (as in a 'Case')+-- matches the subexpression.+matching ::+ BuilderOps rep =>+ [(SubExp, Maybe PrimValue)] ->+ Builder rep SubExp+matching = letSubExp "match" <=< eAll <=< sequence . mapMaybe cmp+ where+ cmp (se, Just (BoolValue True)) =+ Just $ pure se+ cmp (se, Just v) =+ Just . letSubExp "match_val" . BasicOp $+ CmpOp (CmpEq (primValueType v)) se (Constant v)+ cmp (_, Nothing) = Nothing -makeSafe :: Exp rep -> Maybe (Exp rep)-makeSafe (BasicOp (BinOp (SDiv t _) x y)) =- Just $ BasicOp (BinOp (SDiv t Safe) x y)-makeSafe (BasicOp (BinOp (SDivUp t _) x y)) =- Just $ BasicOp (BinOp (SDivUp t Safe) x y)-makeSafe (BasicOp (BinOp (SQuot t _) x y)) =- Just $ BasicOp (BinOp (SQuot t Safe) x y)-makeSafe (BasicOp (BinOp (UDiv t _) x y)) =- Just $ BasicOp (BinOp (UDiv t Safe) x y)-makeSafe (BasicOp (BinOp (UDivUp t _) x y)) =- Just $ BasicOp (BinOp (UDivUp t Safe) x y)-makeSafe (BasicOp (BinOp (SMod t _) x y)) =- Just $ BasicOp (BinOp (SMod t Safe) x y)-makeSafe (BasicOp (BinOp (SRem t _) x y)) =- Just $ BasicOp (BinOp (SRem t Safe) x y)-makeSafe (BasicOp (BinOp (UMod t _) x y)) =- Just $ BasicOp (BinOp (UMod t Safe) x y)-makeSafe _ =- Nothing+matchingExactlyThis ::+ BuilderOps rep =>+ [SubExp] ->+ [[Maybe PrimValue]] ->+ [Maybe PrimValue] ->+ Builder rep SubExp+matchingExactlyThis ses prior this = do+ prior_matches <- mapM (matching . zip ses) prior+ letSubExp "matching_just_this"+ =<< eBinOp+ LogAnd+ (eUnOp Not (eAny prior_matches))+ (eSubExp =<< matching (zip ses this)) -emptyOfType :: MonadBuilder m => [VName] -> Type -> m (Exp (Rep m))-emptyOfType _ Mem {} =- error "emptyOfType: Cannot hoist non-existential memory."-emptyOfType _ Acc {} =- error "emptyOfType: Cannot hoist accumulator."-emptyOfType _ (Prim pt) =- pure $ BasicOp $ SubExp $ Constant $ blankPrimValue pt-emptyOfType ctx_names (Array et shape _) = do- let dims = map zeroIfContext $ shapeDims shape- pure $ BasicOp $ Scratch et dims+-- | We are willing to hoist potentially unsafe statements out of+-- matches, but they must be protected by adding a branch on top of+-- them. (This means such hoisting is not worth it unless they are in+-- turn hoisted out of a loop somewhere.)+protectCaseHoisted ::+ SimplifiableRep rep =>+ -- | Scrutinee.+ [SubExp] ->+ -- | Pattern of previosu cases.+ [[Maybe PrimValue]] ->+ -- | Pattern of this case.+ [Maybe PrimValue] ->+ SimpleM rep (Stms (Wise rep), a) ->+ SimpleM rep (Stms (Wise rep), a)+protectCaseHoisted ses prior vs m = do+ (hoisted, x) <- m+ ops <- asks $ protectHoistedOpS . fst+ hoisted' <- runBuilder_ $ do+ if not $ all (safeExp . stmExp) hoisted+ then do+ cond' <- matchingExactlyThis ses prior vs+ mapM_ (protectIf ops unsafeOrCostly cond') hoisted+ else addStms hoisted+ pure (hoisted', x) where- zeroIfContext (Var v) | v `elem` ctx_names = intConst Int64 0- zeroIfContext se = se+ unsafeOrCostly e = not (safeExp e) || not (cheapExp e) -- | Statements that are not worth hoisting out of loops, because they -- are unsafe, and added safety (by 'protectLoopHoisted') may inhibit@@ -407,9 +442,11 @@ SimpleM rep (Stm (Wise rep)) nonrecSimplifyStm (Let pat (StmAux cs attrs (_, dec)) e) = do cs' <- simplify cs- (pat', pat_cs) <- collectCerts $ simplifyPat $ removePatWisdom pat+ e' <- simplifyExpBase e+ simplifyPat <- asks $ simplifyPatFromExpS . fst+ (pat', pat_cs) <- collectCerts $ simplifyPat (removePatWisdom pat) e' let aux' = StmAux (cs' <> pat_cs) attrs dec- mkWiseStm pat' aux' <$> simplifyExpBase e+ pure $ mkWiseStm pat' aux' e' -- Bottom-up simplify a statement. Recurses into sub-Bodies and Ops. -- Does not copy-propagate into the pattern and similar, as it is@@ -623,9 +660,9 @@ cheapExp (BasicOp Concat {}) = False cheapExp (BasicOp Manifest {}) = False cheapExp DoLoop {} = False-cheapExp (If _ tbranch fbranch _) =- all cheapStm (bodyStms tbranch)- && all cheapStm (bodyStms fbranch)+cheapExp (Match _ cases defbranch _) =+ all (all cheapStm . bodyStms . caseBody) cases+ && all cheapStm (bodyStms defbranch) cheapExp (Op op) = cheapOp op cheapExp _ = True -- Used to be False, but -- let's try it out.@@ -634,21 +671,12 @@ loopInvariantStm vtable = all (`nameIn` ST.availableAtClosestLoop vtable) . namesToList . freeIn -hoistCommon ::- SimplifiableRep rep =>- UT.UsageTable ->- [UT.Usages] ->- SubExp ->- IfDec (BranchType rep) ->- Body (Wise rep) ->- Body (Wise rep) ->- SimpleM- rep- ( Body (Wise rep),- Body (Wise rep),- Stms (Wise rep)- )-hoistCommon res_usage res_usages cond (IfDec _ ifsort) body1 body2 = do+matchBlocker ::+ (ASTRep rep, CanBeWise (Op rep), FreeIn a) =>+ a ->+ MatchDec rt ->+ SimpleM rep (BlockPred (Wise rep))+matchBlocker cond (MatchDec _ ifsort) = do is_alloc_fun <- asksEngineEnv $ isAllocation . envHoistBlockers branch_blocker <- asksEngineEnv $ blockHoistBranch . envHoistBlockers vtable <- askVtable@@ -667,13 +695,13 @@ is_alloc_fun stm || ( ST.loopDepth vtable > 0 && cond_loop_invariant- && ifsort /= IfFallback+ && ifsort /= MatchFallback && loopInvariantStm vtable stm -- Avoid hoisting out something that might change the -- asymptotics of the program. && all primType (patTypes (stmPat stm)) )- || ( ifsort /= IfFallback+ || ( ifsort /= MatchFallback && any (`UT.isSize` usage) (patNames (stmPat stm)) && all primType (patTypes (stmPat stm)) )@@ -694,7 +722,7 @@ | is_alloc_fun stm = False isNotHoistableBnd _ _ _ = -- Hoist aggressively out of versioning branches.- ifsort /= IfEquiv+ ifsort /= MatchEquiv block = branch_blocker@@ -702,14 +730,7 @@ `andAlso` notDesirableToHoist ) `orIf` isConsuming-- (hoisted1, body1') <-- protectIfHoisted cond True $- simplifyBody block res_usage res_usages body1- (hoisted2, body2') <-- protectIfHoisted cond False $- simplifyBody block res_usage res_usages body2- pure (body1', body2', hoisted1 <> hoisted2)+ pure block -- | Simplify a single body. simplifyBody ::@@ -797,16 +818,27 @@ Pat (LetDec (Wise rep)) -> Exp (Wise rep) -> SimpleM rep (Exp (Wise rep), Stms (Wise rep))-simplifyExp usage (Pat pes) (If cond tbranch fbranch ifdec@(IfDec ts ifsort)) = do- -- Here, we have to check whether 'cond' puts a bound on some free- -- variable, and if so, chomp it. We should also try to do CSE- -- across branches.+simplifyExp usage (Pat pes) (Match ses cases defbody ifdec@(MatchDec ts ifsort)) = do let pes_usages = map (fromMaybe mempty . (`UT.lookup` usage) . patElemName) pes- cond' <- simplify cond+ ses' <- mapM simplify ses ts' <- mapM simplify ts- (tbranch', fbranch', hoisted) <-- hoistCommon usage pes_usages cond' ifdec tbranch fbranch- pure (If cond' tbranch' fbranch' $ IfDec ts' ifsort, hoisted)+ let pats = map casePat cases+ block <- matchBlocker ses ifdec+ (cases_hoisted, cases') <-+ unzip <$> zipWithM (simplifyCase block ses' pes_usages) (inits pats) cases+ (defbody_hoisted, defbody') <-+ protectCaseHoisted ses' pats [] $+ simplifyBody block usage pes_usages defbody+ pure+ ( Match ses' cases' defbody' $ MatchDec ts' ifsort,+ mconcat $ defbody_hoisted : cases_hoisted+ )+ where+ simplifyCase block ses' pes_usages prior (Case vs body) = do+ (hoisted, body') <-+ protectCaseHoisted ses' prior vs $+ simplifyBody block usage pes_usages body+ pure (hoisted, Case vs body') simplifyExp _ _ (DoLoop merge form loopbody) = do let (params, args) = unzip merge params' <- mapM (traverse simplify) params@@ -990,15 +1022,6 @@ cs' <- simplify cs (se', se_cs) <- collectCerts $ simplify se pure $ SubExpRes (se_cs <> cs') se'--simplifyPat ::- (SimplifiableRep rep, Simplifiable dec) =>- Pat dec ->- SimpleM rep (Pat dec)-simplifyPat (Pat xs) =- Pat <$> mapM inspect xs- where- inspect (PatElem name rep) = PatElem name <$> simplify rep instance Simplifiable () where simplify = pure
src/Futhark/Optimise/Simplify/Rep.hs view
@@ -315,8 +315,8 @@ -- | Construct a 'Wise' expression. informExp :: Informing rep => Exp rep -> Exp (Wise rep)-informExp (If cond tbranch fbranch (IfDec ts ifsort)) =- If cond (informBody tbranch) (informBody fbranch) (IfDec ts ifsort)+informExp (Match cond cases defbody (MatchDec ts ifsort)) =+ Match cond (map (fmap informBody) cases) (informBody defbody) (MatchDec ts ifsort) informExp (DoLoop merge form loopbody) = let form' = case form of ForLoop i it bound params -> ForLoop i it bound params
src/Futhark/Optimise/Simplify/Rule.hs view
@@ -24,7 +24,7 @@ SimplificationRule (..), RuleGeneric, RuleBasicOp,- RuleIf,+ RuleMatch, RuleDoLoop, -- * Top-down rules@@ -32,7 +32,7 @@ TopDownRule, TopDownRuleGeneric, TopDownRuleBasicOp,- TopDownRuleIf,+ TopDownRuleMatch, TopDownRuleDoLoop, TopDownRuleOp, @@ -41,7 +41,7 @@ BottomUpRule, BottomUpRuleGeneric, BottomUpRuleBasicOp,- BottomUpRuleIf,+ BottomUpRuleMatch, BottomUpRuleDoLoop, BottomUpRuleOp, @@ -72,7 +72,7 @@ LocalScope rep ) -instance (ASTRep rep, BuilderOps rep) => MonadBuilder (RuleM rep) where+instance (BuilderOps rep) => MonadBuilder (RuleM rep) where type Rep (RuleM rep) = rep mkExpDecM pat e = RuleM $ mkExpDecM pat e mkBodyM stms res = RuleM $ mkBodyM stms res@@ -116,14 +116,14 @@ Rule rep ) -type RuleIf rep a =+type RuleMatch rep a = a -> Pat (LetDec rep) -> StmAux (ExpDec rep) ->- ( SubExp,- Body rep,+ ( [SubExp],+ [Case (Body rep)], Body rep,- IfDec (BranchType rep)+ MatchDec (BranchType rep) ) -> Rule rep @@ -149,7 +149,7 @@ data SimplificationRule rep a = RuleGeneric (RuleGeneric rep a) | RuleBasicOp (RuleBasicOp rep a)- | RuleIf (RuleIf rep a)+ | RuleMatch (RuleMatch rep a) | RuleDoLoop (RuleDoLoop rep a) | RuleOp (RuleOp rep a) @@ -158,7 +158,7 @@ data Rules rep a = Rules { rulesAny :: [SimplificationRule rep a], rulesBasicOp :: [SimplificationRule rep a],- rulesIf :: [SimplificationRule rep a],+ rulesMatch :: [SimplificationRule rep a], rulesDoLoop :: [SimplificationRule rep a], rulesOp :: [SimplificationRule rep a] }@@ -178,7 +178,7 @@ type TopDownRuleBasicOp rep = RuleBasicOp rep (TopDown rep) -type TopDownRuleIf rep = RuleIf rep (TopDown rep)+type TopDownRuleMatch rep = RuleMatch rep (TopDown rep) type TopDownRuleDoLoop rep = RuleDoLoop rep (TopDown rep) @@ -194,7 +194,7 @@ type BottomUpRuleBasicOp rep = RuleBasicOp rep (BottomUp rep) -type BottomUpRuleIf rep = RuleIf rep (BottomUp rep)+type BottomUpRuleMatch rep = RuleMatch rep (BottomUp rep) type BottomUpRuleDoLoop rep = RuleDoLoop rep (BottomUp rep) @@ -231,19 +231,20 @@ groupRules :: [SimplificationRule m a] -> Rules m a groupRules rs = Rules- rs- (filter forBasicOp rs)- (filter forIf rs)- (filter forDoLoop rs)- (filter forOp rs)+ { rulesAny = rs,+ rulesBasicOp = filter forBasicOp rs,+ rulesMatch = filter forMatch rs,+ rulesDoLoop = filter forDoLoop rs,+ rulesOp = filter forOp rs+ } forBasicOp RuleBasicOp {} = True forBasicOp RuleGeneric {} = True forBasicOp _ = False - forIf RuleIf {} = True- forIf RuleGeneric {} = True- forIf _ = False+ forMatch RuleMatch {} = True+ forMatch RuleGeneric {} = True+ forMatch _ = False forDoLoop RuleDoLoop {} = True forDoLoop RuleGeneric {} = True@@ -283,7 +284,7 @@ BasicOp {} -> rulesBasicOp DoLoop {} -> rulesDoLoop Op {} -> rulesOp- If {} -> rulesIf+ Match {} -> rulesMatch _ -> rulesAny applyRule :: SimplificationRule rep a -> a -> Stm rep -> Rule rep@@ -291,8 +292,8 @@ applyRule (RuleBasicOp f) a (Let pat aux (BasicOp e)) = f a pat aux e applyRule (RuleDoLoop f) a (Let pat aux (DoLoop merge form body)) = f a pat aux (merge, form, body)-applyRule (RuleIf f) a (Let pat aux (If cond tbody fbody ifsort)) =- f a pat aux (cond, tbody, fbody, ifsort)+applyRule (RuleMatch f) a (Let pat aux (Match cond cases defbody ifsort)) =+ f a pat aux (cond, cases, defbody, ifsort) applyRule (RuleOp f) a (Let pat aux (Op op)) = f a pat aux op applyRule _ _ _ =
src/Futhark/Optimise/Simplify/Rules.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} @@ -22,7 +21,6 @@ import Control.Monad import Control.Monad.State-import Data.Either import Data.List (insert, unzip4, zip4) import qualified Data.Map.Strict as M import Data.Maybe@@ -36,20 +34,18 @@ import Futhark.Optimise.Simplify.Rules.BasicOp import Futhark.Optimise.Simplify.Rules.Index import Futhark.Optimise.Simplify.Rules.Loop+import Futhark.Optimise.Simplify.Rules.Match import Futhark.Util topDownRules :: BuilderOps rep => [TopDownRule rep] topDownRules = [ RuleGeneric constantFoldPrimFun,- RuleIf ruleIf,- RuleIf hoistBranchInvariant, RuleGeneric withAccTopDown ] bottomUpRules :: (BuilderOps rep, TraverseOpStms rep) => [BottomUpRule rep] bottomUpRules =- [ RuleIf removeDeadBranchResult,- RuleGeneric withAccBottomUp,+ [ RuleGeneric withAccBottomUp, RuleBasicOp simplifyIndex ] @@ -57,7 +53,11 @@ -- functional semantics, and so probably should not be applied after -- memory block merging. standardRules :: (BuilderOps rep, TraverseOpStms rep, Aliased rep) => RuleBook rep-standardRules = ruleBook topDownRules bottomUpRules <> loopRules <> basicOpRules+standardRules =+ ruleBook topDownRules bottomUpRules+ <> loopRules+ <> basicOpRules+ <> matchRules -- | Turn @copy(x)@ into @x@ iff @x@ is not used after this copy -- statement and it can be consumed.@@ -135,211 +135,6 @@ seType (Constant v) = Just $ Prim $ primValueType v simplifyIndex _ _ _ _ = Skip -ruleIf :: BuilderOps rep => TopDownRuleIf rep-ruleIf _ pat _ (e1, tb, fb, IfDec _ ifsort)- | Just branch <- checkBranch,- ifsort /= IfFallback || isCt1 e1 = Simplify $ do- let ses = bodyResult branch- addStms $ bodyStms branch- sequence_- [ certifying cs $ letBindNames [patElemName p] $ BasicOp $ SubExp se- | (p, SubExpRes cs se) <- zip (patElems pat) ses- ]- where- checkBranch- | isCt1 e1 = Just tb- | isCt0 e1 = Just fb- | otherwise = Nothing---- IMPROVE: the following two rules can be generalised to work in more--- cases, especially when the branches have bindings, or return more--- than one value.------ if c then True else v == c || v-ruleIf- _- pat- _- ( cond,- Body _ tstms [SubExpRes tcs (Constant (BoolValue True))],- Body _ fstms [SubExpRes fcs se],- IfDec ts _- )- | null tstms,- null fstms,- [Prim Bool] <- map extTypeOf ts =- Simplify $ certifying (tcs <> fcs) $ letBind pat $ BasicOp $ BinOp LogOr cond se--- When type(x)==bool, if c then x else y == (c && x) || (!c && y)-ruleIf _ pat _ (cond, tb, fb, IfDec ts _)- | Body _ tstms [SubExpRes tcs tres] <- tb,- Body _ fstms [SubExpRes fcs fres] <- fb,- all (safeExp . stmExp) $ tstms <> fstms,- all ((== Prim Bool) . extTypeOf) ts = Simplify $ do- addStms tstms- addStms fstms- e <-- eBinOp- LogOr- (pure $ BasicOp $ BinOp LogAnd cond tres)- ( eBinOp- LogAnd- (pure $ BasicOp $ UnOp Not cond)- (pure $ BasicOp $ SubExp fres)- )- certifying (tcs <> fcs) $ letBind pat e-ruleIf _ pat _ (_, tbranch, _, IfDec _ IfFallback)- | all (safeExp . stmExp) $ bodyStms tbranch = Simplify $ do- let ses = bodyResult tbranch- addStms $ bodyStms tbranch- sequence_- [ certifying cs $ letBindNames [patElemName p] $ BasicOp $ SubExp se- | (p, SubExpRes cs se) <- zip (patElems pat) ses- ]-ruleIf _ pat _ (cond, tb, fb, _)- | Body _ _ [SubExpRes tcs (Constant (IntValue t))] <- tb,- Body _ _ [SubExpRes fcs (Constant (IntValue f))] <- fb =- if oneIshInt t && zeroIshInt f && tcs == mempty && fcs == mempty- then- Simplify $- letBind pat $- BasicOp $- ConvOp (BToI (intValueType t)) cond- else- if zeroIshInt t && oneIshInt f- then Simplify $ do- cond_neg <- letSubExp "cond_neg" $ BasicOp $ UnOp Not cond- letBind pat $ BasicOp $ ConvOp (BToI (intValueType t)) cond_neg- else Skip--- Simplify------ let z = if c then x else y------ to------ let z = y------ in the case where 'x' is a loop parameter with initial value 'y'--- and the new value of the loop parameter is 'z'. ('x' and 'y' can--- be flipped.)-ruleIf vtable (Pat [pe]) aux (_c, tb, fb, IfDec [_] _)- | Body _ tstms [SubExpRes xcs x] <- tb,- null tstms,- Body _ fstms [SubExpRes ycs y] <- fb,- null fstms,- matches x y || matches y x =- Simplify $- certifying (stmAuxCerts aux <> xcs <> ycs) $- letBind (Pat [pe]) $- BasicOp $- SubExp y- where- z = patElemName pe- matches (Var x) y- | Just (initial, res) <- ST.lookupLoopParam x vtable =- initial == y && res == Var z- matches _ _ = False-ruleIf _ _ _ _ = Skip---- | Move out results of a conditional expression whose computation is--- either invariant to the branches (only done for results used for--- existentials), or the same in both branches.-hoistBranchInvariant :: BuilderOps rep => TopDownRuleIf rep-hoistBranchInvariant _ pat _ (cond, tb, fb, IfDec ret ifsort) = Simplify $ do- let tses = bodyResult tb- fses = bodyResult fb- (hoistings, (pes, ts, res)) <-- fmap (fmap unzip3 . partitionEithers) . mapM branchInvariant $- zip4 [0 ..] (patElems pat) ret (zip tses fses)- let ctx_fixes = catMaybes hoistings- (tses', fses') = unzip res- tb' = tb {bodyResult = tses'}- fb' = fb {bodyResult = fses'}- ret' = foldr (uncurry fixExt) ts ctx_fixes- if not $ null hoistings -- Was something hoisted?- then do- -- We may have to add some reshapes if we made the type- -- less existential.- tb'' <- reshapeBodyResults tb' $ map extTypeOf ret'- fb'' <- reshapeBodyResults fb' $ map extTypeOf ret'- letBind (Pat pes) $ If cond tb'' fb'' (IfDec ret' ifsort)- else cannotSimplify- where- bound_in_branches =- namesFromList . concatMap (patNames . stmPat) $- bodyStms tb <> bodyStms fb- invariant Constant {} = True- invariant (Var v) = v `notNameIn` bound_in_branches-- branchInvariant (i, pe, t, (tse, fse))- -- Do both branches return the same value?- | tse == fse = do- certifying (resCerts tse <> resCerts fse) $- letBindNames [patElemName pe] $- BasicOp $- SubExp $- resSubExp tse- hoisted i pe-- -- Do both branches return values that are free in the- -- branch, and are we not the only pattern element? The- -- latter is to avoid infinite application of this rule.- | invariant $ resSubExp tse,- invariant $ resSubExp fse,- patSize pat > 1,- Prim _ <- patElemType pe = do- bt <- expTypesFromPat $ Pat [pe]- letBindNames [patElemName pe]- =<< ( If cond- <$> resultBodyM [resSubExp tse]- <*> resultBodyM [resSubExp fse]- <*> pure (IfDec bt ifsort)- )- hoisted i pe- | otherwise =- pure $ Right (pe, t, (tse, fse))-- hoisted i pe = pure $ Left $ Just (i, Var $ patElemName pe)-- reshapeBodyResults body rets = buildBody_ $ do- ses <- bodyBind body- let (ctx_ses, val_ses) = splitFromEnd (length rets) ses- (ctx_ses ++) <$> zipWithM reshapeResult val_ses rets- reshapeResult (SubExpRes cs (Var v)) t@Array {} = do- v_t <- lookupType v- let newshape = arrayDims $ removeExistentials t v_t- SubExpRes cs- <$> if newshape /= arrayDims v_t- then letSubExp "branch_ctx_reshaped" (shapeCoerce newshape v)- else pure $ Var v- reshapeResult se _ =- pure se---- | Remove the return values of a branch, that are not actually used--- after a branch. Standard dead code removal can remove the branch--- if *none* of the return values are used, but this rule is more--- precise.-removeDeadBranchResult :: BuilderOps rep => BottomUpRuleIf rep-removeDeadBranchResult (_, used) pat _ (e1, tb, fb, IfDec rettype ifsort)- | -- Only if there is no existential binding...- all (`notNameIn` foldMap freeIn (patElems pat)) (patNames pat),- -- Figure out which of the names in 'pat' are used...- patused <- map (`UT.isUsedDirectly` used) $ patNames pat,- -- If they are not all used, then this rule applies.- not (and patused) =- -- Remove the parts of the branch-results that correspond to dead- -- return value bindings. Note that this leaves dead code in the- -- branch bodies, but that will be removed later.- let tses = bodyResult tb- fses = bodyResult fb- pick :: [a] -> [a]- pick = map snd . filter fst . zip patused- tb' = tb {bodyResult = pick tses}- fb' = fb {bodyResult = pick fses}- pat' = pick $ patElems pat- rettype' = pick rettype- in Simplify $ letBind (Pat pat') $ If e1 tb' fb' $ IfDec rettype' ifsort- | otherwise = Skip- withAccTopDown :: BuilderOps rep => TopDownRuleGeneric rep -- A WithAcc with no accumulators is sent to Valhalla. withAccTopDown _ (Let pat aux (WithAcc [] lam)) = Simplify . auxing aux $ do@@ -465,16 +260,6 @@ getRidOf (pes, _) = not $ any ((`UT.used` utable) . patElemName) pes keepNonAccRes (pe, _) = patElemName pe `UT.used` utable withAccBottomUp _ _ = Skip---- Some helper functions--isCt1 :: SubExp -> Bool-isCt1 (Constant v) = oneIsh v-isCt1 _ = False--isCt0 :: SubExp -> Bool-isCt0 (Constant v) = zeroIsh v-isCt0 _ = False -- Note [Dead Code Elimination for WithAcc] --
src/Futhark/Optimise/Simplify/Rules/BasicOp.hs view
@@ -196,9 +196,8 @@ case se of Var v | not $ null $ sliceDims is -> do v_reshaped <-- letExp (baseString v ++ "_reshaped") $- BasicOp $- Reshape (map DimNew $ arrayDims dest_t) v+ letExp (baseString v ++ "_reshaped") . BasicOp $+ Reshape ReshapeArbitrary (arrayShape dest_t) v letBind pat $ BasicOp $ Copy v_reshaped _ -> letBind pat $ BasicOp $ ArrayLit [se] $ rowType dest_t ruleBasicOp vtable pat (StmAux cs1 attrs _) (Update safety1 dest1 is1 (Var v1))@@ -217,7 +216,7 @@ where simplifyWith (Var v) x | Just stm <- ST.lookupStm v vtable,- If p tbranch fbranch _ <- stmExp stm,+ Match [p] [Case [Just (BoolValue True)] tbranch] fbranch _ <- stmExp stm, Just (y, z) <- returns v (stmPat stm) tbranch fbranch, not $ boundInBody tbranch `namesIntersect` freeIn y,@@ -264,23 +263,20 @@ in letBind pat $ BasicOp $ Replicate (Shape [n]) se ruleBasicOp vtable pat aux (Index idd slice) | Just inds <- sliceIndices slice,- Just (BasicOp (Reshape newshape idd2), idd_cs) <- ST.lookupExp idd vtable,+ Just (BasicOp (Reshape k newshape idd2), idd_cs) <- ST.lookupExp idd vtable, length newshape == length inds = Simplify $- case shapeCoercion newshape of- Just _ ->- certifying idd_cs $- auxing aux $- letBind pat $- BasicOp $- Index idd2 slice- Nothing -> do+ case k of+ ReshapeCoerce ->+ certifying idd_cs . auxing aux . letBind pat . BasicOp $+ Index idd2 slice+ ReshapeArbitrary -> do -- Linearise indices and map to old index space. oldshape <- arrayDims <$> lookupType idd2 let new_inds = reshapeIndex (map pe64 oldshape)- (map pe64 $ newDims newshape)+ (map pe64 $ shapeDims newshape) (map pe64 inds) new_inds' <- mapM (toSubExp "new_index") new_inds
src/Futhark/Optimise/Simplify/Rules/ClosedForm.hs view
@@ -39,7 +39,7 @@ -- | @foldClosedForm look foldfun accargs arrargs@ determines whether -- each of the results of @foldfun@ can be expressed in a closed form. foldClosedForm ::- (ASTRep rep, BuilderOps rep) =>+ (BuilderOps rep) => VarLookup rep -> Pat (LetDec rep) -> Lambda rep ->@@ -68,10 +68,10 @@ BasicOp $ CmpOp (CmpEq int64) inputsize (intConst Int64 0) letBind pat- =<< ( If (Var isEmpty)- <$> resultBodyM accs+ =<< ( Match [Var isEmpty]+ <$> (pure . Case [Just $ BoolValue True] <$> resultBodyM accs) <*> renameBody closedBody- <*> pure (IfDec [primBodyType t] IfNormal)+ <*> pure (MatchDec [primBodyType t] MatchNormal) ) where knownBnds = determineKnownBindings look lam accs arrs@@ -79,7 +79,7 @@ -- | @loopClosedForm pat respat merge bound bodys@ determines whether -- the do-loop can be expressed in a closed form. loopClosedForm ::- (ASTRep rep, BuilderOps rep) =>+ (BuilderOps rep) => Pat (LetDec rep) -> [(FParam rep, SubExp)] -> Names ->@@ -108,10 +108,10 @@ CmpOp (CmpSlt it) bound (intConst it 0) letBind pat- =<< ( If (Var isEmpty)- <$> resultBodyM mergeexp+ =<< ( Match [Var isEmpty]+ <$> (pure . Case [Just (BoolValue True)] <$> resultBodyM mergeexp) <*> renameBody closedBody- <*> pure (IfDec [primBodyType t] IfNormal)+ <*> pure (MatchDec [primBodyType t] MatchNormal) ) where (mergepat, mergeexp) = unzip merge
src/Futhark/Optimise/Simplify/Rules/Index.hs view
@@ -154,18 +154,16 @@ not consuming, ST.available src vtable -> Just $ pure $ IndexResult cs src $ Slice inds- Just (Reshape newshape src, cs)- | Just newdims <- shapeCoercion newshape,- Just olddims <- arrayDims <$> seType (Var src),- changed_dims <- zipWith (/=) newdims olddims,+ Just (Reshape ReshapeCoerce newshape src, cs)+ | Just olddims <- arrayDims <$> seType (Var src),+ changed_dims <- zipWith (/=) (shapeDims newshape) olddims, not $ or $ drop (length inds) changed_dims -> Just $ pure $ IndexResult cs src $ Slice inds- | Just newdims <- shapeCoercion newshape,- Just olddims <- arrayDims <$> seType (Var src),+ | Just olddims <- arrayDims <$> seType (Var src), length newshape == length inds,- length olddims == length newdims ->+ length olddims == length (shapeDims newshape) -> Just $ pure $ IndexResult cs src $ Slice inds- Just (Reshape [_] v2, cs)+ Just (Reshape _ (Shape [_]) v2, cs) | Just [_] <- arrayDims <$> seType (Var v2) -> Just $ pure $ IndexResult cs v2 $ Slice inds Just (Concat d (x :| xs) _, cs)@@ -197,14 +195,13 @@ (thisres, thisstms) <- collectStms $ do i' <- letSubExp "index_concat_i" $ BasicOp $ BinOp (Sub Int64 OverflowWrap) i start letSubExp "index_concat" . BasicOp . Index x' $- Slice $- ibef ++ DimFix i' : iaft+ Slice (ibef ++ DimFix i' : iaft) thisbody <- mkBodyM thisstms [subExpRes thisres] (altres, altstms) <- collectStms $ mkBranch xs_and_starts' altbody <- mkBodyM altstms [subExpRes altres] letSubExp "index_concat_branch" $- If cmp thisbody altbody $- IfDec [primBodyType res_t] IfNormal+ Match [cmp] [Case [Just $ BoolValue True] thisbody] altbody $+ MatchDec [primBodyType res_t] MatchNormal SubExpResult cs <$> mkBranch xs_and_starts Just (ArrayLit ses _, cs) | DimFix (Constant (IntValue (Int64Value i))) : inds' <- inds,
+ src/Futhark/Optimise/Simplify/Rules/Match.hs view
@@ -0,0 +1,251 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++-- | Match simplification rules.+module Futhark.Optimise.Simplify.Rules.Match (matchRules) where++import Control.Monad+import Data.Either+import Data.List (partition, transpose, unzip4, zip5)+import Futhark.Analysis.PrimExp.Convert+import qualified Futhark.Analysis.SymbolTable as ST+import qualified Futhark.Analysis.UsageTable as UT+import Futhark.Construct+import Futhark.IR+import Futhark.Optimise.Simplify.Rule+import Futhark.Util++-- Does this case always match the scrutinees?+caseAlwaysMatches :: [SubExp] -> Case a -> Bool+caseAlwaysMatches ses = and . zipWith match ses . casePat+ where+ match se (Just v) = se == Constant v+ match _ Nothing = True++-- Can this case never match the scrutinees?+caseNeverMatches :: [SubExp] -> Case a -> Bool+caseNeverMatches ses = or . zipWith impossible ses . casePat+ where+ impossible (Constant v1) (Just v2) = v1 /= v2+ impossible _ _ = False++ruleMatch :: BuilderOps rep => TopDownRuleMatch rep+-- Remove impossible cases.+ruleMatch _ pat _ (cond, cases, defbody, ifdec)+ | (impossible, cases') <- partition (caseNeverMatches cond) cases,+ not $ null impossible =+ Simplify $ letBind pat $ Match cond cases' defbody ifdec+-- Find new default case.+ruleMatch _ pat _ (cond, cases, _, ifdec)+ | (always_matches, cases') <- partition (caseAlwaysMatches cond) cases,+ new_default : _ <- reverse always_matches =+ Simplify $ letBind pat $ Match cond cases' (caseBody new_default) ifdec+-- Remove caseless match.+ruleMatch _ pat (StmAux cs _ _) (_, [], defbody, _) = Simplify $ do+ defbody_res <- bodyBind defbody+ certifying cs $ forM_ (zip (patElems pat) defbody_res) $ \(pe, res) ->+ certifying (resCerts res) . letBind (Pat [pe]) $+ BasicOp (SubExp $ resSubExp res)+-- IMPROVE: the following two rules can be generalised to work in more+-- cases, especially when the branches have bindings, or return more+-- than one value.+--+-- if c then True else v == c || v+ruleMatch+ _+ pat+ _+ ( [cond],+ [ Case+ [Just (BoolValue True)]+ (Body _ tstms [SubExpRes tcs (Constant (BoolValue True))])+ ],+ Body _ fstms [SubExpRes fcs se],+ MatchDec ts _+ )+ | null tstms,+ null fstms,+ [Prim Bool] <- map extTypeOf ts =+ Simplify $ certifying (tcs <> fcs) $ letBind pat $ BasicOp $ BinOp LogOr cond se+-- When type(x)==bool, if c then x else y == (c && x) || (!c && y)+ruleMatch _ pat _ ([cond], [Case [Just (BoolValue True)] tb], fb, MatchDec ts _)+ | Body _ tstms [SubExpRes tcs tres] <- tb,+ Body _ fstms [SubExpRes fcs fres] <- fb,+ all (safeExp . stmExp) $ tstms <> fstms,+ all ((== Prim Bool) . extTypeOf) ts = Simplify $ do+ addStms tstms+ addStms fstms+ e <-+ eBinOp+ LogOr+ (pure $ BasicOp $ BinOp LogAnd cond tres)+ ( eBinOp+ LogAnd+ (pure $ BasicOp $ UnOp Not cond)+ (pure $ BasicOp $ SubExp fres)+ )+ certifying (tcs <> fcs) $ letBind pat e+ruleMatch _ pat _ (_, [Case _ tbranch], _, MatchDec _ MatchFallback)+ | all (safeExp . stmExp) $ bodyStms tbranch = Simplify $ do+ let ses = bodyResult tbranch+ addStms $ bodyStms tbranch+ sequence_+ [ certifying cs $ letBindNames [patElemName p] $ BasicOp $ SubExp se+ | (p, SubExpRes cs se) <- zip (patElems pat) ses+ ]+ruleMatch _ pat _ ([cond], [Case [Just (BoolValue True)] tb], fb, _)+ | Body _ _ [SubExpRes tcs (Constant (IntValue t))] <- tb,+ Body _ _ [SubExpRes fcs (Constant (IntValue f))] <- fb =+ if oneIshInt t && zeroIshInt f && tcs == mempty && fcs == mempty+ then+ Simplify . letBind pat . BasicOp $+ ConvOp (BToI (intValueType t)) cond+ else+ if zeroIshInt t && oneIshInt f+ then Simplify $ do+ cond_neg <- letSubExp "cond_neg" $ BasicOp $ UnOp Not cond+ letBind pat $ BasicOp $ ConvOp (BToI (intValueType t)) cond_neg+ else Skip+-- Simplify+--+-- let z = if c then x else y+--+-- to+--+-- let z = y+--+-- in the case where 'x' is a loop parameter with initial value 'y'+-- and the new value of the loop parameter is 'z'. ('x' and 'y' can+-- be flipped.)+ruleMatch vtable (Pat [pe]) aux (_c, [Case _ tb], fb, MatchDec [_] _)+ | Body _ tstms [SubExpRes xcs x] <- tb,+ null tstms,+ Body _ fstms [SubExpRes ycs y] <- fb,+ null fstms,+ matches x y || matches y x =+ Simplify . certifying (stmAuxCerts aux <> xcs <> ycs) $+ letBind (Pat [pe]) (BasicOp $ SubExp y)+ where+ z = patElemName pe+ matches (Var x) y+ | Just (initial, res) <- ST.lookupLoopParam x vtable =+ initial == y && res == Var z+ matches _ _ = False+ruleMatch _ _ _ _ = Skip++-- | Move out results of a conditional expression whose computation is+-- either invariant to the branches (only done for results used for+-- existentials), or the same in both branches.+hoistBranchInvariant :: BuilderOps rep => TopDownRuleMatch rep+hoistBranchInvariant _ pat _ (cond, cases, defbody, MatchDec ret ifsort) =+ let case_reses = map (bodyResult . caseBody) cases+ defbody_res = bodyResult defbody+ (hoistings, (pes, ts, case_reses_tr, defbody_res')) =+ (fmap unzip4 . partitionEithers) . map branchInvariant $+ zip5 [0 ..] (patElems pat) ret (transpose case_reses) defbody_res+ in if null hoistings+ then Skip+ else Simplify $ do+ ctx_fixes <- sequence hoistings+ let onCase (Case vs body) case_res = Case vs $ body {bodyResult = case_res}+ cases' = zipWith onCase cases $ transpose case_reses_tr+ defbody' = defbody {bodyResult = defbody_res'}+ ret' = foldr (uncurry fixExt) ts ctx_fixes+ -- We may have to add some reshapes if we made the type+ -- less existential.+ cases'' <- mapM (traverse $ reshapeBodyResults $ map extTypeOf ret') cases'+ defbody'' <- reshapeBodyResults (map extTypeOf ret') defbody'+ letBind (Pat pes) $ Match cond cases'' defbody'' (MatchDec ret' ifsort)+ where+ bound_in_branches =+ namesFromList . concatMap (patNames . stmPat) $+ foldMap (bodyStms . caseBody) cases <> bodyStms defbody++ branchInvariant (i, pe, t, case_reses, defres)+ -- If just one branch has a variant result, then we give up.+ | namesIntersect bound_in_branches $ freeIn $ defres : case_reses =+ noHoisting+ -- Do all branches return the same value?+ | all ((== resSubExp defres) . resSubExp) case_reses = Left $ do+ certifying (foldMap resCerts case_reses <> resCerts defres) $+ letBindNames [patElemName pe] . BasicOp . SubExp $+ resSubExp defres+ hoisted i pe++ -- Do all branches return values that are free in the+ -- branch, and are we not the only pattern element? The+ -- latter is to avoid infinite application of this rule.+ | not $ namesIntersect bound_in_branches $ freeIn $ defres : case_reses,+ patSize pat > 1,+ Prim _ <- patElemType pe = Left $ do+ bt <- expTypesFromPat $ Pat [pe]+ letBindNames [patElemName pe]+ =<< ( Match cond+ <$> ( zipWith Case (map casePat cases)+ <$> mapM (resultBodyM . pure . resSubExp) case_reses+ )+ <*> resultBodyM [resSubExp defres]+ <*> pure (MatchDec bt ifsort)+ )+ hoisted i pe+ | otherwise = noHoisting+ where+ noHoisting = Right (pe, t, case_reses, defres)++ hoisted i pe = pure (i, Var $ patElemName pe)++ reshapeBodyResults rets body = buildBody_ $ do+ ses <- bodyBind body+ let (ctx_ses, val_ses) = splitFromEnd (length rets) ses+ (ctx_ses ++) <$> zipWithM reshapeResult val_ses rets+ reshapeResult (SubExpRes cs (Var v)) t@Array {} = do+ v_t <- lookupType v+ let newshape = arrayDims $ removeExistentials t v_t+ SubExpRes cs+ <$> if newshape /= arrayDims v_t+ then letSubExp "branch_ctx_reshaped" (shapeCoerce newshape v)+ else pure $ Var v+ reshapeResult se _ =+ pure se++-- | Remove the return values of a branch, that are not actually used+-- after a branch. Standard dead code removal can remove the branch+-- if *none* of the return values are used, but this rule is more+-- precise.+removeDeadBranchResult :: BuilderOps rep => BottomUpRuleMatch rep+removeDeadBranchResult (_, used) pat _ (cond, cases, defbody, MatchDec rettype ifsort)+ | -- Only if there is no existential binding...+ all (`notNameIn` foldMap freeIn (patElems pat)) (patNames pat),+ -- Figure out which of the names in 'pat' are used...+ patused <- map (`UT.isUsedDirectly` used) $ patNames pat,+ -- If they are not all used, then this rule applies.+ not (and patused) = do+ -- Remove the parts of the branch-results that correspond to dead+ -- return value bindings. Note that this leaves dead code in the+ -- branch bodies, but that will be removed later.+ let pick :: [a] -> [a]+ pick = map snd . filter fst . zip patused+ pat' = pick $ patElems pat+ rettype' = pick rettype+ Simplify $ do+ cases' <- mapM (traverse $ onBody pick) cases+ defbody' <- onBody pick defbody+ letBind (Pat pat') $ Match cond cases' defbody' $ MatchDec rettype' ifsort+ | otherwise = Skip+ where+ onBody pick (Body _ stms res) = mkBodyM stms $ pick res++topDownRules :: BuilderOps rep => [TopDownRule rep]+topDownRules =+ [ RuleMatch ruleMatch,+ RuleMatch hoistBranchInvariant+ ]++bottomUpRules :: (BuilderOps rep) => [BottomUpRule rep]+bottomUpRules =+ [ RuleMatch removeDeadBranchResult+ ]++matchRules :: (BuilderOps rep) => RuleBook rep+matchRules = ruleBook topDownRules bottomUpRules
src/Futhark/Optimise/Simplify/Rules/Simple.hs view
@@ -264,48 +264,48 @@ simplifyAssert _ _ _ = Nothing +-- No-op reshape. simplifyIdentityReshape :: SimpleRule rep-simplifyIdentityReshape _ seType (Reshape newshape v)+simplifyIdentityReshape _ seType (Reshape _ newshape v) | Just t <- seType $ Var v,- newDims newshape == arrayDims t -- No-op reshape.- =+ newshape == arrayShape t = resIsSubExp $ Var v simplifyIdentityReshape _ _ _ = Nothing simplifyReshapeReshape :: SimpleRule rep-simplifyReshapeReshape defOf _ (Reshape newshape v)- | Just (BasicOp (Reshape oldshape v2), v_cs) <- defOf v =- Just (Reshape (fuseReshape oldshape newshape) v2, v_cs)+simplifyReshapeReshape defOf _ (Reshape k1 newshape v)+ | Just (BasicOp (Reshape k2 _ v2), v_cs) <- defOf v =+ Just (Reshape (max k1 k2) newshape v2, v_cs) simplifyReshapeReshape _ _ _ = Nothing simplifyReshapeScratch :: SimpleRule rep-simplifyReshapeScratch defOf _ (Reshape newshape v)+simplifyReshapeScratch defOf _ (Reshape _ newshape v) | Just (BasicOp (Scratch bt _), v_cs) <- defOf v =- Just (Scratch bt $ newDims newshape, v_cs)+ Just (Scratch bt $ shapeDims newshape, v_cs) simplifyReshapeScratch _ _ _ = Nothing simplifyReshapeReplicate :: SimpleRule rep-simplifyReshapeReplicate defOf seType (Reshape newshape v)+simplifyReshapeReplicate defOf seType (Reshape _ newshape v) | Just (BasicOp (Replicate _ se), v_cs) <- defOf v, Just oldshape <- arrayShape <$> seType se,- shapeDims oldshape `isSuffixOf` newDims newshape =+ shapeDims oldshape `isSuffixOf` shapeDims newshape = let new = take (length newshape - shapeRank oldshape) $- newDims newshape+ shapeDims newshape in Just (Replicate (Shape new) se, v_cs) simplifyReshapeReplicate _ _ _ = Nothing simplifyReshapeIota :: SimpleRule rep-simplifyReshapeIota defOf _ (Reshape newshape v)+simplifyReshapeIota defOf _ (Reshape _ newshape v) | Just (BasicOp (Iota _ offset stride it), v_cs) <- defOf v,- [n] <- newDims newshape =+ [n] <- shapeDims newshape = Just (Iota n offset stride it, v_cs) simplifyReshapeIota _ _ _ = Nothing simplifyReshapeConcat :: SimpleRule rep-simplifyReshapeConcat defOf seType (Reshape newshape v) = do+simplifyReshapeConcat defOf seType (Reshape ReshapeCoerce newshape v) = do (BasicOp (Concat d arrs _), v_cs) <- defOf v- (bef, w', aft) <- focusNth d =<< shapeCoercion newshape+ (bef, w', aft) <- focusNth d $ shapeDims newshape (arr_bef, _, arr_aft) <- focusNth d <=< fmap arrayDims $ seType $ Var $ NE.head arrs guard $ arr_bef == bef@@ -323,10 +323,9 @@ -- If we are size-coercing a slice, then we might as well just use a -- different slice instead. simplifyReshapeIndex :: SimpleRule rep-simplifyReshapeIndex defOf _ (Reshape newshape v)- | Just ds <- shapeCoercion newshape,- Just (BasicOp (Index v' slice), v_cs) <- defOf v,- slice' <- Slice $ reshapeSlice (unSlice slice) ds,+simplifyReshapeIndex defOf _ (Reshape ReshapeCoerce newshape v)+ | Just (BasicOp (Index v' slice), v_cs) <- defOf v,+ slice' <- Slice $ reshapeSlice (unSlice slice) $ shapeDims newshape, slice' /= slice = Just (Index v' slice', v_cs) simplifyReshapeIndex _ _ _ = Nothing@@ -335,22 +334,13 @@ -- instead use the original array and update the slice dimensions. simplifyUpdateReshape :: SimpleRule rep simplifyUpdateReshape defOf seType (Update safety dest slice (Var v))- | Just (BasicOp (Reshape newshape v'), v_cs) <- defOf v,- Just _ <- shapeCoercion newshape,+ | Just (BasicOp (Reshape ReshapeCoerce _ v'), v_cs) <- defOf v, Just ds <- arrayDims <$> seType (Var v'), slice' <- Slice $ reshapeSlice (unSlice slice) ds, slice' /= slice = Just (Update safety dest slice' $ Var v', v_cs) simplifyUpdateReshape _ _ _ = Nothing -improveReshape :: SimpleRule rep-improveReshape _ seType (Reshape newshape v)- | Just t <- seType $ Var v,- newshape' <- informReshape (arrayDims t) newshape,- newshape' /= newshape =- Just (Reshape newshape' v, mempty)-improveReshape _ _ _ = Nothing- -- | If we are copying a scratch array (possibly indirectly), just turn it into a scratch by -- itself. copyScratchToScratch :: SimpleRule rep@@ -364,7 +354,7 @@ case asBasicOp . fst =<< defOf v of Just Scratch {} -> True Just (Rearrange _ v') -> isActuallyScratch v'- Just (Reshape _ v') -> isActuallyScratch v'+ Just (Reshape _ _ v') -> isActuallyScratch v' _ -> False copyScratchToScratch _ _ _ = Nothing@@ -384,8 +374,7 @@ simplifyReshapeIota, simplifyReshapeConcat, simplifyReshapeIndex,- simplifyUpdateReshape,- improveReshape+ simplifyUpdateReshape ] -- | Try to simplify the given t'BasicOp', returning a new t'BasicOp'
src/Futhark/Optimise/Sink.hs view
@@ -81,13 +81,16 @@ multiplicity :: Constraints rep => Stm rep -> M.Map VName Int multiplicity stm = case stmExp stm of- If cond tbranch fbranch _ ->- free cond 1 `comb` free tbranch 1 `comb` free fbranch 1- Op {} -> free stm 2- DoLoop {} -> free stm 2- _ -> free stm 1+ Match cond cases defbody _ ->+ foldl comb mempty $+ free 1 cond+ : free 1 defbody+ : map (free 1 . caseBody) cases+ Op {} -> free 2 stm+ DoLoop {} -> free 2 stm+ _ -> free 1 stm where- free x k = M.fromList $ zip (namesToList $ freeIn x) $ repeat k+ free k x = M.fromList $ zip (namesToList $ freeIn x) $ repeat k comb = M.unionWith (+) optimiseBranch ::@@ -172,12 +175,15 @@ in if patElemName pe `nameIn` sunk then (stms', sunk) else (stm : stms', sunk)- | If cond tbranch fbranch ret <- stmExp stm =- let (tbranch', tsunk) = optimiseBranch onOp vtable sinking tbranch- (fbranch', fsunk) = optimiseBranch onOp vtable sinking fbranch+ | Match cond cases defbody ret <- stmExp stm =+ let onCase (Case vs body) =+ let (body', body_sunk) = optimiseBranch onOp vtable sinking body+ in (Case vs body', body_sunk)+ (cases', cases_sunk) = unzip $ map onCase cases+ (defbody', defbody_sunk) = optimiseBranch onOp vtable sinking defbody (stms', sunk) = optimiseStms' vtable' sinking stms- in ( stm {stmExp = If cond tbranch' fbranch' ret} : stms',- tsunk <> fsunk <> sunk+ in ( stm {stmExp = Match cond cases' defbody' ret} : stms',+ mconcat cases_sunk <> defbody_sunk <> sunk ) | DoLoop merge lform body <- stmExp stm = let comps = (merge, lform, body)
src/Futhark/Optimise/TileLoops.hs view
@@ -750,8 +750,9 @@ if null dims_on_top || null (arrayDims arr_t) -- Second check is for accumulators. then pure arr else do- let new_shape = unit_dims ++ arrayDims arr_t- letExp (baseString arr) $ BasicOp $ Reshape (map DimNew new_shape) arr+ let new_shape = Shape $ unit_dims ++ arrayDims arr_t+ letExp (baseString arr) . BasicOp $+ Reshape ReshapeArbitrary new_shape arr let tile_dims = zip (map snd dims_on_top) unit_dims ++ dims pure $ TileReturns mempty tile_dims arr'
@@ -311,8 +311,10 @@ _ -> pure env changeIxFnEnv :: IxFnEnv -> VName -> Exp GPU -> TileM IxFnEnv-changeIxFnEnv env y (BasicOp (Reshape shp_chg x)) =- composeIxfuns env y x (`IxFun.reshape` map (fmap ExpMem.pe64) shp_chg)+changeIxFnEnv env y (BasicOp (Reshape ReshapeArbitrary shp_chg x)) =+ composeIxfuns env y x (`IxFun.reshape` fmap ExpMem.pe64 (shapeDims shp_chg))+changeIxFnEnv env y (BasicOp (Reshape ReshapeCoerce shp_chg x)) =+ composeIxfuns env y x (`IxFun.coerce` fmap ExpMem.pe64 (shapeDims shp_chg)) changeIxFnEnv env y (BasicOp (Manifest perm x)) = do tp <- lookupType x case tp of@@ -323,8 +325,6 @@ _ -> error "In TileLoops/Shared.hs, changeIxFnEnv: manifest applied to a non-array!" changeIxFnEnv env y (BasicOp (Rearrange perm x)) = composeIxfuns env y x (`IxFun.permute` perm)-changeIxFnEnv env y (BasicOp (Rotate rs x)) =- composeIxfuns env y x (`IxFun.rotate` fmap ExpMem.pe64 rs) changeIxFnEnv env y (BasicOp (Index x slc)) = composeIxfuns env y x (`IxFun.slice` (Slice $ map (fmap ExpMem.pe64) $ unSlice slc)) changeIxFnEnv env y (BasicOp (Opaque _ (Var x))) =
src/Futhark/Pass/ExpandAllocations.hs view
@@ -9,6 +9,7 @@ import Control.Monad.Reader import Control.Monad.State import Control.Monad.Writer+import Data.Either (rights) import Data.List (find, foldl') import qualified Data.Map.Strict as M import Data.Maybe@@ -86,26 +87,20 @@ transformStm :: Stm GPUMem -> ExpandM (Stms GPUMem) -- It is possible that we are unable to expand allocations in some -- code versions. If so, we can remove the offending branch. Only if--- both versions fail do we propagate the error.-transformStm (Let pat aux (If cond tbranch fbranch (IfDec ts IfEquiv))) = do- tbranch' <- (Right <$> transformBody tbranch) `catchError` (pure . Left)- fbranch' <- (Right <$> transformBody fbranch) `catchError` (pure . Left)- case (tbranch', fbranch') of- (Left _, Right fbranch'') ->- pure $ useBranch fbranch''- (Right tbranch'', Left _) ->- pure $ useBranch tbranch''- (Right tbranch'', Right fbranch'') ->- pure $ oneStm $ Let pat aux $ If cond tbranch'' fbranch'' (IfDec ts IfEquiv)- (Left e, _) ->+-- all versions fail do we propagate the error.+-- FIXME: this can remove safety checks if the default branch fails!+transformStm (Let pat aux (Match cond cases defbody (MatchDec ts MatchEquiv))) = do+ let onCase (Case vs body) =+ (Right . Case vs <$> transformBody body) `catchError` (pure . Left)+ cases' <- rights <$> mapM onCase cases+ defbody' <- (Right <$> transformBody defbody) `catchError` (pure . Left)+ case (cases', defbody') of+ ([], Left e) -> throwError e- where- bindRes pe (SubExpRes cs se) =- certify cs $ Let (Pat [pe]) (defAux ()) $ BasicOp $ SubExp se-- useBranch b =- bodyStms b- <> stmsFromList (zipWith bindRes (patElems pat) (bodyResult b))+ (_ : _, Left _) ->+ pure $ oneStm $ Let pat aux $ Match cond (init cases') (caseBody $ last cases') (MatchDec ts MatchEquiv)+ (_, Right defbody'') ->+ pure $ oneStm $ Let pat aux $ Match cond cases' defbody'' (MatchDec ts MatchEquiv) transformStm (Let pat aux e) = do (stms, e') <- transformExp =<< mapExpM transform e pure $ stms <> oneStm (Let pat aux e')@@ -531,11 +526,9 @@ offset_ixfun = IxFun.slice root_ixfun . Slice $ [DimSlice 0 num_threads' 1, DimFix gtid]- shapechange =- if length old_shape == 1- then map DimCoercion old_shape- else map DimNew old_shape- in IxFun.reshape offset_ixfun shapechange+ in if length old_shape == 1+ then IxFun.coerce offset_ixfun old_shape+ else IxFun.reshape offset_ixfun old_shape -- | A map from memory block names to new index function bases. type RebaseMap = M.Map VName (([TPrimExp Int64 VName], PrimType) -> IxFun)
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -45,7 +45,11 @@ import Control.Monad.Reader import Control.Monad.State import Control.Monad.Writer-import Data.List (foldl', partition, zip5)+import Data.Bifunctor (first)+import Data.Either (partitionEithers)+import Data.Foldable (toList)+import Data.List (foldl', transpose, zip4)+import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M import Data.Maybe import qualified Data.Set as S@@ -58,7 +62,7 @@ import Futhark.Optimise.Simplify.Rep (mkWiseBody) import Futhark.Pass import Futhark.Tools-import Futhark.Util (maybeNth, splitAt3, splitFromEnd, takeLast)+import Futhark.Util (maybeNth, splitAt3) -- | The subexpression giving the number of elements we should -- allocate space for. See 'ChunkMap' comment.@@ -310,7 +314,7 @@ pure $ MemAcc acc ispace ts u summaryForBindage def_space chunkmap t@(Array pt shape u) NoHint = do m <- allocForArray' chunkmap t def_space- pure $ directIxFun pt shape u m t+ pure $ MemArray pt shape u $ ArrayIn m $ IxFun.iota $ map pe64 $ arrayDims t summaryForBindage _ _ t@(Array pt _ _) (Hint ixfun space) = do bytes <- letSubExp "bytes" <=< toExp . untyped $@@ -321,27 +325,15 @@ m <- letExp "mem" $ Op $ Alloc bytes space pure $ MemArray pt (arrayShape t) NoUniqueness $ ArrayIn m ixfun -lookupMemSpace :: (HasScope rep m, Monad m) => VName -> m Space-lookupMemSpace v = do- t <- lookupType v- case t of- Mem space -> pure space- _ -> error $ "lookupMemSpace: " ++ pretty v ++ " is not a memory block."--directIxFun :: PrimType -> Shape -> u -> VName -> Type -> MemBound u-directIxFun bt shape u mem t =- let ixf = IxFun.iota $ map pe64 $ arrayDims t- in MemArray bt shape u $ ArrayIn mem ixf- allocInFParams :: (Allocable fromrep torep inner) => [(FParam fromrep, Space)] -> ([FParam torep] -> AllocM fromrep torep a) -> AllocM fromrep torep a allocInFParams params m = do- (valparams, (ctxparams, memparams)) <-+ (valparams, (memparams, ctxparams)) <- runWriterT $ mapM (uncurry allocInFParam) params- let params' = ctxparams <> memparams <> valparams+ let params' = memparams <> ctxparams <> valparams summary = scopeOfFParams params' localScope summary $ m params' @@ -359,7 +351,7 @@ let memname = baseString (paramName param) <> "_mem" ixfun = IxFun.iota $ map pe64 $ shapeDims shape mem <- lift $ newVName memname- tell ([], [Param (paramAttrs param) mem $ MemMem pspace])+ tell ([Param (paramAttrs param) mem $ MemMem pspace], []) pure param {paramDec = MemArray pt shape u $ ArrayIn mem ixfun} Prim pt -> pure param {paramDec = MemPrim pt}@@ -368,6 +360,38 @@ Acc acc ispace ts u -> pure param {paramDec = MemAcc acc ispace ts u} +ensureRowMajorArray ::+ (Allocable fromrep torep inner) =>+ Maybe Space ->+ VName ->+ AllocM fromrep torep (VName, VName)+ensureRowMajorArray space_ok v = do+ (mem, ixfun) <- lookupArraySummary v+ mem_space <- lookupMemSpace mem+ default_space <- askDefaultSpace+ let space = fromMaybe default_space space_ok+ if numLMADs ixfun == 1+ && ixFunPerm ixfun == [0 .. IxFun.rank ixfun - 1]+ && length (IxFun.base ixfun) == IxFun.rank ixfun+ && maybe True (== mem_space) space_ok+ && IxFun.contiguous ixfun+ then pure (mem, v)+ else allocLinearArray space (baseString v) v++ensureArrayIn ::+ (Allocable fromrep torep inner) =>+ Space ->+ SubExp ->+ WriterT ([SubExp], [SubExp]) (AllocM fromrep torep) SubExp+ensureArrayIn _ (Constant v) =+ error $ "ensureArrayIn: " ++ pretty v ++ " cannot be an array."+ensureArrayIn space (Var v) = do+ (mem', v') <- lift $ ensureRowMajorArray (Just space) v+ (_, ixfun) <- lift $ lookupArraySummary v'+ ctx <- lift $ mapM (letSubExp "ixfun_arg" <=< toExp) (toList ixfun)+ tell ([Var mem'], ctx)+ pure $ Var v'+ allocInMergeParams :: (Allocable fromrep torep inner) => [(FParam fromrep, SubExp)] ->@@ -377,21 +401,19 @@ ) -> AllocM fromrep torep a allocInMergeParams merge m = do- ((valparams, valargs, handle_loop_subexps), (ctx_params, mem_params)) <-+ ((valparams, valargs, handle_loop_subexps), (mem_params, ctx_params)) <- runWriterT $ unzip3 <$> mapM allocInMergeParam merge- let mergeparams' = ctx_params <> mem_params <> valparams+ let mergeparams' = mem_params <> ctx_params <> valparams summary = scopeOfFParams mergeparams' mk_loop_res ses = do- (ses', (ctxargs, memargs)) <-+ (ses', (memargs, ctxargs)) <- runWriterT $ zipWithM ($) handle_loop_subexps ses- pure (ctxargs <> memargs, ses')+ pure (memargs <> ctxargs, ses') (valctx_args, valargs') <- mk_loop_res valargs let merge' =- zip- (ctx_params <> mem_params <> valparams)- (valctx_args <> valargs')+ zip (mem_params <> ctx_params <> valparams) (valctx_args <> valargs') localScope summary $ m merge' mk_loop_res where param_names = namesFromList $ map (paramName . fst) merge@@ -407,7 +429,7 @@ 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- tell ([], [Var res_mem'])+ tell ([Var res_mem'], []) pure $ Var res' scalarRes _ _ _ se = pure se @@ -439,7 +461,7 @@ allocInMergeParam (mergeparam, Var v') else do p <- newParam "mem_param" $ MemMem v_mem_space- tell ([], [p])+ tell ([p], []) pure ( mergeparam {paramDec = MemArray pt shape u $ ArrayIn (paramName p) v_ixfun},@@ -447,28 +469,27 @@ scalarRes param_t v_mem_space v_ixfun ) _ -> do- (v', ext_ixfun, substs, v_mem') <-- lift $ existentializeArray v_mem_space v+ (v_mem', v') <- lift $ ensureRowMajorArray Nothing v+ (_, v_ixfun') <- lift $ lookupArraySummary v' v_mem_space' <- lift $ lookupMemSpace v_mem' - (ctx_params, param_ixfun_substs) <-- fmap unzip . forM substs $ \e -> do- p <- newParam "ctx_param_ext" $ MemPrim $ primExpType $ untyped e- pure (p, fmap Free $ le64 $ paramName p)-- tell (ctx_params, [])+ ctx_params <-+ replicateM (length v_ixfun') $+ newParam "ctx_param_ext" (MemPrim int64) param_ixfun <- instantiateIxFun $ IxFun.substituteInIxFun- (M.fromList $ zip (fmap Ext [0 ..]) param_ixfun_substs)- ext_ixfun+ ( M.fromList . zip (fmap Ext [0 ..]) $+ map (le64 . Free . paramName) ctx_params+ )+ (IxFun.existentialize v_ixfun') mem_param <- newParam "mem_param" $ MemMem v_mem_space'- tell ([], [mem_param])+ tell ([mem_param], ctx_params) pure ( mergeparam {paramDec = MemArray pt shape u $ ArrayIn (paramName mem_param) param_ixfun},- v',+ Var v', ensureArrayIn v_mem_space' ) allocInMergeParam (mergeparam, se) = doDefault mergeparam se =<< lift askDefaultSpace@@ -477,26 +498,6 @@ mergeparam' <- allocInFParam mergeparam space pure (mergeparam', se, linearFuncallArg (paramType mergeparam) space) --- Returns the existentialized index function, the list of substituted values and the memory location.-existentializeArray ::- (Allocable fromrep torep inner) =>- Space ->- VName ->- AllocM fromrep torep (SubExp, ExtIxFun, [TPrimExp Int64 VName], VName)-existentializeArray space v = do- (mem', ixfun) <- lookupArraySummary v- sp <- lookupMemSpace mem'-- let (ext_ixfun', substs') = runState (IxFun.existentialize ixfun) []-- case (ext_ixfun', sp == space) of- (Just x, True) -> pure (Var v, x, substs', mem')- _ -> do- (mem, v') <- allocLinearArray space (baseString v) v- ixfun' <- fromJust <$> lookupIxFun v'- let (ext_ixfun, substs) = runState (IxFun.existentialize ixfun') []- pure (Var v', fromJust ext_ixfun, substs, mem)- arrayWithIxFun :: (MonadBuilder m, Op (Rep m) ~ MemOp inner, LetDec (Rep m) ~ LetDecMem) => ChunkMap ->@@ -512,28 +513,6 @@ letBind (Pat [PatElem v_copy $ MemArray pt shape u $ ArrayIn mem ixfun]) $ BasicOp $ Copy v pure (mem, v_copy) -ensureArrayIn ::- (Allocable fromrep torep inner) =>- Space ->- SubExp ->- WriterT ([SubExp], [SubExp]) (AllocM fromrep torep) SubExp-ensureArrayIn _ (Constant v) =- error $ "ensureArrayIn: " ++ pretty v ++ " cannot be an array."-ensureArrayIn space (Var v) = do- (sub_exp, _, substs, mem) <- lift $ existentializeArray space v- (ctx_vals, _) <-- unzip- <$> mapM- ( \s -> do- vname <- lift $ letExp "ctx_val" =<< toExp s- pure (Var vname, fmap Free $ primExpFromSubExp int64 $ Var vname)- )- substs-- tell (ctx_vals, [Var mem])-- pure sub_exp- ensureDirectArray :: (Allocable fromrep torep inner) => Maybe Space ->@@ -552,25 +531,39 @@ -- binding for the size of the memory block. allocLinearArray space (baseString v) v -allocLinearArray ::+allocPermArray :: (Allocable fromrep torep inner) => Space ->+ [Int] -> String -> VName -> AllocM fromrep torep (VName, VName)-allocLinearArray space s v = do+allocPermArray space perm s v = do t <- lookupType v case t of Array pt shape u -> do mem <- allocForArray t space- v' <- newVName $ s <> "_linear"- let ixfun = directIxFun pt shape u mem t- pat = Pat [PatElem v' ixfun]- addStm $ Let pat (defAux ()) $ BasicOp $ Copy v+ v' <- newVName $ s <> "_desired_form"+ let info =+ MemArray pt shape u . ArrayIn mem $+ IxFun.permute (IxFun.iota $ map pe64 $ arrayDims t) perm+ pat = Pat [PatElem v' info]+ addStm $ Let pat (defAux ()) $ BasicOp $ Manifest perm v pure (mem, v') _ ->- error $ "allocLinearArray: " ++ pretty t+ error $ "allocPermArray: " ++ pretty t +allocLinearArray ::+ (Allocable fromrep torep inner) =>+ Space ->+ String ->+ VName ->+ AllocM fromrep torep (VName, VName)+allocLinearArray space s v = do+ t <- lookupType v+ let perm = [0 .. arrayRank t - 1]+ allocPermArray space perm s v+ funcallArgs :: (Allocable fromrep torep inner) => [(SubExp, Diet)] ->@@ -592,7 +585,7 @@ WriterT ([SubExp], [SubExp]) (AllocM fromrep torep) SubExp linearFuncallArg Array {} space (Var v) = do (mem, arg') <- lift $ ensureDirectArray (Just space) v- tell ([], [Var mem])+ tell ([Var mem], []) pure $ Var arg' linearFuncallArg _ _ arg = pure arg@@ -736,6 +729,191 @@ allocInLambda params body = mkLambda params . allocInStms (bodyStms body) $ pure $ bodyResult body +numLMADs :: IxFun -> Int+numLMADs = length . IxFun.ixfunLMADs++ixFunPerm :: IxFun -> [Int]+ixFunPerm = map IxFun.ldPerm . IxFun.lmadDims . NE.head . IxFun.ixfunLMADs++ixFunMon :: IxFun -> [IxFun.Monotonicity]+ixFunMon = map IxFun.ldMon . IxFun.lmadDims . NE.head . IxFun.ixfunLMADs++data MemReq+ = MemReq Space [Int] [IxFun.Monotonicity] Rank Bool+ | NeedsLinearisation Space+ deriving (Eq, Show)++combMemReqs :: MemReq -> MemReq -> MemReq+combMemReqs x@NeedsLinearisation {} _ = x+combMemReqs _ y@NeedsLinearisation {} = y+combMemReqs x@(MemReq x_space _ _ _ _) y@MemReq {} =+ if x == y then x else NeedsLinearisation x_space++type MemReqType = MemInfo (Ext SubExp) NoUniqueness MemReq++combMemReqTypes :: MemReqType -> MemReqType -> MemReqType+combMemReqTypes (MemArray pt shape u x) (MemArray _ _ _ y) =+ MemArray pt shape u $ combMemReqs x y+combMemReqTypes x _ = x++contextRets :: MemReqType -> [MemInfo d u r]+contextRets (MemArray _ shape _ (MemReq space _ _ (Rank base_rank) _)) =+ -- Memory + offset + base_rank + (stride,size)*rank.+ MemMem space+ : MemPrim int64+ : replicate base_rank (MemPrim int64)+ ++ replicate (2 * shapeRank shape) (MemPrim int64)+contextRets (MemArray _ shape _ (NeedsLinearisation space)) =+ -- Memory + offset + (base,stride,size)*rank.+ MemMem space+ : MemPrim int64+ : replicate (3 * shapeRank shape) (MemPrim int64)+contextRets _ = []++-- Add memory information to the body, but do not return memory/ixfun+-- information. Instead, return restrictions on what the index+-- function should look like. We will then (crudely) unify these+-- restrictions across all bodies.+allocInMatchBody ::+ (Allocable fromrep torep inner) =>+ [ExtType] ->+ Body fromrep ->+ AllocM fromrep torep (Body torep, [MemReqType])+allocInMatchBody rets (Body _ stms res) =+ buildBody . allocInStms stms $ do+ restrictions <- zipWithM restriction rets (map resSubExp res)+ pure (res, restrictions)+ where+ restriction t se = do+ v_info <- subExpMemInfo se+ case (t, v_info) of+ (Array pt shape u, MemArray _ _ _ (ArrayIn mem ixfun)) -> do+ space <- lookupMemSpace mem+ pure . MemArray pt shape u $+ if numLMADs ixfun == 1+ then+ MemReq+ space+ (ixFunPerm ixfun)+ (ixFunMon ixfun)+ (Rank $ length $ IxFun.base ixfun)+ (IxFun.contiguous ixfun)+ else NeedsLinearisation space+ (_, MemMem space) -> pure $ MemMem space+ (_, MemPrim pt) -> pure $ MemPrim pt+ (_, MemAcc acc ispace ts u) -> pure $ MemAcc acc ispace ts u+ _ -> error $ "allocInMatchBody: mismatch: " ++ show (t, v_info)++mkBranchRet :: [MemReqType] -> [BranchTypeMem]+mkBranchRet reqs =+ let (ctx_rets, res_rets) = foldl helper ([], []) $ zip reqs offsets+ in ctx_rets ++ res_rets+ where+ numCtxNeeded = length . contextRets++ offsets = scanl (+) 0 $ map numCtxNeeded reqs+ num_new_ctx = last offsets++ helper (ctx_rets_acc, res_rets_acc) (req, ctx_offset) =+ ( ctx_rets_acc ++ contextRets req,+ res_rets_acc ++ [inspect ctx_offset req]+ )++ arrayInfo rank (NeedsLinearisation space) =+ (space, [0 .. rank - 1], repeat IxFun.Inc, rank, True)+ arrayInfo _ (MemReq space perm mon (Rank base_rank) contig) =+ (space, perm, mon, base_rank, contig)++ inspect ctx_offset (MemArray pt shape u req) =+ let shape' = fmap (adjustExt num_new_ctx) shape+ (space, perm, mon, base_rank, contig) = arrayInfo (shapeRank shape) req+ in MemArray pt shape' u . ReturnsNewBlock space ctx_offset $+ convert+ <$> IxFun.mkExistential base_rank (zip perm mon) contig (ctx_offset + 1)+ inspect _ (MemAcc acc ispace ts u) = MemAcc acc ispace ts u+ inspect _ (MemPrim pt) = MemPrim pt+ inspect _ (MemMem space) = MemMem space++ convert (Ext i) = le64 (Ext i)+ convert (Free v) = Free <$> pe64 v++ adjustExt :: Int -> Ext a -> Ext a+ adjustExt _ (Free v) = Free v+ adjustExt k (Ext i) = Ext (k + i)++addCtxToMatchBody ::+ (Allocable fromrep torep inner) =>+ [MemReqType] ->+ Body torep ->+ AllocM fromrep torep (Body torep)+addCtxToMatchBody reqs body = buildBody_ $ do+ res <- zipWithM linearIfNeeded reqs =<< bodyBind body+ ctx <- concat <$> mapM resCtx res+ pure $ ctx ++ res+ where+ linearIfNeeded (MemArray _ _ _ (NeedsLinearisation space)) (SubExpRes cs (Var v)) =+ SubExpRes cs . Var . snd <$> ensureRowMajorArray (Just space) v+ linearIfNeeded _ res =+ pure res++ resCtx (SubExpRes _ Constant {}) =+ pure []+ resCtx (SubExpRes _ (Var v)) = do+ info <- lookupMemInfo v+ case info of+ MemPrim {} -> pure []+ MemAcc {} -> pure []+ MemMem {} -> pure [] -- should not happen+ MemArray _ _ _ (ArrayIn mem ixfun) -> do+ ixfun_exts <- mapM (letSubExp "ixfun_ext" <=< toExp) $ toList ixfun+ pure $ subExpRes (Var mem) : subExpsRes ixfun_exts++-- Do a a simple form of invariance analysis to simplify a Match. It+-- is unfortunate that we have to do it here, but functions such as+-- scalarRes will look carefully at the index functions before the+-- simplifier has a chance to run. In a perfect world we would+-- simplify away those copies afterwards. XXX; this should be fixed by+-- a more general copy-removal pass. See+-- Futhark.Optimise.EntryPointMem for a very specialised version of+-- the idea, but which could perhaps be generalised.+simplifyMatch ::+ Mem rep inner =>+ [Case (Body rep)] ->+ Body rep ->+ [BranchTypeMem] ->+ ( [Case (Body rep)],+ Body rep,+ [BranchTypeMem]+ )+simplifyMatch cases defbody ts =+ let case_reses = map (bodyResult . caseBody) cases+ defbody_res = bodyResult defbody+ (ctx_fixes, variant) =+ partitionEithers . map branchInvariant $+ zip4 [0 ..] (transpose case_reses) defbody_res ts+ (cases_reses, defbody_reses, ts') = unzip3 variant+ in ( zipWith onCase cases (transpose cases_reses),+ onBody defbody defbody_reses,+ foldr (uncurry fixExt) ts' ctx_fixes+ )+ where+ bound_in_branches =+ namesFromList . concatMap (patNames . stmPat) $+ foldMap (bodyStms . caseBody) cases <> bodyStms defbody++ onCase c res = fmap (`onBody` res) c+ onBody body res = body {bodyResult = res}++ branchInvariant (i, case_reses, defres, t)+ -- If even one branch has a variant result, then we give up.+ | namesIntersect bound_in_branches $ freeIn $ defres : case_reses =+ Right (case_reses, defres, t)+ -- Do all branches return the same value?+ | all ((== resSubExp defres) . resSubExp) case_reses =+ Left (i, resSubExp defres)+ | otherwise =+ Right (case_reses, defres, t)+ allocInExp :: (Allocable fromrep torep inner) => Exp fromrep ->@@ -746,8 +924,8 @@ localScope (scopeOf form') $ do body' <- buildBody_ . allocInStms bodystms $ do- (val_ses, valres') <- mk_loop_val $ map resSubExp bodyres- pure $ subExpsRes val_ses <> zipWith SubExpRes (map resCerts bodyres) valres'+ (valctx, valres') <- mk_loop_val $ map resSubExp bodyres+ pure $ subExpsRes valctx <> zipWith SubExpRes (map resCerts bodyres) valres' pure $ DoLoop merge' form' body' allocInExp (Apply fname args rettype loc) = do args' <- funcallArgs args@@ -756,76 +934,17 @@ where mems = replicate num_arrays (MemMem DefaultSpace) num_arrays = length $ filter ((> 0) . arrayRank . declExtTypeOf) rettype-allocInExp (If cond tbranch0 fbranch0 (IfDec rets ifsort)) = do- let num_rets = length rets- -- switch to the explicit-mem rep, but do nothing about results- (tbranch, tm_ixfs) <- allocInIfBody num_rets tbranch0- (fbranch, fm_ixfs) <- allocInIfBody num_rets fbranch0- tspaces <- mkSpaceOks num_rets tbranch- fspaces <- mkSpaceOks num_rets fbranch- -- try to generalize (antiunify) the index functions of the then and else bodies- let sp_substs = zipWith generalize (zip tspaces tm_ixfs) (zip fspaces fm_ixfs)- (spaces, subs) = unzip sp_substs- tsubs = map (selectSub fst) subs- fsubs = map (selectSub snd) subs- (tbranch', trets) <- addResCtxInIfBody rets tbranch spaces tsubs- (fbranch', frets) <- addResCtxInIfBody rets fbranch spaces fsubs- if frets /= trets- then error "In allocInExp, IF case: antiunification of then/else produce different ExtInFn!"- else do- -- above is a sanity check; implementation continues on else branch- let res_then = bodyResult tbranch'- res_else = bodyResult fbranch'- size_ext = length res_then - length trets- (ind_ses0, r_then_else) =- partition (\(r_then, r_else, _) -> r_then == r_else) $- zip3 res_then res_else [0 .. size_ext - 1]- (r_then_ext, r_else_ext, _) = unzip3 r_then_else- ind_ses =- zipWith- (\(se, _, i) k -> (i - k, se))- ind_ses0- [0 .. length ind_ses0 - 1]- rets'' = foldl (\acc (i, SubExpRes _ se) -> fixExt i se acc) trets ind_ses- tbranch'' = tbranch' {bodyResult = r_then_ext ++ drop size_ext res_then}- fbranch'' = fbranch' {bodyResult = r_else_ext ++ drop size_ext res_else}- res_if_expr = If cond tbranch'' fbranch'' $ IfDec rets'' ifsort- pure res_if_expr+allocInExp (Match ses cases defbody (MatchDec rets ifsort)) = do+ (defbody', def_reqs) <- allocInMatchBody rets defbody+ (cases', cases_reqs) <- unzip <$> mapM onCase cases+ let reqs = zipWith (foldl combMemReqTypes) def_reqs (transpose cases_reqs)+ defbody'' <- addCtxToMatchBody reqs defbody'+ cases'' <- mapM (traverse $ addCtxToMatchBody reqs) cases'+ let (cases''', defbody''', rets') =+ simplifyMatch cases'' defbody'' $ mkBranchRet reqs+ pure $ Match ses cases''' defbody''' $ MatchDec rets' ifsort where- generalize ::- (Maybe Space, Maybe IxFun) ->- (Maybe Space, Maybe IxFun) ->- (Maybe Space, Maybe (ExtIxFun, [(TPrimExp Int64 VName, TPrimExp Int64 VName)]))- generalize (Just sp1, Just ixf1) (Just sp2, Just ixf2) =- if sp1 /= sp2- then (Just sp1, Nothing)- else case IxFun.leastGeneralGeneralization (fmap untyped ixf1) (fmap untyped ixf2) of- Just (ixf, m) ->- ( Just sp1,- Just- ( fmap TPrimExp ixf,- zip (map (TPrimExp . fst) m) (map (TPrimExp . snd) m)- )- )- Nothing -> (Just sp1, Nothing)- generalize (mbsp1, _) _ = (mbsp1, Nothing)-- selectSub ::- ((a, a) -> a) ->- Maybe (ExtIxFun, [(a, a)]) ->- Maybe (ExtIxFun, [a])- selectSub f (Just (ixfn, m)) = Just (ixfn, map f m)- selectSub _ Nothing = Nothing- allocInIfBody ::- (Allocable fromrep torep inner) =>- Int ->- Body fromrep ->- AllocM fromrep torep (Body torep, [Maybe IxFun])- allocInIfBody num_vals (Body _ stms res) =- buildBody . allocInStms stms $ do- let (_, val_res) = splitFromEnd num_vals res- mem_ixfs <- mapM (subExpIxFun . resSubExp) val_res- pure (res, mem_ixfs)+ onCase (Case vs body) = first (Case vs) <$> allocInMatchBody rets body allocInExp (WithAcc inputs bodylam) = WithAcc <$> mapM onInput inputs <*> onLambda bodylam where@@ -893,120 +1012,6 @@ handle op } -lookupIxFun ::- (Allocable fromrep torep inner) =>- VName ->- AllocM fromrep torep (Maybe IxFun)-lookupIxFun v = do- info <- lookupMemInfo v- case info of- MemArray _ptp _shp _u (ArrayIn _ ixf) -> pure $ Just ixf- _ -> pure Nothing--subExpIxFun ::- (Allocable fromrep torep inner) =>- SubExp ->- AllocM fromrep torep (Maybe IxFun)-subExpIxFun Constant {} = pure Nothing-subExpIxFun (Var v) = lookupIxFun v--addResCtxInIfBody ::- (Allocable fromrep torep inner) =>- [ExtType] ->- Body torep ->- [Maybe Space] ->- [Maybe (ExtIxFun, [TPrimExp Int64 VName])] ->- AllocM fromrep torep (Body torep, [BodyReturns])-addResCtxInIfBody ifrets (Body _ stms res) spaces substs = buildBody $ do- mapM_ addStm stms- let offsets = scanl (+) 0 $ zipWith numCtxNeeded ifrets substs- num_new_ctx = last offsets- (ctx, ctx_rets, res', res_rets) <-- foldM (helper num_new_ctx) ([], [], [], []) $- zip5 ifrets res substs spaces offsets- pure (ctx <> res', ctx_rets ++ res_rets)- where- numCtxNeeded Array {} Nothing = 1- numCtxNeeded Array {} (Just (_, m)) = length m + 1- numCtxNeeded _ _ = 0-- helper- num_new_ctx- (ctx_acc, ctx_rets_acc, res_acc, res_rets_acc)- (ifr, r, mbixfsub, sp, ctx_offset) =- case mbixfsub of- Nothing -> do- -- does NOT generalize/antiunify; ensure direct- r' <- ensureDirect sp r- (mem_ctx_ses, mem_ctx_rets) <- unzip <$> bodyReturnMemCtx r'- let body_ret = inspect num_new_ctx ctx_offset ifr sp- pure- ( ctx_acc ++ mem_ctx_ses,- ctx_rets_acc ++ mem_ctx_rets,- res_acc ++ [r'],- res_rets_acc ++ [body_ret]- )- Just (ixfn, m) -> do- -- generalizes- let i = length m- ext_ses <- mapM (toSubExp "ixfn_exist") m- (mem_ctx_ses, mem_ctx_rets) <- unzip <$> bodyReturnMemCtx r- let sp' = fromMaybe DefaultSpace sp- ixfn' = fmap (adjustExtPE ctx_offset) ixfn- exttp = case ifr of- Array pt shape u ->- MemArray pt (fmap (adjustExt num_new_ctx) shape) u $- ReturnsNewBlock sp' (ctx_offset + i) ixfn'- _ -> error "Impossible case reached in addResCtxInIfBody"- pure- ( ctx_acc ++ subExpsRes ext_ses ++ mem_ctx_ses,- ctx_rets_acc ++ map (const (MemPrim int64)) ext_ses ++ mem_ctx_rets,- res_acc ++ [r],- res_rets_acc ++ [exttp]- )-- inspect num_new_ctx k (Array pt shape u) space =- let space' = fromMaybe DefaultSpace space- shape' = fmap (adjustExt num_new_ctx) shape- bodyret =- MemArray pt shape' u . ReturnsNewBlock space' k $- IxFun.iota $- map convert $- shapeDims shape'- in bodyret- inspect _ _ (Acc acc ispace ts u) _ = MemAcc acc ispace ts u- inspect _ _ (Prim pt) _ = MemPrim pt- inspect _ _ (Mem space) _ = MemMem space-- convert (Ext i) = le64 (Ext i)- convert (Free v) = Free <$> pe64 v-- adjustExt :: Int -> Ext a -> Ext a- adjustExt _ (Free v) = Free v- adjustExt k (Ext i) = Ext (k + i)-- adjustExtPE :: Int -> TPrimExp t (Ext VName) -> TPrimExp t (Ext VName)- adjustExtPE k = fmap (adjustExt k)--mkSpaceOks ::- (Mem torep inner, LocalScope torep m) =>- Int ->- Body torep ->- m [Maybe Space]-mkSpaceOks num_vals (Body _ stms res) =- inScopeOf stms $ mapM (mkSpaceOK . resSubExp) $ takeLast num_vals res- where- mkSpaceOK (Var v) = do- v_info <- lookupMemInfo v- case v_info of- MemArray _ _ _ (ArrayIn mem _) -> do- mem_info <- lookupMemInfo mem- case mem_info of- MemMem space -> pure $ Just space- _ -> pure Nothing- _ -> pure Nothing- mkSpaceOK _ = pure Nothing- allocInLoopForm :: (Allocable fromrep torep inner) => LoopForm fromrep ->@@ -1020,9 +1025,7 @@ case paramType p of Array pt shape u -> do dims <- map pe64 . arrayDims <$> lookupType a- let ixfun' =- IxFun.slice ixfun $- fullSliceNum dims [DimFix $ le64 i]+ let ixfun' = IxFun.slice ixfun $ fullSliceNum dims [DimFix $ le64 i] pure (p {paramDec = MemArray pt shape u $ ArrayIn mem ixfun'}, a) Prim bt -> pure (p {paramDec = MemPrim bt}, a)@@ -1072,8 +1075,7 @@ nohints = map (const NoHint) names mkLetNamesB'' ::- ( BuilderOps rep,- Mem rep inner,+ ( Mem rep inner, LetDec rep ~ LetDecMem, OpReturns (Engine.OpWithWisdom inner), ExpDec rep ~ (),@@ -1097,13 +1099,15 @@ ( Engine.SimplifiableRep rep, ExpDec rep ~ (), BodyDec rep ~ (),+ LetDec rep ~ LetDecMem,+ OpReturns (Engine.OpWithWisdom inner), Mem rep inner ) => (Engine.OpWithWisdom inner -> UT.UsageTable) -> (Engine.OpWithWisdom inner -> Engine.SimpleM rep (Engine.OpWithWisdom inner, Stms (Engine.Wise rep))) -> SimpleOps rep simplifiable innerUsage simplifyInnerOp =- SimpleOps mkExpDecS' mkBodyS' protectOp opUsage simplifyOp+ SimpleOps mkExpDecS' mkBodyS' protectOp opUsage simplifyPat simplifyOp where mkExpDecS' _ pat e = pure $ Engine.mkWiseExpDec pat () e@@ -1115,8 +1119,8 @@ fbody <- resultBodyM [intConst Int64 0] size' <- letSubExp "hoisted_alloc_size" $- If taken tbody fbody $- IfDec [MemPrim int64] IfFallback+ Match [taken] [Case [Just $ BoolValue True] tbody] fbody $+ MatchDec [MemPrim int64] MatchFallback letBind pat $ Op $ Alloc size' space protectOp _ _ _ = Nothing @@ -1132,6 +1136,26 @@ simplifyOp (Inner k) = do (k', hoisted) <- simplifyInnerOp k pure (Inner k', hoisted)++ simplifyPat (Pat pes) e = do+ rets <- expReturns e+ Pat <$> zipWithM update pes rets+ where+ names = map patElemName pes+ update+ (PatElem pe_v (MemArray pt shape u (ArrayIn mem _)))+ (MemArray _ _ _ (Just (ReturnsInBlock _ ixfun)))+ | Just ixfun' <- traverse (traverse inst) ixfun =+ PatElem pe_v+ <$> ( MemArray pt+ <$> Engine.simplify shape+ <*> pure u+ <*> (ArrayIn <$> Engine.simplify mem <*> pure ixfun')+ )+ where+ inst (Ext i) = maybeNth i names+ inst (Free v) = Just v+ update pe _ = traverse Engine.simplify pe data ExpHint = NoHint
src/Futhark/Pass/ExplicitAllocations/GPU.hs view
@@ -126,7 +126,7 @@ hint Prim {} (ConcatReturns _ SplitContiguous w elems_per_thread _) = do let ixfun_base = IxFun.iota [sExt64 num_threads, pe64 elems_per_thread] ixfun_tr = IxFun.permute ixfun_base [1, 0]- ixfun = IxFun.reshape ixfun_tr $ map (DimNew . pe64) [w]+ ixfun = IxFun.reshape ixfun_tr [pe64 w] pure $ Hint ixfun DefaultSpace hint _ _ = pure NoHint
src/Futhark/Pass/ExtractKernels.hs view
@@ -289,10 +289,11 @@ unbalancedStm _ DoLoop {} = False unbalancedStm bound (WithAcc _ lam) = unbalancedBody bound (lambdaBody lam)- unbalancedStm bound (If cond tbranch fbranch _) =- cond- `subExpBound` bound- && (unbalancedBody bound tbranch || unbalancedBody bound fbranch)+ unbalancedStm bound (Match ses cases defbody _) =+ any (`subExpBound` bound) ses+ && ( any (unbalancedBody bound . caseBody) cases+ || unbalancedBody bound defbody+ ) unbalancedStm _ (BasicOp _) = False unbalancedStm _ (Apply fname _ _ _) =@@ -342,9 +343,8 @@ alt_stms <- kernelAlternatives alts_pat default_body alts let alt_body = mkBody alt_stms $ varsRes $ patNames alts_pat - letBind pat $- If cond alt alt_body $- IfDec (staticShapes (patTypes pat)) IfEquiv+ letBind pat . Match [cond] [Case [Just $ BoolValue True] alt] alt_body $+ MatchDec (staticShapes (patTypes pat)) MatchEquiv transformLambda :: KernelPath -> Lambda SOACS -> DistribM (Lambda GPU) transformLambda path (Lambda params body ret) =@@ -360,10 +360,10 @@ | "sequential_outer" `inAttrs` stmAuxAttrs aux = transformStms path . stmsToList . fmap (certify (stmAuxCerts aux)) =<< runBuilder_ (FOT.transformSOAC pat soac)-transformStm path (Let pat aux (If c tb fb rt)) = do- tb' <- transformBody path tb- fb' <- transformBody path fb- pure $ oneStm $ Let pat aux $ If c tb' fb' rt+transformStm path (Let pat aux (Match c cases defbody rt)) = do+ cases' <- mapM (traverse $ transformBody path) cases+ defbody' <- transformBody path defbody+ pure $ oneStm $ Let pat aux $ Match c cases' defbody' rt transformStm path (Let pat aux (WithAcc inputs lam)) = oneStm . Let pat aux <$> (WithAcc (map transformInput inputs) <$> transformLambda path lam)@@ -611,8 +611,11 @@ mapLike w lam' | DoLoop _ _ body <- stmExp stm = bodyInterest body * 10- | If _ tbody fbody _ <- stmExp stm =- max (bodyInterest tbody) (bodyInterest fbody)+ | Match _ cases defbody _ <- stmExp stm =+ foldl+ max+ (bodyInterest defbody)+ (map (bodyInterest . caseBody) cases) | Op (Screma w _ (ScremaForm _ _ lam')) <- stmExp stm = zeroIfTooSmall w + bodyInterest (lambdaBody lam') | Op (Stream _ _ Sequential _ lam') <- stmExp stm =
src/Futhark/Pass/ExtractKernels/BlockedKernel.hs view
@@ -129,7 +129,7 @@ SegMap lvl kspace (lambdaReturnType map_lam) kbody dummyDim ::- (MonadFreshNames m, MonadBuilder m) =>+ MonadBuilder m => Pat Type -> m (Pat Type, [(VName, SubExp)], m ()) dummyDim pat = do
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -322,7 +322,7 @@ && not ("sequential" `inAttrs` stmAuxAttrs (stmAux stm)) isMap BasicOp {} = False isMap Apply {} = False- isMap If {} = False+ isMap Match {} = False isMap (DoLoop _ ForLoop {} body) = bodyContainsParallelism body isMap (DoLoop _ WhileLoop {} _) = False isMap (WithAcc _ lam) = bodyContainsParallelism $ lambdaBody lam@@ -413,11 +413,10 @@ pure acc' _ -> addStmToAcc stm acc-maybeDistributeStm stm@(Let pat _ (If cond tbranch fbranch ret)) acc+maybeDistributeStm stm@(Let pat _ (Match cond cases defbody ret)) acc | all (`notNameIn` freeIn pat) (patNames pat),- bodyContainsParallelism tbranch- || bodyContainsParallelism fbranch- || not (all primType (ifReturns ret)) =+ any bodyContainsParallelism (defbody : map caseBody cases)+ || not (all primType (matchReturns ret)) = distributeSingleStm acc stm >>= \case Just (kernels, res, nest, acc') | not $@@ -429,7 +428,7 @@ nest' <- expandKernelNest pat_unused nest addPostStms kernels types <- asksScope scopeForSOACs- let branch = Branch perm pat cond tbranch fbranch ret+ let branch = Branch perm pat cond cases defbody ret stms <- (`runReaderT` types) $ simplifyStms . oneStm =<< interchangeBranch nest' branch@@ -606,12 +605,10 @@ [ Let (Pat [PatElem arr' arr_t]) aux $ BasicOp $ Copy arr, Let outerpat aux $ BasicOp $ Rearrange perm' arr' ]-maybeDistributeStm stm@(Let _ aux (BasicOp (Reshape reshape stm_arr))) acc =+maybeDistributeStm stm@(Let _ aux (BasicOp (Reshape k reshape stm_arr))) acc = distributeSingleUnaryStm acc stm stm_arr $ \nest outerpat arr -> do- let reshape' =- map DimNew (kernelNestWidths nest)- ++ map DimNew (newDims reshape)- pure $ oneStm $ Let outerpat aux $ BasicOp $ Reshape reshape' arr+ let reshape' = Shape (kernelNestWidths nest) <> reshape+ pure $ oneStm $ Let outerpat aux $ BasicOp $ Reshape k reshape' arr maybeDistributeStm stm@(Let _ aux (BasicOp (Rotate rots stm_arr))) acc = distributeSingleUnaryStm acc stm stm_arr $ \nest outerpat arr -> do let rots' = map (const $ intConst Int64 0) (kernelNestWidths nest) ++ rots
src/Futhark/Pass/ExtractKernels/Interchange.hs view
@@ -189,11 +189,11 @@ -- | An encoding of a branch with alongside its result pattern. data Branch- = Branch [Int] (Pat Type) SubExp (Body SOACS) (Body SOACS) (IfDec (BranchType SOACS))+ = Branch [Int] (Pat Type) [SubExp] [Case (Body SOACS)] (Body SOACS) (MatchDec (BranchType SOACS)) branchStm :: Branch -> Stm SOACS-branchStm (Branch _ pat cond tbranch fbranch ret) =- Let pat (defAux ()) $ If cond tbranch fbranch ret+branchStm (Branch _ pat cond cases defbody ret) =+ Let pat (defAux ()) $ Match cond cases defbody ret interchangeBranch1 :: (MonadFreshNames m, HasScope SOACS m) =>@@ -201,7 +201,7 @@ LoopNesting -> m Branch interchangeBranch1- (Branch perm branch_pat cond tbranch fbranch (IfDec ret if_sort))+ (Branch perm branch_pat cond cases defbody (MatchDec ret if_sort)) (MapNesting pat aux w params_and_arrs) = do let ret' = map (`arrayOfRow` Free w) ret pat' = Pat $ rearrangeShape perm $ patElems pat@@ -218,11 +218,10 @@ map_stm = Let branch_pat' aux $ Op $ Screma w arrs $ mapSOAC lam pure $ mkBody (oneStm map_stm) res - tbranch' <- runBodyBuilder $ mkBranch tbranch- fbranch' <- runBodyBuilder $ mkBranch fbranch- pure $- Branch [0 .. patSize pat - 1] pat' cond tbranch' fbranch' $- IfDec ret' if_sort+ cases' <- mapM (traverse $ runBodyBuilder . mkBranch) cases+ defbody' <- runBodyBuilder $ mkBranch defbody+ pure . Branch [0 .. patSize pat - 1] pat' cond cases' defbody' $+ MatchDec ret' if_sort -- | Given a (parallel) map nesting and an inner branch, move the maps -- inside the branch. The result is the resulting branch expression,
src/Futhark/Pass/ExtractKernels/Intragroup.hs view
@@ -209,12 +209,11 @@ form' = case form of ForLoop i it bound inps -> ForLoop i it bound inps WhileLoop cond -> WhileLoop cond- If cond tbody fbody ifdec -> do- tbody' <- intraGroupBody lvl tbody- fbody' <- intraGroupBody lvl fbody- certifying (stmAuxCerts aux) $- letBind pat $- If cond tbody' fbody' ifdec+ Match cond cases defbody ifdec -> do+ cases' <- mapM (traverse $ intraGroupBody lvl) cases+ defbody' <- intraGroupBody lvl defbody+ certifying (stmAuxCerts aux) . letBind pat $+ Match cond cases' defbody' ifdec Op soac | "sequential_outer" `inAttrs` stmAuxAttrs aux -> intraGroupStms lvl . fmap (certify (stmAuxCerts aux))
src/Futhark/Pass/ExtractMulticore.hs view
@@ -121,9 +121,11 @@ localScope (scopeOfFParams (map fst merge) <> scopeOf form') $ transformBody body pure $ oneStm $ Let pat aux $ DoLoop merge form' body'-transformStm (Let pat aux (If cond tbranch fbranch ret)) =+transformStm (Let pat aux (Match ses cases defbody ret)) = oneStm . Let pat aux- <$> (If cond <$> transformBody tbranch <*> transformBody fbranch <*> pure ret)+ <$> (Match ses <$> mapM transformCase cases <*> transformBody defbody <*> pure ret)+ where+ transformCase (Case vs body) = Case vs <$> transformBody body transformStm (Let pat aux (WithAcc inputs lam)) = oneStm . Let pat aux <$> (WithAcc <$> mapM transformInput inputs <*> transformLambda lam)
src/Futhark/Pass/KernelBabysitting.hs view
@@ -65,7 +65,7 @@ case M.lookup name m of Just (Let _ _ (BasicOp (Opaque _ (Var arr)))) -> nonlinearInMemory arr m Just (Let _ _ (BasicOp (Rearrange perm _))) -> Just $ Just $ rearrangeInverse perm- Just (Let _ _ (BasicOp (Reshape _ arr))) -> nonlinearInMemory arr m+ Just (Let _ _ (BasicOp (Reshape _ _ arr))) -> nonlinearInMemory arr m Just (Let _ _ (BasicOp (Manifest perm _))) -> Just $ Just perm Just (Let pat _ (Op (SegOp (SegMap _ _ ts _)))) -> nonlinear@@ -150,7 +150,7 @@ m (Maybe (VName, Slice SubExp)) traverseKernelBodyArrayIndexes ::- (Applicative f, Monad f) =>+ Monad f => Names -> Names -> Scope GPU ->@@ -474,12 +474,10 @@ 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+ 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@@ -488,19 +486,17 @@ extradim_shape = Shape $ pre_dims ++ [num_chunks', per_chunk] ++ post_dims tr_perm = [0 .. d - 1] ++ map (+ d) ([1] ++ [2 .. shapeRank extradim_shape - 1 - d] ++ [0]) arr_extradim <-- letExp (arr_name <> "_extradim") $- BasicOp $- Reshape (map DimNew $ shapeDims extradim_shape) arr_padded+ 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+ letExp (arr_name <> "_extradim_tr") . BasicOp $+ Manifest tr_perm arr_extradim arr_inv_tr <-- letExp (arr_name <> "_inv_tr") $- BasicOp $- Reshape- (map DimCoercion pre_dims ++ map DimNew (w_padded : post_dims))- arr_extradim_tr+ letExp (arr_name <> "_inv_tr") . 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)
src/Futhark/Passes.hs view
@@ -22,6 +22,7 @@ import Futhark.IR.SeqMem (SeqMem) import Futhark.Optimise.CSE import Futhark.Optimise.DoubleBuffer+import Futhark.Optimise.EntryPointMem import Futhark.Optimise.Fusion import Futhark.Optimise.GenRedOpt import Futhark.Optimise.HistAccs@@ -125,6 +126,8 @@ >>> onePass Seq.explicitAllocations >>> passes [ performCSE False,+ simplifySeqMem,+ entryPointMemSeq, simplifySeqMem ] @@ -138,6 +141,7 @@ [ simplifyGPUMem, performCSE False, simplifyGPUMem,+ entryPointMemGPU, doubleBufferGPU, simplifyGPUMem, MemoryBlockMerging.optimise,@@ -170,6 +174,7 @@ [ simplifyMCMem, performCSE False, simplifyMCMem,+ entryPointMemMC, doubleBufferMC, simplifyMCMem ]
src/Futhark/Tools.hs view
@@ -126,9 +126,8 @@ -- Finally, the array parameters are set to the arrays (but reshaped -- to make the types work out; this will be simplified rapidly). forM_ (zip arr_params arrs) $ \(p, arr) ->- letBindNames [paramName p] $- BasicOp $- Reshape (map DimCoercion $ arrayDims $ paramType p) arr+ letBindNames [paramName p] . BasicOp $+ Reshape ReshapeCoerce (arrayShape $ paramType p) arr -- Then we just inline the lambda body. mapM_ addStm $ bodyStms $ lambdaBody lam@@ -140,7 +139,7 @@ certifying cs $ case (arrayDims $ patElemType pe, se) of (dims, Var v) | not $ null dims ->- letBindNames [patElemName pe] $ BasicOp $ Reshape (map DimCoercion dims) v+ letBindNames [patElemName pe] $ BasicOp $ Reshape ReshapeCoerce (Shape dims) v _ -> letBindNames [patElemName pe] $ BasicOp $ SubExp se -- | Split the parameters of a stream reduction lambda into the chunk
src/Futhark/Transform/Substitute.hs view
@@ -183,9 +183,6 @@ identType = substituteNames substs $ identType v } -instance Substitute d => Substitute (DimChange d) where- substituteNames substs = fmap $ substituteNames substs- instance Substitute d => Substitute (DimIndex d) where substituteNames substs = fmap $ substituteNames substs
src/Futhark/Util/Log.hs view
@@ -51,11 +51,11 @@ -- | Append an entire log. addLog :: Log -> m () -instance (Applicative m, Monad m) => MonadLogger (WriterT Log m) where+instance Monad m => MonadLogger (WriterT Log m) where addLog = tell -instance (Applicative m, Monad m) => MonadLogger (Control.Monad.RWS.Lazy.RWST r Log s m) where+instance Monad m => MonadLogger (Control.Monad.RWS.Lazy.RWST r Log s m) where addLog = tell -instance (Applicative m, Monad m) => MonadLogger (Control.Monad.RWS.Strict.RWST r Log s m) where+instance Monad m => MonadLogger (Control.Monad.RWS.Strict.RWST r Log s m) where addLog = tell
src/Language/Futhark/FreeVars.hs view
@@ -11,7 +11,6 @@ import qualified Data.Map.Strict as M import qualified Data.Set as S-import Futhark.IR.Pretty () import Language.Futhark.Prop import Language.Futhark.Syntax
src/Language/Futhark/Interpreter.hs view
@@ -37,6 +37,7 @@ ( find, foldl', genericLength,+ genericTake, intercalate, isPrefixOf, transpose,@@ -1917,8 +1918,9 @@ pure $ toArray (ShapeDim m (ShapeDim n shape)) $ map (toArray (ShapeDim n shape)) $- transpose $- map (snd . fromArray) xs'+ -- Slight hack to work around empty dimensions.+ genericTake m $+ transpose (map (snd . fromArray) xs') ++ repeat [] def "rotate" = Just $ fun2t $ \i xs -> do let (shape, xs') = fromArray xs
src/Language/Futhark/TypeChecker.hs view
@@ -158,7 +158,8 @@ typeError loc1 mempty $ "Duplicate definition of" <+> ppr space- <+> pprName name <> ". Previously defined at"+ <+> pprName name <> "."+ </> "Previously defined at" <+> text (locStr loc2) <> "." checkForDuplicateDecs :: [DecBase NoInfo Name] -> TypeM ()
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -130,7 +130,12 @@ vtable <- asks $ scopeVtable . termScope let isGlobal v = case v `M.lookup` vtable of Just (BoundV Global _ _) -> True- _ -> False+ Just EqualityF {} -> True+ Just OverloadedF {} -> True+ Just (BoundV Local _ _) -> False+ Just (BoundV Nonlocal _ _) -> False+ Just WasConsumed {} -> False+ Nothing -> False pure . S.map AliasBound . S.filter (not . isGlobal) $ allOccurring closure S.\\ mconcat (map patNames params)
unittests/Futhark/IR/Mem/IxFun/Alg.hs view
@@ -5,8 +5,8 @@ iota, offsetIndex, permute,- rotate, reshape,+ coerce, slice, flatSlice, rebase,@@ -15,14 +15,11 @@ ) where -import Futhark.IR.Pretty () import Futhark.IR.Prop import Futhark.IR.Syntax- ( DimChange (..),- DimIndex (..),+ ( DimIndex (..), FlatDimIndex (..), FlatSlice (..),- ShapeChange, Slice (..), flatSliceDims, sliceDims,@@ -41,10 +38,10 @@ data IxFun num = Direct (Shape num) | Permute (IxFun num) Permutation- | Rotate (IxFun num) (Indices num) | Index (IxFun num) (Slice num) | FlatIndex (IxFun num) (FlatSlice num)- | Reshape (IxFun num) (ShapeChange num)+ | Reshape (IxFun num) (Shape num)+ | Coerce (IxFun num) (Shape num) | OffsetIndex (IxFun num) num | Rebase (IxFun num) (IxFun num) deriving (Eq, Show)@@ -53,13 +50,16 @@ ppr (Direct dims) = text "Direct" <> parens (commasep $ map ppr dims) ppr (Permute fun perm) = ppr fun <> ppr perm- ppr (Rotate fun offsets) = ppr fun <> brackets (commasep $ map ((text "+" <>) . ppr) offsets) ppr (Index fun is) = ppr fun <> ppr is ppr (FlatIndex fun is) = ppr fun <> ppr is ppr (Reshape fun oldshape) = ppr fun <> text "->reshape"- <> parens (commasep (map ppr oldshape))+ <> parens (ppr oldshape)+ ppr (Coerce fun oldshape) =+ ppr fun+ <> text "->coerce"+ <> parens (ppr oldshape) ppr (OffsetIndex fun i) = ppr fun <> text "->offset_index" <> parens (ppr i) ppr (Rebase new_base fun) =@@ -74,9 +74,6 @@ permute :: IxFun num -> Permutation -> IxFun num permute = Permute -rotate :: IxFun num -> Indices num -> IxFun num-rotate = Rotate- slice :: IxFun num -> Slice num -> IxFun num slice = Index @@ -86,9 +83,12 @@ rebase :: IxFun num -> IxFun num -> IxFun num rebase = Rebase -reshape :: IxFun num -> ShapeChange num -> IxFun num+reshape :: IxFun num -> Shape num -> IxFun num reshape = Reshape +coerce :: IxFun num -> Shape num -> IxFun num+coerce = Reshape+ shape :: IntegralExp num => IxFun num ->@@ -97,14 +97,14 @@ dims shape (Permute ixfun perm) = rearrangeShape perm $ shape ixfun-shape (Rotate ixfun _) =- shape ixfun shape (Index _ how) = sliceDims how shape (FlatIndex ixfun how) = flatSliceDims how <> tail (shape ixfun) shape (Reshape _ dims) =- map newDim dims+ dims+shape (Coerce _ dims) =+ dims shape (OffsetIndex ixfun _) = shape ixfun shape (Rebase _ ixfun) =@@ -123,10 +123,6 @@ index fun is_old where is_old = rearrangeShape (rearrangeInverse perm) is_new-index (Rotate fun offsets) is =- index fun $ zipWith mod (zipWith (+) is offsets) dims- where- dims = shape fun index (Index fun (Slice js)) is = index fun (adjust js is) where@@ -138,8 +134,10 @@ where f i (FlatDimIndex _ s) = i * s index (Reshape fun newshape) is =- let new_indices = reshapeIndex (shape fun) (newDims newshape) is+ let new_indices = reshapeIndex (shape fun) newshape is in index fun new_indices+index (Coerce fun _) is =+ index fun is index (OffsetIndex fun i) is = case shape fun of d : ds ->@@ -150,17 +148,17 @@ Direct old_shape -> if old_shape == shape new_base then new_base- else reshape new_base $ map DimCoercion old_shape+ else reshape new_base old_shape Permute ixfun perm -> permute (rebase new_base ixfun) perm- Rotate ixfun offsets ->- rotate (rebase new_base ixfun) offsets Index ixfun iis -> slice (rebase new_base ixfun) iis FlatIndex ixfun iis -> flatSlice (rebase new_base ixfun) iis Reshape ixfun new_shape -> reshape (rebase new_base ixfun) new_shape+ Coerce ixfun new_shape ->+ coerce (rebase new_base ixfun) new_shape OffsetIndex ixfun s -> offsetIndex (rebase new_base ixfun) s r@Rebase {} ->
unittests/Futhark/IR/Mem/IxFunTests.hs view
@@ -100,12 +100,6 @@ test_slice_iota, test_reshape_slice_iota1, test_permute_slice_iota,- test_rotate_rotate_permute_slice_iota,- test_slice_rotate_permute_slice_iota1,- test_slice_rotate_permute_slice_iota2,- test_slice_rotate_permute_slice_iota3,- test_permute_rotate_slice_permute_slice_iota,- test_reshape_rotate_iota, test_reshape_permute_iota, test_reshape_slice_iota2, test_reshape_slice_iota3,@@ -119,10 +113,7 @@ test_slice_flatSlice_iota, test_flatSlice_flatSlice_iota, test_flatSlice_slice_iota,- test_flatSlice_rotate_iota,- test_flatSlice_rotate_slice_iota,- test_flatSlice_transpose_slice_iota,- test_rotate_flatSlice_transpose_slice_iota+ test_flatSlice_transpose_slice_iota ] singleton :: TestTree -> [TestTree]@@ -149,7 +140,7 @@ compareOps $ reshape (slice (iota [n, n, n]) slice3)- [DimNew (n `P.div` 2), DimNew (n `P.div` 3)]+ [n `P.div` 2, n `P.div` 3] test_permute_slice_iota :: [TestTree] test_permute_slice_iota =@@ -158,114 +149,13 @@ compareOps $ permute (slice (iota [n, n, n]) slice3) [1, 0] -test_rotate_rotate_permute_slice_iota :: [TestTree]-test_rotate_rotate_permute_slice_iota =- singleton $- testCase "rotate . rotate . permute . slice . iota" $- compareOps $- let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]- in rotate (rotate ixfun [2, 1]) [1, 2]--test_slice_rotate_permute_slice_iota1 :: [TestTree]-test_slice_rotate_permute_slice_iota1 =- singleton $- testCase "slice . rotate . permute . slice . iota 1" $- compareOps $- let slice2 =- Slice- [ DimSlice 0 n 1,- DimSlice 1 (n `P.div` 2) 2,- DimSlice 0 n 1- ]- slice13 =- Slice- [ DimSlice 2 (n `P.div` 3) 3,- DimSlice 0 (n `P.div` 2) 1,- DimSlice 1 (n `P.div` 2) 2- ]- ixfun = permute (slice (iota [n, n, n]) slice2) [2, 1, 0]- ixfun' = slice (rotate ixfun [3, 1, 2]) slice13- in ixfun'--test_slice_rotate_permute_slice_iota2 :: [TestTree]-test_slice_rotate_permute_slice_iota2 =- singleton $- testCase "slice . rotate . permute . slice . iota 2" $- compareOps $- let slice2 =- Slice- [ DimSlice 0 (n `P.div` 2) 1,- DimFix (n `P.div` 2),- DimSlice 0 (n `P.div` 3) 1- ]- slice13 =- Slice- [ DimSlice 2 (n `P.div` 3) 3,- DimSlice 0 n 1,- DimSlice 1 (n `P.div` 2) 2- ]- ixfun = permute (slice (iota [n, n, n]) slice13) [2, 1, 0]- ixfun' = slice (rotate ixfun [3, 1, 2]) slice2- in ixfun'--test_slice_rotate_permute_slice_iota3 :: [TestTree]-test_slice_rotate_permute_slice_iota3 =- singleton $- testCase "slice . rotate . permute . slice . iota 3" $- compareOps $- -- full-slice of (-1) stride- let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]- ixfun' = rotate ixfun [2, 1]-- (n1, m1) = case IxFunLMAD.shape (fst ixfun') of- [a, b] -> (a, b)- _ -> error "expecting 2 dimensions at this point!"- negslice =- Slice- [ DimSlice 0 n1 1,- DimSlice (m1 - 1) m1 (-1)- ]- ixfun'' = rotate (slice ixfun' negslice) [1, 2]- in ixfun''--test_permute_rotate_slice_permute_slice_iota :: [TestTree]-test_permute_rotate_slice_permute_slice_iota =- singleton $- testCase "permute . rotate . slice . permute . slice . iota" $- compareOps $- -- contiguousness- let slice33 =- Slice- [ DimFix (n `P.div` 2),- DimSlice (n - 1) (n `P.div` 3) (-1),- DimSlice 0 n 1- ]- ixfun = permute (slice (iota [n, n, n]) slice33) [1, 0]- m = n `P.div` 3- slice1 =- Slice- [ DimSlice (n - 1) n (-1),- DimSlice 2 (m - 2) 1- ]- ixfun' = permute (rotate (slice ixfun slice1) [1, 2]) [1, 0]- in ixfun'--test_reshape_rotate_iota :: [TestTree]-test_reshape_rotate_iota =- -- negative reshape test- singleton $- testCase "reshape . rotate . iota" $- compareOps $- let newdims = [DimNew (n * n), DimCoercion n]- in reshape (rotate (iota [n, n, n]) [1, 0, 0]) newdims- test_reshape_permute_iota :: [TestTree] test_reshape_permute_iota = -- negative reshape test singleton $ testCase "reshape . permute . iota" $ compareOps $- let newdims = [DimNew (n * n), DimCoercion n]+ let newdims = [n * n, n] in reshape (permute (iota [n, n, n]) [1, 2, 0]) newdims test_reshape_slice_iota2 :: [TestTree]@@ -274,7 +164,7 @@ singleton $ testCase "reshape . slice . iota 2" $ compareOps $- let newdims = [DimNew (n * n), DimCoercion n]+ let newdims = [n * n, n] slc = Slice [ DimFix (n `P.div` 2),@@ -290,7 +180,7 @@ singleton $ testCase "reshape . slice . iota 3" $ compareOps $- let newdims = [DimNew (n * n), DimCoercion n]+ let newdims = [n * n, n] slc = Slice [ DimFix (n `P.div` 2),@@ -303,13 +193,13 @@ test_complex1 :: [TestTree] test_complex1 = singleton $- testCase "reshape . permute . rotate . slice . permute . slice . iota 1" $+ testCase "reshape . permute . slice . permute . slice . iota 1" $ compareOps $ let newdims =- [ DimCoercion n,- DimCoercion n,- DimNew n,- DimCoercion ((n `P.div` 3) - 2)+ [ n,+ n,+ n,+ (n `P.div` 3) - 2 ] slice33 = Slice@@ -327,18 +217,18 @@ DimSlice (n - 1) n (-1), DimSlice 1 (m - 2) (-1) ]- ixfun' = reshape (rotate (slice ixfun slice1) [1, 2, 3, 4]) newdims+ ixfun' = reshape (slice ixfun slice1) newdims in ixfun' test_complex2 :: [TestTree] test_complex2 = singleton $- testCase "reshape . permute . rotate . slice . permute . slice . iota 2" $+ testCase "reshape . permute . slice . permute . slice . iota 2" $ compareOps $ let newdims =- [ DimCoercion n,- DimNew (n * n),- DimCoercion ((n `P.div` 3) - 2)+ [ n,+ n * n,+ (n `P.div` 3) - 2 ] slc2 = Slice@@ -357,7 +247,7 @@ DimSlice (n - 1) n (-1), DimSlice 1 (m - 2) (-1) ]- ixfun' = reshape (rotate (slice ixfun slice1) [1, 0, 0, 2]) newdims+ ixfun' = reshape (slice ixfun slice1) newdims in ixfun' test_rebase1 :: [TestTree]@@ -371,8 +261,8 @@ DimSlice 2 (n - 2) 1, DimSlice 3 (n - 3) 1 ]- ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]- ixfn_orig = rotate (permute (iota [n - 3, n - 2]) [1, 0]) [1, 2]+ ixfn_base = permute (slice (iota [n, n, n]) slice_base) [1, 0]+ ixfn_orig = permute (iota [n - 3, n - 2]) [1, 0] ixfn_rebase = rebase ixfn_base ixfn_orig in ixfn_rebase @@ -392,8 +282,8 @@ [ DimSlice (n - 4) (n - 3) (-1), DimSlice (n - 3) (n - 2) (-1) ]- ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]- ixfn_orig = rotate (permute (slice (iota [n - 3, n - 2]) slice_orig) [1, 0]) [1, 2]+ ixfn_base = permute (slice (iota [n, n, n]) slice_base) [1, 0]+ ixfn_orig = permute (slice (iota [n - 3, n - 2]) slice_orig) [1, 0] ixfn_rebase = rebase ixfn_base ixfn_orig in ixfn_rebase @@ -415,8 +305,8 @@ [ DimSlice (n3 - 1) n3 (-1), DimSlice (n2 - 1) n2 (-1) ]- ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]- ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]+ ixfn_base = permute (slice (iota [n, n, n]) slice_base) [1, 0]+ ixfn_orig = permute (slice (iota [n3, n2]) slice_orig) [1, 0] ixfn_rebase = rebase ixfn_base ixfn_orig in ixfn_rebase @@ -435,8 +325,8 @@ [ DimSlice (n3 - 1) n3 (-1), DimSlice 0 n2 1 ]- ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]- ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]+ ixfn_base = permute (slice (iota [n, n, n]) slice_base) [1, 0]+ ixfn_orig = permute (slice (iota [n3, n2]) slice_orig) [1, 0] in [ testCase "rebase mixed monotonicities" $ compareOps $ rebase ixfn_base ixfn_orig@@ -479,38 +369,11 @@ where flat_slice_1 = FlatSlice 17 [FlatDimIndex 3 27, FlatDimIndex 3 10, FlatDimIndex 3 1] -test_flatSlice_rotate_iota :: [TestTree]-test_flatSlice_rotate_iota =- singleton $- testCase "flatSlice . rotate . iota " $- compareOps $- flatSlice (rotate (iota [10, 10]) [2, 5]) flat_slice_1- where- flat_slice_1 = FlatSlice 3 [FlatDimIndex 2 2, FlatDimIndex 2 1]--test_flatSlice_rotate_slice_iota :: [TestTree]-test_flatSlice_rotate_slice_iota =- singleton $- testCase "flatSlice . rotate . slice . iota " $- compareOps $- flatSlice (rotate (slice (iota [20, 20]) $ Slice [DimSlice 1 5 2, DimSlice 0 5 2]) [2, 3]) flat_slice_1- where- flat_slice_1 = FlatSlice 1 [FlatDimIndex 2 2]- test_flatSlice_transpose_slice_iota :: [TestTree] test_flatSlice_transpose_slice_iota = singleton $ testCase "flatSlice . transpose . slice . iota " $ compareOps $ flatSlice (permute (slice (iota [20, 20]) $ Slice [DimSlice 1 5 2, DimSlice 0 5 2]) [1, 0]) flat_slice_1- where- flat_slice_1 = FlatSlice 1 [FlatDimIndex 2 2]--test_rotate_flatSlice_transpose_slice_iota :: [TestTree]-test_rotate_flatSlice_transpose_slice_iota =- singleton $- testCase "flatSlice . transpose . slice . iota " $- compareOps $- rotate (flatSlice (permute (slice (iota [20, 20]) $ Slice [DimSlice 1 5 2, DimSlice 1 5 2]) [1, 0]) flat_slice_1) [2, 1] where flat_slice_1 = FlatSlice 1 [FlatDimIndex 2 2]
unittests/Futhark/IR/Mem/IxFunWrapper.hs view
@@ -4,8 +4,8 @@ ( IxFun, iota, permute,- rotate, reshape,+ coerce, slice, flatSlice, rebase,@@ -14,13 +14,11 @@ import qualified Futhark.IR.Mem.IxFun as I import qualified Futhark.IR.Mem.IxFun.Alg as IA-import Futhark.IR.Syntax (FlatSlice, ShapeChange, Slice)+import Futhark.IR.Syntax (FlatSlice, Slice) import Futhark.Util.IntegralExp type Shape num = [num] -type Indices num = [num]- type Permutation = [Int] type IxFun num = (I.IxFun num, IA.IxFun num)@@ -38,19 +36,19 @@ IxFun num permute (l, a) x = (I.permute l x, IA.permute a x) -rotate ::+reshape :: (Eq num, IntegralExp num) => IxFun num ->- Indices num ->+ Shape num -> IxFun num-rotate (l, a) x = (I.rotate l x, IA.rotate a x)+reshape (l, a) x = (I.reshape l x, IA.reshape a x) -reshape ::+coerce :: (Eq num, IntegralExp num) => IxFun num ->- ShapeChange num ->+ Shape num -> IxFun num-reshape (l, a) x = (I.reshape l x, IA.reshape a x)+coerce (l, a) x = (I.coerce l x, IA.coerce a x) slice :: (Eq num, IntegralExp num) =>
unittests/Futhark/IR/Prop/ReshapeTests.hs view
@@ -5,93 +5,40 @@ ) where -import Control.Applicative import Futhark.IR.Prop.Constants import Futhark.IR.Prop.Reshape import Futhark.IR.Syntax import Test.Tasty import Test.Tasty.HUnit-import Test.Tasty.QuickCheck-import Prelude -tests :: TestTree-tests =- testGroup "ReshapeTests" $- fuseReshapeTests- ++ informReshapeTests- ++ reshapeOuterTests- ++ reshapeInnerTests- ++ [ fuseReshapeProp,- informReshapeProp- ]--fuseReshapeTests :: [TestTree]-fuseReshapeTests =- [ testCase (unwords ["fuseReshape ", show d1, show d2]) $- fuseReshape (d1 :: ShapeChange Int) d2 @?= dres -- type signature to avoid warning- | (d1, d2, dres) <-- [ ([DimCoercion 1], [DimNew 1], [DimCoercion 1]),- ([DimNew 1], [DimCoercion 1], [DimNew 1]),- ([DimCoercion 1, DimNew 2], [DimNew 1, DimNew 2], [DimCoercion 1, DimNew 2]),- ([DimNew 1, DimNew 2], [DimCoercion 1, DimNew 2], [DimNew 1, DimNew 2])- ]- ]--informReshapeTests :: [TestTree]-informReshapeTests =- [ testCase (unwords ["informReshape ", show shape, show sc, show sc_res]) $- informReshape (shape :: [Int]) sc @?= sc_res -- type signature to avoid warning- | (shape, sc, sc_res) <-- [ ([1, 2], [DimNew 1, DimNew 3], [DimCoercion 1, DimNew 3]),- ([2, 2], [DimNew 1, DimNew 3], [DimNew 1, DimNew 3])- ]- ]- reshapeOuterTests :: [TestTree] reshapeOuterTests = [ testCase (unwords ["reshapeOuter", show sc, show n, show shape, "==", show sc_res]) $- reshapeOuter (intShapeChange sc) n (intShape shape) @?= intShapeChange sc_res+ reshapeOuter (intShape sc) n (intShape shape) @?= intShape sc_res | (sc, n, shape, sc_res) <-- [ ([DimNew 1], 1, [4, 3], [DimNew 1, DimCoercion 3]),- ([DimNew 1], 2, [4, 3], [DimNew 1]),- ([DimNew 2, DimNew 2], 1, [4, 3], [DimNew 2, DimNew 2, DimNew 3]),- ([DimNew 2, DimNew 2], 2, [4, 3], [DimNew 2, DimNew 2])+ [ ([1], 1, [4, 3], [1, 3]),+ ([1], 2, [4, 3], [1]),+ ([2, 2], 1, [4, 3], [2, 2, 3]),+ ([2, 2], 2, [4, 3], [2, 2]) ] ] reshapeInnerTests :: [TestTree] reshapeInnerTests = [ testCase (unwords ["reshapeInner", show sc, show n, show shape, "==", show sc_res]) $- reshapeInner (intShapeChange sc) n (intShape shape) @?= intShapeChange sc_res+ reshapeInner (intShape sc) n (intShape shape) @?= intShape sc_res | (sc, n, shape, sc_res) <-- [ ([DimNew 1], 1, [4, 3], [DimCoercion 4, DimNew 1]),- ([DimNew 1], 0, [4, 3], [DimNew 1]),- ([DimNew 2, DimNew 2], 1, [4, 3], [DimNew 4, DimNew 2, DimNew 2]),- ([DimNew 2, DimNew 2], 0, [4, 3], [DimNew 2, DimNew 2])+ [ ([1], 1, [4, 3], [4, 1]),+ ([1], 0, [4, 3], [1]),+ ([2, 2], 1, [4, 3], [4, 2, 2]),+ ([2, 2], 0, [4, 3], [2, 2]) ] ] intShape :: [Int] -> Shape intShape = Shape . map (intConst Int32 . toInteger) -intShapeChange :: ShapeChange Int -> ShapeChange SubExp-intShapeChange = map (fmap $ intConst Int32 . toInteger)--fuseReshapeProp :: TestTree-fuseReshapeProp = testProperty "fuseReshape result matches second argument" prop- where- prop :: ShapeChange Int -> ShapeChange Int -> Bool- prop sc1 sc2 = map newDim (fuseReshape sc1 sc2) == map newDim sc2--informReshapeProp :: TestTree-informReshapeProp = testProperty "informReshape result matches second argument" prop- where- prop :: [Int] -> ShapeChange Int -> Bool- prop sc1 sc2 = map newDim (informReshape sc1 sc2) == map newDim sc2--instance Arbitrary d => Arbitrary (DimChange d) where- arbitrary =- oneof- [ DimNew <$> arbitrary,- DimCoercion <$> arbitrary- ]+tests :: TestTree+tests =+ testGroup "ReshapeTests" $+ reshapeOuterTests ++ reshapeInnerTests