futhark 0.19.3 → 0.19.4
raw patch · 24 files changed
+322/−128 lines, 24 filesdep ~versions
Dependency ranges changed: versions
Files
- docs/language-reference.rst +5/−2
- docs/server-protocol.rst +14/−10
- futhark.cabal +1/−1
- rts/c/server.h +47/−5
- rts/python/server.py +27/−13
- rts/python/values.py +7/−6
- src/Futhark/Analysis/SymbolTable.hs +1/−0
- src/Futhark/CLI/Dev.hs +2/−0
- src/Futhark/CodeGen/Backends/GenericC.hs +3/−0
- src/Futhark/CodeGen/Backends/GenericPython.hs +25/−0
- src/Futhark/IR/Parse.hs +20/−0
- src/Futhark/IR/SegOp.hs +33/−19
- src/Futhark/Internalise/Defunctionalise.hs +7/−12
- src/Futhark/Optimise/Fusion.hs +14/−7
- src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs +11/−1
- src/Futhark/Optimise/Simplify/Engine.hs +2/−0
- src/Futhark/Optimise/Simplify/Rules.hs +13/−6
- src/Futhark/Optimise/Simplify/Rules/BasicOp.hs +10/−3
- src/Futhark/Pass/ExtractKernels.hs +0/−20
- src/Futhark/Pass/ExtractKernels/DistributeNests.hs +11/−2
- src/Futhark/Server.hs +8/−1
- src/Futhark/Version.hs +1/−3
- src/Language/Futhark/TypeChecker/Terms.hs +1/−1
- src/Language/Futhark/TypeChecker/Unify.hs +59/−16
docs/language-reference.rst view
@@ -190,12 +190,14 @@ first class. See :ref:`hofs` for the details. .. productionlist::- stringlit: '"' `stringchar` '"'+ stringlit: '"' `stringchar`* '"'+ charlit: "'" `stringchar` "'" stringchar: <any source character except "\" or newline or quotes> String literals are supported, but only as syntactic sugar for UTF-8 encoded arrays of ``u8`` values. There is no character type in-Futhark.+Futhark, but character literals are interpreted as integers of the+corresponding Unicode code point. Declarations ------------@@ -407,6 +409,7 @@ atom: `literal` : | `qualid` ("." `fieldid`)* : | `stringlit`+ : | `charlit` : | "(" ")" : | "(" `exp` ")" ("." `fieldid`)* : | "(" `exp` ("," `exp`)* ")"
docs/server-protocol.rst view
@@ -21,16 +21,20 @@ Basics ------ -Each command is sent as a *single line* on standard input. The-response is sent on standard output. The server will print ``%%% OK``-on a line by itself to indicate that a command has finished. It will-also print ``%%% OK`` at startup once initialisation has finished. If-initialisation fails, the process will terminate. If a command fails,-the server will print ``%%% FAILURE`` followed by the error message,-and then ``%%% OK`` when it is ready for more input. Some output may-also precede ``%%% FAILURE``, e.g. logging statements that occured-before failure was detected. Fatal errors (that lead to server-shutdown) may be printed to stderr.+Each command is sent as a *single line* on standard input. A command+consists of space-separated *words*. A word is either a sequence of+non-space characters (``foo``), *or* double quotes surrounding a+sequence of non-newline and non-quote characters (``"foo bar"``).++The response is sent on standard output. The server will print ``%%%+OK`` on a line by itself to indicate that a command has finished. It+will also print ``%%% OK`` at startup once initialisation has+finished. If initialisation fails, the process will terminate. If a+command fails, the server will print ``%%% FAILURE`` followed by the+error message, and then ``%%% OK`` when it is ready for more input.+Some output may also precede ``%%% FAILURE``, e.g. logging statements+that occured before failure was detected. Fatal errors (that lead to+server shutdown) may be printed to stderr. Variables ---------
futhark.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 -- Run 'cabal2nix . >futhark.nix' after adding deps. name: futhark-version: 0.19.3+version: 0.19.4 synopsis: An optimising compiler for a functional, array-oriented language. description: Futhark is a small programming language designed to be compiled to
rts/c/server.h view
@@ -488,19 +488,61 @@ free(report); } +char *next_word(char **line) {+ char *p = *line;++ while (isspace(*p)) {+ p++;+ }++ if (*p == 0) {+ return NULL;+ }++ if (*p == '"') {+ char *save = p+1;+ // Skip ahead till closing quote.+ p++;++ while (*p && *p != '"') {+ p++;+ }++ if (*p == '"') {+ *p = 0;+ *line = p+1;+ return save;+ } else {+ return NULL;+ }+ } else {+ char *save = p;+ // Skip ahead till next whitespace.++ while (*p && !isspace(*p)) {+ p++;+ }++ if (*p) {+ *p = 0;+ *line = p+1;+ } else {+ *line = p;+ }+ return save;+ }+}+ void process_line(struct server_state *s, char *line) {- int max_num_tokens = 100;+ int max_num_tokens = 1000; const char* tokens[max_num_tokens]; int num_tokens = 0;- char *saveptr; - char *tmp = line;- while ((tokens[num_tokens] = strtok_r(tmp, " \n", &saveptr)) != NULL) {+ while ((tokens[num_tokens] = next_word(&line)) != NULL) { num_tokens++; if (num_tokens == max_num_tokens) { futhark_panic(1, "Line too long.\n"); }- tmp = NULL; } const char *command = tokens[0];
rts/python/server.py view
@@ -2,6 +2,7 @@ import sys import time+import shlex # For string splitting class Server: def __init__(self, ctx):@@ -81,23 +82,36 @@ for (out_vname, val) in zip(out_vnames, vals): self._vars[out_vname] = val + def _store_val(self, f, value):+ # In case we are using the PyOpenCL backend, we first+ # need to convert OpenCL arrays to ordinary NumPy+ # arrays. We do this in a nasty way.+ if isinstance(value, opaque):+ for component in value.data:+ self._store_val(f, component)+ elif isinstance(value, np.number) or isinstance(value, bool) or isinstance(value, np.bool_) or isinstance(value, np.ndarray):+ # Ordinary NumPy value.+ f.write(construct_binary_value(value))+ else:+ # Assuming PyOpenCL array.+ f.write(construct_binary_value(value.get()))+ def _cmd_store(self, args): fname = self._get_arg(args, 0) with open(fname, 'wb') as f: for i in range(1, len(args)):- vname = args[i]- value = self._get_var(vname)- # In case we are using the PyOpenCL backend, we first- # need to convert OpenCL arrays to ordinary NumPy- # arrays. We do this in a nasty way.- if isinstance(value, np.number) or isinstance(value, bool) or isinstance(value, np.bool_) or isinstance(value, np.ndarray):- # Ordinary NumPy value.- f.write(construct_binary_value(self._vars[vname]))- else:- # Assuming PyOpenCL array.- f.write(construct_binary_value(self._vars[vname].get()))+ self._store_val(f, self._get_var(args[i])) + def _restore_val(self, reader, typename):+ if typename in self._ctx.opaques:+ vs = []+ for t in self._ctx.opaques[typename]:+ vs += [read_value(t, reader)]+ return opaque(typename, *vs)+ else:+ return read_value(typename, reader)+ def _cmd_restore(self, args): if len(args) % 2 == 0: raise self.Failure('Invalid argument count')@@ -116,7 +130,7 @@ raise self.Failure('Variable already exists: %s' % vname) try:- self._vars[vname] = read_value(typename, reader)+ self._vars[vname] = self._restore_val(reader, typename) except ValueError: raise self.Failure('Failed to restore variable %s.\n' 'Possibly malformed data in %s.\n'@@ -139,7 +153,7 @@ } def _process_line(self, line):- words = line.split()+ words = shlex.split(line) if words == []: raise self.Failure('Empty line') else:
rts/python/values.py view
@@ -371,7 +371,8 @@ def bin_reader(f): fmt = FUTHARK_PRIMTYPES[t]['bin_format'] size = FUTHARK_PRIMTYPES[t]['size']- return struct.unpack('<' + fmt, f.get_chars(size))[0]+ tf = FUTHARK_PRIMTYPES[t]['numpy_type']+ return tf(struct.unpack('<' + fmt, f.get_chars(size))[0]) return bin_reader read_bin_i8 = mk_bin_scalar_reader('i8')@@ -536,18 +537,18 @@ shape = [] elem_count = 1 for i in range(rank):- bin_size = read_bin_u64(f)+ bin_size = read_bin_i64(f) elem_count *= bin_size shape.append(bin_size) bin_fmt = FUTHARK_PRIMTYPES[bin_type_enum]['bin_format'] # We first read the expected number of types into a bytestring,- # then use np.fromstring. This is because np.fromfile does not+ # then use np.frombuffer. This is because np.fromfile does not # work on things that are insufficiently file-like, like a network # stream. bytes = f.get_chars(elem_count * FUTHARK_PRIMTYPES[expected_type]['size'])- arr = np.fromstring(bytes, dtype=FUTHARK_PRIMTYPES[bin_type_enum]['numpy_type'])+ arr = np.frombuffer(bytes, dtype=FUTHARK_PRIMTYPES[bin_type_enum]['numpy_type']) arr.shape = shape return arr@@ -664,9 +665,9 @@ bytes[3:7] = type_strs[t] for i in range(len(shape)):- bytes[7+i*8:7+(i+1)*8] = np.int64(shape[i]).tostring()+ bytes[7+i*8:7+(i+1)*8] = np.int64(shape[i]).tobytes() - bytes[7+len(shape)*8:] = np.ascontiguousarray(v).tostring()+ bytes[7+len(shape)*8:] = np.ascontiguousarray(v).tobytes() return bytes
src/Futhark/Analysis/SymbolTable.hs view
@@ -14,6 +14,7 @@ entryDepth, entryLetBoundDec, entryIsSize,+ entryStm, -- * Lookup elem,
src/Futhark/CLI/Dev.hs view
@@ -646,6 +646,8 @@ runPolyPasses config base (SOACS prog) ), (".fut_soacs", readCore parseSOACS SOACS),+ (".fut_seq", readCore parseSeq Seq),+ (".fut_seq_mem", readCore parseSeqMem SeqMem), (".fut_kernels", readCore parseKernels Kernels), (".fut_kernels_mem", readCore parseKernelsMem KernelsMem), (".fut_mc", readCore parseMC MC),
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -1337,7 +1337,9 @@ // _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+$esc:("#ifndef _GNU_SOURCE") // Avoid possible double-definition warning. $esc:("#define _GNU_SOURCE")+$esc:("#endif") |] -- We may generate variables that are never used (e.g. for@@ -1354,6 +1356,7 @@ $esc:("#pragma GCC diagnostic ignored \"-Wunused-variable\"") $esc:("#pragma GCC diagnostic ignored \"-Wparentheses\"") $esc:("#pragma GCC diagnostic ignored \"-Wunused-label\"")+$esc:("#pragma GCC diagnostic ignored \"-Wunused-but-set-variable\"") $esc:("#endif") $esc:("#ifdef __clang__")
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -321,6 +321,22 @@ } ] +functionExternalValues :: Imp.Function a -> [Imp.ExternalValue]+functionExternalValues fun = Imp.functionResult fun ++ Imp.functionArgs fun++opaqueDefs :: Imp.Functions a -> M.Map String [PyExp]+opaqueDefs (Imp.Functions funs) =+ mconcat . map evd . concatMap (functionExternalValues . snd) $+ filter (Imp.functionEntry . snd) funs+ where+ evd Imp.TransparentValue {} = mempty+ evd (Imp.OpaqueValue name vds) =+ M.singleton name $ map (String . vd) vds+ vd (Imp.ScalarValue pt s _) =+ readTypeEnum pt s+ vd (Imp.ArrayValue _ _ pt s dims) =+ concat (replicate (length dims) "[]") ++ readTypeEnum pt s+ -- | The class generated by the code generator must have a -- constructor, although it can be vacuous. data Constructor = Constructor [String] [PyStmt]@@ -392,6 +408,9 @@ [ ClassDef $ Class class_name $ Assign (Var "entry_points") (Dict entry_point_types) :+ Assign+ (Var "opaques")+ (Dict $ zip (map String opaque_names) (map Tuple opaque_payloads)) : map FunDef (constructor' : definitions ++ entry_points) ] ToServer -> do@@ -405,6 +424,9 @@ ++ [ ClassDef ( Class class_name $ Assign (Var "entry_points") (Dict entry_point_types) :+ Assign+ (Var "opaques")+ (Dict $ zip (map String opaque_names) (map Tuple opaque_payloads)) : map FunDef (constructor' : definitions ++ entry_points) ), Assign@@ -440,6 +462,9 @@ parse_options_server = generateOptionParser (standardOptions ++ options)++ (opaque_names, opaque_payloads) =+ unzip $ M.toList $ opaqueDefs $ Imp.defFuns prog selectEntryPoint entry_point_names entry_points = [ Assign (Var "entry_points") $
src/Futhark/IR/Parse.hs view
@@ -9,6 +9,8 @@ parseKernelsMem, parseMC, parseMCMem,+ parseSeq,+ parseSeqMem, ) where @@ -33,6 +35,8 @@ import Futhark.IR.SOACS (SOACS) import qualified Futhark.IR.SOACS.SOAC as SOAC import qualified Futhark.IR.SegOp as SegOp+import Futhark.IR.Seq (Seq)+import Futhark.IR.SeqMem (SeqMem) import Futhark.Util.Pretty (prettyText) import Text.Megaparsec import Text.Megaparsec.Char hiding (space)@@ -881,6 +885,16 @@ prSOACS = PR pDeclExtType pExtType pDeclType pType pType (pSOAC prSOACS) () () +prSeq :: PR Seq+prSeq =+ PR pDeclExtType pExtType pDeclType pType pType empty () ()++prSeqMem :: PR SeqMem+prSeqMem =+ PR pRetTypeMem pBranchTypeMem pFParamMem pLParamMem pLetDecMem op () ()+ where+ op = pMemOp empty+ prKernels :: PR Kernels prKernels = PR pDeclExtType pExtType pDeclType pType pType op () ()@@ -912,6 +926,12 @@ parseSOACS :: FilePath -> T.Text -> Either T.Text (Prog SOACS) parseSOACS = parseLore prSOACS++parseSeq :: FilePath -> T.Text -> Either T.Text (Prog Seq)+parseSeq = parseLore prSeq++parseSeqMem :: FilePath -> T.Text -> Either T.Text (Prog SeqMem)+parseSeqMem = parseLore prSeqMem parseKernels :: FilePath -> T.Text -> Either T.Text (Prog Kernels) parseKernels = parseLore prKernels
src/Futhark/IR/SegOp.hs view
@@ -336,6 +336,10 @@ TC.TypeM lore () checkKernelBody ts (KernelBody (_, dec) stms kres) = do TC.checkBodyLore dec+ -- We consume the kernel results (when applicable) before+ -- type-checking the stms, so we will get an error if a statement+ -- uses an array that is written to in a result.+ mapM_ consumeKernelResult kres TC.checkStms stms $ do unless (length ts == length kres) $ TC.bad $@@ -346,6 +350,11 @@ ++ " values." zipWithM_ checkKernelResult kres ts where+ consumeKernelResult (WriteReturns _ arr _) =+ TC.consume =<< TC.lookupAliases arr+ consumeKernelResult _ =+ pure ()+ checkKernelResult (Returns _ what) t = TC.require [t] what checkKernelResult (WriteReturns shape arr res) t = do@@ -365,7 +374,6 @@ ++ pretty shape ++ ", but destination array has type " ++ pretty arr_t- TC.consume =<< TC.lookupAliases arr checkKernelResult (ConcatReturns o w per_thread_elems v) t = do case o of SplitContiguous -> return ()@@ -1048,26 +1056,34 @@ simplifyKernelBody space (KernelBody _ stms res) = do par_blocker <- Engine.asksEngineEnv $ Engine.blockHoistPar . Engine.envHoistBlockers + -- Ensure we do not try to use anything that is consumed in the result. ((body_stms, body_res), hoisted) <-- Engine.localVtable (<> scope_vtable) $- Engine.localVtable (\vtable -> vtable {ST.simplifyMemory = True}) $- Engine.blockIf- ( Engine.hasFree bound_here- `Engine.orIf` Engine.isOp- `Engine.orIf` par_blocker- `Engine.orIf` Engine.isConsumed- )- $ Engine.simplifyStms stms $ do- res' <-- Engine.localVtable (ST.hideCertified $ namesFromList $ M.keys $ scopeOf stms) $- mapM Engine.simplify res- return ((res', UT.usages $ freeIn res'), mempty)+ Engine.localVtable (flip (foldl' (flip ST.consume)) (foldMap consumedInResult res))+ . Engine.localVtable (<> scope_vtable)+ . Engine.localVtable (\vtable -> vtable {ST.simplifyMemory = True})+ . Engine.enterLoop+ $ Engine.blockIf+ ( Engine.hasFree bound_here+ `Engine.orIf` Engine.isOp+ `Engine.orIf` par_blocker+ `Engine.orIf` Engine.isConsumed+ )+ $ Engine.simplifyStms stms $ do+ res' <-+ Engine.localVtable (ST.hideCertified $ namesFromList $ M.keys $ scopeOf stms) $+ mapM Engine.simplify res+ return ((res', UT.usages $ freeIn res'), mempty) return (mkWiseKernelBody () body_stms body_res, hoisted) where scope_vtable = segSpaceSymbolTable space bound_here = namesFromList $ M.keys $ scopeOfSegSpace space + consumedInResult (WriteReturns _ arr _) =+ [arr]+ consumedInResult _ =+ []+ segSpaceSymbolTable :: ASTLore lore => SegSpace -> ST.SymbolTable lore segSpaceSymbolTable (SegSpace flat gtids_and_dims) = foldl' f (ST.fromScope $ M.singleton flat $ IndexName Int64) gtids_and_dims@@ -1362,15 +1378,13 @@ ) $ segSpaceDims space index kpe' =- letBind (Pattern [] [kpe']) $- BasicOp $- Index arr $- outer_slice <> remaining_slice+ letBindNames [patElemName kpe'] . BasicOp . Index arr $+ outer_slice <> remaining_slice if patElemName kpe `UT.isConsumed` used then do precopy <- newVName $ baseString (patElemName kpe) <> "_precopy" index kpe {patElemName = precopy}- letBind (Pattern [] [kpe]) $ BasicOp $ Copy precopy+ letBindNames [patElemName kpe] $ BasicOp $ Copy precopy else index kpe return ( kpes'',
src/Futhark/Internalise/Defunctionalise.hs view
@@ -251,8 +251,8 @@ return ((x, decs), const mempty) -- | Looks up the associated static value for a given name in the environment.-lookupVar :: StructType -> SrcLoc -> VName -> DefM StaticVal-lookupVar t loc x = do+lookupVar :: StructType -> VName -> DefM StaticVal+lookupVar t x = do env <- askEnv case M.lookup x env of Just (Binding (Just (dims, sv_t)) sv) -> do@@ -263,14 +263,9 @@ Nothing -- If the variable is unknown, it may refer to the 'intrinsics' -- module, which we will have to treat specially. | baseTag x <= maxIntrinsicTag -> return IntrinsicSV- | otherwise -> -- Anything not in scope is going to be an- -- existential size.- return $ Dynamic $ Scalar $ Prim $ Signed Int64 | otherwise ->- error $- "Variable " ++ pretty x ++ " at "- ++ locStr loc- ++ " is out of scope."+ -- Anything not in scope is going to be an existential size.+ return $ Dynamic $ Scalar $ Prim $ Signed Int64 -- Like patternDimNames, but ignores sizes that are only found in -- funtion types.@@ -461,7 +456,7 @@ (e', sv) <- defuncExp e return (RecordFieldExplicit vn e' loc', (vn, sv)) defuncField (RecordFieldImplicit vn (Info t) loc') = do- sv <- lookupVar (toStruct t) loc' vn+ sv <- lookupVar (toStruct t) vn case sv of -- If the implicit field refers to a dynamic function, we -- convert it to an explicit field with a record closing over@@ -486,7 +481,7 @@ incl' <- mapM defuncExp' incl return (Range e1' me' incl' t loc, Dynamic t') defuncExp e@(Var qn (Info t) loc) = do- sv <- lookupVar (toStruct t) loc (qualLeaf qn)+ sv <- lookupVar (toStruct t) (qualLeaf qn) case sv of -- If the variable refers to a dynamic function, we return its closure -- representation (i.e., a record expression capturing the free variables@@ -931,7 +926,7 @@ ++ show sv1 defuncApply depth e@(Var qn (Info t) loc) = do let (argtypes, _) = unfoldFunType t- sv <- lookupVar (toStruct t) loc (qualLeaf qn)+ sv <- lookupVar (toStruct t) (qualLeaf qn) case sv of DynamicFun _ _
src/Futhark/Optimise/Fusion.hs view
@@ -140,14 +140,20 @@ varsAliases :: Names -> FusionGM Names varsAliases = fmap mconcat . mapM varAliases . namesToList -checkForUpdates :: FusedRes -> Exp -> FusionGM FusedRes-checkForUpdates res (BasicOp (Update src is _)) = do- res' <-- foldM addVarToInfusible res $- src : namesToList (mconcat $ map freeIn is)- aliases <- varAliases src+updateKerInPlaces :: FusedRes -> ([VName], [VName]) -> FusionGM FusedRes+updateKerInPlaces res (ip_vs, other_infuse_vs) = do+ res' <- foldM addVarToInfusible res (ip_vs ++ other_infuse_vs)+ aliases <- mconcat <$> mapM varAliases ip_vs let inspectKer k = k {inplace = aliases <> inplace k} return res' {kernels = M.map inspectKer $ kernels res'}++checkForUpdates :: FusedRes -> Exp -> FusionGM FusedRes+checkForUpdates res (BasicOp (Update src is _)) = do+ let ifvs = namesToList $ mconcat $ map freeIn is+ updateKerInPlaces res ([src], ifvs)+checkForUpdates res (Op (Futhark.Scatter _ _ _ written_info)) = do+ let updt_arrs = map (\(_, _, x) -> x) written_info+ updateKerInPlaces res (updt_arrs, []) checkForUpdates res _ = return res -- | Updates the environment: (i) the @soacs@ (map) by binding each pattern@@ -747,7 +753,8 @@ -- set to force horizontal fusion. It is not possible to -- producer/consumer-fuse Scatter anyway. fres' <- addNamesToInfusible fres $ namesFromList $ patternNames pat- mapLike fres' soac lam+ fres'' <- mapLike fres' soac lam+ checkForUpdates fres'' e Right soac@(SOAC.Hist _ _ lam _) -> do -- We put the variables produced by Hist into the infusible -- set to force horizontal fusion. It is not possible to
src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs view
@@ -81,7 +81,8 @@ scope (Let pat aux (Op (SegOp (SegMap lvl space ts kbody)))) updates- | all ((`elem` patternNames pat) . updateValue) updates = do+ | all ((`elem` patternNames pat) . updateValue) updates,+ not source_used_in_kbody = do mk <- lowerUpdatesIntoSegMap scope pat updates space kbody Just $ do (pat', kbody', poststms) <- mk@@ -89,6 +90,15 @@ return $ certify cs (Let pat' aux $ Op $ SegOp $ SegMap lvl space ts kbody') : stmsToList poststms+ where+ -- This check is a bit more conservative than ideal. In a perfect+ -- world, we would allow indexing a[i,j] if the update is also+ -- to exactly a[i,j], as that will not create cross-iteration+ -- dependencies. (Although the type checker wouldn't be able to+ -- permit this anyway.)+ source_used_in_kbody =+ mconcat (map (`lookupAliases` scope) (namesToList (freeIn kbody)))+ `namesIntersect` mconcat (map ((`lookupAliases` scope) . updateSource) updates) lowerUpdateKernels scope stm updates = lowerUpdate scope stm updates lowerUpdatesIntoSegMap ::
src/Futhark/Optimise/Simplify/Engine.hs view
@@ -59,6 +59,7 @@ ST.SymbolTable, hoistStms, blockIf,+ enterLoop, module Futhark.Optimise.Simplify.Lore, ) where@@ -220,6 +221,7 @@ usedCerts :: Certificates -> SimpleM lore () usedCerts cs = modify $ \(a, b, c) -> (a, b, cs <> c) +-- | Indicate in the symbol table that we have descended into a loop. enterLoop :: SimpleM lore a -> SimpleM lore a enterLoop = localVtable ST.deepen
src/Futhark/Optimise/Simplify/Rules.hs view
@@ -22,6 +22,7 @@ import Control.Monad import Data.Either+import Data.List (find) import qualified Data.Map.Strict as M import Data.Maybe import Futhark.Analysis.PrimExp.Convert@@ -59,19 +60,25 @@ -- statement and it can be consumed. -- -- This simplistic rule is only valid before we introduce memory.-removeUnnecessaryCopy :: BinderOps lore => BottomUpRuleBasicOp lore+removeUnnecessaryCopy :: (BinderOps lore, Aliased lore) => BottomUpRuleBasicOp lore removeUnnecessaryCopy (vtable, used) (Pattern [] [d]) _ (Copy v) | not (v `UT.isConsumed` used), (not (v `UT.used` used) && consumable) || not (patElemName d `UT.isConsumed` used) = Simplify $ letBindNames [patElemName d] $ BasicOp $ SubExp $ Var v where- -- We need to make sure we can even consume the original.- -- This is currently a hacky check, much too conservative,- -- because we don't have the information conveniently- -- available.+ -- We need to make sure we can even consume the original. The big+ -- missing piece here is that we cannot do copy removal inside of+ -- 'map' and other SOACs, but those cases tend to be handled in+ -- later representations anyway. consumable = case M.lookup v $ ST.toScope vtable of Just (FParamName info) -> unique $ declTypeOf info- _ -> False+ _ -> fromMaybe False $ do+ e <- ST.lookup v vtable+ pat <- stmPattern <$> ST.entryStm e+ guard $ ST.entryDepth e == ST.loopDepth vtable+ pe <- find ((== v) . patElemName) (patternElements pat)+ guard $ aliasesOf pe == mempty+ pure True removeUnnecessaryCopy _ _ _ _ = Skip constantFoldPrimFun :: BinderOps lore => TopDownRuleGeneric lore
src/Futhark/Optimise/Simplify/Rules/BasicOp.hs view
@@ -124,15 +124,22 @@ | -- We produce the to-be-concatenated arrays in reverse order, so -- reverse them back. y : ys <-- reverse $- foldl' fuseConcatArg mempty $- map (toConcatArg vtable) $ x : xs,+ forSingleArray $+ reverse $+ foldl' fuseConcatArg mempty $+ map (toConcatArg vtable) $ x : xs, length xs /= length ys = Simplify $ do elem_type <- lookupType x y' <- fromConcatArg elem_type y ys' <- mapM (fromConcatArg elem_type) ys auxing aux $ letBind pat $ BasicOp $ Concat 0 y' ys' outer_w+ where+ -- If we fuse so much that there is only a single input left, then+ -- it must have the right size.+ forSingleArray [(ArgReplicate _ v, cs)] =+ [(ArgReplicate [outer_w] v, cs)]+ forSingleArray ys = ys simplifyConcat _ _ _ _ = Skip ruleBasicOp :: BinderOps lore => TopDownRuleBasicOp lore
src/Futhark/Pass/ExtractKernels.hs view
@@ -384,26 +384,6 @@ lvl <- segThreadCapped [w] "segscan" $ NoRecommendation SegNoVirt addStms . fmap (certify cs) =<< segScan lvl res_pat w scan_ops map_lam_sequential arrs [] []-- -- We are only willing to generate code for scanomaps that do not- -- involve array accumulators, and do not have parallelism in their- -- map function. Such cases will fall through to the- -- screma-splitting case, and produce an ordinary map and scan.- -- Hopefully, the scan then triggers the ISWIM case above (otherwise- -- we will still crash in code generation). However, if the map- -- lambda is already identity, let's just go ahead here.- | Just (scans, map_lam) <- isScanomapSOAC form,- ( all primType (concatMap (lambdaReturnType . scanLambda) scans)- && not (lambdaContainsParallelism map_lam)- )- || isIdentityLambda map_lam = runBinder_ $ do- scan_ops <- forM scans $ \(Scan scan_lam nes) -> do- let scan_lam' = soacsLambdaToKernels scan_lam- return $ SegBinOp Noncommutative scan_lam' nes mempty-- let map_lam' = soacsLambdaToKernels map_lam- lvl <- segThreadCapped [w] "segscan" $ NoRecommendation SegNoVirt- addStms =<< segScan lvl res_pat w scan_ops map_lam' arrs [] [] transformStm path (Let res_pat aux (Op (Screma w arrs form))) | Just [Reduce comm red_fun nes] <- isReduceSOAC form, let comm'
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -802,8 +802,12 @@ groupScatterResults (zip3 as_ws as_ns as_inps) (is' ++ vs) & map (inPlaceReturn ispace) & KernelBody () k_body_stms+ -- Remove unused kernel inputs, since some of these might+ -- reference the array we are scattering into.+ kernel_inps' =+ filter ((`nameIn` freeIn k_body) . kernelInputName) kernel_inps - (k, k_bnds) <- mapKernel mk_lvl ispace kernel_inps rts k_body+ (k, k_bnds) <- mapKernel mk_lvl ispace kernel_inps' rts k_body traverse renameStm <=< runBinder_ $ do addStms k_bnds@@ -863,9 +867,14 @@ WriteReturns (arrayShape arr_t) arr' [(map DimFix write_is, v')] ) + -- Remove unused kernel inputs, since some of these might+ -- reference the array we are scattering into.+ let kernel_inps' =+ filter ((`nameIn` freeIn kstms) . kernelInputName) kernel_inps+ mk_lvl <- mkSegLevel (k, prestms) <-- mapKernel mk_lvl ispace kernel_inps [res_t] $+ mapKernel mk_lvl ispace kernel_inps' [res_t] $ KernelBody () kstms [res] traverse renameStm <=< runBinder_ $ do
src/Futhark/Server.hs view
@@ -118,9 +118,16 @@ Left (CmdFailure xs ys) -> Left $ CmdFailure (l : xs) ys Right ls' -> Right $ l : ls' +-- Words with spaces in them must be quoted.+quoteWord :: Text -> Text+quoteWord t+ | Just _ <- T.find (== ' ') t =+ "\"" <> t <> "\""+ | otherwise = t+ sendCommand :: Server -> [Text] -> IO (Either CmdFailure [Text]) sendCommand s command = do- let command' = T.unwords command+ let command' = T.unwords $ map quoteWord command when (serverDebug s) $ T.hPutStrLn stderr $ ">>> " <> command'
src/Futhark/Version.hs view
@@ -41,6 +41,4 @@ branch | $(gitBranch) == "master" = "" | otherwise = $(gitBranch) ++ " @ "- dirty- | $(gitDirtyTracked) = " [modified]"- | otherwise = ""+ dirty = if $(gitDirtyTracked) then " [modified]" else ""
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -3151,7 +3151,7 @@ Uniqueness -> TermTypeM (TypeBase dim as) arrayOfM loc t shape u = do- zeroOrderType (mkUsage loc "use as array element") "type used in array" t+ arrayElemType (mkUsage loc "use as array element") "type used in array" t return $ arrayOf t shape u updateTypes :: ASTMappable e => e -> TermTypeM e
src/Language/Futhark/TypeChecker/Unify.hs view
@@ -22,6 +22,7 @@ dimNotes, mkTypeVarName, zeroOrderType,+ arrayElemType, mustHaveConstr, mustHaveField, mustBeOneOf,@@ -647,23 +648,26 @@ let tp' = removeUniqueness tp modifyConstraints $ M.insert vn (lvl, Constraint tp' usage) case snd <$> M.lookup vn constraints of- Just (NoConstraint Unlifted unlift_usage) -> do- let bcs' =- breadCrumb- ( Matching $- "When verifying that" <+> pquote (pprName vn)- <+> textwrap "is not instantiated with a function type, due to"- <+> ppr unlift_usage- )- bcs- zeroOrderTypeWith usage bcs' tp'+ Just (NoConstraint l unlift_usage)+ | l < Lifted -> do+ let bcs' =+ breadCrumb+ ( Matching $+ "When verifying that" <+> pquote (pprName vn)+ <+> textwrap "is not instantiated with a function type, due to"+ <+> ppr unlift_usage+ )+ bcs - when (hasEmptyDims tp') $- unifyError usage mempty bcs $- "Type variable" <+> pprName vn- <+> "cannot be instantiated with type containing anonymous sizes:"- </> indent 2 (ppr tp)- </> textwrap "This is usually because the size of an array returned by a higher-order function argument cannot be determined statically. This can also be due to the return size being a value parameter. Add type annotation to clarify."+ arrayElemTypeWith usage bcs' tp'++ when (l == Unlifted) $ do+ when (hasEmptyDims tp') $+ unifyError usage mempty bcs $+ "Type variable" <+> pprName vn+ <+> "cannot be instantiated with type containing anonymous sizes:"+ </> indent 2 (ppr tp)+ </> textwrap "This is usually because the size of an array returned by a higher-order function argument cannot be determined statically. This can also be due to the return size being a value parameter. Add type annotation to clarify." Just (Equality _) -> equalityType usage tp' Just (Overloaded ts old_usage)@@ -901,6 +905,45 @@ m () zeroOrderType usage desc = zeroOrderTypeWith usage $ breadCrumb bc noBreadCrumbs+ where+ bc = Matching $ "When checking" <+> textwrap desc++arrayElemTypeWith ::+ (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+ Usage ->+ BreadCrumbs ->+ TypeBase dim as ->+ m ()+arrayElemTypeWith usage bcs t = do+ unless (orderZero t) $+ unifyError usage mempty bcs $+ "Type" </> indent 2 (ppr t) </> "found to be functional."+ mapM_ mustBeZeroOrder . S.toList . typeVars $ t+ where+ mustBeZeroOrder vn = do+ constraints <- getConstraints+ case M.lookup vn constraints of+ Just (lvl, NoConstraint _ _) ->+ modifyConstraints $ M.insert vn (lvl, NoConstraint SizeLifted usage)+ Just (_, ParamType l ploc)+ | l `elem` [Lifted, SizeLifted] ->+ unifyError usage mempty bcs $+ "Type parameter"+ <+> pquote (pprName vn)+ <+> "bound at"+ <+> text (locStr ploc)+ <+> "is lifted and cannot be an array element."+ _ -> return ()++-- | Assert that this type must be valid as an array element.+arrayElemType ::+ (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+ Usage ->+ String ->+ TypeBase dim as ->+ m ()+arrayElemType usage desc =+ arrayElemTypeWith usage $ breadCrumb bc noBreadCrumbs where bc = Matching $ "When checking" <+> textwrap desc