packages feed

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 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