packages feed

futhark 0.18.5 → 0.18.6

raw patch · 113 files changed

+4605/−1201 lines, 113 filesdep +bytestring-to-vectordep ~megaparsecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: bytestring-to-vector

Dependency ranges changed: megaparsec

API changes (from Hackage documentation)

- Futhark.Analysis.Metrics: instance GHC.Read.Read Futhark.Analysis.Metrics.AstMetrics
- Futhark.Analysis.Metrics: instance GHC.Show.Show Futhark.Analysis.Metrics.AstMetrics
- Futhark.Bench: [runExtraOptions] :: RunOptions -> [String]
- Futhark.Bench: [runRunner] :: RunOptions -> String
- Futhark.Compiler: readLibrary :: (MonadError CompilerError m, MonadIO m) => [FilePath] -> m (Warnings, Imports, VNameSource)
- Futhark.Compiler.Program: readLibraryWithBasis :: (MonadError CompilerError m, MonadIO m) => Basis -> [FilePath] -> m (Warnings, Imports, VNameSource)
+ Futhark.Analysis.Metrics.Type: AstMetrics :: Map Text Int -> AstMetrics
+ Futhark.Analysis.Metrics.Type: instance GHC.Read.Read Futhark.Analysis.Metrics.Type.AstMetrics
+ Futhark.Analysis.Metrics.Type: instance GHC.Show.Show Futhark.Analysis.Metrics.Type.AstMetrics
+ Futhark.Analysis.Metrics.Type: newtype AstMetrics
+ Futhark.Bench: Result :: [RunResult] -> Map Text Int -> Text -> Result
+ Futhark.Bench: [memoryMap] :: Result -> Map Text Int
+ Futhark.Bench: [runResults] :: Result -> [RunResult]
+ Futhark.Bench: [stdErr] :: Result -> Text
+ Futhark.Bench: data Result
+ Futhark.Bench: instance Data.Aeson.Types.FromJSON.FromJSON Futhark.Bench.Result
+ Futhark.Bench: instance Data.Aeson.Types.ToJSON.ToJSON Futhark.Bench.Result
+ Futhark.Bench: instance GHC.Classes.Eq Futhark.Bench.Result
+ Futhark.Bench: instance GHC.Show.Show Futhark.Bench.Result
+ Futhark.CLI.Literate: instance GHC.Classes.Eq Futhark.CLI.Literate.Failure
+ Futhark.CLI.Literate: instance GHC.Classes.Ord Futhark.CLI.Literate.Failure
+ Futhark.CLI.Literate: instance GHC.Show.Show Futhark.CLI.Literate.AnimParams
+ Futhark.CLI.Literate: instance GHC.Show.Show Futhark.CLI.Literate.Block
+ Futhark.CLI.Literate: instance GHC.Show.Show Futhark.CLI.Literate.Directive
+ Futhark.CLI.Literate: instance GHC.Show.Show Futhark.CLI.Literate.Failure
+ Futhark.CLI.Literate: instance Text.PrettyPrint.Mainland.Class.Pretty Futhark.CLI.Literate.Directive
+ Futhark.CLI.Literate: main :: String -> [String] -> IO ()
+ Futhark.CodeGen.Backends.CCUDA: [cServer] :: CParts -> String
+ Futhark.CodeGen.Backends.CCUDA: asServer :: CParts -> String
+ Futhark.CodeGen.Backends.COpenCL: [cServer] :: CParts -> String
+ Futhark.CodeGen.Backends.COpenCL: asServer :: CParts -> String
+ Futhark.CodeGen.Backends.GenericC: [cServer] :: CParts -> String
+ Futhark.CodeGen.Backends.GenericC: asServer :: CParts -> String
+ Futhark.CodeGen.Backends.GenericC: onClear :: BlockItem -> CompilerM op s ()
+ Futhark.CodeGen.Backends.GenericC.Server: serverDefs :: [Option] -> Functions a -> [Definition]
+ Futhark.CodeGen.Backends.GenericPython: data CompilerMode
+ Futhark.CodeGen.Backends.GenericPython.Definitions: pyServer :: String
+ Futhark.CodeGen.Backends.MulticoreC: [cServer] :: CParts -> String
+ Futhark.CodeGen.Backends.MulticoreC: asServer :: CParts -> String
+ Futhark.CodeGen.Backends.SequentialC: [cServer] :: CParts -> String
+ Futhark.CodeGen.Backends.SequentialC: asServer :: CParts -> String
+ Futhark.CodeGen.Backends.SimpleRep: csum :: [Exp] -> Exp
+ Futhark.CodeGen.Backends.SimpleRep: externalValueType :: ExternalValue -> Type
+ Futhark.CodeGen.Backends.SimpleRep: loadValueHeader :: Signedness -> PrimType -> Int -> Exp -> Exp -> [Stm]
+ Futhark.CodeGen.Backends.SimpleRep: storageSize :: PrimType -> Int -> Exp -> Exp
+ Futhark.CodeGen.Backends.SimpleRep: storeValueHeader :: Signedness -> PrimType -> Int -> Exp -> Exp -> [Stm]
+ Futhark.Compiler: [futharkEntryPoints] :: FutharkConfig -> [Name]
+ Futhark.Compiler.CLI: ToServer :: CompilerMode
+ Futhark.Compiler.Program: readLibrary :: (MonadError CompilerError m, MonadIO m) => [Name] -> [FilePath] -> m (Warnings, Imports, VNameSource)
+ Futhark.Script: Call :: EntryName -> [Exp] -> Exp
+ Futhark.Script: Const :: PrimValue -> Exp
+ Futhark.Script: Record :: [(Text, Exp)] -> Exp
+ Futhark.Script: SFun :: EntryName -> [TypeName] -> [TypeName] -> [ScriptValue v] -> ScriptValue v
+ Futhark.Script: STFun :: [TypeName] -> [TypeName] -> ScriptValueType
+ Futhark.Script: STValue :: TypeName -> ScriptValueType
+ Futhark.Script: SValue :: TypeName -> v -> ScriptValue v
+ Futhark.Script: ServerVar :: TypeName -> VarName -> Exp
+ Futhark.Script: Tuple :: [Exp] -> Exp
+ Futhark.Script: data Exp
+ Futhark.Script: data ScriptServer
+ Futhark.Script: data ScriptValue v
+ Futhark.Script: data ScriptValueType
+ Futhark.Script: evalExp :: (MonadError Text m, MonadIO m) => ScriptServer -> Exp -> m ExpValue
+ Futhark.Script: evalExpToGround :: (MonadError Text m, MonadIO m) => ScriptServer -> Exp -> m (Compound Value)
+ Futhark.Script: freeValue :: (MonadError Text m, MonadIO m) => ScriptServer -> ExpValue -> m ()
+ Futhark.Script: getExpValue :: (MonadError Text m, MonadIO m) => ScriptServer -> ExpValue -> m (Compound Value)
+ Futhark.Script: instance Data.Foldable.Foldable Futhark.Script.ScriptValue
+ Futhark.Script: instance Data.Traversable.Traversable Futhark.Script.ScriptValue
+ Futhark.Script: instance GHC.Base.Functor Futhark.Script.ScriptValue
+ Futhark.Script: instance GHC.Show.Show Futhark.Script.Exp
+ Futhark.Script: instance GHC.Show.Show Futhark.Script.ScriptValueType
+ Futhark.Script: instance Text.PrettyPrint.Mainland.Class.Pretty Futhark.Script.Exp
+ Futhark.Script: instance Text.PrettyPrint.Mainland.Class.Pretty Futhark.Script.ScriptValueType
+ Futhark.Script: parseExp :: Parser () -> Parser Exp
+ Futhark.Script: scriptValueType :: ScriptValue v -> ScriptValueType
+ Futhark.Script: type ExpValue = Compound (ScriptValue VarName)
+ Futhark.Script: valueToExp :: ExpValue -> Exp
+ Futhark.Script: varsInExp :: Exp -> Set EntryName
+ Futhark.Script: withScriptServer :: FilePath -> [FilePath] -> (ScriptServer -> IO a) -> IO a
+ Futhark.Server: CmdFailure :: [Text] -> [Text] -> CmdFailure
+ Futhark.Server: [failureLog] :: CmdFailure -> [Text]
+ Futhark.Server: [failureMsg] :: CmdFailure -> [Text]
+ Futhark.Server: cmdCall :: Server -> EntryName -> [VarName] -> [VarName] -> IO (Either CmdFailure [Text])
+ Futhark.Server: cmdClear :: Server -> IO (Maybe CmdFailure)
+ Futhark.Server: cmdFree :: Server -> [VarName] -> IO (Maybe CmdFailure)
+ Futhark.Server: cmdInputs :: Server -> EntryName -> IO (Either CmdFailure [TypeName])
+ Futhark.Server: cmdOutputs :: Server -> EntryName -> IO (Either CmdFailure [TypeName])
+ Futhark.Server: cmdReport :: Server -> IO (Either CmdFailure [Text])
+ Futhark.Server: cmdRestore :: Server -> FilePath -> [(VarName, TypeName)] -> IO (Maybe CmdFailure)
+ Futhark.Server: cmdStore :: Server -> FilePath -> [VarName] -> IO (Maybe CmdFailure)
+ Futhark.Server: data CmdFailure
+ Futhark.Server: data Server
+ Futhark.Server: instance GHC.Classes.Eq Futhark.Server.CmdFailure
+ Futhark.Server: instance GHC.Classes.Ord Futhark.Server.CmdFailure
+ Futhark.Server: instance GHC.Show.Show Futhark.Server.CmdFailure
+ Futhark.Server: type EntryName = Text
+ Futhark.Server: type TypeName = Text
+ Futhark.Server: type VarName = Text
+ Futhark.Server: withServer :: FilePath -> [FilePath] -> (Server -> IO a) -> IO a
+ Futhark.Test: checkResult :: (MonadError Text m, MonadIO m) => FilePath -> [Value] -> [Value] -> m ()
+ Futhark.Test: readResults :: (MonadIO m, MonadError Text m) => Server -> [VarName] -> FilePath -> m [Value]
+ Futhark.Test: withValuesFile :: MonadIO m => FutharkExe -> FilePath -> Values -> (FilePath -> IO a) -> m a
+ Futhark.Test.Values: ValueAtom :: v -> Compound v
+ Futhark.Test.Values: ValueRecord :: Map Text (Compound v) -> Compound v
+ Futhark.Test.Values: ValueTuple :: [Compound v] -> Compound v
+ Futhark.Test.Values: class GetValue t
+ Futhark.Test.Values: class PutValue t
+ Futhark.Test.Values: data Compound v
+ Futhark.Test.Values: getValue :: GetValue t => Value -> Maybe t
+ Futhark.Test.Values: instance Data.Foldable.Foldable Futhark.Test.Values.Compound
+ Futhark.Test.Values: instance Data.Traversable.Traversable Futhark.Test.Values.Compound
+ Futhark.Test.Values: instance Futhark.Test.Values.GetValue GHC.Int.Int16
+ Futhark.Test.Values: instance Futhark.Test.Values.GetValue GHC.Int.Int32
+ Futhark.Test.Values: instance Futhark.Test.Values.GetValue GHC.Int.Int64
+ Futhark.Test.Values: instance Futhark.Test.Values.GetValue GHC.Int.Int8
+ Futhark.Test.Values: instance Futhark.Test.Values.GetValue GHC.Types.Bool
+ Futhark.Test.Values: instance Futhark.Test.Values.PutValue Language.Futhark.Syntax.PrimValue
+ Futhark.Test.Values: instance Futhark.Test.Values.PutValue [Futhark.Test.Values.Value]
+ Futhark.Test.Values: instance GHC.Base.Functor Futhark.Test.Values.Compound
+ Futhark.Test.Values: instance GHC.Classes.Eq Futhark.Test.Values.ValueType
+ Futhark.Test.Values: instance GHC.Classes.Eq v => GHC.Classes.Eq (Futhark.Test.Values.Compound v)
+ Futhark.Test.Values: instance GHC.Classes.Ord Futhark.Test.Values.ValueType
+ Futhark.Test.Values: instance GHC.Classes.Ord v => GHC.Classes.Ord (Futhark.Test.Values.Compound v)
+ Futhark.Test.Values: instance GHC.Show.Show v => GHC.Show.Show (Futhark.Test.Values.Compound v)
+ Futhark.Test.Values: instance Text.PrettyPrint.Mainland.Class.Pretty v => Text.PrettyPrint.Mainland.Class.Pretty (Futhark.Test.Values.Compound v)
+ Futhark.Test.Values: mkCompound :: [v] -> Compound v
+ Futhark.Test.Values: putValue :: PutValue t => t -> Maybe Value
+ Futhark.Test.Values: type CompoundValue = Compound Value
+ Futhark.Test.Values: valueElems :: Value -> [Value]
+ Futhark.Test.Values: valueShape :: Value -> [Int]
+ Futhark.Test.Values.Parser: parsePrimType :: Parser PrimType
+ Futhark.Test.Values.Parser: parsePrimValue :: Parser PrimValue
+ Futhark.Test.Values.Parser: parseType :: Parser ValueType
+ Futhark.Test.Values.Parser: parseValue :: Parser () -> Parser Value
+ Futhark.Util.Pretty: prettyTextOneLine :: Pretty a => a -> Text
- Futhark.Bench: DataResult :: String -> Either Text ([RunResult], Text) -> DataResult
+ Futhark.Bench: DataResult :: String -> Either Text Result -> DataResult
- Futhark.Bench: RunOptions :: String -> Int -> [String] -> Int -> Int -> Maybe (Int -> IO ()) -> RunOptions
+ Futhark.Bench: RunOptions :: Int -> Int -> Int -> Maybe (Int -> IO ()) -> RunOptions
- Futhark.Bench: benchmarkDataset :: RunOptions -> FutharkExe -> FilePath -> Text -> Values -> Maybe Success -> FilePath -> IO (Either Text ([RunResult], Text))
+ Futhark.Bench: benchmarkDataset :: Server -> RunOptions -> FutharkExe -> FilePath -> Text -> Values -> Maybe Success -> FilePath -> IO (Either Text ([RunResult], Text))
- Futhark.CodeGen.Backends.CCUDA: CParts :: String -> String -> String -> String -> CParts
+ Futhark.CodeGen.Backends.CCUDA: CParts :: String -> String -> String -> String -> String -> CParts
- Futhark.CodeGen.Backends.COpenCL: CParts :: String -> String -> String -> String -> CParts
+ Futhark.CodeGen.Backends.COpenCL: CParts :: String -> String -> String -> String -> String -> CParts
- Futhark.CodeGen.Backends.GenericC: CParts :: String -> String -> String -> String -> CParts
+ Futhark.CodeGen.Backends.GenericC: CParts :: String -> String -> String -> String -> String -> CParts
- Futhark.CodeGen.Backends.GenericPython: compileProg :: MonadFreshNames m => Maybe String -> Constructor -> [PyStmt] -> [PyStmt] -> Operations op s -> s -> [PyStmt] -> [Option] -> Definitions op -> m String
+ Futhark.CodeGen.Backends.GenericPython: compileProg :: MonadFreshNames m => CompilerMode -> String -> Constructor -> [PyStmt] -> [PyStmt] -> Operations op s -> s -> [PyStmt] -> [Option] -> Definitions op -> m String
- Futhark.CodeGen.Backends.MulticoreC: CParts :: String -> String -> String -> String -> CParts
+ Futhark.CodeGen.Backends.MulticoreC: CParts :: String -> String -> String -> String -> String -> CParts
- Futhark.CodeGen.Backends.PyOpenCL: compileProg :: MonadFreshNames m => Maybe String -> Prog KernelsMem -> m (Warnings, String)
+ Futhark.CodeGen.Backends.PyOpenCL: compileProg :: MonadFreshNames m => CompilerMode -> String -> Prog KernelsMem -> m (Warnings, String)
- Futhark.CodeGen.Backends.SequentialC: CParts :: String -> String -> String -> String -> CParts
+ Futhark.CodeGen.Backends.SequentialC: CParts :: String -> String -> String -> String -> String -> CParts
- Futhark.CodeGen.Backends.SequentialPython: compileProg :: MonadFreshNames m => Maybe String -> Prog SeqMem -> m (Warnings, String)
+ Futhark.CodeGen.Backends.SequentialPython: compileProg :: MonadFreshNames m => CompilerMode -> String -> Prog SeqMem -> m (Warnings, String)
- Futhark.Compiler: FutharkConfig :: (Verbosity, Maybe FilePath) -> Bool -> Bool -> Bool -> FutharkConfig
+ Futhark.Compiler: FutharkConfig :: (Verbosity, Maybe FilePath) -> Bool -> Bool -> Bool -> [Name] -> FutharkConfig
- Futhark.Compiler: readProgram :: (MonadError CompilerError m, MonadIO m) => FilePath -> m (Warnings, Imports, VNameSource)
+ Futhark.Compiler: readProgram :: (MonadError CompilerError m, MonadIO m) => [Name] -> FilePath -> m (Warnings, Imports, VNameSource)
- Futhark.Test: runProgram :: MonadIO m => FutharkExe -> FilePath -> [String] -> String -> Text -> Values -> m (ExitCode, ByteString, ByteString)
+ Futhark.Test: runProgram :: FutharkExe -> FilePath -> [String] -> String -> Text -> Values -> IO (ExitCode, ByteString, ByteString)

Files

docs/c-api.rst view
@@ -117,6 +117,13 @@    subsequent call to the function returns ``NULL``, until a new error    occurs. +.. c:function:: void futhark_context_set_logging_file(struct futhark_context *ctx, FILE* f)++   Set the stream used to print diagnostics, debug prints, and logging+   messages during runtime.  This is ``stderr`` by default.  Even when+   this is used to re-route logging messages, fatal errors will still+   only be printed to ``stderr``.+ .. c:function:: char *futhark_context_report(struct futhark_context *ctx)     Produce a human-readable C string with debug and profiling@@ -139,21 +146,17 @@ ------  Primitive types (``i32``, ``bool``, etc) are mapped directly to their-corresponding C type.  For each distinct array type (without sizes),-an opaque C struct is defined.  Complex types (records, nested tuples)-are also assigned an opaque C struct.  In the general case, these-types will be named with a random hash.  However, if you insert an-explicit type annotation (and the type name contains only characters-valid for C identifiers), the indicated name will be used.  Note that-arrays contain brackets, which are usually not valid in identifiers.-Defining a simple type alias is the best way around this.+corresponding C type.  For each distinct array type of primitives+(ignoring sizes), an opaque C struct is defined.  For types that do+not map cleanly to C, including records, sum types, and arrays of+tuples, see :ref:`opaques`. -All values share a similar API, which is illustrated here for the case-of the type ``[]i32``.  The creation/retrieval functions are all-asynchronous, so make sure to call :c:func:`futhark_context_sync` when-appropriate.  Memory management is entirely manual.  All values that-are created with a ``new`` function, or returned from an entry point,-*must* at some point be freed manually.  Values are internally+All array values share a similar API, which is illustrated here for+the case of the type ``[]i32``.  The creation/retrieval functions are+all asynchronous, so make sure to call :c:func:`futhark_context_sync`+when appropriate.  Memory management is entirely manual.  All values+that are created with a ``new`` function, or returned from an entry+point, *must* at some point be freed manually.  Values are internally reference counted, so even for entry points that return their input unchanged, you should still free both the input and the output - this will not result in a double free.@@ -195,6 +198,66 @@    Return a pointer to the shape of the array, with one element per    dimension.  The lifetime of the shape is the same as ``arr``, and    should *not* be manually freed.++.. _opaques:++Opaque values+~~~~~~~~~~~~~++Each instance of a complex type in an entry point (records, nested+tuples, etc) is represented by an opaque C struct named+``futhark_opaque_foo``.  In the general case, ``foo`` will be a hash+of the internal representation.  However, if you insert explicit type+annotations in the entry point (and the type name contains only+characters valid for C identifiers), the indicated name will be used.+Note that arrays contain brackets, which are usually not valid in+identifiers.  Defining a simple type abbreviation is the best way+around this.++The API for opaque values is similar to that of arrays, and the same+rules for memory management apply.  You cannot construct them from+scratch, but must obtain them via entry points (or deserialisation,+see :c:func:`futhark_restore_opaque_foo`).++.. c:struct:: futhark_opaque_foo++   An opaque struct representing a Futhark value of type ``foo``.++.. c:function:: int futhark_free_opaque_foo(struct futhark_context *ctx, struct futhark_opaque_foo *obj)++   Free the value.  In practice, this merely decrements the reference+   count by one.  The value (or at least this reference) may not be+   used again after this function returns.++.. c:function:: int futhark_store_opaque_foo(struct futhark_context *ctx, const struct futhark_opaque_foo *obj, void **p, size_t *n);++   Serialise an opaque value to a byte sequence, which can later be+   restored with :c:func:`futhark_restore_opaque_foo`.  The byte+   representation is not otherwise specified.  The variable pointed to+   by ``n`` will always be set to the number of bytes needed to+   represent the value.  The ``p`` parameter is more complex:++   * If ``p`` is ``NULL``, the function will write to ``*n``, but not+     actually serialise the opaque value.++   * If ``*p`` is ``NULL``, the function will allocate sufficient+     storage with ``malloc()``, serialise the value, and write the+     address of the byte representation to ``*p``.++   * Otherwise, the serialised representation of the value will be+     stored at ``*p``, which *must* have room for at least ``*n``+     bytes.++   Returns 0 on success.++.. c:function:: struct futhark_opaque_foo* futhark_restore_opaque_foo(struct futhark_context *ctx, const void *p);++   Restore a byte sequence previously written with+   :c:func:`futhark_store_opaque_foo`.  Returns ``NULL`` on failure.+   The byte sequence does not need to have been generated by the same+   program *instance*, but it *must* have been generated by the same+   Futhark program, and compiled with the same version of the Futhark+   compiler.  Entry points ------------
docs/conf.py view
@@ -261,6 +261,7 @@ # (source start file, name, description, authors, manual section). man_pages = [     ('man/futhark', 'futhark', 'a parallel functional array language', [], 1),+    ('man/futhark-autotune', 'futhark-autotune', 'calibrate run-time parameters', [], 1),     ('man/futhark-c', 'futhark-c', 'compile Futhark to sequential C', [], 1),     ('man/futhark-multicore', 'futhark-multicore', 'compile Futhark to multithreaded C', [], 1),     ('man/futhark-opencl', 'futhark-opencl', 'compile Futhark to OpenCL', [], 1),@@ -273,7 +274,8 @@     ('man/futhark-bench', 'futhark-bench', 'benchmark Futhark programs', [], 1),     ('man/futhark-doc', 'futhark-doc', 'generate documentation for Futhark code', [], 1),     ('man/futhark-dataset', 'futhark-dataset', 'generate random data sets', [], 1),-    ('man/futhark-pkg', 'futhark-pkg', 'manage Futhark packages', [], 1)+    ('man/futhark-pkg', 'futhark-pkg', 'manage Futhark packages', [], 1),+    ('man/futhark-literate', 'futhark-literate', 'execute literate Futhark program', [], 1) ]  # If true, show URL addresses after external links.
docs/index.rst view
@@ -32,6 +32,7 @@    language-reference.rst    c-api.rst    package-management.rst+   server-protocol.rst    c-porting-guide.rst    versus-other-languages.rst    hacking.rst@@ -55,4 +56,5 @@    man/futhark-python.rst    man/futhark-repl.rst    man/futhark-run.rst+   man/futhark-literate.rst    man/futhark-test.rst
docs/man/futhark-c.rst view
@@ -31,6 +31,9 @@ -h   Print help text to standard output and exit. +--entry NAME+  Treat the specified top-level function as an entry point.+ --library   Generate a library instead of an executable.  Appends ``.c``/``.h``   to the name indicated by the ``-o`` option to determine output
docs/man/futhark-cuda.rst view
@@ -35,6 +35,9 @@ -h   Print help text to standard output and exit. +--entry NAME+  Treat the specified top-level function as an entry point.+ --library   Generate a library instead of an executable.  Appends ``.c``/``.h``   to the name indicated by the ``-o`` option to determine output
+ docs/man/futhark-literate.rst view
@@ -0,0 +1,195 @@+.. role:: ref(emphasis)++.. _futhark-literate(1):++================+futhark-literate+================++SYNOPSIS+========++futhark literate [options...] program++DESCRIPTION+===========++The command ``futhark literate foo.fut`` will compile the given+program and then generate a Markdown file ``foo.md`` that contains a+prettyprinted form of the program.  This is useful for demonstrating+programming techniques.++* Top-level comments that start with a line comment marker (``--``)+  and a space in the first column will be turned into ordinary text in+  the Markdown file.++* Ordinary top-level definitions will be enclosed in Markdown code+  blocks.++* Any *directives* will be executed and replaced with their output.+  See below.++**Warning:** Do not run untrusted programs.  See SECURITY below.++Directives+==========++A directive is a way to embed the results the running the program.+Depending on the directive, this can be as simple as printing the+textual representation of the result, or as complex as running an+external plotting program and referencing a generated image.++Any directives that produce images for a program ``foo.fut`` will+place them in the directory ``foo-img/``.  If this directory already+exists, it will be deleted.++A directive is a line starting with ``-- >``, which must follow an+empty line.  Arguments to the directive follow on the remainder of the+line.  Any expression arguments are given in a very restricted subset+of Futhark called *FutharkScript* (see below).++Some directives take mandatory or optional parameters.  These are+entered after a semicolon *and a linebreak*.++The following directives are supported:++* ``> e``++  Shows the result of executing the FutharkScript expression ``e``,+  which can have any (transparent) type.++* ``> :anim e[; parameters...]``++  Creates a video from ``e``.  The optional parameters are lines of+  the form *key: value*:++  * ``repeat: <true|false>``++  * ``fps: <int>``++  * ``format: <webm|gif>``++  Shells out to ``ffmpeg`` to actually create the video file.++  ``e`` must be one of the following:++  * A 3D array where the 2D elements is of a type acceptable to+    ``:img``, and the outermost dimension is the number of frames.++  * A triple ``(s -> (img,s), s, i64)``, for some types ``s`` and+    ``img``, where ``img`` is an array acceptable to ``:img``.  This+    means not all frames have to be held in memory at once.++* ``> :brief <directive>``++  The same as the given *directive* (which must not start with another+  ``>``), but suppress parameters when printing it.++* ``> :covert <directive>``++  The same as the given *directive* (which must not start with another+  ``>``), but do not show the directive itself in the output, only its+  result.++* ``> :img e``++  Visualises ``e``, which must be of type ``[][]i32`` or ``[][]u32``+  (interpreted as rows of ARGB pixel values).  Shells out to+  ``convert`` (from ImageMagick) to generate the image.++* ``> :plot2d e[; size=(height,width)]``++  Shows a plot generated with ``gnuplot`` of ``e``, which must be an+  expression of type ``([]t, []t)``, where ``t`` is some numeric type.+  The two arrays must have the same length and are interpreted as+  ``x`` and ``y`` values, respectively.++  The expression may also be a record, where each field will be+  plotted separately and must have the type mentioned above.++* ``> :gnuplot e; script...``++  Similar to ``plot2d``, except that it uses the provided Gnuplot+  script.  The ``e`` argument must be a record whose fields are tuples+  of one-dimensional arrays, and the data will be available in+  temporary files whose names are in variables named after the record+  fields.  Each file will contain a column of data for each array in+  the corresponding tuple.++  Use ``set term png size width,height`` to change the size to+  ``width`` by ``height`` pixels.++FutharkScript+=============++Only an extremely limited subset of Futhark is supported:++.. productionlist::+   scriptexp:   `id` `scriptexp`*+            : | "(" `scriptexp` ")"+            : | "(" `scriptexp` ( "," `scriptexp` )+ ")"+            : | "{" "}"+            : | "{" (`id` = `scriptexp`) ("," `id` = `scriptexp`)* "}"+            : | `literal`++Any numeric literals *must* have a type suffix.++OPTIONS+=======++--backend=name++  The backend used when compiling Futhark programs (without leading+  ``futhark``, e.g. just ``opencl``).  Defaults to ``c``.++--futhark=program++  The program used to perform operations (eg. compilation).  Defaults+  to the binary running ``futhark literate`` itself.++--output=FILE++  Override the default output file.  The image directory will be set+  to the provided ``FILE`` with its extension stripped and ``-img/``+  appended.++--pass-option=opt++  Pass an option to benchmark programs that are being run.  For+  example, we might want to run OpenCL programs on a specific device::++    futhark literate prog.fut --backend=opencl --pass-option=-dHawaii++--pass-compiler-option=opt++  Pass an extra option to the compiler when compiling the programs.++--skip-compilation++  Do not run the compiler, and instead assume that the program has+  already been compiled.  Use with caution.++--stop-on-error++  Terminate immediately without producing an output file if a+  directive fails.  Otherwise a file will still be produced, and+  failing directives will be followed by an error message.++-v, --verbose++  Print verbose information on stderr about directives as they are+  executing.++SECURITY+========++Some directives (e.g. ``:gnuplot``) can run arbitrary shell commands.+Running an untrusted literate Futhark program is as dangerous as+running a shell script you downloaded off the Internet.  Before+running a program from an unknown source, you should always give it a+quick read to see if anything looks fishy.++SEE ALSO+========++:ref:`futhark-test(1)`, :ref:`futhark-bench(1)`
docs/man/futhark-multicore.rst view
@@ -31,6 +31,9 @@ -h   Print help text to standard output and exit. +--entry NAME+  Treat the specified top-level function as an entry point.+ --library   Generate a library instead of an executable.  Appends ``.c``/``.h``   to the name indicated by the ``-o`` option to determine output
docs/man/futhark-opencl.rst view
@@ -31,6 +31,9 @@ -h   Print help text to standard output and exit. +--entry NAME+  Treat the specified top-level function as an entry point.+ --library   Generate a library instead of an executable.  Appends ``.c``/``.h``   to the name indicated by the ``-o`` option to determine output
docs/man/futhark-pyopencl.rst view
@@ -37,6 +37,9 @@ -h   Print help text to standard output and exit. +--entry NAME+  Treat the specified top-level function as an entry point.+ --library   Instead of compiling to an executable program, generate a Python   module that can be imported by other Python code.  The module will
docs/man/futhark-python.rst view
@@ -31,6 +31,9 @@ -h   Print help text to standard output and exit. +--entry NAME+  Treat the specified top-level function as an entry point.+ --library   Instead of compiling to an executable program, generate a Python   module that can be imported by other Python code.  The module will
docs/man/futhark-test.rst view
@@ -20,8 +20,8 @@  A Futhark test program is an ordinary Futhark program, with at least one test block describing input/output test cases and possibly other-options.  A test block consists of commented-out text with the-following overall format::+options.  The last line must end in a newline.  A test block consists+of commented-out text with the following overall format::    description   ==
docs/man/futhark.rst view
@@ -68,4 +68,4 @@ SEE ALSO ======== -:ref:`futhark-opencl(1)`, :ref:`futhark-c(1)`, :ref:`futhark-py(1)`, :ref:`futhark-pyopencl(1)`, :ref:`futhark-dataset(1)`, :ref:`futhark-doc(1)`, :ref:`futhark-test(1)`, :ref:`futhark-bench(1)`, :ref:`futhark-run(1)`, :ref:`futhark-repl(1)`+:ref:`futhark-opencl(1)`, :ref:`futhark-c(1)`, :ref:`futhark-py(1)`, :ref:`futhark-pyopencl(1)`, :ref:`futhark-dataset(1)`, :ref:`futhark-doc(1)`, :ref:`futhark-test(1)`, :ref:`futhark-bench(1)`, :ref:`futhark-run(1)`, :ref:`futhark-repl(1)`, :ref:`futhark-literate(1)`
+ docs/server-protocol.rst view
@@ -0,0 +1,110 @@+.. _server-protocol:++Server Protocol+===============++A Futhark program can be compiled to a *server executable*.  Such a+server maintains a Futhark context and presents a line-oriented+interface (over stdin/stdout) for loading and dumping values, as well+as calling the entry points in the program.  The main advantage over+the plain executable interface is that program initialisation is done+only *once*, and we can work with opaque values.++The server interface is not intended for human consumption, but is+useful for writing tools on top of Futhark programs, without having to+use the C API.  Futhark's built-in benchmarking and testing tools use+server executables.++A server executable is started like any other executable, and supports+most of the same command line options.++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.  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+---------++Some commands produce or read variables.  A variable is a mapping from+a name to a Futhark value.  Values can be both transparent (arrays and+primitives), but they can also be *opaque* values.  These can be+produced by entry points and passed to other entry points, but cannot+be directly inspected.++Types+-----++All variables have types, and all entry points accept inputs and+produce outputs of defined types.  The notion of transparent and+opaque types are the same as in the C API: primitives and array of+primitives are directly supported, and everything else is treated as+opaque.  When printed, types follow basic Futhark type syntax+*without* sizes (e.g. ``[][]i32``).++Commands+--------++The following commands are supported.++``call`` *entry* *o1* ... *oN* *i1* ... *oM*+............................................++Call the given entry point with input from the variables *i1* to *oM*.+The results are stored in *o1* to *oN*, which must not already exist.++``restore`` *file* *v1* *t1* ... *vN* *tN*+..........................................++Load *N* values from *file* and store them in the variables *v1* to+*vN* of types *t1* to *tN*, which must not already exist.++``store`` *file* *v1* ... *vN*+..............................++Store the *N* values in variables *v1* to *vN* in *file*.++``free`` *v1* ... *vN*+......................++Delete the given variables.++``inputs`` *entry*+..................++Print the types of inputs accepted by the given entry point, one per+line.++``outputs`` *entry*+...................++Print the types of outputs produced by the given entry point, one per+line.++``clear``+.........++Clear all internal caches and counters maintained by the Futhark+context.  Corresponds to :c:func:`futhark_context_clear_caches`.++``pause_profiling``+...................++Corresponds to :c:func:`futhark_context_pause_profiling`.++``unpause_profiling``+.....................++Corresponds to :c:func:`futhark_context_unpause_profiling`.++``report``+..........++Corresponds to :c:func:`futhark_context_report`.
docs/usage.rst view
@@ -13,11 +13,13 @@ but there may be minor differences and quirks due to characteristics of the specific backends. -There are two main ways of compiling a Futhark program: to an-executable (by using ``--executable``, which is the default), and to a-library (``--library``).  Executables can be run immediately, but are-useful mostly for testing and benchmarking.  Libraries can be called-from non-Futhark code.+There are three main ways of compiling a Futhark program: to an+ordinary executable (by using ``--executable``, which is the default),+to a *server executable* (``--server``), and to a library+(``--library``).  Plain executables can be run immediately, but are+useful mostly for testing and benchmarking.  Server executables are+discussed in :ref:`server-protocol`. Libraries can be called from+non-Futhark code.  .. _executable: @@ -78,6 +80,19 @@      Print help text to standard output and exit. +  ``-D``++    Print debugging information on standard error.  Exactly what is+    printed, and how it looks, depends on which Futhark compiler is+    used.  This option may also enable more conservative (and slower)+    execution, such as frequently synchronising to check for errors.++Non-Server Executable Options+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^++The following options are only supported on non-server executables,+because they make no sense in a server context.+   ``-t FILE``      Print the time taken to execute the program to the indicated file,@@ -92,13 +107,6 @@     warmup run).  The program result is only printed once, after the     last run.  If combined with ``-t``, one measurement is printed per     run.  This is a good way to perform benchmarking.--  ``-D``--    Print debugging information on standard error.  Exactly what is-    printed, and how it looks, depends on which Futhark compiler is-    used.  This option may also enable more conservative (and slower)-    execution, such as frequently synchronising to check for errors.    ``-b`` 
futhark.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name:           futhark-version:        0.18.5+version:        0.18.6 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to@@ -59,6 +59,7 @@       Futhark.Analysis.HORep.MapNest       Futhark.Analysis.HORep.SOAC       Futhark.Analysis.Metrics+      Futhark.Analysis.Metrics.Type       Futhark.Analysis.PrimExp       Futhark.Analysis.PrimExp.Convert       Futhark.Analysis.PrimExp.Generalize@@ -78,6 +79,7 @@       Futhark.CLI.Dataset       Futhark.CLI.Dev       Futhark.CLI.Doc+      Futhark.CLI.Literate       Futhark.CLI.Misc       Futhark.CLI.Multicore       Futhark.CLI.OpenCL@@ -94,6 +96,7 @@       Futhark.CodeGen.Backends.COpenCL.Boilerplate       Futhark.CodeGen.Backends.GenericC       Futhark.CodeGen.Backends.GenericC.CLI+      Futhark.CodeGen.Backends.GenericC.Server       Futhark.CodeGen.Backends.GenericC.Options       Futhark.CodeGen.Backends.GenericPython       Futhark.CodeGen.Backends.GenericPython.AST@@ -231,8 +234,11 @@       Futhark.Pkg.Info       Futhark.Pkg.Solve       Futhark.Pkg.Types+      Futhark.Script+      Futhark.Server       Futhark.Test       Futhark.Test.Values+      Futhark.Test.Values.Parser       Futhark.Tools       Futhark.Transform.CopyPropagate       Futhark.Transform.FirstOrderTransform@@ -287,6 +293,7 @@     , binary >=0.8.3     , blaze-html >=0.9.0.1     , bytestring >=0.10.8+    , bytestring-to-vector >=0.3.0.1     , containers >=0.6.2.1     , directory >=1.3.0.0     , directory-tree >=0.12.1@@ -299,7 +306,7 @@     , language-c-quote >=0.12     , mainland-pretty >=0.6.1     , cmark-gfm >=0.2.1-    , megaparsec >=8.0.0+    , megaparsec >=9.0.0     , mtl >=2.2.1     , neat-interpolation >=0.3     , parallel >=3.2.1.0
prelude/math.fut view
@@ -44,7 +44,10 @@   val >=: t -> t -> bool   val !=: t -> t -> bool -  val negate: t-> t+  -- | Arithmetic negation (use `!` for bitwise negation).+  val neg: t -> t+  -- | Deprecated alias for `neg`.+  val negate: t -> t   val max: t -> t -> t   val min: t -> t -> t @@ -278,14 +281,15 @@   let sgn (x: i8) = intrinsics.ssignum8 x   let abs (x: i8) = intrinsics.abs8 x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = intrinsics.smax8 (x, y)   let min (x: t) (y: t) = intrinsics.smin8 (x, y)    let highest = 127i8   let lowest = highest + 1i8 -  let num_bits = 8+  let num_bits = 8i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -350,14 +354,15 @@   let sgn (x: i16) = intrinsics.ssignum16 x   let abs (x: i16) = intrinsics.abs16 x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = intrinsics.smax16 (x, y)   let min (x: t) (y: t) = intrinsics.smin16 (x, y)    let highest = 32767i16   let lowest = highest + 1i16 -  let num_bits = 16+  let num_bits = 16i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -425,14 +430,15 @@   let sgn (x: i32) = intrinsics.ssignum32 x   let abs (x: i32) = intrinsics.abs32 x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = intrinsics.smax32 (x, y)   let min (x: t) (y: t) = intrinsics.smin32 (x, y) -  let highest = 2147483647+  let highest = 2147483647i32   let lowest = highest + 1 -  let num_bits = 32+  let num_bits = 32i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -500,14 +506,15 @@   let sgn (x: i64) = intrinsics.ssignum64 x   let abs (x: i64) = intrinsics.abs64 x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = intrinsics.smax64 (x, y)   let min (x: t) (y: t) = intrinsics.smin64 (x, y)    let highest = 9223372036854775807i64   let lowest = highest + 1i64 -  let num_bits = 64+  let num_bits = 64i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | intrinsics.zext_i32_i64 (b intrinsics.<< bit))@@ -575,14 +582,15 @@   let sgn (x: u8) = unsign (intrinsics.usignum8 (sign x))   let abs (x: u8) = x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = unsign (intrinsics.umax8 (sign x, sign y))   let min (x: t) (y: t) = unsign (intrinsics.umin8 (sign x, sign y))    let highest = 255u8   let lowest = 0u8 -  let num_bits = 8+  let num_bits = 8i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -650,14 +658,15 @@   let sgn (x: u16) = unsign (intrinsics.usignum16 (sign x))   let abs (x: u16) = x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = unsign (intrinsics.umax16 (sign x, sign y))   let min (x: t) (y: t) = unsign (intrinsics.umin16 (sign x, sign y))    let highest = 65535u16   let lowest = 0u16 -  let num_bits = 16+  let num_bits = 16i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -728,11 +737,12 @@   let highest = 4294967295u32   let lowest = highest + 1u32 -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = unsign (intrinsics.umax32 (sign x, sign y))   let min (x: t) (y: t) = unsign (intrinsics.umin32 (sign x, sign y)) -  let num_bits = 32+  let num_bits = 32i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -800,14 +810,15 @@   let sgn (x: u64) = unsign (intrinsics.usignum64 (sign x))   let abs (x: u64) = x -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = unsign (intrinsics.umax64 (sign x, sign y))   let min (x: t) (y: t) = unsign (intrinsics.umin64 (sign x, sign y))    let highest = 18446744073709551615u64   let lowest = highest + 1u64 -  let num_bits = 64+  let num_bits = 64i32   let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)   let set_bit (bit: i32) (x: t) (b: i32) =     ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))@@ -863,7 +874,8 @@   let (x: f64) >= (y: f64) = intrinsics.le64 (y, x)   let (x: f64) != (y: f64) = intrinsics.! (x == y) -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = intrinsics.fmax64 (x, y)   let min (x: t) (y: t) = intrinsics.fmin64 (x, y) @@ -907,7 +919,7 @@   let to_bits (x: f64): u64 = u64m.i64 (intrinsics.to_bits64 x)   let from_bits (x: u64): f64 = intrinsics.from_bits64 (intrinsics.sign_i64 x) -  let num_bits = 64+  let num_bits = 64i32   let get_bit (bit: i32) (x: t) = u64m.get_bit bit (to_bits x)   let set_bit (bit: i32) (x: t) (b: i32) = from_bits (u64m.set_bit bit (to_bits x) b) @@ -970,7 +982,8 @@   let (x: f32) >= (y: f32) = intrinsics.le32 (y, x)   let (x: f32) != (y: f32) = intrinsics.! (x == y) -  let negate (x: t) = -x+  let neg (x: t) = -x+  let negate = neg   let max (x: t) (y: t) = intrinsics.fmax32 (x, y)   let min (x: t) (y: t) = intrinsics.fmin32 (x, y) @@ -1014,7 +1027,7 @@   let to_bits (x: f32): u32 = u32m.i32 (intrinsics.to_bits32 x)   let from_bits (x: u32): f32 = intrinsics.from_bits32 (intrinsics.sign_i32 x) -  let num_bits = 32+  let num_bits = 32i32   let get_bit (bit: i32) (x: t) = u32m.get_bit bit (to_bits x)   let set_bit (bit: i32) (x: t) (b: i32) = from_bits (u32m.set_bit bit (to_bits x) b) 
rts/c/cuda.h view
@@ -495,7 +495,7 @@     struct profiling_record record = ctx->profiling_records[i];      float ms;-    if ((err = cudaEventElapsedTime(&ms, record.events[0], record.events[1])) != CUDA_SUCCESS) {+    if ((err = cudaEventElapsedTime(&ms, record.events[0], record.events[1])) != cudaSuccess) {       return err;     } @@ -503,10 +503,10 @@     *record.runs += 1;     *record.runtime += ms*1000; -    if ((err = cudaEventDestroy(record.events[0])) != CUDA_SUCCESS) {+    if ((err = cudaEventDestroy(record.events[0])) != cudaSuccess) {       return err;     }-    if ((err = cudaEventDestroy(record.events[1])) != CUDA_SUCCESS) {+    if ((err = cudaEventDestroy(record.events[1])) != cudaSuccess) {       return err;     } @@ -515,7 +515,7 @@    ctx->profiling_records_used = 0; -  return CUDA_SUCCESS;+  return cudaSuccess; }  // Returns pointer to two events.
rts/c/scheduler.h view
@@ -853,9 +853,6 @@   struct worker *worker = worker_local;    int err = 0;-  if (task->iterations == 0) {-    return err;-  }    // How much sequential work was performed by the task   int64_t task_timer = 0;
+ rts/c/server.h view
@@ -0,0 +1,630 @@+// Start of server.h.++// Forward declarations of things that we technically don't know until+// the application header file is included, but which we need.+struct futhark_context;+char *futhark_context_get_error(struct futhark_context *ctx);+int futhark_context_sync(struct futhark_context *ctx);+int futhark_context_clear_caches(struct futhark_context *ctx);++typedef int (*restore_fn)(const void*, FILE *, struct futhark_context*, void*);+typedef void (*store_fn)(const void*, FILE *, struct futhark_context*, void*);+typedef int (*free_fn)(const void*, struct futhark_context*, void*);++struct type {+  const char *name;+  restore_fn restore;+  store_fn store;+  free_fn free;+  const void *aux;+};++int free_scalar(const void *aux, struct futhark_context *ctx, void *p) {+  (void)aux;+  (void)ctx;+  (void)p;+  // Nothing to do.+  return 0;+}++#define DEF_SCALAR_TYPE(T)                                              \+  int restore_##T(const void *aux, FILE *f,                             \+                  struct futhark_context *ctx, void *p) {               \+    (void)aux;                                                          \+    (void)ctx;                                                          \+    return read_scalar(f, &T##_info, p);                                \+  }                                                                     \+                                                                        \+  void store_##T(const void *aux, FILE *f,                              \+                 struct futhark_context *ctx, void *p) {                \+    (void)aux;                                                          \+    (void)ctx;                                                          \+    write_scalar(f, 1, &T##_info, p);                                   \+  }                                                                     \+                                                                        \+  struct type type_##T =                                                \+    { .name = #T,                                                       \+      .restore = restore_##T,                                           \+      .store = store_##T,                                               \+      .free = free_scalar                                               \+    }                                                                   \++DEF_SCALAR_TYPE(i8);+DEF_SCALAR_TYPE(i16);+DEF_SCALAR_TYPE(i32);+DEF_SCALAR_TYPE(i64);+DEF_SCALAR_TYPE(u8);+DEF_SCALAR_TYPE(u16);+DEF_SCALAR_TYPE(u32);+DEF_SCALAR_TYPE(u64);+DEF_SCALAR_TYPE(f32);+DEF_SCALAR_TYPE(f64);+DEF_SCALAR_TYPE(bool);++struct value {+  struct type *type;+  union {+    void *v_ptr;+    int8_t  v_i8;+    int16_t v_i16;+    int32_t v_i32;+    int64_t v_i64;++    uint8_t  v_u8;+    uint16_t v_u16;+    uint32_t v_u32;+    uint64_t v_u64;++    float v_f32;+    double v_f64;++    bool v_bool;+  } value;+};++void* value_ptr(struct value *v) {+  if (v->type == &type_i8) {+    return &v->value.v_i8;+  }+  if (v->type == &type_i16) {+    return &v->value.v_i16;+  }+  if (v->type == &type_i32) {+    return &v->value.v_i32;+  }+  if (v->type == &type_i64) {+    return &v->value.v_i64;+  }+  if (v->type == &type_u8) {+    return &v->value.v_u8;+  }+  if (v->type == &type_u16) {+    return &v->value.v_u16;+  }+  if (v->type == &type_u32) {+    return &v->value.v_u32;+  }+  if (v->type == &type_u64) {+    return &v->value.v_u64;+  }+  if (v->type == &type_f32) {+    return &v->value.v_f32;+  }+  if (v->type == &type_f64) {+    return &v->value.v_f64;+  }+  if (v->type == &type_bool) {+    return &v->value.v_bool;+  }+  return &v->value.v_ptr;+}++struct variable {+  // NULL name indicates free slot.  Name is owned by this struct.+  char *name;+  struct value value;+};++typedef int (*entry_point_fn)(struct futhark_context*, void**, void**);++struct entry_point {+  const char *name;+  entry_point_fn f;+  struct type **out_types;+  struct type **in_types;+};++int entry_num_ins(struct entry_point *e) {+  int count = 0;+  while (e->in_types[count]) {+    count++;+  }+  return count;+}++int entry_num_outs(struct entry_point *e) {+  int count = 0;+  while (e->out_types[count]) {+    count++;+  }+  return count;+}++struct futhark_prog {+  // Last entry point identified by NULL name.+  struct entry_point *entry_points;+  // Last type identified by NULL name.+  struct type **types;+};++struct server_state {+  struct futhark_prog prog;+  struct futhark_context *ctx;+  int variables_capacity;+  struct variable *variables;+};++struct variable* get_variable(struct server_state *s,+                              const char *name) {+  for (int i = 0; i < s->variables_capacity; i++) {+    if (s->variables[i].name != NULL &&+        strcmp(s->variables[i].name, name) == 0) {+      return &s->variables[i];+    }+  }++  return NULL;+}++struct variable* create_variable(struct server_state *s,+                                 const char *name,+                                 struct type *type) {+  int found = -1;+  for (int i = 0; i < s->variables_capacity; i++) {+    if (found == -1 && s->variables[i].name == NULL) {+      found = i;+    } else if (s->variables[i].name != NULL &&+               strcmp(s->variables[i].name, name) == 0) {+      return NULL;+    }+  }++  if (found != -1) {+    // Found a free spot.+    s->variables[found].name = strdup(name);+    s->variables[found].value.type = type;+    return &s->variables[found];+  }++  // Need to grow the buffer.+  found = s->variables_capacity;+  s->variables_capacity *= 2;+  s->variables = realloc(s->variables,+                         s->variables_capacity * sizeof(struct variable));++  s->variables[found].name = strdup(name);+  s->variables[found].value.type = type;++  for (int i = found+1; i < s->variables_capacity; i++) {+    s->variables[i].name = NULL;+  }++  return &s->variables[found];+}++void drop_variable(struct variable *v) {+  free(v->name);+  v->name = NULL;+}++int arg_exists(const char *args[], int i) {+  return args[i] != NULL;+}++const char* get_arg(const char *args[], int i) {+  if (!arg_exists(args, i)) {+    futhark_panic(1, "Insufficient command args.\n");+  }+  return args[i];+}++struct type* get_type(struct server_state *s, const char *name) {+  for (int i = 0; s->prog.types[i]; i++) {+    if (strcmp(s->prog.types[i]->name, name) == 0) {+      return s->prog.types[i];+    }+  }++  futhark_panic(1, "Unknown type %s\n", name);+  return NULL;+}++struct entry_point* get_entry_point(struct server_state *s, const char *name) {+  for (int i = 0; s->prog.entry_points[i].name; i++) {+    if (strcmp(s->prog.entry_points[i].name, name) == 0) {+      return &s->prog.entry_points[i];+    }+  }++  return NULL;+}++// Print the command-done marker, indicating that we are ready for+// more input.+void ok() {+  printf("%%%%%% OK\n");+  fflush(stdout);+}++// Print the failure marker.  Output is now an error message until the+// next ok().+void failure() {+  printf("%%%%%% FAILURE\n");+}++void error_check(struct server_state *s, int err) {+  if (err != 0) {+    failure();+    char *error = futhark_context_get_error(s->ctx);+    puts(error);+    free(error);+  }+}++void cmd_call(struct server_state *s, const char *args[]) {+  const char *name = get_arg(args, 0);++  struct entry_point *e = get_entry_point(s, name);++  if (e == NULL) {+    failure();+    printf("Unknown entry point: %s\n", name);+    return;+  }++  int num_outs = entry_num_outs(e);+  int num_ins = entry_num_ins(e);+  void* outs[num_outs];+  void* ins[num_ins];++  for (int i = 0; i < num_ins; i++) {+    const char *in_name = get_arg(args, 1+num_outs+i);+    struct variable *v = get_variable(s, in_name);+    if (v == NULL) {+      failure();+      printf("Unknown variable: %s\n", in_name);+      return;+    }+    if (v->value.type != e->in_types[i]) {+      failure();+      printf("Wrong input type.  Expected %s, got %s.\n",+             e->in_types[i]->name, v->value.type->name);+      return;+    }+    ins[i] = value_ptr(&v->value);+  }++  for (int i = 0; i < num_outs; i++) {+    const char *out_name = get_arg(args, 1+i);+    struct variable *v = create_variable(s, out_name, e->out_types[i]);+    if (v == NULL) {+      failure();+      printf("Variable already exists: %s\n", out_name);+      return;+    }+    outs[i] = value_ptr(&v->value);+  }++  int64_t t_start = get_wall_time();+  int err = e->f(s->ctx, outs, ins);+  err |= futhark_context_sync(s->ctx);+  int64_t t_end = get_wall_time();+  long long int elapsed_usec = t_end - t_start;+  printf("runtime: %lld\n", elapsed_usec);++  error_check(s, err);+  if (err != 0) {+    // Need to uncreate the output variables, which would otherwise be left+    // in an uninitialised state.+    for (int i = 0; i < num_outs; i++) {+      const char *out_name = get_arg(args, 1+i);+      struct variable *v = get_variable(s, out_name);+      if (v) {+        drop_variable(v);+      }+    }+  }+}++void cmd_restore(struct server_state *s, const char *args[]) {+  const char *fname = get_arg(args, 0);++  FILE *f = fopen(fname, "rb");+  if (f == NULL) {+    failure();+    printf("Failed to open %s: %s\n", fname, strerror(errno));+  } else {+    int values = 0;+    for (int i = 1; arg_exists(args, i); i+=2, values++) {+      const char *vname = get_arg(args, i);+      const char *type = get_arg(args, i+1);++      struct type *t = get_type(s, type);+      struct variable *v = create_variable(s, vname, t);++      if (v == NULL) {+        failure();+        printf("Variable already exists: %s\n", vname);+        return;+      }++      if (t->restore(t->aux, f, s->ctx, value_ptr(&v->value)) != 0) {+        failure();+        printf("Failed to restore variable %s.\n"+               "Possibly malformed data in %s (errno: %s)\n",+               vname, fname, strerror(errno));+        drop_variable(v);+        break;+      }+    }++    if (end_of_input(f) != 0) {+      failure();+      printf("Expected EOF after reading %d values from %s\n",+             values, fname);+    }++    fclose(f);+  }++  int err = futhark_context_sync(s->ctx);+  error_check(s, err);+}++void cmd_store(struct server_state *s, const char *args[]) {+  const char *fname = get_arg(args, 0);++  FILE *f = fopen(fname, "wb");+  if (f == NULL) {+    failure();+    printf("Failed to open %s: %s\n", fname, strerror(errno));+  } else {+    for (int i = 1; arg_exists(args, i); i++) {+      const char *vname = get_arg(args, i);+      struct variable *v = get_variable(s, vname);++      if (v == NULL) {+        failure();+        printf("Unknown variable: %s\n", vname);+        return;+      }++      struct type *t = v->value.type;+      t->store(t->aux, f, s->ctx, value_ptr(&v->value));+    }+    fclose(f);+  }+}++void cmd_free(struct server_state *s, const char *args[]) {+  for (int i = 0; arg_exists(args, i); i++) {+    const char *name = get_arg(args, i);+    struct variable *v = get_variable(s, name);++    if (v == NULL) {+      failure();+      printf("Unknown variable: %s\n", name);+      return;+    }++    struct type *t = v->value.type;++    int err = t->free(t->aux, s->ctx, value_ptr(&v->value));+    error_check(s, err);+    drop_variable(v);+  }+}++void cmd_inputs(struct server_state *s, const char *args[]) {+  const char *name = get_arg(args, 0);+  struct entry_point *e = get_entry_point(s, name);++  if (e == NULL) {+    failure();+    printf("Unknown entry point: %s\n", name);+    return;+  }++  int num_ins = entry_num_ins(e);+  for (int i = 0; i < num_ins; i++) {+    puts(e->in_types[i]->name);+  }+}++void cmd_outputs(struct server_state *s, const char *args[]) {+  const char *name = get_arg(args, 0);+  struct entry_point *e = get_entry_point(s, name);++  if (e == NULL) {+    failure();+    printf("Unknown entry point: %s\n", name);+    return;+  }++  int num_outs = entry_num_outs(e);+  for (int i = 0; i < num_outs; i++) {+    puts(e->out_types[i]->name);+  }+}++void cmd_clear(struct server_state *s, const char *args[]) {+  (void)args;+  int err = 0;+  for (int i = 0; i < s->variables_capacity; i++) {+    struct variable *v = &s->variables[i];+    if (v->name != NULL) {+      err |= v->value.type->free(v->value.type->aux, s->ctx, value_ptr(&v->value));+      drop_variable(v);+    }+  }+  err |= futhark_context_clear_caches(s->ctx);+  error_check(s, err);+}++void cmd_pause_profiling(struct server_state *s, const char *args[]) {+  (void)args;+  futhark_context_pause_profiling(s->ctx);+}++void cmd_unpause_profiling(struct server_state *s, const char *args[]) {+  (void)args;+  futhark_context_unpause_profiling(s->ctx);+}++void cmd_report(struct server_state *s, const char *args[]) {+  (void)args;+  char *report = futhark_context_report(s->ctx);+  puts(report);+  free(report);+}++void process_line(struct server_state *s, char *line) {+  int max_num_tokens = 100;+  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) {+    num_tokens++;+    if (num_tokens == max_num_tokens) {+      futhark_panic(1, "Line too long.\n");+    }+    tmp = NULL;+  }++  const char *command = tokens[0];++  if (command == NULL) {+    failure();+    printf("Empty line\n");+  } else if (strcmp(command, "call") == 0) {+    cmd_call(s, tokens+1);+  } else if (strcmp(command, "restore") == 0) {+    cmd_restore(s, tokens+1);+  } else if (strcmp(command, "store") == 0) {+    cmd_store(s, tokens+1);+  } else if (strcmp(command, "free") == 0) {+    cmd_free(s, tokens+1);+  } else if (strcmp(command, "inputs") == 0) {+    cmd_inputs(s, tokens+1);+  } else if (strcmp(command, "outputs") == 0) {+    cmd_outputs(s, tokens+1);+  } else if (strcmp(command, "clear") == 0) {+    cmd_clear(s, tokens+1);+  } else if (strcmp(command, "pause_profiling") == 0) {+    cmd_pause_profiling(s, tokens+1);+  } else if (strcmp(command, "unpause_profiling") == 0) {+    cmd_unpause_profiling(s, tokens+1);+  } else if (strcmp(command, "report") == 0) {+    cmd_report(s, tokens+1);+  } else {+    futhark_panic(1, "Unknown command: %s\n", command);+  }+  ok();+}++void run_server(struct futhark_prog *prog, struct futhark_context *ctx) {+  char *line = NULL;+  size_t buflen = 0;+  ssize_t linelen;++  struct server_state s = {+    .ctx = ctx,+    .variables_capacity = 100,+    .prog = *prog+  };++  s.variables = malloc(s.variables_capacity * sizeof(struct variable));++  for (int i = 0; i < s.variables_capacity; i++) {+    s.variables[i].name = NULL;+  }++  while ((linelen = getline(&line, &buflen, stdin)) > 0) {+    process_line(&s, line);+  }++  free(line);+}++// The aux struct lets us write generic method implementations without+// code duplication.++typedef void* (*array_new_fn)(struct futhark_context *, const void*, const int64_t*);+typedef const int64_t* (*array_shape_fn)(struct futhark_context*, void*);+typedef int (*array_values_fn)(struct futhark_context*, void*, void*);+typedef int (*array_free_fn)(struct futhark_context*, void*);++struct array_aux {+  int rank;+  const struct primtype_info_t* info;+  const char *name;+  array_new_fn new;+  array_shape_fn shape;+  array_values_fn values;+  array_free_fn free;+};++int restore_array(const struct array_aux *aux, FILE *f,+                  struct futhark_context *ctx, void *p) {+  void *data = NULL;+  int64_t shape[aux->rank];+  if (read_array(f, aux->info, &data, shape, aux->rank) != 0) {+    return 1;+  }++  void *arr = aux->new(ctx, data, shape);+  if (arr == NULL) {+    return 1;+  }++  *(void**)p = arr;+  free(data);+  return 0;+}++void store_array(const struct array_aux *aux, FILE *f,+                 struct futhark_context *ctx, void *p) {+  void *arr = *(void**)p;+  const int64_t *shape = aux->shape(ctx, arr);+  int64_t size = sizeof(aux->info->size);+  for (int i = 0; i < aux->rank; i++) {+    size *= shape[i];+  }+  int32_t *data = malloc(size);+  assert(aux->values(ctx, arr, data) == 0);+  assert(futhark_context_sync(ctx) == 0);+  assert(write_array(f, 1, aux->info, data, shape, aux->rank) == 0);+  free(data);+}++int free_array(const struct array_aux *aux,+                struct futhark_context *ctx, void *p) {+  void *arr = *(void**)p;+  return aux->free(ctx, arr);+}++typedef int (*opaque_free_fn)(struct futhark_context*, void*);++struct opaque_aux {+  opaque_free_fn free;+};++int free_opaque(const struct opaque_aux *aux,+                 struct futhark_context *ctx, void *p) {+  void *obj = *(void**)p;+  return aux->free(ctx, obj);+}++// End of server.h.
+ rts/python/server.py view
@@ -0,0 +1,165 @@+# Start of server.py++import sys+import time++class Server:+    def __init__(self, ctx):+        self._ctx = ctx+        self._vars = {}++    class Failure(BaseException):+        def __init__(self, msg):+            self.msg = msg++    def _get_arg(self, args, i):+        if i < len(args):+            return args[i]+        else:+            raise self.Failure('Insufficient command args')++    def _get_entry_point(self, entry):+        if entry in self._ctx.entry_points:+            return self._ctx.entry_points[entry]+        else:+            raise self.Failure('Unknown entry point: %s' % entry)++    def _check_var(self, vname):+        if not vname in self._vars:+            raise self.Failure('Unknown variable: %s' % vname)++    def _get_var(self, vname):+        self._check_var(vname)+        return self._vars[vname]++    def _cmd_inputs(self, args):+        entry = self._get_arg(args, 0)+        for t in self._get_entry_point(entry)[0]:+            print(t)++    def _cmd_outputs(self, args):+        entry = self._get_arg(args, 0)+        for t in self._get_entry_point(entry)[1]:+            print(t)++    def _cmd_dummy(self, args):+        pass++    def _cmd_free(self, args):+        for vname in args:+            self._check_var(vname)+            del self._vars[vname]++    def _cmd_call(self, args):+        entry = self._get_entry_point(self._get_arg(args, 0))+        num_ins = len(entry[0])+        num_outs = len(entry[1])++        if len(args) != 1 + num_outs + num_ins:+            raise self.Failure('Invalid argument count, expected %d')++        out_vnames = args[1:num_outs+1]++        for out_vname in out_vnames:+            if out_vname in self._vars:+                raise self.Failure('Variable already exists: %s' % out_vname)++        in_vnames = args[1+num_outs:]+        ins = [ self._get_var(in_vname) for in_vname in in_vnames ]++        try:+            (runtime, vals) = getattr(self._ctx, args[0])(*ins)+        except Exception as e:+            raise self.Failure(str(e))++        print('runtime: %d' % runtime)++        if num_outs == 1:+            self._vars[out_vnames[0]] = vals+        else:+            for (out_vname, val) in zip(out_vnames, vals):+                self._vars[out_vname] = val++    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, np.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()))++    def _cmd_restore(self, args):+        if len(args) % 2 == 0:+            raise self.Failure('Invalid argument count')++        fname = args[0]+        args = args[1:]++        with open(fname, 'rb') as f:+            reader = ReaderInput(f)+            while args != []:+                vname = args[0]+                typename = args[1]+                args = args[2:]++                if vname in self._vars:+                    raise self.Failure('Variable already exists: %s' % vname)++                try:+                    self._vars[vname] = read_value(typename, reader)+                except ValueError:+                    raise self.Failure('Failed to restore variable %s.\n'+                                       'Possibly malformed data in %s.\n'+                                       % (vname, fname))++            skip_spaces(reader)+            if reader.get_char() != b'':+                raise self.Failure('Expected EOF after reading values')++    _commands = { 'inputs': _cmd_inputs,+                  'outputs': _cmd_outputs,+                  'call': _cmd_call,+                  'restore': _cmd_restore,+                  'store': _cmd_store,+                  'free': _cmd_free,+                  'clear': _cmd_dummy,+                  'pause_profiling': _cmd_dummy,+                  'unpause_profiling': _cmd_dummy,+                  'report': _cmd_dummy+                 }++    def _process_line(self, line):+        words = line.split()+        if words == []:+            raise self.Failure('Empty line')+        else:+            cmd = words[0]+            args = words[1:]+            if cmd in self._commands:+                self._commands[cmd](self, args)+            else:+                raise self.Failure('Unknown command: %s' % cmd)++    def run(self):+        while True:+            line = sys.stdin.readline()+            if line == '':+                return+            try:+                self._process_line(line)+            except self.Failure as e:+                print('%%% FAILURE')+                print(e.msg)+            print('%%% OK', flush=True)+++# End of server.py
src/Futhark/Actions.hs view
@@ -77,6 +77,7 @@       actionProcedure = liftIO . putStrLn . pretty . snd <=< ImpGenKernels.compileProgOpenCL     } +-- | Convert the program to CPU multicore ImpCode and print it to stdout. multicoreImpCodeGenAction :: Action MCMem multicoreImpCodeGenAction =   Action@@ -160,6 +161,9 @@         ToExecutable -> do           liftIO $ writeFile cpath $ SequentialC.asExecutable cprog           runCC cpath outpath ["-O3", "-std=c99"] ["-lm"]+        ToServer -> do+          liftIO $ writeFile cpath $ SequentialC.asServer cprog+          runCC cpath outpath ["-O3", "-std=c99"] ["-lm"]  -- | The @futhark opencl@ action. compileOpenCLAction :: FutharkConfig -> CompilerMode -> FilePath -> Action KernelsMem@@ -190,6 +194,9 @@         ToExecutable -> do           liftIO $ writeFile cpath $ COpenCL.asExecutable cprog           runCC cpath outpath ["-O", "-std=c99"] ("-lm" : extra_options)+        ToServer -> do+          liftIO $ writeFile cpath $ COpenCL.asServer cprog+          runCC cpath outpath ["-O", "-std=c99"] ("-lm" : extra_options)  -- | The @futhark cuda@ action. compileCUDAAction :: FutharkConfig -> CompilerMode -> FilePath -> Action KernelsMem@@ -217,6 +224,9 @@         ToExecutable -> do           liftIO $ writeFile cpath $ CCUDA.asExecutable cprog           runCC cpath outpath ["-O", "-std=c99"] ("-lm" : extra_options)+        ToServer -> do+          liftIO $ writeFile cpath $ CCUDA.asServer cprog+          runCC cpath outpath ["-O", "-std=c99"] ("-lm" : extra_options)  -- | The @futhark multicore@ action. compileMulticoreAction :: FutharkConfig -> CompilerMode -> FilePath -> Action MCMem@@ -239,4 +249,7 @@           liftIO $ writeFile cpath impl         ToExecutable -> do           liftIO $ writeFile cpath $ MulticoreC.asExecutable cprog+          runCC cpath outpath ["-O", "-std=c99"] ["-lm", "-pthread"]+        ToServer -> do+          liftIO $ writeFile cpath $ MulticoreC.asServer cprog           runCC cpath outpath ["-O", "-std=c99"] ["-lm", "-pthread"]
src/Futhark/Analysis/Metrics.hs view
@@ -24,25 +24,8 @@ import qualified Data.Map.Strict as M import Data.Text (Text) import qualified Data.Text as T+import Futhark.Analysis.Metrics.Type import Futhark.IR---- | AST metrics are simply a collection from identifiable node names--- to the number of times that node appears.-newtype AstMetrics = AstMetrics (M.Map Text Int)--instance Show AstMetrics where-  show (AstMetrics m) = unlines $ map metric $ M.toList m-    where-      metric (k, v) = pretty k ++ " " ++ pretty v--instance Read AstMetrics where-  readsPrec _ s =-    maybe [] success $ mapM onLine $ lines s-    where-      onLine l = case words l of-        [k, x] | [(n, "")] <- reads x -> Just (T.pack k, n)-        _ -> Nothing-      success m = [(AstMetrics $ M.fromList m, "")]  -- | Compute the metrics for some operation. class OpMetrics op where
+ src/Futhark/Analysis/Metrics/Type.hs view
@@ -0,0 +1,26 @@+-- | The data type definition for "Futhark.Analysis.Metrics", factored+-- out to simplify the module import hierarchies when working on the+-- test modules.+module Futhark.Analysis.Metrics.Type (AstMetrics (..)) where++import qualified Data.Map.Strict as M+import Data.Text (Text)+import qualified Data.Text as T++-- | AST metrics are simply a collection from identifiable node names+-- to the number of times that node appears.+newtype AstMetrics = AstMetrics (M.Map Text Int)++instance Show AstMetrics where+  show (AstMetrics m) = unlines $ map metric $ M.toList m+    where+      metric (k, v) = T.unpack k ++ " " ++ show v++instance Read AstMetrics where+  readsPrec _ s =+    maybe [] success $ mapM onLine $ lines s+    where+      onLine l = case words l of+        [k, x] | [(n, "")] <- reads x -> Just (T.pack k, n)+        _ -> Nothing+      success m = [(AstMetrics $ M.fromList m, "")]
src/Futhark/Analysis/PrimExp.hs view
@@ -147,19 +147,19 @@ instance FreeIn v => FreeIn (TPrimExp t v) where   freeIn' = freeIn' . untyped --- | This expression is of type 'Int8'.+-- | This expression is of type t'Int8'. isInt8 :: PrimExp v -> TPrimExp Int8 v isInt8 = TPrimExp --- | This expression is of type 'Int16'.+-- | This expression is of type t'Int16'. isInt16 :: PrimExp v -> TPrimExp Int16 v isInt16 = TPrimExp --- | This expression is of type 'Int32'.+-- | This expression is of type t'Int32'. isInt32 :: PrimExp v -> TPrimExp Int32 v isInt32 = TPrimExp --- | This expression is of type 'Int64'.+-- | This expression is of type t'Int64'. isInt64 :: PrimExp v -> TPrimExp Int64 v isInt64 = TPrimExp @@ -167,11 +167,11 @@ isBool :: PrimExp v -> TPrimExp Bool v isBool = TPrimExp --- | This expression is of type 'Float'.+-- | This expression is of type t'Float'. isF32 :: PrimExp v -> TPrimExp Float v isF32 = TPrimExp --- | This expression is of type 'Double'.+-- | This expression is of type t'Double'. isF64 :: PrimExp v -> TPrimExp Double v isF64 = TPrimExp 
src/Futhark/Analysis/Rephrase.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE GADTs #-}  -- | Facilities for changing the lore of some fragment, with no -- context.  We call this "rephrasing", for no deep reason.
src/Futhark/Analysis/SymbolTable.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}  module Futhark.Analysis.SymbolTable   ( SymbolTable (bindings, loopDepth, availableAtClosestLoop, simplifyMemory),
src/Futhark/Bench.hs view
@@ -7,6 +7,7 @@   ( RunResult (..),     DataResult (..),     BenchResult (..),+    Result (..),     encodeBenchResults,     decodeBenchResults,     binaryName,@@ -18,21 +19,18 @@ where  import Control.Applicative-import Control.Concurrent (forkIO, killThread, threadDelay) import Control.Monad.Except import qualified Data.Aeson as JSON import qualified Data.ByteString.Char8 as SBS import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.HashMap.Strict as HM+import qualified Data.Map as M+import Data.Maybe import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Data.Text.IO as T+import Futhark.Server import Futhark.Test import System.Exit import System.FilePath-import System.IO-import System.IO.Error-import System.IO.Temp (withSystemTempFile) import System.Process.ByteString (readProcessWithExitCode) import System.Timeout (timeout) @@ -40,10 +38,17 @@ newtype RunResult = RunResult {runMicroseconds :: Int}   deriving (Eq, Show) --- | The results for a single named dataset is either an error--- message, or runtime measurements along the stderr that was+data Result = Result+  { runResults :: [RunResult],+    memoryMap :: M.Map T.Text Int,+    stdErr :: T.Text+  }+  deriving (Eq, Show)++-- | The results for a single named dataset is either an error message, or+-- runtime measurements, the number of bytes used, and the stderr that was -- produced.-data DataResult = DataResult String (Either T.Text ([RunResult], T.Text))+data DataResult = DataResult String (Either T.Text Result)   deriving (Eq, Show)  -- | The results for all datasets for some benchmark program.@@ -54,6 +59,12 @@  newtype BenchResults = BenchResults {unBenchResults :: [BenchResult]} +instance JSON.ToJSON Result where+  toJSON (Result runres memmap err) = JSON.toJSON (runres, memmap, err)++instance JSON.FromJSON Result where+  parseJSON = fmap (\(runres, memmap, err) -> Result runres memmap err) . JSON.parseJSON+ instance JSON.ToJSON RunResult where   toJSON = JSON.toJSON . runMicroseconds @@ -74,15 +85,16 @@         DataResult (T.unpack k)           <$> ((Right <$> success v) <|> (Left <$> JSON.parseJSON v))       success = JSON.withObject "result" $ \o ->-        (,) <$> o JSON..: "runtimes" <*> o JSON..: "stderr"+        Result <$> o JSON..: "runtimes" <*> o JSON..: "bytes" <*> o JSON..: "stderr"  dataResultJSON :: DataResult -> (T.Text, JSON.Value) dataResultJSON (DataResult desc (Left err)) =   (T.pack desc, JSON.toJSON err)-dataResultJSON (DataResult desc (Right (runtimes, progerr))) =+dataResultJSON (DataResult desc (Right (Result runtimes bytes progerr))) =   ( T.pack desc,     JSON.object       [ ("runtimes", JSON.toJSON $ map runMicroseconds runtimes),+        ("bytes", JSON.toJSON bytes),         ("stderr", JSON.toJSON progerr)       ]   )@@ -117,67 +129,9 @@  --- Running benchmarks -readRuntime :: T.Text -> Maybe Int-readRuntime s = case reads $ T.unpack s of-  [(runtime, _)] -> Just runtime-  _ -> Nothing--didNotFail :: FilePath -> ExitCode -> T.Text -> ExceptT T.Text IO ()-didNotFail _ ExitSuccess _ =-  return ()-didNotFail program (ExitFailure code) stderr_s =-  throwError $-    T.pack $-      program ++ " failed with error code " ++ show code-        ++ " and output:\n"-        ++ T.unpack stderr_s--compareResult ::-  (MonadError T.Text m, MonadIO m) =>-  FilePath ->-  (SBS.ByteString, [Value]) ->-  (SBS.ByteString, [Value]) ->-  m ()-compareResult program (expected_bs, expected_vs) (actual_bs, actual_vs) =-  case compareValues1 actual_vs expected_vs of-    Just mismatch -> do-      let actualf = program `replaceExtension` "actual"-          expectedf = program `replaceExtension` "expected"-      liftIO $ SBS.writeFile actualf actual_bs-      liftIO $ SBS.writeFile expectedf expected_bs-      throwError $-        T.pack actualf <> " and " <> T.pack expectedf-          <> " do not match:\n"-          <> T.pack (show mismatch)-    Nothing ->-      return ()--runResult ::-  (MonadError T.Text m, MonadIO m) =>-  FilePath ->-  ExitCode ->-  SBS.ByteString ->-  SBS.ByteString ->-  m (SBS.ByteString, [Value])-runResult program ExitSuccess stdout_s _ =-  case valuesFromByteString "stdout" $ LBS.fromStrict stdout_s of-    Left e -> do-      let actualf = program `replaceExtension` "actual"-      liftIO $ SBS.writeFile actualf stdout_s-      throwError $ T.pack $ show e <> "\n(See " <> actualf <> ")"-    Right vs -> return (stdout_s, vs)-runResult program (ExitFailure code) _ stderr_s =-  throwError $-    T.pack $-      binaryName program ++ " failed with error code " ++ show code-        ++ " and output:\n"-        ++ T.unpack (T.decodeUtf8 stderr_s)- -- | How to run a benchmark. data RunOptions = RunOptions-  { runRunner :: String,-    runRuns :: Int,-    runExtraOptions :: [String],+  { runRuns :: Int,     runTimeout :: Int,     runVerbose :: Int,     -- | Invoked for every runtime measured during the run.  Can be@@ -185,33 +139,15 @@     runResultAction :: Maybe (Int -> IO ())   } --- | Like @tail -f@, but running an arbitrary IO action per line.-follow :: (String -> IO ()) -> FilePath -> IO ()-follow f fname = go 0-  where-    go i = do-      i' <- withFile fname ReadMode $ \h -> do-        hSeek h AbsoluteSeek i-        goH h i-      go i'--    goH h i = do-      res <- tryIOError $ hGetLine h-      case res of-        Left e-          | isEOFError e -> do-            threadDelay followDelayMicroseconds-            pure i-          | otherwise -> ioError e-        Right l -> do-          f l-          goH h =<< hTell h+cmdMaybe :: (MonadError T.Text m, MonadIO m) => IO (Maybe CmdFailure) -> m ()+cmdMaybe = maybe (pure ()) (throwError . T.unlines . failureMsg) <=< liftIO -    triesPerSecond = 10-    followDelayMicroseconds = 1000000 `div` triesPerSecond+cmdEither :: (MonadError T.Text m, MonadIO m) => IO (Either CmdFailure a) -> m a+cmdEither = either (throwError . T.unlines . failureMsg) pure <=< liftIO  -- | Run the benchmark program on the indicated dataset. benchmarkDataset ::+  Server ->   RunOptions ->   FutharkExe ->   FilePath ->@@ -220,77 +156,72 @@   Maybe Success ->   FilePath ->   IO (Either T.Text ([RunResult], T.Text))-benchmarkDataset opts futhark program entry input_spec expected_spec ref_out =-  -- We store the runtime in a temporary file.-  withSystemTempFile "futhark-bench" $ \tmpfile h -> do-    hClose h -- We will be writing and reading this ourselves.-    input <- getValuesBS futhark dir input_spec-    let getValuesAndBS (SuccessValues vs) = do-          vs' <- getValues futhark dir vs-          bs <- getValuesBS futhark dir vs-          return (LBS.toStrict bs, vs')-        getValuesAndBS SuccessGenerateValues =-          getValuesAndBS $ SuccessValues $ InFile ref_out-    maybe_expected <- maybe (return Nothing) (fmap Just . getValuesAndBS) expected_spec-    let options =-          runExtraOptions opts-            ++ [ "-e",-                 T.unpack entry,-                 "-t",-                 tmpfile,-                 "-r",-                 show $ runRuns opts,-                 "-b"-               ]+benchmarkDataset server opts futhark program entry input_spec expected_spec ref_out = runExceptT $ do+  output_types <- cmdEither $ cmdOutputs server entry+  input_types <- cmdEither $ cmdInputs server entry+  let outs = ["out" <> T.pack (show i) | i <- [0 .. length output_types -1]]+      ins = ["in" <> T.pack (show i) | i <- [0 .. length input_types -1]]+      freeOuts = cmdMaybe (cmdFree server outs)+      freeIns = cmdMaybe (cmdFree server ins) -    -- Explicitly prefixing the current directory is necessary for-    -- readProcessWithExitCode to find the binary when binOutputf has-    -- no program component.-    let (to_run, to_run_args)-          | null $ runRunner opts = ("." </> binaryName program, options)-          | otherwise = (runRunner opts, binaryName program : options)+  cmdMaybe . liftIO $ cmdClear server -    when (runVerbose opts > 1) $-      putStrLn $-        unwords-          [ "Running executable",-            show to_run,-            "with arguments",-            show to_run_args-          ]+  cmdMaybe . liftIO . withValuesFile futhark dir input_spec $ \values_f ->+    cmdRestore server values_f (zip ins input_types) -    let onResult l-          | Just f <- runResultAction opts,-            [(x, "")] <- reads l =-            f x-          | otherwise =-            pure ()-    watcher <- forkIO $ follow onResult tmpfile+  let runtime l+        | Just l' <- T.stripPrefix "runtime: " l,+          [(x, "")] <- reads $ T.unpack l' =+          Just x+        | otherwise =+          Nothing -    run_res <--      timeout (runTimeout opts * 1000000) $-        readProcessWithExitCode to_run to_run_args $-          LBS.toStrict input+      doRun = do+        call_lines <- cmdEither (cmdCall server entry outs ins)+        case mapMaybe runtime call_lines of+          [call_runtime] -> do+            liftIO $ fromMaybe (const $ pure ()) (runResultAction opts) call_runtime+            return (RunResult call_runtime, call_lines)+          [] -> throwError "Could not find runtime in output."+          ls -> throwError $ "Ambiguous runtimes: " <> T.pack (show ls) -    killThread watcher+  maybe_call_logs <- liftIO . timeout (runTimeout opts * 1000000) . runExceptT $ do+    -- First one uncounted warmup run.+    void $ cmdEither $ cmdCall server entry outs ins+    freeOuts+    xs <- replicateM (runRuns opts -1) (doRun <* freeOuts)+    y <- doRun+    pure $ xs ++ [y] -    runExceptT $ case run_res of-      Just (progCode, output, progerr) -> do-        case maybe_expected of-          Nothing ->-            didNotFail program progCode $ T.decodeUtf8 progerr-          Just expected ->-            compareResult program expected-              =<< runResult program progCode output progerr-        runtime_result <- liftIO $ T.readFile tmpfile-        runtimes <- case mapM readRuntime $ T.lines runtime_result of-          Just runtimes -> return $ map RunResult runtimes-          Nothing -> throwError $ "Runtime file has invalid contents:\n" <> runtime_result+  call_logs <- case maybe_call_logs of+    Nothing ->+      throwError . T.pack $+        "Execution exceeded " ++ show (runTimeout opts) ++ " seconds."+    Just x -> liftEither x -        return (runtimes, T.decodeUtf8 progerr)-      Nothing ->-        throwError $ T.pack $ "Execution exceeded " ++ show (runTimeout opts) ++ " seconds."+  freeIns++  report <- cmdEither $ cmdReport server++  vs <- readResults server outs program <* freeOuts++  maybe_expected <-+    liftIO $ maybe (return Nothing) (fmap Just . getExpectedValues) expected_spec++  case maybe_expected of+    Just expected -> checkResult program expected vs+    Nothing -> pure ()++  return+    ( map fst call_logs,+      T.unlines $ map (T.unlines . snd) call_logs <> report+    )   where+    getExpectedValues (SuccessValues vs) =+      getValues futhark dir vs+    getExpectedValues SuccessGenerateValues =+      getExpectedValues $ SuccessValues $ InFile ref_out+     dir = takeDirectory program  -- | How to compile a benchmark.@@ -328,7 +259,7 @@         liftIO $           readProcessWithExitCode             futhark-            ( [compBackend opts, program, "-o", binaryName program]+            ( [compBackend opts, program, "-o", binaryName program, "--server"]                 <> compOptions opts             )             ""
src/Futhark/CLI/Autotune.hs view
@@ -13,10 +13,10 @@ import qualified Data.Text as T import Data.Tree import Futhark.Bench+import Futhark.Server import Futhark.Test import Futhark.Util (maxinum) import Futhark.Util.Options-import System.Console.GetOpt import System.Environment (getExecutablePath) import System.Exit import System.FilePath@@ -49,23 +49,14 @@         compOptions = mempty       } -runOptions :: Path -> Int -> AutotuneOptions -> RunOptions-runOptions path timeout_s opts =+runOptions :: Int -> AutotuneOptions -> RunOptions+runOptions timeout_s opts =   RunOptions-    { runRunner = "",-      runRuns = optRuns opts,-      runExtraOptions =-        "--default-threshold" :-        show (optDefaultThreshold opts) :-        "-L" :-        map opt path-          ++ optExtraOptions opts,+    { runRuns = optRuns opts,       runTimeout = timeout_s,       runVerbose = optVerbose opts,       runResultAction = Nothing     }-  where-    opt (name, val) = "--size=" ++ name ++ "=" ++ show val  type Path = [(String, Int)] @@ -88,6 +79,16 @@  type DatasetName = String +serverOptions :: Path -> AutotuneOptions -> [String]+serverOptions path opts =+  "--default-threshold" :+  show (optDefaultThreshold opts) :+  "-L" :+  map opt path+    ++ optExtraOptions opts+  where+    opt (name, val) = "--size=" ++ name ++ "=" ++ show val+ prepare :: AutotuneOptions -> FutharkExe -> FilePath -> IO [(DatasetName, RunDataset, T.Text)] prepare opts futhark prog = do   spec <- testSpecFromFileOrDie prog@@ -118,15 +119,11 @@               Just (runDescription trun, run entry_point trun expected)           _ -> Nothing -  fmap concat $-    forM truns $ \ios ->-      forM-        ( mapMaybe-            (runnableDataset $ iosEntryPoint ios)-            (iosTestRuns ios)-        )-        $ \(dataset, do_run) ->-          return (dataset, do_run, iosEntryPoint ios)+  fmap concat . forM truns $ \ios -> do+    let cases =+          mapMaybe (runnableDataset $ iosEntryPoint ios) (iosTestRuns ios)+    forM cases $ \(dataset, do_run) ->+      return (dataset, do_run, iosEntryPoint ios)   where     run entry_point trun expected timeout path = do       let bestRuntime :: ([RunResult], T.Text) -> ([(String, Int)], Int)@@ -135,20 +132,26 @@               minimum $ map runMicroseconds runres             ) -          ropts = runOptions path timeout opts+          ropts = runOptions timeout opts        when (optVerbose opts > 1) $-        putStrLn $ "Running with options: " ++ unwords (runExtraOptions ropts)+        putStrLn $ "Running with options: " ++ unwords (serverOptions path opts) -      either (Left . T.unpack) (Right . bestRuntime)-        <$> benchmarkDataset-          ropts-          futhark-          prog-          entry_point-          (runInput trun)-          expected-          (testRunReferenceOutput prog entry_point trun)+      -- XXX: it is really inefficient to start a new server for every+      -- run, but unfortunately we can only set threshold parameters+      -- on startup.+      let progbin = "." </> dropExtension prog+      withServer progbin (serverOptions path opts) $ \server ->+        either (Left . T.unpack) (Right . bestRuntime)+          <$> benchmarkDataset+            server+            ropts+            futhark+            prog+            entry_point+            (runInput trun)+            expected+            (testRunReferenceOutput prog entry_point trun)  --- Benchmarking a program 
src/Futhark/CLI/Bench.hs view
@@ -10,21 +10,24 @@ import qualified Data.ByteString.Char8 as SBS import qualified Data.ByteString.Lazy.Char8 as LBS import Data.Either+import Data.Function ((&)) import Data.IORef import Data.List (foldl', sortBy)+import qualified Data.Map as M import Data.Maybe import Data.Ord import qualified Data.Text as T import Futhark.Bench+import Futhark.Server import Futhark.Test import Futhark.Util (fancyTerminal, maxinum, maybeNth, pmapIO) import Futhark.Util.Console import Futhark.Util.Options import System.Console.ANSI (clearLine)-import System.Console.GetOpt import System.Directory import System.Environment import System.Exit+import System.FilePath import System.IO import Text.Printf import Text.Regex.TDFA@@ -161,20 +164,31 @@   where     hasRuns (InputOutputs _ runs) = not $ null runs +withProgramServer :: FilePath -> FilePath -> [String] -> (Server -> IO a) -> IO a+withProgramServer program runner extra_options f = do+  -- Explicitly prefixing the current directory is necessary for+  -- readProcessWithExitCode to find the binary when binOutputf has+  -- no path component.+  let binOutputf = dropExtension program+      binpath = "." </> binOutputf++      (to_run, to_run_args)+        | null runner = (binpath, extra_options)+        | otherwise = (runner, binpath : extra_options)++  liftIO $ withServer to_run to_run_args f+ runBenchmark :: BenchOptions -> FutharkExe -> (FilePath, [InputOutputs]) -> IO [BenchResult]-runBenchmark opts futhark (program, cases) = mapM forInputOutputs $ filter relevant cases+runBenchmark opts futhark (program, cases) = do+  (tuning_opts, tuning_desc) <- determineTuning (optTuning opts) program+  let runopts = "-L" : optExtraOptions opts ++ tuning_opts+  withProgramServer program (optRunner opts) runopts $ \server ->+    mapM (forInputOutputs server tuning_desc) $ filter relevant cases   where-    forInputOutputs (InputOutputs entry_name runs) = do-      (tuning_opts, tuning_desc) <- determineTuning (optTuning opts) program-+    forInputOutputs server tuning_desc (InputOutputs entry_name runs) = do       putStr $ inBold $ "\nResults for " ++ program' ++ tuning_desc ++ ":\n"-      let opts' =-            opts-              { optExtraOptions =-                  optExtraOptions opts ++ tuning_opts-              }       BenchResult program' . catMaybes-        <$> mapM (runBenchmarkCase opts' futhark program entry_name pad_to) runs+        <$> mapM (runBenchmarkCase server opts futhark program entry_name pad_to) runs       where         program' =           if entry_name == "main"@@ -188,9 +202,7 @@ runOptions :: (Int -> IO ()) -> BenchOptions -> RunOptions runOptions f opts =   RunOptions-    { runRunner = optRunner opts,-      runRuns = optRuns opts,-      runExtraOptions = optExtraOptions opts,+    { runRuns = optRuns opts,       runTimeout = optTimeout opts,       runVerbose = optVerbose opts,       runResultAction = Just f@@ -254,6 +266,7 @@       ((maxinum runtimes / avg - 1) * 100)  runBenchmarkCase ::+  Server ->   BenchOptions ->   FutharkExe ->   FilePath ->@@ -261,12 +274,12 @@   Int ->   TestRun ->   IO (Maybe DataResult)-runBenchmarkCase _ _ _ _ _ (TestRun _ _ RunTimeFailure {} _ _) =+runBenchmarkCase _ _ _ _ _ _ (TestRun _ _ RunTimeFailure {} _ _) =   return Nothing -- Not our concern, we are not a testing tool.-runBenchmarkCase opts _ _ _ _ (TestRun tags _ _ _ _)+runBenchmarkCase _ opts _ _ _ _ (TestRun tags _ _ _ _)   | any (`elem` tags) $ optExcludeCase opts =     return Nothing-runBenchmarkCase opts futhark program entry pad_to tr@(TestRun _ input_spec (Succeeds expected_spec) _ dataset_desc) = do+runBenchmarkCase server opts futhark program entry pad_to tr@(TestRun _ input_spec (Succeeds expected_spec) _ dataset_desc) = do   prompt <- mkProgressPrompt (optRuns opts) pad_to dataset_desc    -- Report the dataset name before running the program, so that if an@@ -275,6 +288,7 @@    res <-     benchmarkDataset+      server       (runOptions (prompt . Just) opts)       futhark       program@@ -297,7 +311,20 @@         putStr $ descString dataset_desc pad_to        reportResult runtimes-      return $ Just $ DataResult dataset_desc $ Right (runtimes, errout)+      Result runtimes (getMemoryUsage errout) errout+        & Right+        & DataResult dataset_desc+        & Just+        & return++getMemoryUsage :: T.Text -> M.Map T.Text Int+getMemoryUsage t =+  foldMap matchMap $ T.lines t+  where+    mem_regex = "Peak memory usage for space '([^']+)': ([0-9]+) bytes." :: T.Text+    matchMap line = case (line =~ mem_regex :: (T.Text, T.Text, T.Text, [T.Text])) of+      (_, _, _, [device, bytes]) -> M.singleton device (read $ T.unpack bytes)+      _ -> mempty  commandLineOptions :: [FunOptDescr BenchOptions] commandLineOptions =
src/Futhark/CLI/Check.hs view
@@ -7,7 +7,6 @@ import Futhark.Util.Options import Futhark.Util.Pretty (pretty) import Language.Futhark.Warnings-import System.Console.GetOpt import System.IO  newtype CheckConfig = CheckConfig {checkWarn :: Bool}
src/Futhark/CLI/Dataset.hs view
@@ -11,8 +11,8 @@ import qualified Data.Map.Strict as M import qualified Data.Text as T import Data.Vector.Generic (freeze)-import qualified Data.Vector.Unboxed as UVec-import qualified Data.Vector.Unboxed.Mutable as UMVec+import qualified Data.Vector.Storable as SVec+import qualified Data.Vector.Storable.Mutable as USVec import Data.Word import Futhark.Test.Values import Futhark.Util.Options@@ -26,7 +26,6 @@     Value,     ValueType,   )-import System.Console.GetOpt import System.Exit import System.IO import System.Random.PCG (Variate, initialize, uniformR)@@ -171,17 +170,17 @@   Either String (RandomConfiguration -> OutputFormat -> Word64 -> IO ()) tryMakeGenerator t   | Just vs <- readValues $ BS.pack t =-    return $ \_ fmt _ -> mapM_ (putValue fmt) vs+    return $ \_ fmt _ -> mapM_ (outValue fmt) vs   | otherwise = do     t' <- toValueType =<< either (Left . show) Right (parseType name (T.pack t))     return $ \conf fmt seed -> do       let v = randomValue conf t' seed-      putValue fmt v+      outValue fmt v   where     name = "option " ++ t-    putValue Text = putStrLn . pretty-    putValue Binary = BS.putStr . Bin.encode-    putValue Type = putStrLn . pretty . valueType+    outValue Text = putStrLn . pretty+    outValue Binary = BS.putStr . Bin.encode+    outValue Type = putStrLn . pretty . valueType  toValueType :: UncheckedTypeExp -> Either String ValueType toValueType TETuple {} = Left "Cannot handle tuples yet."@@ -282,9 +281,9 @@     gen range final = randomVector (range conf) final ds seed  randomVector ::-  (UMVec.Unbox v, Variate v) =>+  (SVec.Storable v, Variate v) =>   Range v ->-  (UVec.Vector Int -> UVec.Vector v -> Value) ->+  (SVec.Vector Int -> SVec.Vector v -> Value) ->   [Int] ->   Word64 ->   Value@@ -292,15 +291,15 @@   -- USe some nice impure computation where we can preallocate a   -- vector of the desired size, populate it via the random number   -- generator, and then finally reutrn a frozen binary vector.-  arr <- UMVec.new n+  arr <- USVec.new n   g <- initialize 6364136223846793006 seed   let fill i         | i < n = do           v <- uniformR range g-          UMVec.write arr i v+          USVec.write arr i v           fill $! i + 1         | otherwise =-          final (UVec.fromList ds) <$> freeze arr+          final (SVec.fromList ds) . SVec.convert <$> freeze arr   fill 0   where     n = product ds
src/Futhark/CLI/Dev.hs view
@@ -50,9 +50,9 @@ import Futhark.Util.Log import Futhark.Util.Options import qualified Futhark.Util.Pretty as PP+import Language.Futhark.Core (nameFromString) import Language.Futhark.Parser (parseFuthark) import qualified Language.SexpGrammar as Sexp-import System.Console.GetOpt import System.Exit import System.FilePath import System.IO@@ -474,6 +474,19 @@       ["safe"]       (NoArg $ Right $ changeFutharkConfig $ \opts -> opts {futharkSafe = True})       "Ignore 'unsafe'.",+    Option+      []+      ["entry-points"]+      ( ReqArg+          ( \arg -> Right $+              changeFutharkConfig $ \opts ->+                opts+                  { futharkEntryPoints = nameFromString arg : futharkEntryPoints opts+                  }+          )+          "NAME"+      )+      "Treat this function as an additional entry point.",     typedPassOption soacsProg Seq firstOrderTransform "f",     soacsPassOption fuseSOACs "o",     soacsPassOption inlineFunctions [],@@ -561,6 +574,8 @@               . intersperse ""               . map (if futharkPrintAST config then show else pretty) +          readProgram' = readProgram (futharkEntryPoints (futharkConfig config)) file+       case futharkPipeline config of         PrettyPrint -> liftIO $ do           maybe_prog <- parseFuthark file <$> T.readFile file@@ -570,7 +585,7 @@               | futharkPrintAST config -> print prog               | otherwise -> putStrLn $ pretty prog         TypeCheck -> do-          (_, imports, _) <- readProgram file+          (_, imports, _) <- readProgram'           liftIO $             forM_ (map snd imports) $ \fm ->               putStrLn $@@ -578,17 +593,17 @@                   then show $ fileProg fm                   else pretty $ fileProg fm         Defunctorise -> do-          (_, imports, src) <- readProgram file+          (_, imports, src) <- readProgram'           liftIO $ p $ evalState (Defunctorise.transformProg imports) src         Monomorphise -> do-          (_, imports, src) <- readProgram file+          (_, imports, src) <- readProgram'           liftIO $             p $               flip evalState src $                 Defunctorise.transformProg imports                   >>= Monomorphise.transformProg         LiftLambdas -> do-          (_, imports, src) <- readProgram file+          (_, imports, src) <- readProgram'           liftIO $             p $               flip evalState src $@@ -596,7 +611,7 @@                   >>= Monomorphise.transformProg                   >>= LiftLambdas.transformProg         Defunctionalise -> do-          (_, imports, src) <- readProgram file+          (_, imports, src) <- readProgram'           liftIO $             p $               flip evalState src $
src/Futhark/CLI/Doc.hs view
@@ -17,7 +17,6 @@ import Futhark.Util (directoryContents, trim) import Futhark.Util.Options import Language.Futhark.Syntax (DocComment (..), progDoc)-import System.Console.GetOpt import System.Directory (createDirectoryIfMissing) import System.Exit import System.FilePath@@ -50,7 +49,7 @@             liftIO $ do               mapM_ (hPutStrLn stderr . ("Found source file " <>)) files               hPutStrLn stderr "Reading files..."-          (_w, imports, _vns) <- readLibrary files+          (_w, imports, _vns) <- readLibrary [] files           liftIO $ printDecs config outdir files $ nubBy sameImport imports      sameImport (x, _) (y, _) = x == y
+ src/Futhark/CLI/Literate.hs view
@@ -0,0 +1,742 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++-- | @futhark literate@+module Futhark.CLI.Literate (main) where++import Control.Monad.Except+import Data.Bifunctor (bimap, first, second)+import Data.Bits+import qualified Data.ByteString.Char8 as BS+import Data.Char+import Data.Functor+import Data.Int (Int64)+import Data.List (foldl', transpose)+import qualified Data.Map as M+import Data.Maybe+import qualified Data.Set as S+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T+import qualified Data.Vector.Storable as SVec+import Data.Void+import Futhark.Script+import Futhark.Server+import Futhark.Test+import Futhark.Test.Values+import Futhark.Util (nubOrd, runProgramWithExitCode)+import Futhark.Util.Options+import Futhark.Util.Pretty (prettyText, prettyTextOneLine)+import qualified Futhark.Util.Pretty as PP+import System.Directory+  ( createDirectoryIfMissing,+    removeFile,+    removePathForcibly,+  )+import System.Environment (getExecutablePath)+import System.Exit+import System.FilePath+import System.IO+import System.IO.Temp (withSystemTempDirectory, withSystemTempFile)+import Text.Megaparsec hiding (failure, token)+import Text.Megaparsec.Char+import Text.Printf++data AnimParams = AnimParams+  { animFPS :: Maybe Int,+    animLoop :: Maybe Bool,+    animAutoplay :: Maybe Bool,+    animFormat :: Maybe T.Text+  }+  deriving (Show)++defaultAnimParams :: AnimParams+defaultAnimParams =+  AnimParams+    { animFPS = Nothing,+      animLoop = Nothing,+      animAutoplay = Nothing,+      animFormat = Nothing+    }++data Directive+  = DirectiveRes Exp+  | DirectiveBrief Directive+  | DirectiveCovert Directive+  | DirectiveImg Exp+  | DirectivePlot Exp (Maybe (Int, Int))+  | DirectiveGnuplot Exp T.Text+  | DirectiveAnim Exp AnimParams+  deriving (Show)++varsInDirective :: Directive -> S.Set EntryName+varsInDirective (DirectiveRes e) = varsInExp e+varsInDirective (DirectiveBrief d) = varsInDirective d+varsInDirective (DirectiveCovert d) = varsInDirective d+varsInDirective (DirectiveImg e) = varsInExp e+varsInDirective (DirectivePlot e _) = varsInExp e+varsInDirective (DirectiveGnuplot e _) = varsInExp e+varsInDirective (DirectiveAnim e _) = varsInExp e++pprDirective :: Bool -> Directive -> PP.Doc+pprDirective _ (DirectiveRes e) =+  "> " <> PP.align (PP.ppr e)+pprDirective _ (DirectiveBrief f) =+  pprDirective False f+pprDirective _ (DirectiveCovert f) =+  pprDirective False f+pprDirective _ (DirectiveImg e) =+  "> :img " <> PP.align (PP.ppr e)+pprDirective True (DirectivePlot e (Just (h, w))) =+  PP.stack+    [ "> :plot2d " <> PP.ppr e <> ";",+      "size: (" <> PP.ppr w <> "," <> PP.ppr h <> ")"+    ]+pprDirective _ (DirectivePlot e _) =+  "> :plot2d " <> PP.align (PP.ppr e)+pprDirective True (DirectiveGnuplot e script) =+  PP.stack $+    "> :gnuplot " <> PP.align (PP.ppr e) <> ";" :+    map PP.strictText (T.lines script)+pprDirective False (DirectiveGnuplot e _) =+  "> :gnuplot " <> PP.align (PP.ppr e)+pprDirective False (DirectiveAnim e _) =+  "> :anim " <> PP.align (PP.ppr e)+pprDirective True (DirectiveAnim e params) =+  "> :anim " <> PP.ppr e+    <> if null params' then mempty else PP.stack $ ";" : params'+  where+    params' =+      catMaybes+        [ p "fps" animFPS PP.ppr,+          p "loop" animLoop ppBool,+          p "autoplay" animAutoplay ppBool,+          p "format" animFormat PP.strictText+        ]+    ppBool b = if b then "true" else "false"+    p s f ppr = do+      x <- f params+      Just $ s <> ": " <> ppr x++instance PP.Pretty Directive where+  ppr = pprDirective True++data Block+  = BlockCode T.Text+  | BlockComment T.Text+  | BlockDirective Directive+  deriving (Show)++varsInScripts :: [Block] -> S.Set EntryName+varsInScripts = foldMap varsInBlock+  where+    varsInBlock (BlockDirective d) = varsInDirective d+    varsInBlock BlockCode {} = mempty+    varsInBlock BlockComment {} = mempty++type Parser = Parsec Void T.Text++postlexeme :: Parser ()+postlexeme = void $ hspace *> optional (try $ eol *> "-- " *> postlexeme)++lexeme :: Parser a -> Parser a+lexeme p = p <* postlexeme++token :: T.Text -> Parser ()+token = void . try . lexeme . string++parseInt :: Parser Int+parseInt = lexeme $ read <$> some (satisfy isDigit)++restOfLine :: Parser T.Text+restOfLine = takeWhileP Nothing (/= '\n') <* eol++parseBlockComment :: Parser T.Text+parseBlockComment = T.unlines <$> some line+  where+    line = ("-- " *> restOfLine) <|> ("--" *> eol $> "")++parseTestBlock :: Parser T.Text+parseTestBlock =+  T.unlines <$> ((:) <$> header <*> remainder)+  where+    header = "-- ==" <* eol+    remainder = map ("-- " <>) . T.lines <$> parseBlockComment++parseBlockCode :: Parser T.Text+parseBlockCode = T.unlines . noblanks <$> some line+  where+    noblanks = reverse . dropWhile T.null . reverse . dropWhile T.null+    line = try (notFollowedBy "--") *> restOfLine++parsePlotParams :: Parser (Maybe (Int, Int))+parsePlotParams =+  optional $+    ";" *> hspace *> eol *> token "-- size:"+      *> token "("+      *> ((,) <$> parseInt <* token "," <*> parseInt) <* token ")"++parseAnimParams :: Parser AnimParams+parseAnimParams =+  fmap (fromMaybe defaultAnimParams) $+    optional $ ";" *> hspace *> eol *> "-- " *> parseParams defaultAnimParams+  where+    parseParams params =+      choice+        [ choice+            [pLoop params, pFPS params, pAutoplay params, pFormat params]+            >>= parseParams,+          pure params+        ]+    parseBool = token "true" $> True <|> token "false" $> False+    pLoop params = do+      token "loop:"+      b <- parseBool+      pure params {animLoop = Just b}+    pFPS params = do+      token "fps:"+      fps <- parseInt+      pure params {animFPS = Just fps}+    pAutoplay params = do+      token "autoplay:"+      b <- parseBool+      pure params {animAutoplay = Just b}+    pFormat params = do+      token "format:"+      s <- lexeme $ takeWhileP Nothing (not . isSpace)+      pure params {animFormat = Just s}++parseBlock :: Parser Block+parseBlock =+  choice+    [ token "-- >" $> BlockDirective <*> parseDirective <* void eol,+      BlockCode <$> parseTestBlock,+      BlockCode <$> parseBlockCode,+      BlockComment <$> parseBlockComment+    ]+  where+    parseDirective =+      choice+        [ DirectiveRes <$> parseExp postlexeme,+          directiveName "covert" $> DirectiveCovert+            <*> parseDirective,+          directiveName "brief" $> DirectiveBrief+            <*> parseDirective,+          directiveName "img" $> DirectiveImg+            <*> parseExp postlexeme,+          directiveName "plot2d" $> DirectivePlot+            <*> parseExp postlexeme+            <*> parsePlotParams,+          directiveName "gnuplot" $> DirectiveGnuplot+            <*> parseExp postlexeme+            <*> (";" *> hspace *> eol *> parseBlockComment),+          directiveName "anim" $> DirectiveAnim+            <*> parseExp postlexeme+            <*> parseAnimParams+        ]+    directiveName s = try $ token (":" <> s)++parseProg :: FilePath -> T.Text -> Either T.Text [Block]+parseProg fname s =+  either (Left . T.pack . errorBundlePretty) Right $+    parse (many parseBlock <* eof) fname s++parseProgFile :: FilePath -> IO [Block]+parseProgFile prog = do+  pres <- parseProg prog <$> T.readFile prog+  case pres of+    Left err -> do+      T.hPutStr stderr err+      exitFailure+    Right script ->+      pure script++type ScriptM = ExceptT T.Text IO++withTempFile :: (FilePath -> ScriptM a) -> ScriptM a+withTempFile f =+  join . liftIO . withSystemTempFile "futhark-literate" $ \tmpf tmpf_h -> do+    hClose tmpf_h+    either throwError pure <$> runExceptT (f tmpf)++withTempDir :: (FilePath -> ScriptM a) -> ScriptM a+withTempDir f =+  join . liftIO . withSystemTempDirectory "futhark-literate" $ \dir ->+    either throwError pure <$> runExceptT (f dir)++ppmHeader :: Int -> Int -> BS.ByteString+ppmHeader h w =+  "P6\n" <> BS.pack (show w) <> " " <> BS.pack (show h) <> "\n255\n"++rgbIntToImg ::+  (Integral a, Bits a, SVec.Storable a) =>+  Int ->+  Int ->+  SVec.Vector a ->+  BS.ByteString+rgbIntToImg h w bytes =+  ppmHeader h w <> fst (BS.unfoldrN (h * w * 3) byte 0)+  where+    getChan word chan =+      (word `shiftR` (chan * 8)) .&. 0xFF+    byte i =+      Just+        ( chr . max 0 . fromIntegral $+            getChan (bytes SVec.! (i `div` 3)) (2 - (i `mod` 3)),+          i + 1+        )++greyFloatToImg ::+  (RealFrac a, SVec.Storable a) =>+  Int ->+  Int ->+  SVec.Vector a ->+  BS.ByteString+greyFloatToImg h w bytes =+  ppmHeader h w <> fst (BS.unfoldrN (h * w * 3) byte 0)+  where+    byte i =+      Just (chr . max 0 $ round (bytes SVec.! (i `div` 3)) * 255, i + 1)++valueToPPM :: Value -> Maybe BS.ByteString+valueToPPM v@(Word32Value _ bytes)+  | [h, w] <- valueShape v =+    Just $ rgbIntToImg h w bytes+valueToPPM v@(Int32Value _ bytes)+  | [h, w] <- valueShape v =+    Just $ rgbIntToImg h w bytes+valueToPPM v@(Float32Value _ bytes)+  | [h, w] <- valueShape v =+    Just $ greyFloatToImg h w bytes+valueToPPM v@(Float64Value _ bytes)+  | [h, w] <- valueShape v =+    Just $ greyFloatToImg h w bytes+valueToPPM _ = Nothing++valueToPPMs :: Value -> Maybe [BS.ByteString]+valueToPPMs = mapM valueToPPM . valueElems++system :: FilePath -> [String] -> T.Text -> ScriptM T.Text+system prog options input = do+  res <- liftIO $ runProgramWithExitCode prog options $ T.encodeUtf8 input+  case res of+    Left err ->+      throwError $ prog' <> " failed: " <> T.pack (show err)+    Right (ExitSuccess, stdout_t, _) ->+      pure $ T.pack stdout_t+    Right (ExitFailure code', _, stderr_t) ->+      throwError $+        prog' <> " failed with exit code "+          <> T.pack (show code')+          <> " and stderr:\n"+          <> T.pack stderr_t+  where+    prog' = "'" <> T.pack prog <> "'"++ppmToPNG :: FilePath -> ScriptM FilePath+ppmToPNG ppm = do+  void $ system "convert" [ppm, png] mempty+  pure png+  where+    png = ppm `replaceExtension` "png"++formatDataForGnuplot :: [Value] -> T.Text+formatDataForGnuplot = T.unlines . map line . transpose . map valueElems+  where+    line = T.unwords . map prettyText++imgBlock :: FilePath -> T.Text+imgBlock f = "\n\n![](" <> T.pack f <> ")\n\n"++videoBlock :: AnimParams -> FilePath -> T.Text+videoBlock opts f = "\n\n![](" <> T.pack f <> ")" <> opts' <> "\n\n"+  where+    opts'+      | all T.null [loop, autoplay] =+        mempty+      | otherwise =+        "{" <> T.unwords [loop, autoplay] <> "}"+    boolOpt s prop+      | Just b <- prop opts =+        if b then s <> "=\"true\"" else s <> "=\"false\""+      | otherwise =+        mempty+    loop = boolOpt "loop" animLoop+    autoplay = boolOpt "autoplay" animAutoplay++plottable :: CompoundValue -> Maybe [Value]+plottable (ValueTuple vs) = do+  (vs', ns') <- unzip <$> mapM inspect vs+  guard $ length (nubOrd ns') == 1+  Just vs'+  where+    inspect (ValueAtom v)+      | [n] <- valueShape v = Just (v, n)+    inspect _ = Nothing+plottable _ = Nothing++withGnuplotData ::+  [(T.Text, T.Text)] ->+  [(T.Text, [Value])] ->+  ([T.Text] -> [T.Text] -> ScriptM a) ->+  ScriptM a+withGnuplotData sets [] cont = uncurry cont $ unzip $ reverse sets+withGnuplotData sets ((f, vs) : xys) cont =+  withTempFile $ \fname -> do+    liftIO $ T.writeFile fname $ formatDataForGnuplot vs+    withGnuplotData ((f, f <> "='" <> T.pack fname <> "'") : sets) xys cont++processDirective :: FilePath -> ScriptServer -> Int -> Directive -> ScriptM T.Text+processDirective imgdir server i (DirectiveBrief d) =+  processDirective imgdir server i d+processDirective imgdir server i (DirectiveCovert d) =+  processDirective imgdir server i d+processDirective _ server _ (DirectiveRes e) = do+  vs <- evalExpToGround server e+  pure $+    T.unlines+      [ "",+        "```",+        prettyText vs,+        "```",+        ""+      ]+--+processDirective imgdir server i (DirectiveImg e) = do+  vs <- evalExpToGround server e+  case vs of+    ValueAtom v+      | Just ppm <- valueToPPM v -> do+        let ppmfile = imgdir </> "img" <> show i <.> ".ppm"+        liftIO $ createDirectoryIfMissing True imgdir+        liftIO $ BS.writeFile ppmfile ppm+        pngfile <- ppmToPNG ppmfile+        liftIO $ removeFile ppmfile+        pure $ imgBlock pngfile+    _ ->+      throwError $+        "Cannot create image from value of type "+          <> prettyText (fmap valueType vs)+--+processDirective imgdir server i (DirectivePlot e size) = do+  v <- evalExpToGround server e+  case v of+    _+      | Just vs <- plottable2d v ->+        plotWith [(Nothing, vs)]+    ValueRecord m+      | Just m' <- traverse plottable2d m ->+        plotWith $ map (first Just) $ M.toList m'+    _ ->+      throwError $+        "Cannot plot value of type " <> prettyText (fmap valueType v)+  where+    plottable2d v = do+      [x, y] <- plottable v+      Just [x, y]++    pngfile = imgdir </> "plot" <> show i <.> ".png"++    tag (Nothing, xys) j = ("data" <> T.pack (show (j :: Int)), xys)+    tag (Just f, xys) _ = (f, xys)++    plotWith xys = withGnuplotData [] (zipWith tag xys [0 ..]) $ \fs sets -> do+      liftIO $ createDirectoryIfMissing True imgdir+      let size' = T.pack $+            case size of+              Nothing -> "500,500"+              Just (w, h) -> show w ++ "," ++ show h+          plotCmd f title =+            let title' = case title of+                  Nothing -> "notitle"+                  Just x -> "title '" <> x <> "'"+             in f <> " " <> title' <> " with lines"+          cmds = T.intercalate ", " (zipWith plotCmd fs (map fst xys))+          script =+            T.unlines+              [ "set terminal png size " <> size' <> " enhanced",+                "set output '" <> T.pack pngfile <> "'",+                "set key outside",+                T.unlines sets,+                "plot " <> cmds+              ]+      void $ system "gnuplot" [] script+      pure $ imgBlock pngfile+--+processDirective imgdir server i (DirectiveGnuplot e script) = do+  vs <- evalExpToGround server e+  case vs of+    ValueRecord m+      | Just m' <- traverse plottable m ->+        plotWith $ M.toList m'+    _ ->+      throwError $+        "Cannot plot value of type " <> prettyText (fmap valueType vs)+  where+    pngfile = imgdir </> "plot" <> show i <.> ".png"++    plotWith xys = withGnuplotData [] xys $ \_ sets -> do+      liftIO $ createDirectoryIfMissing True imgdir+      let script' =+            T.unlines+              [ "set terminal png enhanced",+                "set output '" <> T.pack pngfile <> "'",+                T.unlines sets,+                script+              ]+      void $ system "gnuplot" [] script'+      pure $ imgBlock pngfile+--+processDirective imgdir server i (DirectiveAnim e params) = do+  when (format `notElem` ["webm", "gif"]) $+    throwError $ "Unknown animation format: " <> format++  v <- evalExp server e+  let nope =+        throwError $+          "Cannot animate value of type " <> prettyText (fmap scriptValueType v)+  case v of+    ValueAtom SValue {} -> do+      ValueAtom arr <- getExpValue server v+      case valueToPPMs arr of+        Nothing -> nope+        Just ppms ->+          withTempDir $ \dir -> do+            zipWithM_ (writePPMFile dir) [0 ..] ppms+            ppmsToVideo dir+    ValueTuple [stepfun, initial, num_frames]+      | ValueAtom (SFun stepfun' _ [_, _] closure) <- stepfun,+        ValueAtom (SValue _ _) <- initial,+        ValueAtom (SValue "i64" _) <- num_frames -> do+        Just (ValueAtom num_frames') <-+          mapM getValue <$> getExpValue server num_frames+        withTempDir $ \dir -> do+          let num_frames_int = fromIntegral (num_frames' :: Int64)+          renderFrames dir (stepfun', map ValueAtom closure) initial num_frames_int+          ppmsToVideo dir+    _ ->+      nope++  when (animFormat params == Just "gif") $ do+    void $ system "ffmpeg" ["-i", webmfile, giffile] mempty+    liftIO $ removeFile webmfile++  pure $ videoBlock params animfile+  where+    framerate = fromMaybe 30 $ animFPS params+    format = fromMaybe "webm" $ animFormat params+    webmfile = imgdir </> "anim" <> show i <.> "webm"+    giffile = imgdir </> "anim" <> show i <.> "gif"+    ppmfile dir j = dir </> printf "frame%010d.ppm" (j :: Int)+    animfile = imgdir </> "anim" <> show i <.> T.unpack format++    renderFrames dir (stepfun, closure) initial num_frames =+      foldM_ frame initial [0 .. num_frames -1]+      where+        frame old_state j = do+          v <- evalExp server . Call stepfun . map valueToExp $ closure ++ [old_state]+          freeValue server old_state++          let nope =+                throwError $+                  "Cannot handle step function return type: "+                    <> prettyText (fmap scriptValueType v)++          case v of+            ValueTuple [arr_v@(ValueAtom SValue {}), new_state] -> do+              ValueAtom arr <- getExpValue server arr_v+              freeValue server arr_v+              case valueToPPM arr of+                Nothing -> nope+                Just ppm -> do+                  writePPMFile dir j ppm+                  pure new_state+            _ -> nope++    ppmsToVideo dir = do+      liftIO $ createDirectoryIfMissing True imgdir+      void $+        system+          "ffmpeg"+          [ "-y",+            "-r",+            show framerate,+            "-i",+            dir </> "frame%010d.ppm",+            "-c:v",+            "libvpx-vp9",+            "-pix_fmt",+            "yuv420p",+            "-b:v",+            "2M",+            webmfile+          ]+          mempty++    writePPMFile dir j ppm = do+      let fname = ppmfile dir j+      liftIO $ BS.writeFile fname ppm++-- Did this script block succeed or fail?+data Failure = Failure | Success+  deriving (Eq, Ord, Show)++data Options = Options+  { scriptBackend :: String,+    scriptFuthark :: Maybe FilePath,+    scriptExtraOptions :: [String],+    scriptCompilerOptions :: [String],+    scriptSkipCompilation :: Bool,+    scriptOutput :: Maybe FilePath,+    scriptVerbose :: Int,+    scriptStopOnError :: Bool+  }++initialOptions :: Options+initialOptions =+  Options+    { scriptBackend = "c",+      scriptFuthark = Nothing,+      scriptExtraOptions = [],+      scriptCompilerOptions = [],+      scriptSkipCompilation = False,+      scriptOutput = Nothing,+      scriptVerbose = 0,+      scriptStopOnError = False+    }++processBlock :: Options -> FilePath -> ScriptServer -> Int -> Block -> IO (Failure, T.Text)+processBlock _ _ _ _ (BlockCode code)+  | T.null code = pure (Success, "\n")+  | otherwise = pure (Success, "\n```futhark\n" <> code <> "```\n\n")+processBlock _ _ _ _ (BlockComment text) =+  pure (Success, text)+processBlock opts imgdir server i (BlockDirective directive) = do+  when (scriptVerbose opts > 0) $+    T.hPutStrLn stderr . prettyText $+      "Processing " <> PP.align (PP.ppr directive) <> "..."+  let prompt = case directive of+        DirectiveCovert _ -> mempty+        DirectiveBrief _ ->+          "```\n" <> prettyText (pprDirective False directive) <> "\n```\n"+        _ ->+          "```\n" <> prettyText (pprDirective True directive) <> "\n```\n"+  r <- runExceptT $ processDirective imgdir server i directive+  second (prompt <>) <$> case r of+    Left err -> failed err+    Right t -> pure (Success, t)+  where+    failed err = do+      let message = prettyTextOneLine directive <> " failed:\n" <> err <> "\n"+      liftIO $ T.hPutStr stderr message+      when (scriptStopOnError opts) exitFailure+      pure+        ( Failure,+          T.unlines ["**FAILED**", "```", err, "```"]+        )++processScript :: Options -> FilePath -> ScriptServer -> [Block] -> IO (Failure, T.Text)+processScript opts imgdir server script =+  bimap (foldl' min Success) mconcat . unzip+    <$> zipWithM (processBlock opts imgdir server) [0 ..] script++commandLineOptions :: [FunOptDescr Options]+commandLineOptions =+  [ Option+      []+      ["backend"]+      ( ReqArg+          (\backend -> Right $ \config -> config {scriptBackend = backend})+          "PROGRAM"+      )+      "The compiler used (defaults to 'c').",+    Option+      []+      ["futhark"]+      ( ReqArg+          (\prog -> Right $ \config -> config {scriptFuthark = Just prog})+          "PROGRAM"+      )+      "The binary used for operations (defaults to same binary as 'futhark script').",+    Option+      "p"+      ["pass-option"]+      ( ReqArg+          ( \opt ->+              Right $ \config ->+                config {scriptExtraOptions = opt : scriptExtraOptions config}+          )+          "OPT"+      )+      "Pass this option to programs being run.",+    Option+      []+      ["pass-compiler-option"]+      ( ReqArg+          ( \opt ->+              Right $ \config ->+                config {scriptCompilerOptions = opt : scriptCompilerOptions config}+          )+          "OPT"+      )+      "Pass this option to the compiler.",+    Option+      []+      ["skip-compilation"]+      (NoArg $ Right $ \config -> config {scriptSkipCompilation = True})+      "Use already compiled program.",+    Option+      "v"+      ["verbose"]+      (NoArg $ Right $ \config -> config {scriptVerbose = scriptVerbose config + 1})+      "Enable logging.  Pass multiple times for more.",+    Option+      "o"+      ["output"]+      (ReqArg (\opt -> Right $ \config -> config {scriptOutput = Just opt}) "FILE")+      "Enable logging.  Pass multiple times for more.",+    Option+      []+      ["stop-on-error"]+      (NoArg $ Right $ \config -> config {scriptStopOnError = True})+      "Stop and do not produce output file if any directive fails."+  ]++-- | Run @futhark script@.+main :: String -> [String] -> IO ()+main = mainWithOptions initialOptions commandLineOptions "program" $ \args opts ->+  case args of+    [prog] -> Just $ do+      futhark <- maybe getExecutablePath return $ scriptFuthark opts++      script <- parseProgFile prog++      unless (scriptSkipCompilation opts) $ do+        let entryOpt v = "--entry=" ++ T.unpack v+            compile_options =+              "--server" :+              map entryOpt (S.toList (varsInScripts script))+                ++ scriptCompilerOptions opts+        when (scriptVerbose opts > 0) $+          T.hPutStrLn stderr $ "Compiling " <> T.pack prog <> "..."+        cres <-+          runExceptT $+            compileProgram compile_options (FutharkExe futhark) (scriptBackend opts) prog+        case cres of+          Left err -> do+            mapM_ (T.hPutStrLn stderr) err+            exitFailure+          Right _ ->+            pure ()++      let mdfile = fromMaybe (prog `replaceExtension` "md") $ scriptOutput opts+          imgdir = dropExtension mdfile <> "-img"+          run_options = scriptExtraOptions opts++      removePathForcibly imgdir++      withScriptServer ("." </> dropExtension prog) run_options $ \server -> do+        (failure, md) <- processScript opts imgdir server script+        when (failure == Failure) exitFailure+        T.writeFile mdfile md+    _ -> Nothing
src/Futhark/CLI/Multicore.hs view
@@ -1,11 +1,13 @@ {-# LANGUAGE FlexibleContexts #-} +-- | @futhark multicore@ module Futhark.CLI.Multicore (main) where  import Futhark.Actions (compileMulticoreAction) import Futhark.Compiler.CLI import Futhark.Passes (multicorePipeline) +-- | Run @futhark multicore@. main :: String -> [String] -> IO () main = compilerMain   ()
src/Futhark/CLI/Pkg.hs view
@@ -21,7 +21,6 @@ import Futhark.Util (directoryContents, maxinum) import Futhark.Util.Log import Futhark.Util.Options-import System.Console.GetOpt import System.Directory import System.Environment import System.Exit
src/Futhark/CLI/PyOpenCL.hs view
@@ -21,14 +21,14 @@   $ \fcfg () mode outpath prog -> do     let class_name =           case mode of-            ToLibrary -> Just $ takeBaseName outpath-            ToExecutable -> Nothing-    pyprog <- handleWarnings fcfg $ PyOpenCL.compileProg class_name prog+            ToLibrary -> takeBaseName outpath+            _ -> "internal"+    pyprog <- handleWarnings fcfg $ PyOpenCL.compileProg mode class_name prog      case mode of       ToLibrary ->         liftIO $ writeFile (outpath `addExtension` "py") pyprog-      ToExecutable -> liftIO $ do+      _ -> liftIO $ do         writeFile outpath pyprog         perms <- liftIO $ getPermissions outpath         setPermissions outpath $ setOwnerExecutable True perms
src/Futhark/CLI/Python.hs view
@@ -21,14 +21,14 @@   $ \fcfg () mode outpath prog -> do     let class_name =           case mode of-            ToLibrary -> Just $ takeBaseName outpath-            ToExecutable -> Nothing-    pyprog <- handleWarnings fcfg $ SequentialPy.compileProg class_name prog+            ToLibrary -> takeBaseName outpath+            _ -> "internal"+    pyprog <- handleWarnings fcfg $ SequentialPy.compileProg mode class_name prog      case mode of       ToLibrary ->         liftIO $ writeFile (outpath `addExtension` "py") pyprog-      ToExecutable -> liftIO $ do+      _ -> liftIO $ do         writeFile outpath pyprog         perms <- liftIO $ getPermissions outpath         setPermissions outpath $ setOwnerExecutable True perms
src/Futhark/CLI/REPL.hs view
@@ -31,7 +31,6 @@ import qualified Language.Futhark.Semantic as T import qualified Language.Futhark.TypeChecker as T import NeatInterpolation (text)-import System.Console.GetOpt import qualified System.Console.Haskeline as Haskeline import System.Directory import System.FilePath@@ -158,7 +157,7 @@   (imports, src, tenv, ienv) <- case maybe_file of     Nothing -> do       -- Load the builtins through the type checker.-      (_, imports, src) <- badOnLeft show =<< runExceptT (readLibrary [])+      (_, imports, src) <- badOnLeft show =<< runExceptT (readLibrary [] [])       -- Then into the interpreter.       ienv <-         foldM@@ -183,7 +182,7 @@       (ws, imports, src) <-         badOnLeft show           =<< liftIO-            ( runExceptT (readProgram file)+            ( runExceptT (readProgram [] file)                 `catch` \(err :: IOException) ->                   return (externalErrorS (show err))             )
src/Futhark/CLI/Run.hs view
@@ -21,7 +21,6 @@ import Language.Futhark.Parser hiding (EOF) import qualified Language.Futhark.Semantic as T import qualified Language.Futhark.TypeChecker as T-import System.Console.GetOpt import System.Exit import System.FilePath import System.IO@@ -118,7 +117,7 @@   (ws, imports, src) <-     badOnLeft show       =<< liftIO-        ( runExceptT (readProgram file)+        ( runExceptT (readProgram [] file)             `catch` \(err :: IOException) ->               return (externalErrorS (show err))         )
src/Futhark/CLI/Test.hs view
@@ -18,7 +18,8 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.IO as T-import Futhark.Analysis.Metrics+import Futhark.Analysis.Metrics.Type+import Futhark.Server import Futhark.Test import Futhark.Util (fancyTerminal) import Futhark.Util.Console@@ -26,7 +27,6 @@ import Futhark.Util.Pretty (prettyText) import Futhark.Util.Table import System.Console.ANSI-import System.Console.GetOpt import qualified System.Console.Terminal.Size as Terminal import System.Environment import System.Exit@@ -49,8 +49,8 @@ runTestM :: TestM () -> IO TestResult runTestM = fmap (either Failure $ const Success) . runExceptT -io :: IO a -> TestM a-io = liftIO+liftExcept :: ExceptT T.Text IO a -> TestM a+liftExcept = either (E.throwError . pure) pure <=< liftIO . runExceptT  context :: T.Text -> TestM a -> TestM a context s = withExceptT $@@ -58,6 +58,9 @@     [] -> []     (e : es') -> (s <> ":\n" <> e) : es' +context1 :: Monad m => T.Text -> ExceptT T.Text m a -> ExceptT T.Text m a+context1 s = withExceptT $ \e -> s <> ":\n" <> e+ accErrors :: [TestM a] -> TestM [a] accErrors tests = do   eithers <- lift $ mapM runExceptT tests@@ -72,6 +75,31 @@   | Failure [T.Text]   deriving (Eq, Show) +pureTestResults :: IO [TestResult] -> TestM ()+pureTestResults m =+  mapM_ check =<< liftIO m+  where+    check Success = pure ()+    check (Failure err) = E.throwError err++withProgramServer :: FilePath -> FilePath -> [String] -> (Server -> IO [TestResult]) -> TestM ()+withProgramServer program runner extra_options f = do+  -- Explicitly prefixing the current directory is necessary for+  -- readProcessWithExitCode to find the binary when binOutputf has+  -- no path component.+  let binOutputf = dropExtension program+      binpath = "." </> binOutputf++      (to_run, to_run_args)+        | null runner = (binpath, extra_options)+        | otherwise = (runner, binpath : extra_options)++      prog_ctx =+        "Running " <> T.pack (unwords $ binpath : extra_options)++  context prog_ctx $+    pureTestResults $ liftIO $ withServer to_run to_run_args f+ data TestCase = TestCase   { _testCaseMode :: TestMode,     testCaseProgram :: FilePath,@@ -87,7 +115,7 @@   x `compare` y = testCaseProgram x `compare` testCaseProgram y  data RunResult-  = ErrorResult Int SBS.ByteString+  = ErrorResult T.Text   | SuccessResult [Value]  progNotFound :: T.Text -> T.Text@@ -108,9 +136,9 @@       check []   where     check opt = do-      futhark <- io $ maybe getExecutablePath return $ configFuthark programs+      futhark <- liftIO $ maybe getExecutablePath return $ configFuthark programs       let opts = ["dev"] ++ opt ++ ["--metrics", program]-      (code, output, err) <- io $ readProcessWithExitCode futhark opts ""+      (code, output, err) <- liftIO $ readProcessWithExitCode futhark opts ""       let output' = T.decodeUtf8 output       case code of         ExitSuccess@@ -152,187 +180,190 @@             <> T.decodeUtf8 futerr       | otherwise = return () +runInterpretedEntry :: FutharkExe -> FilePath -> InputOutputs -> TestM ()+runInterpretedEntry (FutharkExe futhark) program (InputOutputs entry run_cases) =+  let dir = takeDirectory program+      runInterpretedCase run@(TestRun _ inputValues _ index _) =+        unless ("compiled" `elem` runTags run) $+          context+            ( "Entry point: " <> entry+                <> "; dataset: "+                <> T.pack (runDescription run)+            )+            $ do+              input <- T.unlines . map prettyText <$> getValues (FutharkExe futhark) dir inputValues+              expectedResult' <- getExpectedResult (FutharkExe futhark) program entry run+              (code, output, err) <-+                liftIO $+                  readProcessWithExitCode futhark ["run", "-e", T.unpack entry, program] $+                    T.encodeUtf8 input+              case code of+                ExitFailure 127 ->+                  throwError $ progNotFound $ T.pack futhark+                _ ->+                  liftExcept $+                    compareResult entry index program expectedResult'+                      =<< runResult program code output err+   in accErrors_ $ map runInterpretedCase run_cases+ runTestCase :: TestCase -> TestM () runTestCase (TestCase mode program testcase progs) = do-  futhark <- io $ maybe getExecutablePath return $ configFuthark progs+  futhark <- liftIO $ maybe getExecutablePath return $ configFuthark progs+  let checkctx =+        mconcat+          [ "Type-checking with '",+            T.pack futhark,+            " check ",+            T.pack program,+            "'"+          ]   case testAction testcase of     CompileTimeFailure expected_error ->-      context-        ( mconcat-            [ "Type-checking with '",-              T.pack futhark,-              " check ",-              T.pack program,-              "'"-            ]-        )-        $ do-          (code, _, err) <--            io $ readProcessWithExitCode futhark ["check", program] ""-          case code of-            ExitSuccess -> throwError "Expected failure\n"-            ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark-            ExitFailure 1 -> throwError $ T.decodeUtf8 err-            ExitFailure _ -> checkError expected_error err+      context checkctx $ do+        (code, _, err) <-+          liftIO $ readProcessWithExitCode futhark ["check", program] ""+        case code of+          ExitSuccess -> throwError "Expected failure\n"+          ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+          ExitFailure 1 -> throwError $ T.decodeUtf8 err+          ExitFailure _ -> liftExcept $ checkError expected_error $ T.decodeUtf8 err     RunCases {} | mode == TypeCheck -> do       let options = ["check", program] ++ configExtraCompilerOptions progs-      context-        ( mconcat-            [ "Type-checking with '",-              T.pack futhark,-              " check ",-              T.pack program,-              "'"-            ]-        )-        $ do-          (code, _, err) <- io $ readProcessWithExitCode futhark options ""+      context checkctx $ do+        (code, _, err) <- liftIO $ readProcessWithExitCode futhark options "" -          case code of-            ExitSuccess -> return ()-            ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark-            ExitFailure _ -> throwError $ T.decodeUtf8 err+        case code of+          ExitSuccess -> return ()+          ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+          ExitFailure _ -> throwError $ T.decodeUtf8 err     RunCases ios structures warnings -> do       -- Compile up-front and reuse same executable for several entry points.       let backend = configBackend progs-          extra_options = configExtraCompilerOptions progs+          extra_compiler_options = configExtraCompilerOptions progs+       unless (mode == Compile) $         context "Generating reference outputs" $           -- We probably get the concurrency at the test program level,           -- so force just one data set at a time here.           ensureReferenceOutput (Just 1) (FutharkExe futhark) "c" program ios+       unless (mode == Interpreted) $         context ("Compiling with --backend=" <> T.pack backend) $ do-          compileTestProgram extra_options (FutharkExe futhark) backend program warnings+          compileTestProgram extra_compiler_options (FutharkExe futhark) backend program warnings           mapM_ (testMetrics progs program) structures           unless (mode == Compile) $ do             (tuning_opts, _) <-               liftIO $ determineTuning (configTuning progs) program-            let progs' =-                  progs-                    { configExtraOptions =-                        tuning_opts ++ configExtraOptions progs-                    }+            let extra_options = tuning_opts ++ configExtraOptions progs+                runner = configRunner progs             context "Running compiled program" $-              accErrors_ $ map (runCompiledEntry (FutharkExe futhark) program progs') ios+              withProgramServer program runner extra_options $ \server -> do+                let run = runCompiledEntry (FutharkExe futhark) server program+                concat <$> mapM run ios+       unless (mode == Compile || mode == Compiled) $         context "Interpreting" $           accErrors_ $ map (runInterpretedEntry (FutharkExe futhark) program) ios -runInterpretedEntry :: FutharkExe -> FilePath -> InputOutputs -> TestM ()-runInterpretedEntry (FutharkExe futhark) program (InputOutputs entry run_cases) =-  let dir = takeDirectory program-      runInterpretedCase run@(TestRun _ inputValues _ index _) =-        unless ("compiled" `elem` runTags run) $-          context-            ( "Entry point: " <> entry-                <> "; dataset: "-                <> T.pack (runDescription run)-            )-            $ do-              input <- T.unlines . map prettyText <$> getValues (FutharkExe futhark) dir inputValues-              expectedResult' <- getExpectedResult (FutharkExe futhark) program entry run-              (code, output, err) <--                io $-                  readProcessWithExitCode futhark ["run", "-e", T.unpack entry, program] $-                    T.encodeUtf8 input-              case code of-                ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark-                _ ->-                  compareResult entry index program expectedResult'-                    =<< runResult program code output err-   in accErrors_ $ map runInterpretedCase run_cases--runCompiledEntry :: FutharkExe -> FilePath -> ProgConfig -> InputOutputs -> TestM ()-runCompiledEntry futhark program progs (InputOutputs entry run_cases) =-  -- Explicitly prefixing the current directory is necessary for-  -- readProcessWithExitCode to find the binary when binOutputf has-  -- no path component.-  let binOutputf = dropExtension program-      binpath = "." </> binOutputf-      entry_options = ["-e", T.unpack entry]+liftCommand ::+  (MonadError T.Text m, MonadIO m) =>+  IO (Maybe CmdFailure) ->+  m ()+liftCommand m = do+  r <- liftIO m+  case r of+    Just (CmdFailure _ err) -> E.throwError $ T.unlines err+    Nothing -> pure () -      runner = configRunner progs-      extra_options = configExtraOptions progs+runCompiledEntry :: FutharkExe -> Server -> FilePath -> InputOutputs -> IO [TestResult]+runCompiledEntry futhark server program (InputOutputs entry run_cases) = do+  Right output_types <- cmdOutputs server entry+  Right input_types <- cmdInputs server entry+  let outs = ["out" <> T.pack (show i) | i <- [0 .. length output_types -1]]+      ins = ["in" <> T.pack (show i) | i <- [0 .. length input_types -1]]+      onRes = either (Failure . pure) (const Success)+  mapM (fmap onRes . runCompiledCase input_types outs ins) run_cases+  where+    dir = takeDirectory program -      runCompiledCase run@(TestRun _ inputValues _ index _) =-        context-          ( "Entry point: " <> entry-              <> "; dataset: "+    runCompiledCase input_types outs ins run = runExceptT $ do+      let TestRun _ inputValues _ index _ = run+          case_ctx =+            "Entry point: " <> entry <> "; dataset: "               <> T.pack (runDescription run)-          )-          $ do-            expected <- getExpectedResult futhark program entry run-            (progCode, output, progerr) <--              runProgram futhark runner extra_options program entry inputValues-            compareResult entry index program expected-              =<< runResult program progCode output progerr-   in context ("Running " <> T.pack (unwords $ binpath : entry_options ++ extra_options)) $-        accErrors_ $ map runCompiledCase run_cases -checkError :: ExpectedError -> SBS.ByteString -> TestM ()+      context1 case_ctx $ do+        expected <- getExpectedResult futhark program entry run++        (liftCommand . withValuesFile futhark dir inputValues) $ \values_f ->+          cmdRestore server values_f (zip ins input_types)++        call_r <- liftIO $ cmdCall server entry outs ins+        liftCommand $ cmdFree server ins++        res <- case call_r of+          Left (CmdFailure _ err) ->+            pure $ ErrorResult $ T.unlines err+          Right _ ->+            SuccessResult+              <$> readResults server outs program+                <* liftCommand (cmdFree server outs)++        compareResult entry index program expected res++checkError :: MonadError T.Text m => ExpectedError -> T.Text -> m () checkError (ThisError regex_s regex) err-  | not (match regex $ T.unpack $ T.decodeUtf8 err) =-    throwError $+  | not (match regex $ T.unpack err) =+    E.throwError $       "Expected error:\n  " <> regex_s         <> "\nGot error:\n  "-        <> T.decodeUtf8 err+        <> err checkError _ _ =   return () -runResult :: FilePath -> ExitCode -> SBS.ByteString -> SBS.ByteString -> TestM RunResult+runResult ::+  (MonadIO m, MonadError T.Text m) =>+  FilePath ->+  ExitCode ->+  SBS.ByteString ->+  SBS.ByteString ->+  m RunResult runResult program ExitSuccess stdout_s _ =   case valuesFromByteString "stdout" $ LBS.fromStrict stdout_s of     Left e -> do       let actualf = program `addExtension` "actual"-      io $ SBS.writeFile actualf stdout_s-      throwError $ T.pack e <> "\n(See " <> T.pack actualf <> ")"+      liftIO $ SBS.writeFile actualf stdout_s+      E.throwError $ T.pack e <> "\n(See " <> T.pack actualf <> ")"     Right vs -> return $ SuccessResult vs-runResult _ (ExitFailure code) _ stderr_s =-  return $ ErrorResult code stderr_s+runResult _ (ExitFailure _) _ stderr_s =+  return $ ErrorResult $ T.decodeUtf8 stderr_s  compileTestProgram :: [String] -> FutharkExe -> String -> FilePath -> [WarningTest] -> TestM () compileTestProgram extra_options futhark backend program warnings = do-  (_, futerr) <- compileProgram extra_options futhark backend program+  (_, futerr) <- compileProgram ("--server" : extra_options) futhark backend program   testWarnings warnings futerr  compareResult ::+  (MonadIO m, MonadError T.Text m) =>   T.Text ->   Int ->   FilePath ->   ExpectedResult [Value] ->   RunResult ->-  TestM ()+  m () compareResult _ _ _ (Succeeds Nothing) SuccessResult {} =   return ()-compareResult entry index program (Succeeds (Just expectedResult)) (SuccessResult actualResult) =-  case compareValues1 actualResult expectedResult of-    Just mismatch -> do-      let actualf = program <.> T.unpack entry <.> show index <.> "actual"-          expectedf = program <.> T.unpack entry <.> show index <.> "expected"-      io $-        SBS.writeFile actualf $-          T.encodeUtf8 $ T.unlines $ map prettyText actualResult-      io $-        SBS.writeFile expectedf $-          T.encodeUtf8 $ T.unlines $ map prettyText expectedResult-      throwError $-        T.pack actualf <> " and " <> T.pack expectedf-          <> " do not match:\n"-          <> T.pack (show mismatch)-          <> "\n"-    Nothing ->-      return ()-compareResult _ _ _ (RunTimeFailure expectedError) (ErrorResult _ actualError) =+compareResult entry index program (Succeeds (Just expected_vs)) (SuccessResult actual_vs) =+  checkResult+    (program <.> T.unpack entry <.> show index)+    expected_vs+    actual_vs+compareResult _ _ _ (RunTimeFailure expectedError) (ErrorResult actualError) =   checkError expectedError actualError-compareResult _ _ _ (Succeeds _) (ErrorResult code err) =-  throwError $-    "Program failed with error code "-      <> T.pack (show code)-      <> " and stderr:\n  "-      <> T.decodeUtf8 err+compareResult _ _ _ (Succeeds _) (ErrorResult err) =+  E.throwError $ "Function failed with error:\n" <> err compareResult _ _ _ (RunTimeFailure f) (SuccessResult _) =-  throwError $ "Program succeeded, but expected failure:\n  " <> T.pack (show f)+  E.throwError $ "Program succeeded, but expected failure:\n  " <> T.pack (show f)  --- --- Test manager@@ -581,7 +612,7 @@           { configBackend = "c",             configFuthark = Nothing,             configRunner = "",-            configExtraOptions = ["-b"],+            configExtraOptions = [],             configExtraCompilerOptions = [],             configTuning = Just "tuning"           },
src/Futhark/CodeGen/Backends/CCUDA.hs view
@@ -7,6 +7,7 @@     GC.CParts (..),     GC.asLibrary,     GC.asExecutable,+    GC.asServer,   ) where @@ -319,11 +320,11 @@       void *$id:args_arr[] = { $inits:args'' };       typename int64_t $id:time_start = 0, $id:time_end = 0;       if (ctx->debugging) {-        fprintf(stderr, "Launching %s with grid size (", $string:(pretty kernel_name));+        fprintf(ctx->log, "Launching %s with grid size (", $string:(pretty kernel_name));         $stms:(printSizes [grid_x, grid_y, grid_z])-        fprintf(stderr, ") and block size (");+        fprintf(ctx->log, ") and block size (");         $stms:(printSizes [block_x, block_y, block_z])-        fprintf(stderr, ").\n");+        fprintf(ctx->log, ").\n");         $id:time_start = get_wall_time();       }       $items:bef@@ -337,7 +338,7 @@       if (ctx->debugging) {         CUDA_SUCCEED(cuCtxSynchronize());         $id:time_end = get_wall_time();-        fprintf(stderr, "Kernel %s runtime: %ldus\n",+        fprintf(ctx->log, "Kernel %s runtime: %ldus\n",                 $string:(pretty kernel_name), $id:time_end - $id:time_start);       }     }|]@@ -370,6 +371,6 @@       return (offset, Just (size, offset))      printSizes =-      intercalate [[C.cstm|fprintf(stderr, ", ");|]] . map printSize+      intercalate [[C.cstm|fprintf(ctx->log, ", ");|]] . map printSize     printSize e =-      [[C.cstm|fprintf(stderr, "%d", $exp:e);|]]+      [[C.cstm|fprintf(ctx->log, "%ld", (long int)$exp:e);|]]
src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs view
@@ -337,8 +337,10 @@                          int debugging;                          int profiling;                          int profiling_paused;+                         int logging;                          typename lock_t lock;                          char *error;+                         typename FILE *log;                          $sdecls:fields                          $sdecls:kernel_fields                          typename CUdeviceptr global_failure;@@ -371,7 +373,9 @@                  ctx->debugging = ctx->detail_memory = cfg->cu_cfg.debugging;                  ctx->profiling = cfg->profiling;                  ctx->profiling_paused = 0;+                 ctx->logging = cfg->cu_cfg.logging;                  ctx->error = NULL;+                 ctx->log = stderr;                  ctx->cuda.profiling_records_capacity = 200;                  ctx->cuda.profiling_records_used = 0;                  ctx->cuda.profiling_records =@@ -458,12 +462,7 @@                }|]     ) -  GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->-    ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],-      [C.cedecl|int $id:s(struct $id:ctx* ctx) {-                         lock_lock(&ctx->lock);-                         CUDA_SUCCEED(cuda_free_all(&ctx->cuda));-                         lock_unlock(&ctx->lock);-                         return 0;-                       }|]-    )+  GC.onClear+    [C.citem|if (ctx->error == NULL) {+               CUDA_SUCCEED(cuda_free_all(&ctx->cuda));+             }|]
src/Futhark/CodeGen/Backends/COpenCL.hs view
@@ -8,6 +8,7 @@     GC.CParts (..),     GC.asLibrary,     GC.asExecutable,+    GC.asServer,   ) where @@ -318,7 +319,7 @@   GC.stm [C.cstm|$id:v = ctx->sizes.$id:key <= $exp:x';|]   GC.stm     [C.cstm|if (ctx->logging) {-    fprintf(stderr, "Compared %s <= %ld: %s.\n", $string:(pretty key), (long)$exp:x', $id:v ? "true" : "false");+    fprintf(ctx->log, "Compared %s <= %ld: %s.\n", $string:(pretty key), (long)$exp:x', $id:v ? "true" : "false");     }|] callKernel (GetSizeMax v size_class) =   let field = "max_" ++ pretty size_class@@ -389,11 +390,11 @@       const size_t $id:local_work_size[$int:kernel_rank] = {$inits:workgroup_dims'};       typename int64_t $id:time_start = 0, $id:time_end = 0;       if (ctx->debugging) {-        fprintf(stderr, "Launching %s with global work size [", $string:(pretty kernel_name));+        fprintf(ctx->log, "Launching %s with global work size [", $string:(pretty kernel_name));         $stms:(printKernelSize global_work_size)-        fprintf(stderr, "] and local work size [");+        fprintf(ctx->log, "] and local work size [");         $stms:(printKernelSize local_work_size)-        fprintf(stderr, "]; local memory parameters sum to %d bytes.\n", (int)$exp:local_bytes);+        fprintf(ctx->log, "]; local memory parameters sum to %d bytes.\n", (int)$exp:local_bytes);         $id:time_start = get_wall_time();       }       OPENCL_SUCCEED_OR_RETURN(@@ -404,7 +405,7 @@         OPENCL_SUCCEED_FATAL(clFinish(ctx->opencl.queue));         $id:time_end = get_wall_time();         long int $id:time_diff = $id:time_end - $id:time_start;-        fprintf(stderr, "kernel %s runtime: %ldus\n",+        fprintf(ctx->log, "kernel %s runtime: %ldus\n",                 $string:(pretty kernel_name), $id:time_diff);       }     }|]@@ -421,7 +422,7 @@      printKernelSize :: VName -> [C.Stm]     printKernelSize work_size =-      intercalate [[C.cstm|fprintf(stderr, ", ");|]] $+      intercalate [[C.cstm|fprintf(ctx->log, ", ");|]] $         map (printKernelDim work_size) [0 .. kernel_rank -1]     printKernelDim global_work_size i =-      [[C.cstm|fprintf(stderr, "%zu", $id:global_work_size[$int:i]);|]]+      [[C.cstm|fprintf(ctx->log, "%zu", $id:global_work_size[$int:i]);|]]
src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs view
@@ -320,6 +320,7 @@                          int logging;                          typename lock_t lock;                          char *error;+                         typename FILE *log;                          $sdecls:fields                          $sdecls:ctx_opencl_fields                          typename cl_mem global_failure;@@ -342,6 +343,7 @@                      ctx->profiling_paused = 0;                      ctx->logging = cfg->opencl.logging;                      ctx->error = NULL;+                     ctx->log = stderr;                      ctx->opencl.profiling_records_capacity = 200;                      ctx->opencl.profiling_records_used = 0;                      ctx->opencl.profiling_records =@@ -490,16 +492,6 @@                }|]     ) -  GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->-    ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],-      [C.cedecl|int $id:s(struct $id:ctx* ctx) {-                         lock_lock(&ctx->lock);-                         ctx->error = OPENCL_SUCCEED_NONFATAL(opencl_free_all(&ctx->opencl));-                         lock_unlock(&ctx->lock);-                         return ctx->error != NULL;-                       }|]-    )-   GC.publicDef_ "context_get_command_queue" GC.InitDecl $ \s ->     ( [C.cedecl|typename cl_command_queue $id:s(struct $id:ctx* ctx);|],       [C.cedecl|typename cl_command_queue $id:s(struct $id:ctx* ctx) {@@ -507,6 +499,11 @@                }|]     ) +  GC.onClear+    [C.citem|if (ctx->error == NULL) {+                        ctx->error = OPENCL_SUCCEED_NONFATAL(opencl_free_all(&ctx->opencl));+                      }|]+   GC.profileReport [C.citem|OPENCL_SUCCEED_FATAL(opencl_tally_profiling_records(&ctx->opencl));|]   mapM_ GC.profileReport $     costCentreReport $@@ -577,7 +574,7 @@   OPENCL_SUCCEED_FATAL(error);   $items:set_args   if (ctx->debugging) {-    fprintf(stderr, "Created kernel %s.\n", $string:(pretty name));+    fprintf(ctx->log, "Created kernel %s.\n", $string:(pretty name));   }   }|]   where
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -12,6 +12,7 @@     CParts (..),     asLibrary,     asExecutable,+    asServer,      -- * Pluggable compiler     Operations (..),@@ -58,6 +59,7 @@     publicDef,     publicDef_,     profileReport,+    onClear,     HeaderSection (..),     libDecl,     earlyDecl,@@ -86,8 +88,9 @@ import Data.Loc import qualified Data.Map.Strict as M import Data.Maybe-import Futhark.CodeGen.Backends.GenericC.CLI+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.IR.Prop (isBuiltInFunction)@@ -106,6 +109,7 @@     compLibDecls :: DL.DList C.Definition,     compCtxFields :: DL.DList (C.Id, C.Type, Maybe C.Exp),     compProfileItems :: DL.DList C.BlockItem,+    compClearItems :: DL.DList C.BlockItem,     compDeclaredMem :: [(VName, Space)]   } @@ -122,6 +126,7 @@       compLibDecls = mempty,       compCtxFields = mempty,       compProfileItems = mempty,+      compClearItems = mempty,       compDeclaredMem = mempty     } @@ -486,6 +491,10 @@ 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|] @@ -604,7 +613,7 @@   if (block->references != NULL) {     *(block->references) -= 1;     if (ctx->detail_memory) {-      fprintf(stderr, "Unreferencing block %s (allocated as %s) in %s: %d references remaining.\n",+      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) {@@ -612,7 +621,7 @@       $items:free       free(block->references);       if (ctx->detail_memory) {-        fprintf(stderr, "%lld bytes freed (now allocated: %lld bytes)\n",+        fprintf(ctx->log, "%lld bytes freed (now allocated: %lld bytes)\n",                 (long long) block->size, (long long) ctx->$id:usagename);       }     }@@ -635,7 +644,7 @@    ctx->$id:usagename += size;   if (ctx->detail_memory) {-    fprintf(stderr, "Allocating %lld bytes for %s in %s (then allocated: %lld bytes)",+    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);@@ -643,10 +652,10 @@   if (ctx->$id:usagename > ctx->$id:peakname) {     ctx->$id:peakname = ctx->$id:usagename;     if (ctx->detail_memory) {-      fprintf(stderr, " (new peak).\n");+      fprintf(ctx->log, " (new peak).\n");     }   } else if (ctx->detail_memory) {-    fprintf(stderr, ".\n");+    fprintf(ctx->log, ".\n");   }    $items:alloc@@ -670,6 +679,8 @@ } |] +  onClear [C.citem|ctx->$id:peakname = 0;|]+   let peakmsg = "Peak memory usage for " ++ spacedesc ++ ": %lld bytes.\n"   return     ( structdef,@@ -947,6 +958,8 @@ opaqueLibraryFunctions desc vds = do   name <- publicName $ opaqueName desc vds   free_opaque <- publicName $ "free_" ++ opaqueName desc vds+  store_opaque <- publicName $ "store_" ++ opaqueName desc vds+  restore_opaque <- publicName $ "restore_" ++ opaqueName desc vds    let opaque_type = [C.cty|struct $id:name|] @@ -954,23 +967,96 @@         return ()       freeComponent i (ArrayValue _ _ pt signed shape) = do         let rank = length shape+            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 ((tmp = $id:free_array(ctx, obj->$id:(tupleField i))) != 0) {+          [C.cstm|if (obj->$id:field != NULL && (tmp = $id:free_array(ctx, obj->$id:field)) != 0) {                 ret = tmp;              }|] +      storeComponent i (ScalarValue pt sign _) =+        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 (ArrayValue _ _ pt sign shape) =+        let rank = length shape+            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 * sizeof($ty:(primTypeToCType pt));|]+            )+   ctx_ty <- contextType    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 (ScalarValue pt sign _) = 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 (ArrayValue _ _ pt sign shape) = do+        let field = tupleField i+            rank = length shape+            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];|]+        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 * sizeof($ty:(primTypeToCType pt));|]+        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 ..] vds+    stm+      [C.cstm|if (err == 0) {+                $stms:loads+              }|]+   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);|]    -- We do not need to enclose the body in a critical section, because-  -- when we free the components of the opaque, we are calling public-  -- API functions that do their own locking.+  -- when we operate on the components of the opaque, we are calling+  -- public API functions that do their own locking.   return     [C.cunit|           int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj) {@@ -979,8 +1065,30 @@             free(obj);             return ret;           }-           |] +          int $id:store_opaque($ty:ctx_ty *ctx,+                               const $ty:opaque_type *obj, void **p, size_t *n) {+            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;+          }+    |]+ valueDescToCType :: ValueDesc -> CompilerM op s C.Type valueDescToCType (ScalarValue pt signed _) =   return $ signedPrimTypeToCType signed pt@@ -1207,9 +1315,9 @@       let ty' = primTypeToCType ty       decl [C.cdecl|$ty:ty' $id:name;|] --- | The result of compilation to C is four 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+-- | 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 :: String,@@ -1217,9 +1325,21 @@     -- to both CLI and library parts.     cUtils :: String,     cCLI :: String,+    cServer :: String,     cLib :: String   } +gnuSource :: String+gnuSource =+  pretty+    [C.cunit|+// 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+$esc:("#define _GNU_SOURCE")+|]+ -- 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@@ -1248,13 +1368,19 @@ asLibrary :: CParts -> (String, String) asLibrary parts =   ( "#pragma once\n\n" <> cHeader parts,-    disableWarnings <> cHeader parts <> cUtils parts <> cLib parts+    gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cLib parts   )  -- | As executable with command-line interface. asExecutable :: CParts -> String-asExecutable (CParts a b c d) = disableWarnings <> a <> b <> c <> d+asExecutable parts =+  gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cCLI parts <> cLib parts +-- | As server executable.+asServer :: CParts -> String+asServer parts =+  gnuSource <> disableWarnings <> cHeader parts <> cUtils parts <> cServer parts <> cLib parts+ -- | Compile imperative program to a C program.  Always uses the -- function named "main" as entry point, so make sure it is defined. compileProg ::@@ -1275,17 +1401,17 @@   let headerdefs =         [C.cunit| $esc:("// Headers\n")-/* 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 */-$esc:("#define _GNU_SOURCE") $esc:("#include <stdint.h>") $esc:("#include <stddef.h>") $esc:("#include <stdbool.h>")+$esc:("#include <stdio.h>") $esc:("#include <float.h>") $esc:(header_extra) +$esc:("#ifdef __cplusplus")+$esc:("extern \"C\" {")+$esc:("#endif")+ $esc:("\n// Initialisation\n") $edecls:(initDecls endstate) @@ -1301,6 +1427,10 @@ $esc:("\n// Miscellaneous\n") $edecls:(miscDecls endstate) $esc:("#define FUTHARK_BACKEND_"++backend)++$esc:("#ifdef __cplusplus")+$esc:("}")+$esc:("#endif")                            |]    let utildefs =@@ -1325,12 +1455,12 @@   let early_decls = DL.toList $ compEarlyDecls endstate   let lib_decls = DL.toList $ compLibDecls endstate   let clidefs = cliDefs options $ Functions entry_funs+  let serverdefs = serverDefs options $ Functions entry_funs   let libdefs =         [C.cunit| $esc:("#ifdef _MSC_VER\n#define inline __inline\n#endif") $esc:("#include <string.h>")-$esc:("#include <inttypes.h>")-$esc:("#include <ctype.h>")+$esc:("#include <string.h>") $esc:("#include <errno.h>") $esc:("#include <assert.h>") @@ -1355,7 +1485,14 @@ $edecls:entry_point_decls   |] -  return $ CParts (pretty headerdefs) (pretty utildefs) (pretty clidefs) (pretty libdefs)+  return $+    CParts+      { cHeader = pretty headerdefs,+        cUtils = pretty utildefs,+        cCLI = pretty clidefs,+        cServer = pretty serverdefs,+        cLib = pretty libdefs+      }   where     Definitions consts (Functions funs) = prog     entry_funs = filter (functionEntry . snd) funs@@ -1400,6 +1537,7 @@ commonLibFuns :: [C.BlockItem] -> CompilerM op s () commonLibFuns memreport = do   ctx <- contextType+  ops <- asks envOperations   profilereport <- gets $ DL.toList . compProfileItems    publicDef_ "context_report" MiscDecl $ \s ->@@ -1407,7 +1545,7 @@       [C.cedecl|char* $id:s($ty:ctx *ctx) {                  struct str_builder builder;                  str_builder_init(&builder);-                 if (ctx->detail_memory || ctx->profiling) {+                 if (ctx->detail_memory || ctx->profiling || ctx->logging) {                    $items:memreport                  }                  if (ctx->profiling) {@@ -1426,6 +1564,13 @@                        }|]     ) +  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) {@@ -1440,6 +1585,15 @@                }|]     ) +  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@@ -1752,7 +1906,7 @@   e' <- compileExp e   stm     [C.cstm|if (ctx->debugging) {-          fprintf(stderr, $string:fmtstr, $exp:s, ($ty:ety)$exp:e', '\n');+          fprintf(ctx->log, $string:fmtstr, $exp:s, ($ty:ety)$exp:e', '\n');        }|]   where     (fmt, ety) = case primExpType e of@@ -1763,7 +1917,7 @@ compileCode (DebugPrint s Nothing) =   stm     [C.cstm|if (ctx->debugging) {-          fprintf(stderr, "%s\n", $exp:s);+          fprintf(ctx->log, "%s\n", $exp:s);        }|] compileCode c   | Just (name, vol, t, e, c') <- declareAndSet c = do
src/Futhark/CodeGen/Backends/GenericC/CLI.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TupleSections #-} +-- | Code generation for standalone executables. module Futhark.CodeGen.Backends.GenericC.CLI   ( cliDefs,   )@@ -362,6 +363,8 @@       )  {-# NOINLINE cliDefs #-}++-- | Generate Futhark standalone executable code. cliDefs :: [Option] -> Functions a -> [C.Definition] cliDefs options (Functions funs) =   let values_h = $(embedStringFile "rts/c/values.h")@@ -372,12 +375,9 @@       (cli_entry_point_decls, entry_point_inits) =         unzip $ map (uncurry cliEntryPoint) funs    in [C.cunit|-$esc:("#include <string.h>")-$esc:("#include <inttypes.h>")-$esc:("#include <errno.h>")-$esc:("#include <ctype.h>")-$esc:("#include <errno.h>") $esc:("#include <getopt.h>")+$esc:("#include <ctype.h>")+$esc:("#include <inttypes.h>")  $esc:values_h @@ -404,10 +404,6 @@ int main(int argc, char** argv) {   fut_progname = argv[0]; -  struct entry_point_entry entry_points[] = {-    $inits:entry_point_inits-  };-   struct futhark_context_config *cfg = futhark_context_config_new();   assert(cfg != NULL); @@ -426,6 +422,10 @@   if (error != NULL) {     futhark_panic(1, "%s", error);   }++  struct entry_point_entry entry_points[] = {+    $inits:entry_point_inits+  };    if (entry_point != NULL) {     int num_entry_points = sizeof(entry_points) / sizeof(entry_points[0]);
+ src/Futhark/CodeGen/Backends/GenericC/Server.hs view
@@ -0,0 +1,282 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}++-- | Code generation for server executables.+module Futhark.CodeGen.Backends.GenericC.Server+  ( serverDefs,+  )+where++import Data.Bifunctor (first, second)+import Data.FileEmbed+import qualified Data.Map as M+import Futhark.CodeGen.Backends.GenericC.Options+import Futhark.CodeGen.Backends.SimpleRep+import Futhark.CodeGen.ImpCode+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C++genericOptions :: [Option]+genericOptions =+  [ Option+      { optionLongName = "debugging",+        optionShortName = Just 'D',+        optionArgument = NoArgument,+        optionDescription = "Perform possibly expensive internal correctness checks and verbose logging.",+        optionAction = [C.cstm|futhark_context_config_set_debugging(cfg, 1);|]+      },+    Option+      { optionLongName = "log",+        optionShortName = Just 'L',+        optionArgument = NoArgument,+        optionDescription = "Print various low-overhead logging information while running.",+        optionAction = [C.cstm|futhark_context_config_set_logging(cfg, 1);|]+      },+    Option+      { optionLongName = "help",+        optionShortName = Just 'h',+        optionArgument = NoArgument,+        optionDescription = "Print help information and exit.",+        optionAction =+          [C.cstm|{+                   printf("Usage: %s [OPTION]...\nOptions:\n\n%s\nFor more information, consult the Futhark User's Guide or the man pages.\n",+                          fut_progname, option_descriptions);+                   exit(0);+                  }|]+      }+  ]++typeStructName :: ExternalValue -> String+typeStructName (TransparentValue (ScalarValue pt signed _)) =+  let name = prettySigned (signed == TypeUnsigned) pt+   in "type_" ++ name+typeStructName (TransparentValue (ArrayValue _ _ pt signed shape)) =+  let rank = length shape+      name = arrayName pt signed rank+   in "type_" ++ name+typeStructName (OpaqueValue name vds) =+  "type_" ++ opaqueName name vds++valueDescBoilerplate :: ExternalValue -> (String, (C.Initializer, [C.Definition]))+valueDescBoilerplate ev@(TransparentValue (ScalarValue pt signed _)) =+  let name = prettySigned (signed == TypeUnsigned) pt+      type_name = typeStructName ev+   in (name, ([C.cinit|&$id:type_name|], mempty))+valueDescBoilerplate ev@(TransparentValue (ArrayValue _ _ pt signed shape)) =+  let rank = length shape+      name = arrayName pt signed rank+      pt_name = prettySigned (signed == TypeUnsigned) pt+      pretty_name = concat (replicate rank "[]") ++ pt_name+      type_name = typeStructName ev+      aux_name = type_name ++ "_aux"+      info_name = pt_name ++ "_info"+      array_new = "futhark_new_" ++ name+      array_new_wrap = "futhark_new_" ++ name ++ "_wrap"+      array_free = "futhark_free_" ++ name+      array_shape = "futhark_shape_" ++ name+      array_values = "futhark_values_" ++ name+      shape_args = [[C.cexp|shape[$int:i]|] | i <- [0 .. rank -1]]+   in ( name,+        ( [C.cinit|&$id:type_name|],+          [C.cunit|+              void* $id:array_new_wrap(struct futhark_context *ctx,+                                       const void* p,+                                       const typename int64_t* shape) {+                return $id:array_new(ctx, p, $args:shape_args);+              }+              struct array_aux $id:aux_name = {+                .name = $string:pretty_name,+                .rank = $int:rank,+                .info = &$id:info_name,+                .new = (typename array_new_fn)$id:array_new_wrap,+                .free = (typename array_free_fn)$id:array_free,+                .shape = (typename array_shape_fn)$id:array_shape,+                .values = (typename array_values_fn)$id:array_values+              };+              struct type $id:type_name = {+                .name = $string:pretty_name,+                .restore = (typename restore_fn)restore_array,+                .store = (typename store_fn)store_array,+                .free = (typename free_fn)free_array,+                .aux = &$id:aux_name+              };|]+        )+      )+valueDescBoilerplate ev@(OpaqueValue name vds) =+  let type_name = typeStructName ev+      aux_name = type_name ++ "_aux"+      opaque_free = "futhark_free_" ++ opaqueName name vds+      opaque_store = "futhark_store_" ++ opaqueName name vds+      opaque_restore = "futhark_restore_" ++ opaqueName name vds+   in ( name,+        ( [C.cinit|&$id:type_name|],+          [C.cunit|+              struct opaque_aux $id:aux_name = {+                .free = (typename opaque_free_fn)$id:opaque_free+              };+              struct type $id:type_name = {+                .name = $string:name,+                .restore = (typename restore_fn)$id:opaque_store,+                .store = (typename store_fn)$id:opaque_restore,+                .free = (typename free_fn)free_opaque,+                .aux = &$id:aux_name+              };|]+        )+      )++functionExternalValues :: Function a -> [ExternalValue]+functionExternalValues fun = functionResult fun ++ functionArgs fun++entryTypeBoilerplate :: Functions a -> ([C.Initializer], [C.Definition])+entryTypeBoilerplate (Functions funs) =+  second concat $+    unzip $+      M.elems $+        M.fromList $+          map valueDescBoilerplate $+            concatMap (functionExternalValues . snd) $+              filter (functionEntry . snd) funs++oneEntryBoilerplate :: (Name, Function a) -> ([C.Definition], C.Initializer)+oneEntryBoilerplate (name, fun) =+  let entry_f = "futhark_entry_" ++ pretty name+      call_f = "call_" ++ pretty name+      out_types = functionResult fun+      in_types = functionArgs fun+      out_types_name = pretty name ++ "_out_types"+      in_types_name = pretty name ++ "_in_types"+      (out_items, out_args)+        | null out_types = ([C.citems|(void)outs;|], mempty)+        | otherwise = unzip $ zipWith loadOut [0 ..] out_types+      (in_items, in_args)+        | null in_types = ([C.citems|(void)ins;|], mempty)+        | otherwise = unzip $ zipWith loadIn [0 ..] in_types+   in ( [C.cunit|+                struct type* $id:out_types_name[] = {+                  $inits:(map typeStructInit out_types),+                  NULL+                };+                struct type* $id:in_types_name[] = {+                  $inits:(map typeStructInit in_types),+                  NULL+                };+                int $id:call_f(struct futhark_context *ctx, void **outs, void **ins) {+                  $items:out_items+                  $items:in_items+                  return $id:entry_f(ctx, $args:out_args, $args:in_args);+                }+                |],+        [C.cinit|{+            .name = $string:(pretty name),+            .f = $id:call_f,+            .in_types = $id:in_types_name,+            .out_types = $id:out_types_name+            }|]+      )+  where+    typeStructInit t = [C.cinit|&$id:(typeStructName t)|]++    loadOut i ev =+      let v = "out" ++ show (i :: Int)+       in ( [C.citem|$ty:(externalValueType ev) *$id:v = outs[$int:i];|],+            [C.cexp|$id:v|]+          )+    loadIn i ev =+      let v = "in" ++ show (i :: Int)+          evt = externalValueType ev+       in ( [C.citem|$ty:evt $id:v = *($ty:evt*)ins[$int:i];|],+            [C.cexp|$id:v|]+          )++entryBoilerplate :: Functions a -> ([C.Definition], [C.Initializer])+entryBoilerplate (Functions funs) =+  first concat $ unzip $ map oneEntryBoilerplate $ filter (functionEntry . snd) funs++mkBoilerplate ::+  Functions a ->+  ([C.Definition], [C.Initializer], [C.Initializer])+mkBoilerplate funs =+  let (type_inits, type_defs) = entryTypeBoilerplate funs+      (entry_defs, entry_inits) = entryBoilerplate funs+   in (type_defs ++ entry_defs, type_inits, entry_inits)++{-# NOINLINE serverDefs #-}++-- | Generate Futhark server executable code.+serverDefs :: [Option] -> Functions a -> [C.Definition]+serverDefs options funs =+  let server_h = $(embedStringFile "rts/c/server.h")+      values_h = $(embedStringFile "rts/c/values.h")+      tuning_h = $(embedStringFile "rts/c/tuning.h")+      option_parser =+        generateOptionParser "parse_options" $ genericOptions ++ options+      (boilerplate_defs, type_inits, entry_point_inits) =+        mkBoilerplate funs+   in [C.cunit|+$esc:("#include <getopt.h>")+$esc:("#include <ctype.h>")+$esc:("#include <inttypes.h>")++// If the entry point is NULL, the program will terminate after doing initialisation and such.  It is not used for anything else in server mode.+static const char *entry_point = "main";++$esc:values_h+$esc:server_h+$esc:tuning_h++$edecls:boilerplate_defs++struct type* types[] = {+  $inits:type_inits,+  NULL+};++struct entry_point entry_points[] = {+  $inits:entry_point_inits,+  { .name = NULL }+};++struct futhark_prog prog = {+  .types = types,+  .entry_points = entry_points+};++$func:option_parser++int main(int argc, char** argv) {+  fut_progname = argv[0];++  struct futhark_context_config *cfg = futhark_context_config_new();+  assert(cfg != NULL);++  int parsed_options = parse_options(cfg, argc, argv);+  argc -= parsed_options;+  argv += parsed_options;++  if (argc != 0) {+    futhark_panic(1, "Excess non-option: %s\n", argv[0]);+  }++  struct futhark_context *ctx = futhark_context_new(cfg);+  assert (ctx != NULL);++  futhark_context_set_logging_file(ctx, stdout);++  char* error = futhark_context_get_error(ctx);+  if (error != NULL) {+    futhark_panic(1, "Error during context initialisation:\n%s", error);+  }++  if (entry_point != NULL) {+    run_server(&prog, ctx);+  }++  futhark_context_free(ctx);+  futhark_context_config_free(cfg);+}+|]
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -8,6 +8,7 @@ -- sequential and PyOpenCL Python code. module Futhark.CodeGen.Backends.GenericPython   ( compileProg,+    CompilerMode,     Constructor (..),     emptyConstructor,     compileName,@@ -52,6 +53,7 @@ import Futhark.CodeGen.Backends.GenericPython.Definitions import Futhark.CodeGen.Backends.GenericPython.Options import qualified Futhark.CodeGen.ImpCode as Imp+import Futhark.Compiler.CLI (CompilerMode (..)) import Futhark.IR.Primitive hiding (Bool) import Futhark.IR.Prop (isBuiltInFunction, subExpVars) import Futhark.IR.Syntax (Space (..))@@ -266,48 +268,59 @@ standardOptions :: [Option] standardOptions =   [ Option-      { optionLongName = "write-runtime-to",-        optionShortName = Just 't',-        optionArgument = RequiredArgument "str",-        optionAction =-          [ If-              (Var "runtime_file")-              [Exp $ simpleCall "runtime_file.close" []]-              [],-            Assign (Var "runtime_file") $-              simpleCall "open" [Var "optarg", String "w"]-          ]-      },-    Option-      { optionLongName = "runs",-        optionShortName = Just 'r',-        optionArgument = RequiredArgument "str",-        optionAction =-          [ Assign (Var "num_runs") $ Var "optarg",-            Assign (Var "do_warmup_run") $ Bool True-          ]-      },-    Option-      { optionLongName = "entry-point",-        optionShortName = Just 'e',-        optionArgument = RequiredArgument "str",-        optionAction =-          [Assign (Var "entry_point") $ Var "optarg"]-      },-    Option-      { optionLongName = "binary-output",-        optionShortName = Just 'b',-        optionArgument = NoArgument,-        optionAction = [Assign (Var "binary_output") $ Bool True]-      },-    Option       { optionLongName = "tuning",         optionShortName = Nothing,         optionArgument = RequiredArgument "open",         optionAction = [Exp $ simpleCall "read_tuning_file" [Var "sizes", Var "optarg"]]+      },+    Option+      { optionLongName = "log",+        optionShortName = Just 'L',+        optionArgument = NoArgument,+        optionAction = [Pass]       }   ] +executableOptions :: [Option]+executableOptions =+  standardOptions+    ++ [ Option+           { optionLongName = "write-runtime-to",+             optionShortName = Just 't',+             optionArgument = RequiredArgument "str",+             optionAction =+               [ If+                   (Var "runtime_file")+                   [Exp $ simpleCall "runtime_file.close" []]+                   [],+                 Assign (Var "runtime_file") $+                   simpleCall "open" [Var "optarg", String "w"]+               ]+           },+         Option+           { optionLongName = "runs",+             optionShortName = Just 'r',+             optionArgument = RequiredArgument "str",+             optionAction =+               [ Assign (Var "num_runs") $ Var "optarg",+                 Assign (Var "do_warmup_run") $ Bool True+               ]+           },+         Option+           { optionLongName = "entry-point",+             optionShortName = Just 'e',+             optionArgument = RequiredArgument "str",+             optionAction =+               [Assign (Var "entry_point") $ Var "optarg"]+           },+         Option+           { optionLongName = "binary-output",+             optionShortName = Just 'b',+             optionArgument = NoArgument,+             optionAction = [Assign (Var "binary_output") $ Bool True]+           }+       ]+ -- | The class generated by the code generator must have a -- constructor, although it can be vacuous. data Constructor = Constructor [String] [PyStmt]@@ -322,7 +335,8 @@  compileProg ::   MonadFreshNames m =>-  Maybe String ->+  CompilerMode ->+  String ->   Constructor ->   [PyStmt] ->   [PyStmt] ->@@ -332,13 +346,13 @@   [Option] ->   Imp.Definitions op ->   m String-compileProg module_name constructor imports defines ops userstate sync options prog = do+compileProg mode class_name constructor imports defines ops userstate sync options prog = do   src <- getNameSource   let prog' = runCompilerM ops src userstate compileProg'       maybe_shebang =-        case module_name of-          Nothing -> "#!/usr/bin/env python\n"-          Just _ -> ""+        case mode of+          ToLibrary -> ""+          _ -> "#!/usr/bin/env python3\n"   return $     maybe_shebang       ++ pretty@@ -348,7 +362,13 @@                    Assign (Var "sizes") $ Dict []                  ]               ++ defines-              ++ [Escape pyUtility]+              ++ [ Escape pyValues,+                   Escape pyFunctions,+                   Escape pyPanic,+                   Escape pyTuning,+                   Escape pyUtility,+                   Escape pyServer+                 ]               ++ prog'         )   where@@ -361,41 +381,64 @@        let constructor' = constructorToFunDef constructor at_inits -      case module_name of-        Just name -> do+      case mode of+        ToLibrary -> do           (entry_points, entry_point_types) <--            unzip <$> mapM (compileEntryFun sync) (filter (Imp.functionEntry . snd) funs)+            unzip+              <$> mapM+                (compileEntryFun sync DoNotReturnTiming)+                (filter (Imp.functionEntry . snd) funs)           return             [ ClassDef $-                Class name $+                Class class_name $                   Assign (Var "entry_points") (Dict entry_point_types) :                   map FunDef (constructor' : definitions ++ entry_points)             ]-        Nothing -> do-          let classinst = Assign (Var "self") $ simpleCall "internal" []+        ToServer -> do+          (entry_points, entry_point_types) <-+            unzip+              <$> mapM+                (compileEntryFun sync ReturnTiming)+                (filter (Imp.functionEntry . snd) funs)+          return $+            parse_options_server+              ++ [ ClassDef+                     ( Class class_name $+                         Assign (Var "entry_points") (Dict entry_point_types) :+                         map FunDef (constructor' : definitions ++ entry_points)+                     ),+                   Assign+                     (Var "server")+                     (simpleCall "Server" [simpleCall class_name []]),+                   Exp $ simpleCall "server.run" []+                 ]+        ToExecutable -> do+          let classinst = Assign (Var "self") $ simpleCall class_name []           (entry_point_defs, entry_point_names, entry_points) <-             unzip3               <$> mapM                 (callEntryFun sync)                 (filter (Imp.functionEntry . snd) funs)-          return-            ( parse_options-                ++ ClassDef-                  ( Class "internal" $-                      map FunDef $-                        constructor' : definitions-                  ) :-              classinst :-              map FunDef entry_point_defs-                ++ selectEntryPoint entry_point_names entry_points-            )+          return $+            parse_options_executable+              ++ ClassDef+                ( Class class_name $+                    map FunDef $+                      constructor' : definitions+                ) :+            classinst :+            map FunDef entry_point_defs+              ++ selectEntryPoint entry_point_names entry_points -    parse_options =+    parse_options_executable =       Assign (Var "runtime_file") None :       Assign (Var "do_warmup_run") (Bool False) :       Assign (Var "num_runs") (Integer 1) :       Assign (Var "entry_point") (String "main") :       Assign (Var "binary_output") (Bool False) :+      generateOptionParser (executableOptions ++ options)++    parse_options_server =       generateOptionParser (standardOptions ++ options)      selectEntryPoint entry_point_names entry_points =@@ -817,17 +860,44 @@           [srcmem, srcidx, Var "ct.c_byte"]   stm $ Exp $ simpleCall "ct.memmove" [offset_call1, offset_call2, nbytes] +data ReturnTiming = ReturnTiming | DoNotReturnTiming+ compileEntryFun ::   [PyStmt] ->+  ReturnTiming ->   (Name, Imp.Function op) ->   CompilerM op s (PyFunDef, (PyExp, PyExp))-compileEntryFun sync entry = do+compileEntryFun sync timing entry = do   (fname', params, prepareIn, body_lib, _, prepareOut, res, _) <- prepareEntry entry-  let ret = Return $ tupleOrSingle $ map snd res+  let (maybe_sync, ret) =+        case timing of+          DoNotReturnTiming ->+            ( [],+              Return $ tupleOrSingle $ map snd res+            )+          ReturnTiming ->+            ( sync,+              Return $+                Tuple+                  [ Var "runtime",+                    tupleOrSingle $ map snd res+                  ]+            )       (pts, rts) = entryTypes $ snd entry++      do_run =+        Assign (Var "time_start") (simpleCall "time.time" []) :+        body_lib ++ maybe_sync+          ++ [ Assign (Var "runtime") $+                 BinOp+                   "-"+                   (toMicroseconds (simpleCall "time.time" []))+                   (toMicroseconds (Var "time_start"))+             ]+   return     ( Def fname' ("self" : params) $-        prepareIn ++ body_lib ++ prepareOut ++ sync ++ [ret],+        prepareIn ++ do_run ++ prepareOut ++ sync ++ [ret],       (String fname', Tuple [List (map String pts), List (map String rts)])     ) @@ -905,8 +975,10 @@         Exp $ simpleCall "runtime_file.write" [String "\n"],         Exp $ simpleCall "runtime_file.flush" []       ]-    toMicroseconds x =-      simpleCall "int" [BinOp "*" x $ Integer 1000000]++toMicroseconds :: PyExp -> PyExp+toMicroseconds x =+  simpleCall "int" [BinOp "*" x $ Integer 1000000]  compileUnOp :: Imp.UnOp -> String compileUnOp op =
src/Futhark/CodeGen/Backends/GenericPython/Definitions.hs view
@@ -6,6 +6,7 @@     pyValues,     pyPanic,     pyTuning,+    pyServer,   ) where @@ -25,3 +26,6 @@  pyTuning :: String pyTuning = $(embedStringFile "rts/python/tuning.py")++pyServer :: String+pyServer = $(embedStringFile "rts/python/server.py")
src/Futhark/CodeGen/Backends/MulticoreC.hs view
@@ -9,6 +9,7 @@     GC.CParts (..),     GC.asLibrary,     GC.asExecutable,+    GC.asServer,   ) where @@ -26,6 +27,7 @@ import qualified Language.C.Quote.OpenCL as C import qualified Language.C.Syntax as C +-- | Compile the program to ImpCode with multicore operations. compileProg ::   MonadFreshNames m =>   Prog MCMem ->@@ -114,8 +116,10 @@                           int debugging;                           int profiling;                           int profiling_paused;+                          int logging;                           typename lock_t lock;                           char *error;+                          typename FILE *log;                           int total_runs;                           long int total_runtime;                           $sdecls:fields@@ -140,7 +144,9 @@                  ctx->debugging = cfg->debugging;                  ctx->profiling = cfg->profiling;                  ctx->profiling_paused = 0;+                 ctx->logging = 0;                  ctx->error = NULL;+                 ctx->log = stderr;                  create_lock(&ctx->lock);                   int tune_kappa = 0;@@ -356,7 +362,7 @@             then               [ [C.citem|                      for (int i = 0; i < ctx->scheduler.num_threads; i++) {-                       fprintf(stderr,+                       fprintf(ctx->log,                          $string:(format_string name is_array),                          i,                          ctx->$id:runs[i],@@ -371,7 +377,7 @@               ]             else               [ [C.citem|-                    fprintf(stderr,+                    fprintf(ctx->log,                        $string:(format_string name is_array),                        ctx->$id:runs,                        (long int) ctx->$id:total_runtime / (ctx->$id:runs != 0 ? ctx->$id:runs : 1),
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-} +-- | Code generation for Python with OpenCL. module Futhark.CodeGen.Backends.PyOpenCL   ( compileProg,   )@@ -10,7 +11,6 @@ import qualified Data.Map as M import qualified Futhark.CodeGen.Backends.GenericPython as Py import Futhark.CodeGen.Backends.GenericPython.AST-import Futhark.CodeGen.Backends.GenericPython.Definitions import Futhark.CodeGen.Backends.GenericPython.Options import Futhark.CodeGen.Backends.PyOpenCL.Boilerplate import qualified Futhark.CodeGen.ImpCode.OpenCL as Imp@@ -19,13 +19,14 @@ import Futhark.MonadFreshNames import Futhark.Util (zEncodeString) ---maybe pass the config file rather than multiple arguments+-- | Compile the program to Python with calls to OpenCL. compileProg ::   MonadFreshNames m =>-  Maybe String ->+  Py.CompilerMode ->+  String ->   Prog KernelsMem ->   m (ImpGen.Warnings, String)-compileProg module_name prog = do+compileProg mode class_name prog = do   ( ws,     Imp.Program       opencl_code@@ -57,11 +58,7 @@           Assign (Var "default_group_size") None,           Assign (Var "default_num_groups") None,           Assign (Var "default_tile_size") None,-          Assign (Var "fut_opencl_src") $ RawStringLiteral $ opencl_prelude ++ opencl_code,-          Escape pyValues,-          Escape pyFunctions,-          Escape pyPanic,-          Escape pyTuning+          Assign (Var "fut_opencl_src") $ RawStringLiteral $ opencl_prelude ++ opencl_code         ]    let imports =@@ -152,7 +149,8 @@    (ws,)     <$> Py.compileProg-      module_name+      mode+      class_name       constructor       imports       defines
src/Futhark/CodeGen/Backends/SequentialC.hs view
@@ -8,6 +8,7 @@     GC.CParts (..),     GC.asLibrary,     GC.asExecutable,+    GC.asServer,   ) where @@ -80,8 +81,10 @@                           int detail_memory;                           int debugging;                           int profiling;+                          int logging;                           typename lock_t lock;                           char *error;+                          typename FILE *log;                           int profiling_paused;                           $sdecls:fields                         };|]@@ -97,7 +100,9 @@                                   ctx->detail_memory = cfg->debugging;                                   ctx->debugging = cfg->debugging;                                   ctx->profiling = cfg->debugging;+                                  ctx->logging = cfg->debugging;                                   ctx->error = NULL;+                                  ctx->log = stderr;                                   create_lock(&ctx->lock);                                   $stms:init_fields                                   init_constants(ctx);@@ -115,14 +120,6 @@         )        GC.publicDef_ "context_sync" GC.MiscDecl $ \s ->-        ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],-          [C.cedecl|int $id:s(struct $id:ctx* ctx) {-                                 (void)ctx;-                                 return 0;-                               }|]-        )--      GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->         ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],           [C.cedecl|int $id:s(struct $id:ctx* ctx) {                                  (void)ctx;
src/Futhark/CodeGen/Backends/SequentialPython.hs view
@@ -1,3 +1,4 @@+-- | Code generation for sequential Python. module Futhark.CodeGen.Backends.SequentialPython   ( compileProg,   )@@ -6,22 +7,24 @@ import Control.Monad import qualified Futhark.CodeGen.Backends.GenericPython as GenericPython import Futhark.CodeGen.Backends.GenericPython.AST-import Futhark.CodeGen.Backends.GenericPython.Definitions import qualified Futhark.CodeGen.ImpCode.Sequential as Imp import qualified Futhark.CodeGen.ImpGen.Sequential as ImpGen import Futhark.IR.SeqMem import Futhark.MonadFreshNames +-- | Compile the program to Python. compileProg ::   MonadFreshNames m =>-  Maybe String ->+  GenericPython.CompilerMode ->+  String ->   Prog SeqMem ->   m (ImpGen.Warnings, String)-compileProg module_name =+compileProg mode class_name =   ImpGen.compileProg     >=> traverse       ( GenericPython.compileProg-          module_name+          mode+          class_name           GenericPython.emptyConstructor           imports           defines@@ -37,7 +40,7 @@         Import "ctypes" $ Just "ct",         Import "time" Nothing       ]-    defines = [Escape pyValues, Escape pyFunctions, Escape pyPanic, Escape pyTuning]+    defines = []     operations :: GenericPython.Operations Imp.Sequential ()     operations =       GenericPython.defaultOperations
src/Futhark/CodeGen/Backends/SimpleRep.hs view
@@ -12,7 +12,9 @@     signedPrimTypeToCType,     arrayName,     opaqueName,+    externalValueType,     cproduct,+    csum,      -- * Primitive value operations     cIntOps,@@ -21,6 +23,11 @@     cFloat64Ops,     cFloat64Funs,     cFloatConvOps,++    -- * Storing/restoring values in byte sequences+    storageSize,+    storeValueHeader,+    loadValueHeader,   ) where @@ -117,6 +124,16 @@           )     iter x = ((x :: Word32) `shiftR` 16) `xor` x +-- | The type used to expose a Futhark value in the C API.  A pointer+-- in the case of arrays and opaques.+externalValueType :: ExternalValue -> C.Type+externalValueType (OpaqueValue desc vds) =+  [C.cty|struct $id:("futhark_" ++ opaqueName desc vds)*|]+externalValueType (TransparentValue (ArrayValue _ _ pt signed shape)) =+  [C.cty|struct $id:("futhark_" ++ arrayName pt signed (length shape))*|]+externalValueType (TransparentValue (ScalarValue pt signed _)) =+  signedPrimTypeToCType signed pt+ -- | Return an expression multiplying together the given expressions. -- If an empty list is given, the expression @1@ is returned. cproduct :: [C.Exp] -> C.Exp@@ -125,6 +142,14 @@   where     mult x y = [C.cexp|$exp:x * $exp:y|] +-- | Return an expression summing the given expressions.+-- If an empty list is given, the expression @0@ is returned.+csum :: [C.Exp] -> C.Exp+csum [] = [C.cexp|0|]+csum (e : es) = foldl mult e es+  where+    mult x y = [C.cexp|$exp:x + $exp:y|]+ instance C.ToIdent Name where   toIdent = C.toIdent . zEncodeString . nameToString @@ -953,3 +978,65 @@     } $esc:("#endif") |]++storageSize :: PrimType -> Int -> C.Exp -> C.Exp+storageSize pt rank shape =+  [C.cexp|$int:header_size ++          $int:rank * sizeof(typename int64_t) ++          $exp:(cproduct dims) * $int:pt_size|]+  where+    header_size, pt_size :: Int+    header_size = 1 + 1 + 1 + 4 -- 'b' <version> <num_dims> <type>+    pt_size = primByteSize pt+    dims = [[C.cexp|$exp:shape[$int:i]|] | i <- [0 .. rank -1]]++typeStr :: Signedness -> PrimType -> String+typeStr sign pt =+  case (sign, pt) of+    (_, Bool) -> "bool"+    (_, Cert) -> "bool"+    (_, FloatType Float32) -> " f32"+    (_, FloatType Float64) -> " f64"+    (TypeDirect, IntType Int8) -> "  i8"+    (TypeDirect, IntType Int16) -> " i16"+    (TypeDirect, IntType Int32) -> " i32"+    (TypeDirect, IntType Int64) -> " i64"+    (TypeUnsigned, IntType Int8) -> "  u8"+    (TypeUnsigned, IntType Int16) -> " u16"+    (TypeUnsigned, IntType Int32) -> " u32"+    (TypeUnsigned, IntType Int64) -> " u64"++storeValueHeader :: Signedness -> PrimType -> Int -> C.Exp -> C.Exp -> [C.Stm]+storeValueHeader sign pt rank shape dest =+  [C.cstms|+          *$exp:dest++ = 'b';+          *$exp:dest++ = 1;+          *$exp:dest++ = $int:rank;+          memcpy($exp:dest, $string:(typeStr sign pt), 4);+          $exp:dest += 4;+          $stms:copy_shape+          |]+  where+    copy_shape+      | rank == 0 = []+      | otherwise =+        [C.cstms|+                memcpy($exp:dest, $exp:shape, $int:rank*sizeof(typename int64_t));+                $exp:dest += $int:rank*sizeof(typename int64_t);|]++loadValueHeader :: Signedness -> PrimType -> Int -> C.Exp -> C.Exp -> [C.Stm]+loadValueHeader sign pt rank shape src =+  [C.cstms|+     err |= (*$exp:src++ != 'b');+     err |= (*$exp:src++ != 1);+     err |= (*$exp:src++ != $exp:rank);+     err |= (memcmp($exp:src, $string:(typeStr sign pt), 4) != 0);+     $exp:src += 4;+     if (err == 0) {+       $stms:load_shape+       $exp:src += $int:rank*sizeof(typename int64_t);+     }|]+  where+    load_shape+      | rank == 0 = []+      | otherwise = [C.cstms|memcpy($exp:shape, src, $int:rank*sizeof(typename int64_t));|]
src/Futhark/CodeGen/ImpCode.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-} {-# LANGUAGE TupleSections #-} @@ -133,9 +132,8 @@  -- | A description of an externally meaningful value. data ValueDesc-  = -- | An array with memory block, memory block size,-    -- memory space, element type, signedness of element-    -- type (if applicable), and shape.+  = -- | An array with memory block memory space, element type,+    -- signedness of element type (if applicable), and shape.     ArrayValue VName Space PrimType Signedness [DimSize]   | -- | A scalar value with signedness if applicable.     ScalarValue PrimType Signedness VName@@ -601,6 +599,8 @@   traverse _ (DebugPrint s v) =     pure $ DebugPrint s v +-- | The names declared with 'DeclareMem', 'DeclareScalar', and+-- 'DeclareArray' in the given code. declaredIn :: Code a -> Names declaredIn (DeclareMem name _) = oneName name declaredIn (DeclareScalar name _ _) = oneName name
src/Futhark/CodeGen/ImpGen.hs view
@@ -1567,7 +1567,7 @@   error $ "compileAlloc: Invalid pattern: " ++ pretty pat  -- | The number of bytes needed to represent the array in a--- straightforward contiguous format, as an 'Int64' expression.+-- straightforward contiguous format, as an t'Int64' expression. typeSize :: Type -> Count Bytes (Imp.TExp Int64) typeSize t =   Imp.bytes $
src/Futhark/CodeGen/ImpGen/CUDA.hs view
@@ -1,3 +1,4 @@+-- | Code generation for ImpCode with CUDA kernels. module Futhark.CodeGen.ImpGen.CUDA   ( compileProg,     Warnings,@@ -11,5 +12,6 @@ import Futhark.IR.KernelsMem import Futhark.MonadFreshNames +-- | Compile the program to ImpCode with CUDA kernels. compileProg :: MonadFreshNames m => Prog KernelsMem -> m (Warnings, Program) compileProg prog = second kernelsToCUDA <$> compileProgCUDA prog
src/Futhark/CodeGen/ImpGen/Kernels/SegRed.hs view
@@ -71,7 +71,7 @@ -- | Code generation for the body of the SegRed, taking a continuation -- for saving the results of the body.  The results should be -- represented as a pairing of a t'SubExp' along with a list of--- indexes into that 'SubExp' for reading the result.+-- indexes into that t'SubExp' for reading the result. type DoSegBody = ([(SubExp, [Imp.TExp Int64])] -> InKernelGen ()) -> InKernelGen ()  -- | Compile 'SegRed' instance to host-level code with calls to
src/Futhark/CodeGen/ImpGen/Kernels/SegScan.hs view
@@ -1,3 +1,6 @@+-- | Code generation for 'SegScan'.  Dispatches to either a+-- single-pass or two-pass implementation, depending on the nature of+-- the scan and the chosen abckend. module Futhark.CodeGen.ImpGen.Kernels.SegScan (compileSegScan) where  import qualified Futhark.CodeGen.ImpCode.Kernels as Imp
src/Futhark/CodeGen/ImpGen/Multicore.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} +-- | Code generation for ImpCode with multicore operations. module Futhark.CodeGen.ImpGen.Multicore   ( Futhark.CodeGen.ImpGen.Multicore.compileProg,     Warnings,
src/Futhark/CodeGen/ImpGen/Multicore/Base.hs view
@@ -170,8 +170,8 @@     else Imp.Dynamic  -- | Try to extract invariant allocations.  If we assume that the--- given 'Code' is the body of a 'SegOp', then it is always safe to--- move the immediate allocations to the prebody.+-- given 'Imp.Code' is the body of a 'SegOp', then it is always safe+-- to move the immediate allocations to the prebody. extractAllocations :: Imp.Code -> (Imp.Code, Imp.Code) extractAllocations segop_code = f segop_code   where
src/Futhark/CodeGen/ImpGen/Multicore/SegMap.hs view
@@ -1,3 +1,4 @@+-- | Multicore code generation for 'SegMap'. module Futhark.CodeGen.ImpGen.Multicore.SegMap   ( compileSegMap,   )
src/Futhark/CodeGen/ImpGen/OpenCL.hs view
@@ -1,3 +1,4 @@+-- | Code generation for ImpCode with OpenCL kernels. module Futhark.CodeGen.ImpGen.OpenCL   ( compileProg,     Warnings,@@ -11,5 +12,6 @@ import Futhark.IR.KernelsMem import Futhark.MonadFreshNames +-- | Compile the program to ImpCode with OpenCL kernels. compileProg :: MonadFreshNames m => Prog KernelsMem -> m (Warnings, OpenCL.Program) compileProg prog = second kernelsToOpenCL <$> compileProgOpenCL prog
src/Futhark/Compiler.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-}  -- | High-level API for invoking the Futhark compiler.@@ -13,7 +12,6 @@     handleWarnings,     module Futhark.Compiler.Program,     readProgram,-    readLibrary,     readProgramOrDie,   ) where@@ -46,7 +44,9 @@     -- | If true, error on any warnings.     futharkWerror :: Bool,     -- | If True, ignore @unsafe@.-    futharkSafe :: Bool+    futharkSafe :: Bool,+    -- | Additional functions that should be exposed as entry points.+    futharkEntryPoints :: [Name]   }  -- | The default compiler configuration.@@ -56,7 +56,8 @@     { futharkVerbose = (NotVerbose, Nothing),       futharkWarn = True,       futharkWerror = False,-      futharkSafe = False+      futharkSafe = False,+      futharkEntryPoints = []     }  -- | Print a compiler error to stdout.  The 'FutharkConfig' controls@@ -123,7 +124,9 @@   when (pipelineVerbose pipeline_config) $     logMsg ("Reading and type-checking source program" :: String)   (prog_imports, namesrc) <--    handleWarnings config $ (\(a, b, c) -> (a, (b, c))) <$> readProgram file+    handleWarnings config $+      (\(a, b, c) -> (a, (b, c)))+        <$> readProgram (futharkEntryPoints config) file    putNameSource namesrc   when (pipelineVerbose pipeline_config) $@@ -151,22 +154,15 @@ -- | Read and type-check a Futhark program, including all imports. readProgram ::   (MonadError CompilerError m, MonadIO m) =>+  [I.Name] ->   FilePath ->   m (Warnings, Imports, VNameSource)-readProgram = readLibrary . pure---- | Read and type-check a collection of Futhark files, including all--- imports.-readLibrary ::-  (MonadError CompilerError m, MonadIO m) =>-  [FilePath] ->-  m (Warnings, Imports, VNameSource)-readLibrary = readLibraryWithBasis emptyBasis+readProgram extra_eps = readLibrary extra_eps . pure  -- | Not verbose, and terminates process on error. readProgramOrDie :: MonadIO m => FilePath -> m (Warnings, Imports, VNameSource) readProgramOrDie file = liftIO $ do-  res <- runFutharkM (readProgram file) NotVerbose+  res <- runFutharkM (readProgram mempty file) NotVerbose   case res of     Left err -> do       dumpError newFutharkConfig err
src/Futhark/Compiler/CLI.hs view
@@ -14,11 +14,10 @@ import Control.Monad import Data.Maybe import Futhark.Compiler-import Futhark.IR (Prog)+import Futhark.IR (Name, Prog, nameFromString) import Futhark.IR.SOACS (SOACS) import Futhark.Pipeline import Futhark.Util.Options-import System.Console.GetOpt import System.FilePath import System.IO @@ -117,6 +116,11 @@       (NoArg $ Right $ \config -> config {compilerMode = ToExecutable})       "Generate an executable instead of a library (set by default).",     Option+      []+      ["server"]+      (NoArg $ Right $ \config -> config {compilerMode = ToServer})+      "Generate a server executable instead of a library.",+    Option       "w"       []       (NoArg $ Right $ \config -> config {compilerWarn = False})@@ -130,7 +134,20 @@       []       ["safe"]       (NoArg $ Right $ \config -> config {compilerSafe = True})-      "Ignore 'unsafe' in code."+      "Ignore 'unsafe' in code.",+    Option+      []+      ["entry-points"]+      ( ReqArg+          ( \arg -> Right $ \config ->+              config+                { compilerEntryPoints =+                    nameFromString arg : compilerEntryPoints config+                }+          )+          "NAME"+      )+      "Treat this function as an additional entry point."   ]  wrapOption :: CompilerOption cfg -> CoreCompilerOption cfg@@ -156,11 +173,16 @@     compilerWerror :: Bool,     compilerSafe :: Bool,     compilerWarn :: Bool,-    compilerConfig :: cfg+    compilerConfig :: cfg,+    compilerEntryPoints :: [Name]   }  -- | Are we compiling a library or an executable?-data CompilerMode = ToLibrary | ToExecutable deriving (Eq, Ord, Show)+data CompilerMode+  = ToLibrary+  | ToExecutable+  | ToServer+  deriving (Eq, Ord, Show)  -- | The configuration of the compiler. newCompilerConfig :: cfg -> CompilerConfig cfg@@ -172,7 +194,8 @@       compilerWerror = False,       compilerSafe = False,       compilerWarn = True,-      compilerConfig = x+      compilerConfig = x,+      compilerEntryPoints = mempty     }  outputFilePath :: FilePath -> CompilerConfig cfg -> FilePath@@ -185,5 +208,6 @@     { futharkVerbose = compilerVerbose config,       futharkWerror = compilerWerror config,       futharkSafe = compilerSafe config,-      futharkWarn = compilerWarn config+      futharkWarn = compilerWarn config,+      futharkEntryPoints = compilerEntryPoints config     }
src/Futhark/Compiler/Program.hs view
@@ -5,7 +5,7 @@ -- | Low-level compilation parts.  Look at "Futhark.Compiler" for a -- more high-level API. module Futhark.Compiler.Program-  ( readLibraryWithBasis,+  ( readLibrary,     readImports,     Imports,     FileModule (..),@@ -18,9 +18,8 @@ import Control.Exception import Control.Monad import Control.Monad.Except-import Control.Monad.Reader import Control.Monad.State-import Data.List (intercalate)+import Data.List (intercalate, isPrefixOf) import Data.Maybe import qualified Data.Text as T import qualified Data.Text.IO as T@@ -36,40 +35,56 @@ import qualified System.FilePath.Posix as Posix import System.IO.Error --- | A little monad for reading and type-checking a Futhark program.-type CompilerM m = ReaderT [FilePath] (StateT ReaderState m)+readFileSafely :: String -> IO (Maybe (Either String (String, T.Text)))+readFileSafely filepath =+  (Just . Right . (filepath,) <$> T.readFile filepath) `catch` couldNotRead+  where+    couldNotRead e+      | isDoesNotExistError e =+        return Nothing+      | otherwise =+        return $ Just $ Left $ show e -data ReaderState = ReaderState-  { alreadyImported :: Imports,-    nameSource :: VNameSource,-    warnings :: E.Warnings-  }+newtype ReaderState = ReaderState+  {alreadyRead :: [(ImportName, E.UncheckedProg)]} --- | Pre-typechecked imports, including a starting point for the name source.-data Basis = Basis-  { basisImports :: Imports,-    basisNameSource :: VNameSource,-    -- | Files that should be implicitly opened.-    basisRoots :: [String]-  }+-- | A little monad for parsing a Futhark program.+type ReaderM m = StateT ReaderState m --- | A basis that contains no imports, and has a properly initialised--- name source.-emptyBasis :: Basis-emptyBasis =-  Basis-    { basisImports = mempty,-      basisNameSource = src,-      basisRoots = mempty-    }+runReaderM ::+  (MonadError CompilerError m) =>+  ReaderM m a ->+  m [(ImportName, E.UncheckedProg)]+runReaderM m = reverse . alreadyRead <$> execStateT m (ReaderState mempty)++readImportFile ::+  (MonadError CompilerError m, MonadIO m) =>+  ImportName ->+  ReaderM m (T.Text, FilePath)+readImportFile include = do+  -- First we try to find a file of the given name in the search path,+  -- then we look at the builtin library if we have to.  For the+  -- builtins, we don't use the search path.+  r <- liftIO $ readFileSafely $ includeToFilePath include+  case (r, lookup prelude_str prelude) of+    (Just (Right (filepath, s)), _) -> return (s, filepath)+    (Just (Left e), _) -> externalErrorS e+    (Nothing, Just t) -> return (t, prelude_str)+    (Nothing, Nothing) -> externalErrorS not_found   where-    src = newNameSource $ E.maxIntrinsicTag + 1+    prelude_str = "/" Posix.</> includeToString include Posix.<.> "fut" +    not_found =+      "Error at " ++ E.locStr (E.srclocOf include)+        ++ ": could not find import '"+        ++ includeToString include+        ++ "'."+ readImport ::   (MonadError CompilerError m, MonadIO m) =>   [ImportName] ->   ImportName ->-  CompilerM m ()+  ReaderM m () readImport steps include   | include `elem` steps =     externalErrorS $@@ -78,7 +93,7 @@           " -> "           (map includeToString $ reverse $ include : steps)   | otherwise = do-    already_done <- gets $ isJust . lookup (includeToString include) . alreadyImported+    already_done <- gets $ isJust . lookup include . alreadyRead      unless already_done $       uncurry (handleFile steps include) =<< readImportFile include@@ -89,102 +104,98 @@   ImportName ->   T.Text ->   FilePath ->-  CompilerM m ()-handleFile steps include file_contents file_name = do+  ReaderM m ()+handleFile steps import_name file_contents file_name = do   prog <- case parseFuthark file_name file_contents of     Left err -> externalErrorS $ show err     Right prog -> return prog -  mapM_ (readImport steps' . uncurry (mkImportFrom include)) $+  let steps' = import_name : steps+  mapM_ (readImport steps' . uncurry (mkImportFrom import_name)) $     E.progImports prog -  -- It is important to not read these before the above calls to-  -- readImport.-  imports <- gets alreadyImported-  src <- gets nameSource-  roots <- ask+  modify $ \s ->+    s {alreadyRead = (import_name, prog) : alreadyRead s} -  case E.checkProg imports src include $ prependRoots roots prog of-    (prog_ws, Left err) -> do-      prev_ws <- gets warnings-      let ws = prev_ws <> prog_ws-      externalError $-        if anyWarnings ws-          then ppr (prev_ws <> ws) </> line <> ppr err-          else ppr err-    (ws, Right (m, src')) ->-      modify $ \s ->-        s-          { alreadyImported = (includeToString include, m) : imports,-            nameSource = src',-            warnings = warnings s <> ws-          }-  where-    steps' = include : steps+-- | Pre-typechecked imports, including a starting point for the name source.+data Basis = Basis+  { basisImports :: Imports,+    basisNameSource :: VNameSource,+    -- | Files that should be implicitly opened.+    basisRoots :: [String]+  } -readFileSafely :: String -> IO (Maybe (Either String (String, T.Text)))-readFileSafely filepath =-  (Just . Right . (filepath,) <$> T.readFile filepath) `catch` couldNotRead+-- | A basis that contains no imports, and has a properly initialised+-- name source.+emptyBasis :: Basis+emptyBasis =+  Basis+    { basisImports = mempty,+      basisNameSource = src,+      basisRoots = mempty+    }   where-    couldNotRead e-      | isDoesNotExistError e =-        return Nothing-      | otherwise =-        return $ Just $ Left $ show e+    src = newNameSource $ E.maxIntrinsicTag + 1 -readImportFile ::-  (MonadError CompilerError m, MonadIO m) =>-  ImportName ->-  m (T.Text, FilePath)-readImportFile include = do-  -- First we try to find a file of the given name in the search path,-  -- then we look at the builtin library if we have to.  For the-  -- builtins, we don't use the search path.-  r <- liftIO $ readFileSafely $ includeToFilePath include-  case (r, lookup prelude_str prelude) of-    (Just (Right (filepath, s)), _) -> return (s, filepath)-    (Just (Left e), _) -> externalErrorS e-    (Nothing, Just t) -> return (t, prelude_str)-    (Nothing, Nothing) -> externalErrorS not_found+typeCheckProgram ::+  MonadError CompilerError m =>+  Basis ->+  [(ImportName, E.UncheckedProg)] ->+  m (E.Warnings, Imports, VNameSource)+typeCheckProgram basis =+  foldM f (mempty, basisImports basis, basisNameSource basis)   where-    prelude_str = "/" Posix.</> includeToString include Posix.<.> "fut"+    roots = ["/prelude/prelude"] -    not_found =-      "Error at " ++ E.locStr (E.srclocOf include)-        ++ ": could not find import '"-        ++ includeToString include-        ++ "'."+    f (ws, imports, src) (import_name, prog) = do+      let prog'+            | "/prelude" `isPrefixOf` includeToFilePath import_name = prog+            | otherwise = prependRoots roots prog+      case E.checkProg imports src import_name prog' of+        (prog_ws, Left err) -> do+          let ws' = ws <> prog_ws+          externalError $+            if anyWarnings ws'+              then ppr ws' </> line <> ppr err+              else ppr err+        (prog_ws, Right (m, src')) ->+          pure+            ( ws <> prog_ws,+              imports ++ [(includeToString import_name, m)],+              src'+            ) --- | Read Futhark files from some basis, and printing log messages if--- the first parameter is True.-readLibraryWithBasis ::-  (MonadError CompilerError m, MonadIO m) =>-  Basis ->+setEntryPoints ::+  [E.Name] ->   [FilePath] ->-  m-    ( E.Warnings,-      Imports,-      VNameSource-    )-readLibraryWithBasis builtin fps = do-  (_, imps, src) <--    runCompilerM builtin $-      readImport [] $ mkInitialImport "/prelude/prelude"-  let basis = Basis imps src ["/prelude/prelude"]-  readLibrary' basis fps+  [(ImportName, E.UncheckedProg)] ->+  [(ImportName, E.UncheckedProg)]+setEntryPoints extra_eps fps = map onProg+  where+    onProg (name, prog)+      | includeToFilePath name `elem` fps =+        (name, prog {E.progDecs = map onDec (E.progDecs prog)})+      | otherwise =+        (name, prog) --- | Read and type-check a Futhark library (multiple files, relative--- to the same search path), including all imports.-readLibrary' ::+    onDec (E.ValDec vb)+      | E.valBindName vb `elem` extra_eps =+        E.ValDec vb {E.valBindEntryPoint = Just E.NoInfo}+    onDec dec = dec++-- | Read and type-check some Futhark files.+readLibrary ::   (MonadError CompilerError m, MonadIO m) =>-  Basis ->+  -- | Extra functions that should be marked as entry points; only+  -- applies to the immediate files, not any imports imported.+  [E.Name] ->+  -- | The files to read.   [FilePath] ->-  m-    ( E.Warnings,-      Imports,-      VNameSource-    )-readLibrary' basis fps = runCompilerM basis $ mapM onFile fps+  m (E.Warnings, Imports, VNameSource)+readLibrary extra_eps fps =+  typeCheckProgram emptyBasis . setEntryPoints extra_eps fps <=< runReaderM $ do+    readImport [] (mkInitialImport "/prelude/prelude")+    mapM_ onFile fps   where     onFile fp = do       r <- liftIO $ readFileSafely fp@@ -208,22 +219,9 @@       Imports,       VNameSource     )-readImports basis imps =-  runCompilerM basis $ mapM (readImport []) imps--runCompilerM ::-  Monad m =>-  Basis ->-  CompilerM m a ->-  m (E.Warnings, [(String, FileModule)], VNameSource)-runCompilerM (Basis imports src roots) m = do-  let s = ReaderState (reverse imports) src mempty-  s' <- execStateT (runReaderT m roots) s-  return-    ( warnings s',-      reverse $ alreadyImported s',-      nameSource s'-    )+readImports basis imps = do+  files <- runReaderM $ mapM (readImport []) imps+  typeCheckProgram basis files  prependRoots :: [FilePath] -> E.UncheckedProg -> E.UncheckedProg prependRoots roots (E.Prog doc ds) =
src/Futhark/Construct.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE TypeFamilies #-}  -- | = Constructing Futhark ASTs
src/Futhark/Error.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Safe #-}  -- | Futhark error definitions. module Futhark.Error
src/Futhark/IR.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE Safe #-}- -- | A convenient re-export of basic AST modules.  Note that -- "Futhark.IR.Lore" is not exported, as this would -- cause name clashes.  You are advised to use a qualified import of
src/Futhark/IR/Mem.hs view
@@ -134,6 +134,7 @@ import Futhark.Transform.Substitute import qualified Futhark.TypeCheck as TC import Futhark.Util+import Futhark.Util.Pretty (indent, ppr, text, (</>)) import qualified Futhark.Util.Pretty as PP import GHC.Generics (Generic) import Language.SexpGrammar as Sexp@@ -661,7 +662,7 @@     M.Map (Ext VName) (TPrimExp Int64 (Ext VName))   ) getExtMaps ctx_lst_ids =-  ( M.map leafExp $ M.mapKeys Free $ M.fromListWith (flip const) ctx_lst_ids,+  ( M.map leafExp $ M.mapKeys Free $ M.fromListWith (const id) ctx_lst_ids,     M.fromList $       mapMaybe         ( traverse@@ -757,15 +758,14 @@           (x_mem, x_mem_type) <- fetchCtx x_ext           unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $             throwError $-              unwords-                [ "Index function unification failed (ReturnsNewBlock)",-                  "\nixfun of body result: ",-                  pretty y_ixfun,-                  "\nixfun of return type: ",-                  pretty x_ixfun,-                  "\nand context elements: ",-                  pretty ctx_res-                ]+              pretty $+                "Index function unification failed (ReturnsNewBlock)"+                  </> "Ixfun of body result:"+                  </> indent 2 (ppr y_ixfun)+                  </> "Ixfun of return type:"+                  </> indent 2 (ppr x_ixfun)+                  </> "Context elements: "+                  </> indent 2 (ppr ctx_res)           case x_mem_type of             MemMem y_space ->               unless (x_space == y_space) $@@ -801,12 +801,12 @@       bad s =         TC.bad $           TC.TypeError $-            PP.pretty $+            pretty $               "Return type"-                PP.</> PP.indent 2 (ppTuple' rettype)-                PP.</> "cannot match returns of results"-                PP.</> PP.indent 2 (ppTuple' ts)-                PP.</> PP.text s+                </> indent 2 (ppTuple' rettype)+                </> "cannot match returns of results"+                </> indent 2 (ppTuple' ts)+                </> text s    unless (length (S.unions $ map extsInMemInfo rettype) == length ctx_res) $     TC.bad $
src/Futhark/IR/Mem/IxFun.hs view
@@ -564,7 +564,7 @@   IxFun num ->   ShapeChange num ->   Maybe (IxFun num)-reshapeCoercion (IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do+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@@ -580,7 +580,7 @@             dims'             (newDims newshape)       lmad' = LMAD off dims''-  return $ IxFun (lmad' :| lmads) (newDims newshape) cg+  return $ IxFun (lmad' :| lmads) oldbase cg  -- | Handle the case where a reshape operation can stay inside a single LMAD. --@@ -603,7 +603,7 @@   IxFun num ->   ShapeChange num ->   Maybe (IxFun num)-reshapeOneLMAD ixfun@(IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do+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@@ -678,7 +678,7 @@           sortBy (compare `on` fst) $             zip sup_inds dims_sup ++ zip rpt_inds repeats'       lmad' = LMAD off' dims'-  return $ IxFun (setLMADPermutation perm' lmad' :| lmads) (newDims newshape) cg+  return $ IxFun (setLMADPermutation perm' lmad' :| lmads) oldbase cg   where     consecutive _ [] = True     consecutive i [p] = i == p
src/Futhark/IR/Primitive.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE TypeOperators #-}  -- | Definitions of primitive types, the values that inhabit these
src/Futhark/IR/Prop.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE TypeFamilies #-}  -- | This module provides various simple ways to query and manipulate
src/Futhark/IR/Prop/Names.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE UndecidableInstances #-}  -- | Facilities for determining which names are used in some syntactic
src/Futhark/IR/Prop/Types.hs view
@@ -417,7 +417,7 @@     ext (Free _) = Nothing  -- | If all dimensions of the given 'ExtShape' are statically known,--- change to the corresponding 'Shape'.+-- change to the corresponding t'Shape'. hasStaticShape :: TypeBase ExtShape u -> Maybe (TypeBase Shape u) hasStaticShape (Prim bt) = Just $ Prim bt hasStaticShape (Mem space) = Just $ Mem space
src/Futhark/IR/SOACS.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE TypeFamilies #-}  -- | A simple representation with SOACs and nested parallelism.
src/Futhark/IR/SOACS/Simplify.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}  module Futhark.IR.SOACS.Simplify
src/Futhark/IR/Syntax/Core.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-}  -- | The most primitive ("core") aspects of the AST.  Split out of
src/Futhark/IR/Traversals.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE Safe #-}- -- | -- -- Functions for generic traversals across Futhark syntax trees.  The
src/Futhark/Internalise.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-}@@ -28,6 +27,7 @@ import Futhark.Internalise.TypesValues import Futhark.Transform.Rename as I import Futhark.Util (splitAt3)+import Futhark.Util.Pretty (prettyOneLine) import Language.Futhark as E hiding (TypeArg) import Language.Futhark.Semantic (Imports) @@ -177,7 +177,7 @@       | otherwise =         [I.TypeOpaque desc $ length ts]       where-        desc = maybe (pretty t') typeExpOpaqueName $ E.entryAscribed t+        desc = maybe (prettyOneLine t') typeExpOpaqueName $ E.entryAscribed t         t' = noSizes (E.entryType t) `E.setUniqueness` Nonunique     typeExpOpaqueName (TEApply te TypeArgExpDim {} _) =       typeExpOpaqueName te@@ -187,7 +187,7 @@             ++ "_"             ++ show (1 + d)             ++ "d"-    typeExpOpaqueName te = pretty te+    typeExpOpaqueName te = prettyOneLine te      withoutDims (TEArray te _ _) =       let (d, te') = withoutDims te@@ -493,7 +493,8 @@       ()         | Just internalise <- isOverloadedFunction qfname (map fst args) loc ->           internalise desc-        | Just (rettype, _) <- M.lookup fname I.builtInFunctions -> do+        | baseTag (qualLeaf qfname) <= maxIntrinsicTag,+          Just (rettype, _) <- M.lookup fname I.builtInFunctions -> do           let tag ses = [(se, I.Observe) | se <- ses]           args' <- reverse <$> mapM (internaliseArg arg_desc) (reverse args)           let args'' = concatMap tag args'@@ -860,6 +861,11 @@       return ([], id_ses, rest_ses)     compares (E.PatternParens pat _) ses =       compares pat ses+    -- XXX: treat empty tuples and records as bool.+    compares (E.TuplePattern [] loc) ses =+      compares (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc) ses+    compares (E.RecordPattern [] loc) ses =+      compares (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc) ses     compares (E.TuplePattern pats _) ses =       comparesMany pats ses     compares (E.RecordPattern fs _) ses =
src/Futhark/Internalise/Bindings.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Safe #-}  -- | Internalising bindings. module Futhark.Internalise.Bindings
src/Futhark/Internalise/LiftLambdas.hs view
@@ -168,6 +168,8 @@   addValBind $ vb {valBindBody = e}  {-# NOINLINE transformProg #-}++-- | Perform the transformation. transformProg :: MonadFreshNames m => [ValBind] -> m [ValBind] transformProg vbinds =   modifyNameSource $ \namesrc ->
src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs view
@@ -13,7 +13,7 @@ import Control.Monad.Writer import Data.Either import Data.List (find, unzip4)-import Data.Maybe (mapMaybe)+import Data.Maybe (isNothing, mapMaybe) import Futhark.Analysis.PrimExp.Convert import Futhark.Construct import Futhark.IR.Aliases@@ -126,6 +126,14 @@       | Just (DesiredUpdate bindee_nm bindee_dec _cs src slice _val) <-           find ((== v) . updateValue) updates = do         Returns _ se <- Just ret++        -- The slice we're writing per thread must fully cover the+        -- underlying dimensions.+        guard $+          let (dims', slice') =+                unzip . drop (length gtids) . filter (isNothing . dimFix . snd) $+                  zip (arrayDims (typeOf bindee_dec)) slice+           in isFullSlice (Shape dims') slice'          Just $ do           (slice', bodystms) <-
src/Futhark/Optimise/Simplify/ClosedForm.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Safe #-}  -- | This module implements facilities for determining whether a -- reduction or fold can be expressed in a closed form (i.e. not as a
src/Futhark/Optimise/Simplify/Engine.hs view
@@ -574,6 +574,7 @@ cheapExp (BasicOp CmpOp {}) = True cheapExp (BasicOp ConvOp {}) = True cheapExp (BasicOp Copy {}) = False+cheapExp (BasicOp Replicate {}) = False cheapExp (BasicOp Manifest {}) = False cheapExp DoLoop {} = False cheapExp (If _ tbranch fbranch _) =
src/Futhark/Optimise/Simplify/Rules.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} @@ -806,6 +805,10 @@     Just (Copy src, cs)       | Just dims <- arrayDims <$> seType (Var src),         length inds == length dims,+        -- It is generally not safe to simplify a slice of a copy,+        -- because the result may be used in an in-place update of the+        -- original.+        Just _ <- mapM dimFix inds,         not consuming,         ST.available src vtable ->         Just $ pure $ IndexResult cs src inds
src/Futhark/Optimise/TileLoops.hs view
@@ -237,15 +237,17 @@ -- -- The third category duplicates computation, so we only want to do it -- when absolutely necessary.  Currently, this is necessary for--- results that are views of an array (slicing, rotate, etc), because--- these cannot be efficiently represented by a scalar segmap (they'll--- be manifested in memory).+-- results that are views of an array (slicing, rotate, etc) and which+-- results are used after the prelude, because these cannot be+-- efficiently represented by a scalar segmap (they'll be manifested+-- in memory). partitionPrelude ::   VarianceTable ->   Stms Kernels ->   Names ->+  Names ->   (Stms Kernels, Stms Kernels, Stms Kernels)-partitionPrelude variance prestms private =+partitionPrelude variance prestms private used_after =   (invariant_prestms, precomputed_variant_prestms, recomputed_variant_prestms)   where     invariantTo names stm =@@ -264,7 +266,9 @@     mustBeInlinedExp (BasicOp Rearrange {}) = True     mustBeInlinedExp (BasicOp Reshape {}) = True     mustBeInlinedExp _ = False-    mustBeInlined = mustBeInlinedExp . stmExp+    mustBeInlined stm =+      mustBeInlinedExp (stmExp stm)+        && any (`nameIn` used_after) (patternNames (stmPattern stm))      must_be_inlined =       namesFromList $@@ -302,7 +306,7 @@             precomputed_variant_prestms,             recomputed_variant_prestms             ) =-              partitionPrelude variance prestms private'+              partitionPrelude variance prestms private' used        addStms invariant_prestms @@ -337,11 +341,15 @@   Result ->   TileM (Stms Kernels, Tiling, TiledBody) tileDoLoop initial_space variance prestms used_in_body (host_stms, tiling, tiledBody) res_ts pat aux merge i it bound poststms poststms_res = do-  let ( invariant_prestms,+  let prestms_used =+        used_in_body+          <> freeIn poststms+          <> freeIn poststms_res+      ( invariant_prestms,         precomputed_variant_prestms,         recomputed_variant_prestms         ) =-          partitionPrelude variance prestms tiled_kdims+          partitionPrelude variance prestms tiled_kdims prestms_used    let (mergeparams, mergeinits) = unzip merge @@ -356,10 +364,7 @@         let live_set =               namesToList $                 liveSet precomputed_variant_prestms $-                  freeIn recomputed_variant_prestms-                    <> used_in_body-                    <> freeIn poststms-                    <> freeIn poststms_res+                  freeIn recomputed_variant_prestms <> prestms_used          prelude_arrs <-           inScopeOf precomputed_variant_prestms $
src/Futhark/Pass/ExpandAllocations.hs view
@@ -166,7 +166,9 @@    case find badVariant $ M.elems variant_allocs of     Just v ->-      throwError $ "Cannot handle un-sliceable allocation size: " ++ pretty v+      throwError $+        "Cannot handle un-sliceable allocation size: " ++ pretty v+          ++ "\nLikely cause: irregular nested operations inside parallel constructs."     Nothing ->       return () 
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -379,12 +379,20 @@     distributeIfPossible acc >>= \case       Nothing -> addStmToAcc stm acc       Just acc' -> distribute =<< onInnerMap (MapLoop pat (stmAux stm) w lam arrs) acc'-maybeDistributeStm bnd@(Let pat _ (DoLoop [] val form@ForLoop {} body)) acc+maybeDistributeStm bnd@(Let pat aux (DoLoop [] val form@ForLoop {} body)) acc   | null (patternContextElements pat),     bodyContainsParallelism body =     distributeSingleStm acc bnd >>= \case       Just (kernels, res, nest, acc')-        | not $ freeIn form `namesIntersect` boundInKernelNest nest,+        | -- XXX: We cannot distribute if this loop depends on+          -- certificates bound within the loop nest (well, we could,+          -- but interchange would not be valid).  This is not a+          -- fundamental restriction, but an artifact of our+          -- certificate representation, which we should probably+          -- rethink.+          not $+            (freeIn form <> freeIn aux)+              `namesIntersect` boundInKernelNest nest,           Just (perm, pat_unused) <- permutationAndMissing pat res ->           -- We need to pretend pat_unused was used anyway, by adding           -- it to the kernel nest.
+ src/Futhark/Script.hs view
@@ -0,0 +1,305 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++-- | FutharkScript is a (tiny) subset of Futhark used to write small+-- expressions that are evaluated by server executables.  The @futhark+-- literate@ command is the main user.+module Futhark.Script+  ( -- * Server+    ScriptServer,+    withScriptServer,++    -- * Expressions, values, and types+    Exp (..),+    parseExp,+    varsInExp,+    ScriptValueType (..),+    ScriptValue (..),+    scriptValueType,+    ExpValue,++    -- * Evaluation+    evalExp,+    getExpValue,+    evalExpToGround,+    valueToExp,+    freeValue,+  )+where++import Control.Monad.Except+import qualified Data.ByteString.Lazy.Char8 as LBS+import Data.Char+import Data.Foldable (toList)+import Data.IORef+import Data.List (intersperse)+import qualified Data.Map as M+import qualified Data.Set as S+import qualified Data.Text as T+import qualified Data.Text.IO as T+import Data.Traversable+import Data.Void+import Futhark.Server+import qualified Futhark.Test.Values as V+import qualified Futhark.Test.Values.Parser as V+import Futhark.Util (nubOrd)+import Futhark.Util.Pretty hiding (float, line, sep, string, (</>), (<|>))+import Language.Futhark.Prop (primValueType)+import Language.Futhark.Syntax (PrimValue (..))+import System.IO+import System.IO.Temp+import Text.Megaparsec++-- | Like a 'Server', but keeps a bit more state to make FutharkScript+-- more convenient.+data ScriptServer = ScriptServer Server (IORef Int)++-- | Start a server, execute an action, then shut down the server.+-- Similar to 'withServer'.+withScriptServer :: FilePath -> [FilePath] -> (ScriptServer -> IO a) -> IO a+withScriptServer prog options f = withServer prog options $ \server -> do+  counter <- newIORef 0+  f $ ScriptServer server counter++-- | A FutharkScript expression.  This is a simple AST that might not+-- correspond exactly to what the user wrote (e.g. no parentheses or+-- source locations).  This is fine for small expressions, which is+-- all this is meant for.+data Exp+  = Call EntryName [Exp]+  | Const PrimValue+  | Tuple [Exp]+  | Record [(T.Text, Exp)]+  | -- | Server-side variable, *not* Futhark variable (these are+    -- handled in 'Call').+    ServerVar TypeName VarName+  deriving (Show)++instance Pretty Exp where+  ppr = pprPrec 0+  pprPrec _ (ServerVar _ v) = "$" <> strictText v+  pprPrec _ (Const v) = ppr v+  pprPrec i (Call v args) =+    parensIf (i > 0) $ strictText v <+> spread (map (pprPrec 1) args)+  pprPrec _ (Tuple vs) =+    parens $ commasep $ map ppr vs+  pprPrec _ (Record m) = braces $ commasep $ map field m+    where+      field (k, v) = ppr k <> equals <> ppr v++type Parser = Parsec Void T.Text++parseEntryName :: Parser EntryName+parseEntryName =+  fmap T.pack $ (:) <$> satisfy isAlpha <*> many (satisfy constituent)+  where+    constituent c = isAlphaNum c || c == '_'++lexeme :: Parser () -> Parser a -> Parser a+lexeme sep p = p <* sep++inParens :: Parser () -> Parser a -> Parser a+inParens sep = between (lexeme sep "(") (lexeme sep ")")++inBraces :: Parser () -> Parser a -> Parser a+inBraces sep = between (lexeme sep "{") (lexeme sep "}")++-- | Parse a FutharkScript expression.+parseExp :: Parser () -> Parser Exp+parseExp sep =+  choice+    [ inParens sep (mkTuple <$> (parseExp sep `sepBy` pComma)),+      inBraces sep (Record <$> (pField `sepBy` pComma)),+      Call <$> lexeme sep parseEntryName <*> many (parseExp sep),+      Const <$> lexeme sep V.parsePrimValue+    ]+  where+    pField = (,) <$> parseEntryName <*> (pEquals *> parseExp sep)+    pEquals = lexeme sep "="+    pComma = lexeme sep ","+    mkTuple [v] = v+    mkTuple vs = Tuple vs++prettyFailure :: CmdFailure -> T.Text+prettyFailure (CmdFailure bef aft) =+  T.unlines $ bef ++ aft++cmdMaybe :: (MonadError T.Text m, MonadIO m) => IO (Maybe CmdFailure) -> m ()+cmdMaybe m = maybe (pure ()) (throwError . prettyFailure) =<< liftIO m++cmdEither :: (MonadError T.Text m, MonadIO m) => IO (Either CmdFailure a) -> m a+cmdEither m = either (throwError . prettyFailure) pure =<< liftIO m++readVar :: (MonadError T.Text m, MonadIO m) => Server -> VarName -> m V.Value+readVar server v =+  either throwError pure <=< liftIO $+    withSystemTempFile "futhark-server-read" $ \tmpf tmpf_h -> do+      hClose tmpf_h+      store_res <- cmdStore server tmpf [v]+      case store_res of+        Just err -> pure $ Left $ prettyFailure err+        Nothing -> do+          s <- LBS.readFile tmpf+          case V.readValues s of+            Just [val] -> pure $ Right val+            _ -> pure $ Left "Invalid data file produced by Futhark server."++writeVar :: (MonadError T.Text m, MonadIO m) => Server -> VarName -> PrimValue -> m ()+writeVar server v val =+  cmdMaybe . liftIO . withSystemTempFile "futhark-server-write" $ \tmpf tmpf_h -> do+    T.hPutStr tmpf_h $ prettyText val+    hClose tmpf_h+    let t = prettyText $ primValueType val+    cmdRestore server tmpf [(v, t)]++-- | A ScriptValue is either a base value or a partially applied+-- function.  We don't have real first-class functions in+-- FutharkScript, but we sort of have closures.+data ScriptValue v+  = SValue TypeName v+  | -- | Ins, then outs.+    SFun EntryName [TypeName] [TypeName] [ScriptValue v]++instance Functor ScriptValue where+  fmap = fmapDefault++instance Foldable ScriptValue where+  foldMap = foldMapDefault++instance Traversable ScriptValue where+  traverse f (SValue t v) = SValue t <$> f v+  traverse f (SFun fname ins outs vs) =+    SFun fname ins outs <$> traverse (traverse f) vs++-- | The type of a 'ScriptValue' - either a value type or a function type.+data ScriptValueType+  = STValue TypeName+  | -- | Ins, then outs.+    STFun [TypeName] [TypeName]+  deriving (Show)++instance Pretty ScriptValueType where+  ppr (STValue t) = ppr t+  ppr (STFun ins outs) =+    spread $ intersperse "->" (map ppr ins ++ [outs'])+    where+      outs' = case outs of+        [out] -> strictText out+        _ -> parens $ commasep $ map strictText outs++-- | The value that is produced by expression evaluation.  This+-- representation keeps all values on the server.+type ExpValue = V.Compound (ScriptValue VarName)++-- | The type of a 'ScriptValue'.+scriptValueType :: ScriptValue v -> ScriptValueType+scriptValueType (SValue t _) = STValue t+scriptValueType (SFun _ ins outs _) = STFun ins outs++serverVarsInValue :: ExpValue -> S.Set VarName+serverVarsInValue = S.fromList . concatMap isVar . toList+  where+    isVar (SValue _ x) = [x]+    isVar (SFun _ _ _ closure) = concatMap isVar $ toList closure++-- | Convert a value into a corresponding expression.+valueToExp :: ExpValue -> Exp+valueToExp (V.ValueAtom (SValue t v)) =+  ServerVar t v+valueToExp (V.ValueAtom (SFun fname _ _ closure)) =+  Call fname $ map (valueToExp . V.ValueAtom) closure+valueToExp (V.ValueRecord fs) =+  Record $ M.toList $ M.map valueToExp fs+valueToExp (V.ValueTuple fs) =+  Tuple $ map valueToExp fs++-- | Evaluate a FutharkScript expression relative to some running server.+evalExp :: (MonadError T.Text m, MonadIO m) => ScriptServer -> Exp -> m ExpValue+evalExp (ScriptServer server counter) top_level_e = do+  vars <- liftIO $ newIORef []+  let newVar base = liftIO $ do+        x <- readIORef counter+        modifyIORef counter (+ 1)+        let v = base <> prettyText x+        modifyIORef vars (v :)+        pure v++      evalExpToVar e = do+        vs <- evalExpToVars e+        case vs of+          V.ValueAtom (SValue _ v) -> pure v+          V.ValueAtom SFun {} ->+            throwError $ "Expression " <> prettyText e <> " not fully applied."+          _ ->+            throwError $ "Expression " <> prettyText e <> " produced more than one value."+      evalExpToVars (ServerVar t v) =+        pure $ V.ValueAtom $ SValue t v+      evalExpToVars (Call name es) = do+        ins <- mapM evalExpToVar es+        in_types <- cmdEither $ cmdInputs server name+        out_types <- cmdEither $ cmdOutputs server name+        if length in_types == length ins+          then do+            outs <- replicateM (length out_types) $ newVar "out"+            void $ cmdEither $ cmdCall server name outs ins+            pure $ V.mkCompound $ zipWith SValue out_types outs+          else+            pure . V.ValueAtom . SFun name in_types out_types $+              zipWith SValue in_types ins+      evalExpToVars (Const val) = do+        v <- newVar "const"+        writeVar server v val+        pure $ V.ValueAtom $ SValue (prettyText (primValueType val)) v+      evalExpToVars (Tuple es) =+        V.ValueTuple <$> mapM evalExpToVars es+      evalExpToVars e@(Record m) = do+        when (length (nubOrd (map fst m)) /= length (map fst m)) $+          throwError $ "Record " <> prettyText e <> " has duplicate fields."+        V.ValueRecord <$> traverse evalExpToVars (M.fromList m)++  let freeNonresultVars v = do+        let v_vars = serverVarsInValue v+        to_free <- liftIO $ filter (`S.notMember` v_vars) <$> readIORef vars+        cmdMaybe $ cmdFree server to_free+        pure v+      freeVarsOnError e = do+        -- We are intentionally ignoring any errors produced by+        -- cmdFree, because we already have another error to+        -- propagate.  Also, not all of the variables that we put in+        -- 'vars' might actually exist server-side, if we failed in a+        -- Call.+        void $ liftIO $ cmdFree server =<< readIORef vars+        throwError e+  (freeNonresultVars =<< evalExpToVars top_level_e) `catchError` freeVarsOnError++-- | Read actual values from the server.  Fails for values that have+-- no well-defined external representation.+getExpValue ::+  (MonadError T.Text m, MonadIO m) => ScriptServer -> ExpValue -> m (V.Compound V.Value)+getExpValue (ScriptServer server _) e =+  traverse toGround =<< traverse (traverse (readVar server)) e+  where+    toGround (SFun fname _ _ _) =+      throwError $ "Function " <> fname <> " not fully applied."+    toGround (SValue _ v) = pure v++-- | Like 'evalExp', but requires all values to be non-functional.+evalExpToGround ::+  (MonadError T.Text m, MonadIO m) => ScriptServer -> Exp -> m (V.Compound V.Value)+evalExpToGround server e = getExpValue server =<< evalExp server e++-- | The set of Futhark variables that are referenced by the+-- expression - these will have to be entry points in the Futhark+-- program.+varsInExp :: Exp -> S.Set EntryName+varsInExp ServerVar {} = mempty+varsInExp (Call v es) = S.insert v $ foldMap varsInExp es+varsInExp (Tuple es) = foldMap varsInExp es+varsInExp (Record fs) = foldMap (foldMap varsInExp) fs+varsInExp Const {} = mempty++-- | Release all the server-side variables in the value.  Yes,+-- FutharkScript has manual memory management...+freeValue :: (MonadError T.Text m, MonadIO m) => ScriptServer -> ExpValue -> m ()+freeValue (ScriptServer server _) =+  cmdMaybe . cmdFree server . S.toList . serverVarsInValue
+ src/Futhark/Server.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Haskell code for interacting with a Futhark server program (a+-- program compiled with @--server@).+module Futhark.Server+  ( Server,+    withServer,+    CmdFailure (..),+    VarName,+    TypeName,+    EntryName,+    cmdRestore,+    cmdStore,+    cmdCall,+    cmdFree,+    cmdInputs,+    cmdOutputs,+    cmdClear,+    cmdReport,+  )+where++import Control.Exception+import Control.Monad+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import Futhark.Util (isEnvVarAtLeast)+import System.Directory (removeFile)+import System.Exit+import System.IO hiding (stdin, stdout)+import System.IO.Temp+import qualified System.Process as P++-- | A handle to a running server.+data Server = Server+  { serverStdin :: Handle,+    serverStdout :: Handle,+    serverErrLog :: FilePath,+    serverProc :: P.ProcessHandle,+    serverDebug :: Bool+  }++startServer :: FilePath -> [FilePath] -> IO Server+startServer prog options = do+  tmpdir <- getCanonicalTemporaryDirectory+  (err_log_f, err_log_h) <- openTempFile tmpdir "futhark-server-stderr.log"+  (Just stdin, Just stdout, Nothing, phandle) <-+    P.createProcess+      ( (P.proc prog options)+          { P.std_err = P.UseHandle err_log_h,+            P.std_in = P.CreatePipe,+            P.std_out = P.CreatePipe+          }+      )++  code <- P.getProcessExitCode phandle+  case code of+    Just (ExitFailure e) ->+      error $ "Cannot start " ++ prog ++ ": error " ++ show e+    _ ->+      pure $+        Server+          { serverStdin = stdin,+            serverStdout = stdout,+            serverProc = phandle,+            serverDebug = isEnvVarAtLeast "FUTHARK_COMPILER_DEBUGGING" 1,+            serverErrLog = err_log_f+          }++stopServer :: Server -> IO ()+stopServer s = do+  hClose $ serverStdin s+  void $ P.waitForProcess $ serverProc s+  removeFile $ serverErrLog s++-- | Start a server, execute an action, then shut down the server.+withServer :: FilePath -> [FilePath] -> (Server -> IO a) -> IO a+withServer prog options = bracket (startServer prog options) stopServer++-- Read lines of response until the next %%% OK (which is what+-- indicates that the server is ready for new instructions).+responseLines :: Server -> IO [Text]+responseLines s = do+  l <- T.hGetLine $ serverStdout s+  when (serverDebug s) $+    T.hPutStrLn stderr $ "<<< " <> l+  case l of+    "%%% OK" -> pure []+    _ -> (l :) <$> responseLines s++-- | The command failed, and this is why.  The first 'Text' is any+-- output before the failure indincator, and the second Text is the+-- output after the indicator.+data CmdFailure = CmdFailure {failureLog :: [Text], failureMsg :: [Text]}+  deriving (Eq, Ord, Show)++-- Figure out whether the response is a failure, and if so, return the+-- failure message.+checkForFailure :: [Text] -> Either CmdFailure [Text]+checkForFailure [] = Right []+checkForFailure ("%%% FAILURE" : ls) = Left $ CmdFailure mempty ls+checkForFailure (l : ls) =+  case checkForFailure ls of+    Left (CmdFailure xs ys) -> Left $ CmdFailure (l : xs) ys+    Right ls' -> Right $ l : ls'++sendCommand :: Server -> [Text] -> IO (Either CmdFailure [Text])+sendCommand s command = do+  let command' = T.unwords command++  when (serverDebug s) $+    T.hPutStrLn stderr $ ">>> " <> command'++  T.hPutStrLn (serverStdin s) command'+  hFlush $ serverStdin s+  checkForFailure <$> responseLines s `catch` onError+  where+    onError :: IOError -> IO a+    onError e = do+      code <- P.getProcessExitCode $ serverProc s+      let code_msg =+            case code of+              Just (ExitFailure x) ->+                "\nServer process exited unexpectedly with exit code: " ++ show x+              _ -> mempty+      stderr_s <- readFile $ serverErrLog s+      error $+        "After sending command " ++ show command ++ " to server process:"+          ++ show e+          ++ code_msg+          ++ "\nServer stderr:\n"+          ++ stderr_s++-- | The name of a server-side variable.+type VarName = Text++-- | The name of a server-side type.+type TypeName = Text++-- | The name of an entry point.+type EntryName = Text++helpCmd :: Server -> [Text] -> IO (Maybe CmdFailure)+helpCmd s cmd =+  either Just (const Nothing) <$> sendCommand s cmd++cmdRestore :: Server -> FilePath -> [(VarName, TypeName)] -> IO (Maybe CmdFailure)+cmdRestore s fname vars = helpCmd s $ "restore" : T.pack fname : concatMap f vars+  where+    f (v, t) = [v, t]++cmdStore :: Server -> FilePath -> [VarName] -> IO (Maybe CmdFailure)+cmdStore s fname vars = helpCmd s $ "store" : T.pack fname : vars++cmdCall :: Server -> EntryName -> [VarName] -> [VarName] -> IO (Either CmdFailure [T.Text])+cmdCall s entry outs ins =+  sendCommand s $ "call" : entry : outs ++ ins++cmdFree :: Server -> [VarName] -> IO (Maybe CmdFailure)+cmdFree s vs = helpCmd s $ "free" : vs++cmdInputs :: Server -> EntryName -> IO (Either CmdFailure [TypeName])+cmdInputs s entry =+  sendCommand s ["inputs", entry]++cmdOutputs :: Server -> EntryName -> IO (Either CmdFailure [TypeName])+cmdOutputs s entry =+  sendCommand s ["outputs", entry]++cmdClear :: Server -> IO (Maybe CmdFailure)+cmdClear s = helpCmd s ["clear"]++cmdReport :: Server -> IO (Either CmdFailure [T.Text])+cmdReport s = sendCommand s ["report"]
src/Futhark/Test.hs view
@@ -14,12 +14,15 @@     FutharkExe (..),     getValues,     getValuesBS,+    withValuesFile,     compareValues,     compareValues1,+    checkResult,     testRunReferenceOutput,     getExpectedResult,     compileProgram,     runProgram,+    readResults,     ensureReferenceOutput,     determineTuning,     binaryName,@@ -46,6 +49,7 @@ import qualified Control.Exception.Base as E import Control.Monad import Control.Monad.Except+import qualified Data.Binary as Bin import qualified Data.ByteString as SBS import qualified Data.ByteString.Lazy as BS import Data.Char@@ -57,16 +61,11 @@ import qualified Data.Text.Encoding as T import qualified Data.Text.IO as T import Data.Void-import Futhark.Analysis.Metrics-import Futhark.IR.Primitive-  ( FloatType (..),-    IntType (..),-    floatByteSize,-    floatValue,-    intByteSize,-    intValue,-  )+import Futhark.Analysis.Metrics.Type+import Futhark.IR.Primitive (floatByteSize, intByteSize)+import Futhark.Server import Futhark.Test.Values+import Futhark.Test.Values.Parser import Futhark.Util (directoryContents, pmapIO) import Futhark.Util.Pretty (pretty, prettyText) import Language.Futhark.Prop (primByteSize, primValueType)@@ -159,7 +158,7 @@ data GenValue   = -- | Generate a value of the given rank and primitive     -- type.  Scalars are considered 0-ary arrays.-    GenValue [Int] PrimType+    GenValue ValueType   | -- | A fixed non-randomised primitive value.     GenPrim PrimValue   deriving (Show)@@ -167,7 +166,7 @@ -- | A prettyprinted representation of type of value produced by a -- 'GenValue'. genValueType :: GenValue -> String-genValueType (GenValue ds t) =+genValueType (GenValue (ValueType ds t)) =   concatMap (\d -> "[" ++ show d ++ "]") ds ++ pretty t genValueType (GenPrim v) =   pretty v@@ -192,12 +191,15 @@  type Parser = Parsec Void T.Text +postlexeme :: Parser ()+postlexeme = void $ hspace *> optional (try $ eol *> "--" *> postlexeme)+ lexeme :: Parser a -> Parser a-lexeme p = p <* space+lexeme p = p <* postlexeme  -- Like 'lexeme', but does not consume trailing linebreaks. lexeme' :: Parser a -> Parser a-lexeme' p = p <* many (oneOf (" \t" :: String))+lexeme' p = p <* hspace  lexstr :: T.Text -> Parser () lexstr = void . try . lexeme . string@@ -218,22 +220,24 @@   where     num c = ord c - ord '0' -parseDescription :: Parser T.Text-parseDescription = lexeme $ T.pack <$> (anySingle `manyTill` parseDescriptionSeparator)+restOfLine :: Parser T.Text+restOfLine = do+  l <- restOfLine_+  if T.null l then void eol else void eol <|> eof+  pure l -parseDescriptionSeparator :: Parser ()-parseDescriptionSeparator =-  try-    ( string descriptionSeparator-        >> void (satisfy isSpace `manyTill` newline)-    )-    <|> eof+restOfLine_ :: Parser T.Text+restOfLine_ = takeWhileP Nothing (/= '\n') -descriptionSeparator :: T.Text-descriptionSeparator = "=="+parseDescription :: Parser T.Text+parseDescription =+  T.unlines <$> pDescLine `manyTill` pDescriptionSeparator+  where+    pDescLine = "--" *> restOfLine+    pDescriptionSeparator = void $ "-- ==" *> postlexeme  parseTags :: Parser [T.Text]-parseTags = lexstr "tags" *> braces (many parseTag) <|> pure []+parseTags = lexeme' "tags" *> braces (many parseTag) <|> pure []   where     parseTag = T.pack <$> lexeme (some $ satisfy tagConstituent) @@ -252,22 +256,24 @@ parseInputOutputs = do   entrys <- parseEntryPoints   cases <- parseRunCases-  return $ map (`InputOutputs` cases) entrys+  return $+    if null cases+      then []+      else map (`InputOutputs` cases) entrys  parseEntryPoints :: Parser [T.Text]-parseEntryPoints = (lexstr "entry:" *> many entry <* space) <|> pure ["main"]+parseEntryPoints =+  (lexeme' "entry:" *> many entry <* postlexeme)+    <|> pure ["main"]   where     constituent c = not (isSpace c) && c /= '}'     entry = lexeme' $ T.pack <$> some (satisfy constituent)  parseRunTags :: Parser [String]-parseRunTags = many parseTag-  where-    parseTag = try $-      lexeme $ do-        s <- some $ satisfy tagConstituent-        guard $ s `notElem` ["input", "structure", "warning"]-        return s+parseRunTags = many . try . lexeme' $ do+  s <- some $ satisfy tagConstituent+  guard $ s `notElem` ["input", "structure", "warning"]+  return s  parseRunCases :: Parser [TestRun] parseRunCases = parseRunCases' (0 :: Int)@@ -313,7 +319,7 @@  parseExpectedError :: Parser ExpectedError parseExpectedError = lexeme $ do-  s <- T.strip <$> restOfLine+  s <- T.strip <$> restOfLine_ <* postlexeme   if T.null s     then return AnyError     else -- blankCompOpt creates a regular expression that treats@@ -326,110 +332,24 @@ parseGenValue :: Parser GenValue parseGenValue =   choice-    [ GenValue <$> many dim <*> parsePrimType,-      lexeme $-        GenPrim-          <$> choice-            [ i8,-              i16,-              i32,-              i64,-              u8,-              u16,-              u32,-              u64,-              f32,-              f64,-              int SignedValue Int32 ""-            ]-    ]-  where-    digits = some (satisfy isDigit)-    dim =-      between (lexstr "[") (lexstr "]") $-        lexeme $ read <$> digits--    readint :: String -> Integer-    readint = read -- To avoid warnings.-    readfloat :: String -> Double-    readfloat = read -- To avoid warnings.-    int f t s =-      try $-        lexeme $-          f . intValue t . readint <$> digits-            <* string s-            <* notFollowedBy (satisfy isAlphaNum)-    i8 = int SignedValue Int8 "i8"-    i16 = int SignedValue Int16 "i16"-    i32 = int SignedValue Int32 "i32"-    i64 = int SignedValue Int64 "i64"-    u8 = int UnsignedValue Int8 "u8"-    u16 = int UnsignedValue Int16 "u16"-    u32 = int UnsignedValue Int32 "u32"-    u64 = int UnsignedValue Int64 "u64"--    optSuffix s suff = do-      s' <- s-      ((s' <>) <$> suff) <|> pure s'--    float f t s =-      try $-        lexeme $-          f . floatValue t . readfloat-            <$> (digits `optSuffix` (char '.' *> (("." <>) <$> digits)))-            <* string s-            <* notFollowedBy (satisfy isAlphaNum)-    f32 = float FloatValue Float32 "f32"-    f64 = float FloatValue Float64 "f64"--parsePrimType :: Parser PrimType-parsePrimType =-  choice-    [ lexstr "i8" $> Signed Int8,-      lexstr "i16" $> Signed Int16,-      lexstr "i32" $> Signed Int32,-      lexstr "i64" $> Signed Int64,-      lexstr "u8" $> Unsigned Int8,-      lexstr "u16" $> Unsigned Int16,-      lexstr "u32" $> Unsigned Int32,-      lexstr "u64" $> Unsigned Int64,-      lexstr "f32" $> FloatType Float32,-      lexstr "f64" $> FloatType Float64,-      lexstr "bool" $> Bool+    [ GenValue <$> lexeme parseType,+      GenPrim <$> lexeme parsePrimValue     ]  parseValues :: Parser Values parseValues =-  do-    s <- parseBlock-    case valuesFromByteString "input block contents" $ BS.fromStrict $ T.encodeUtf8 s of-      Left err -> fail err-      Right vs -> return $ Values vs-    <|> lexstr "@" *> lexeme (InFile . T.unpack <$> nextWord)--parseBlock :: Parser T.Text-parseBlock = lexeme $ braces (T.pack <$> parseBlockBody 0)--parseBlockBody :: Int -> Parser String-parseBlockBody n = do-  c <- lookAhead anySingle-  case (c, n) of-    ('}', 0) -> return mempty-    ('}', _) -> (:) <$> anySingle <*> parseBlockBody (n -1)-    ('{', _) -> (:) <$> anySingle <*> parseBlockBody (n + 1)-    _ -> (:) <$> anySingle <*> parseBlockBody n--restOfLine :: Parser T.Text-restOfLine = T.pack <$> (anySingle `manyTill` (void newline <|> eof))--nextWord :: Parser T.Text-nextWord = T.pack <$> (anySingle `manyTill` satisfy isSpace)+  choice+    [ Values <$> braces (many $ parseValue postlexeme),+      InFile . T.unpack <$> (lexstr "@" *> lexeme nextWord)+    ]+  where+    nextWord = takeWhileP Nothing $ not . isSpace  parseWarning :: Parser WarningTest parseWarning = lexstr "warning:" >> parseExpectedWarning   where     parseExpectedWarning = lexeme $ do-      s <- T.strip <$> restOfLine+      s <- T.strip <$> restOfLine_       ExpectedWarning s <$> makeRegexOptsM blankCompOpt defaultExecOpt (T.unpack s)  parseExpectedStructure :: Parser StructureTest@@ -458,68 +378,41 @@ testSpec =   ProgramTest <$> parseDescription <*> parseTags <*> parseAction -parserState :: Int -> FilePath -> s -> State s e-parserState line name t =-  State-    { stateInput = t,-      stateOffset = 0,-      statePosState =-        PosState-          { pstateInput = t,-            pstateOffset = 0,-            pstateSourcePos =-              SourcePos-                { sourceName = name,-                  sourceLine = mkPos line,-                  sourceColumn = mkPos 3-                },-            pstateTabWidth = defaultTabWidth,-            pstateLinePrefix = "-- "-          },-      stateParseErrors = []-    }--readTestSpec :: Int -> String -> T.Text -> Either (ParseErrorBundle T.Text Void) ProgramTest-readTestSpec line name t =-  snd $ runParser' (testSpec <* eof) $ parserState line name t--readInputOutputs :: Int -> String -> T.Text -> Either (ParseErrorBundle T.Text Void) [InputOutputs]-readInputOutputs line name t =-  snd $-    runParser' (parseDescription *> space *> parseInputOutputs <* eof) $-      parserState line name t--commentPrefix :: T.Text-commentPrefix = T.pack "--"- couldNotRead :: IOError -> IO (Either String a) couldNotRead = return . Left . show +pProgramTest :: Parser ProgramTest+pProgramTest = do+  void $ many pNonTestLine+  maybe_spec <- optional testSpec <* pEndOfTestBlock <* many pNonTestLine+  case maybe_spec of+    Just spec+      | RunCases old_cases structures warnings <- testAction spec -> do+        cases <- many $ pInputOutputs <* many pNonTestLine+        pure spec {testAction = RunCases (old_cases ++ concat cases) structures warnings}+      | otherwise ->+        many pNonTestLine *> notFollowedBy "-- ==" *> pure spec+          <?> "no more test blocks, since first test block specifies type error."+    Nothing ->+      eof $> noTest+  where+    noTest =+      ProgramTest mempty mempty (RunCases mempty mempty mempty)++    pEndOfTestBlock =+      (void eol <|> eof) *> notFollowedBy "--"+    pNonTestLine =+      void $ notFollowedBy "-- ==" *> restOfLine+    pInputOutputs =+      parseDescription *> parseInputOutputs <* pEndOfTestBlock+ -- | Read the test specification from the given Futhark program. testSpecFromFile :: FilePath -> IO (Either String ProgramTest)-testSpecFromFile path = do-  blocks_or_err <--    (Right . testBlocks <$> T.readFile path)-      `catch` couldNotRead-  case blocks_or_err of-    Left err -> return $ Left err-    Right blocks -> do-      let (first_spec_line, first_spec, rest_specs) =-            case blocks of-              [] -> (0, mempty, [])-              (n, s) : ss -> (n, s, ss)-      case readTestSpec (1 + first_spec_line) path first_spec of-        Left err -> return $ Left $ errorBundlePretty err-        Right v -> return $ foldM moreCases v rest_specs-  where-    moreCases test (lineno, cases) =-      case readInputOutputs lineno path cases of-        Left err -> Left $ errorBundlePretty err-        Right cases' ->-          case testAction test of-            RunCases old_cases structures warnings ->-              Right test {testAction = RunCases (old_cases ++ cases') structures warnings}-            _ -> Left "Secondary test block provided, but primary test block specifies compilation error."+testSpecFromFile path =+  ( either (Left . errorBundlePretty) Right . parse pProgramTest path+      <$> T.readFile path+  )+    `catch` couldNotRead  -- | Like 'testSpecFromFile', but kills the process on error. testSpecFromFileOrDie :: FilePath -> IO ProgramTest@@ -531,28 +424,6 @@       exitFailure     Right spec -> return spec -testBlocks :: T.Text -> [(Int, T.Text)]-testBlocks = mapMaybe isTestBlock . commentBlocks-  where-    isTestBlock (n, block)-      | any ((" " <> descriptionSeparator) `T.isPrefixOf`) block =-        Just (n, T.unlines block)-      | otherwise =-        Nothing--commentBlocks :: T.Text -> [(Int, [T.Text])]-commentBlocks = commentBlocks' . zip [0 ..] . T.lines-  where-    isComment = (commentPrefix `T.isPrefixOf`)-    commentBlocks' ls =-      let ls' = dropWhile (not . isComment . snd) ls-       in case ls' of-            [] -> []-            (n, _) : _ ->-              let (block, ls'') = span (isComment . snd) ls'-                  block' = map (T.drop 2 . snd) block-               in (n, block') : commentBlocks' ls''- -- | Read test specifications from the given path, which can be a file -- or directory containing @.fut@ files and further directories. testSpecsFromPath :: FilePath -> IO (Either String [(FilePath, ProgramTest)])@@ -641,6 +512,24 @@ getValuesBS futhark dir (GenValues gens) =   mconcat <$> mapM (getGenBS futhark dir) gens +-- | Evaluate an IO action while the values are available in a file by+-- some name.  The file will be removed after the action is done.+withValuesFile ::+  MonadIO m =>+  FutharkExe ->+  FilePath ->+  Values ->+  (FilePath -> IO a) ->+  m a+withValuesFile _ dir (InFile file) f+  | takeExtension file /= ".gz" =+    liftIO $ f $ dir </> file+withValuesFile futhark dir vs f =+  liftIO . withSystemTempFile "futhark-input" $ \tmpf tmpf_h -> do+    BS.hPutStr tmpf_h =<< getValuesBS futhark dir vs+    hClose tmpf_h+    f tmpf+ -- | There is a risk of race conditions when multiple programs have -- identical 'GenValues'.  In such cases, multiple threads in 'futhark -- test' might attempt to create the same file (or read from it, while@@ -695,7 +584,7 @@ genFileSize = genSize   where     header_size = 1 + 1 + 1 + 4 -- 'b' <version> <num_dims> <type>-    genSize (GenValue ds t) =+    genSize (GenValue (ValueType ds t)) =       header_size + toInteger (length ds) * 8         + product (map toInteger ds) * primSize t     genSize (GenPrim v) =@@ -788,14 +677,13 @@ -- the Python backends).  The @extra_options@ are passed to the -- program. runProgram ::-  MonadIO m =>   FutharkExe ->   FilePath ->   [String] ->   String ->   T.Text ->   Values ->-  m (ExitCode, SBS.ByteString, SBS.ByteString)+  IO (ExitCode, SBS.ByteString, SBS.ByteString) runProgram futhark runner extra_options prog entry input = do   let progbin = binaryName prog       dir = takeDirectory prog@@ -809,6 +697,29 @@   input' <- getValuesBS futhark dir input   liftIO $ readProcessWithExitCode to_run to_run_args $ BS.toStrict input' +-- | Read the given variables from a running server.+readResults ::+  (MonadIO m, MonadError T.Text m) =>+  Server ->+  [VarName] ->+  FilePath ->+  m [Value]+readResults server outs program =+  join . liftIO . withSystemTempFile "futhark-output" $ \outputf outputh -> do+    hClose outputh+    store_r <- cmdStore server outputf outs+    case store_r of+      Just (CmdFailure _ err) ->+        pure $ throwError $ T.unlines err+      Nothing -> do+        bytes <- BS.readFile outputf+        case valuesFromByteString "output" bytes of+          Left e -> do+            let actualf = program `addExtension` "actual"+            liftIO $ BS.writeFile actualf bytes+            pure $ throwError $ T.pack e <> "\n(See " <> T.pack actualf <> ")"+          Right vs -> pure $ pure vs+ -- | Ensure that any reference output files exist, or create them (by -- compiling the program with the reference compiler and running it on -- the input) if necessary.@@ -874,3 +785,28 @@           " (using " <> takeFileName (program <.> ext) <> ")"         )     else return ([], mempty)++-- | Check that the result is as expected, and write files and throw+-- an error if not.+checkResult ::+  (MonadError T.Text m, MonadIO m) =>+  FilePath ->+  [Value] ->+  [Value] ->+  m ()+checkResult program expected_vs actual_vs =+  case compareValues actual_vs expected_vs of+    mismatch : mismatches -> do+      let actualf = program <.> "actual"+          expectedf = program <.> "expected"+      liftIO $ BS.writeFile actualf $ mconcat $ map Bin.encode actual_vs+      liftIO $ BS.writeFile expectedf $ mconcat $ map Bin.encode expected_vs+      throwError $+        T.pack actualf <> " and " <> T.pack expectedf+          <> " do not match:\n"+          <> T.pack (show mismatch)+          <> if null mismatches+            then mempty+            else "\n...and " <> prettyText (length mismatches) <> " other mismatches."+    [] ->+      return ()
src/Futhark/Test/Values.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Strict #-} {-# LANGUAGE Trustworthy #-}@@ -10,6 +11,8 @@ -- for your test programs. module Futhark.Test.Values   ( Value (..),+    Compound (..),+    CompoundValue,     Vector,      -- * Reading Values@@ -18,11 +21,20 @@     -- * Types of values     ValueType (..),     valueType,+    valueShape, +    -- * Manipulating values+    valueElems,+    mkCompound,+     -- * Comparing Values     compareValues,     compareValues1,     Mismatch,++    -- * Converting values+    GetValue (..),+    PutValue (..),   ) where @@ -31,14 +43,18 @@ import Data.Binary import Data.Binary.Get import Data.Binary.Put-import qualified Data.ByteString.Lazy.Char8 as BS+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy.Char8 as LBS import Data.Char (chr, isSpace, ord) import Data.Int (Int16, Int32, Int64, Int8)-import Data.Vector.Binary+import qualified Data.Map as M+import qualified Data.Text as T+import Data.Traversable import Data.Vector.Generic (freeze)+import qualified Data.Vector.Storable as SVec+import Data.Vector.Storable.ByteString (byteStringToVector, vectorToByteString) import qualified Data.Vector.Unboxed as UVec import qualified Data.Vector.Unboxed.Mutable as UMVec-import Futhark.IR.Pretty () import Futhark.IR.Primitive (PrimValue) import Futhark.IR.Prop.Constants (IsValue (..)) import Futhark.Util (maybeHead)@@ -51,8 +67,8 @@  type STVector s = UMVec.STVector s --- | An Unboxed vector.-type Vector = UVec.Vector+-- | The value vector type.+type Vector = SVec.Vector  -- | An efficiently represented Futhark value.  Use 'pretty' to get a -- human-readable representation, and v'put' to obtain binary a@@ -75,20 +91,22 @@ binaryFormatVersion = 2  instance Binary Value where-  put (Int8Value shape vs) = putBinaryValue "  i8" shape vs putInt8-  put (Int16Value shape vs) = putBinaryValue " i16" shape vs putInt16le-  put (Int32Value shape vs) = putBinaryValue " i32" shape vs putInt32le-  put (Int64Value shape vs) = putBinaryValue " i64" shape vs putInt64le-  put (Word8Value shape vs) = putBinaryValue "  u8" shape vs putWord8-  put (Word16Value shape vs) = putBinaryValue " u16" shape vs putWord16le-  put (Word32Value shape vs) = putBinaryValue " u32" shape vs putWord32le-  put (Word64Value shape vs) = putBinaryValue " u64" shape vs putWord64le-  put (Float32Value shape vs) = putBinaryValue " f32" shape vs putFloatle-  put (Float64Value shape vs) = putBinaryValue " f64" shape vs putDoublele-  put (BoolValue shape vs) = putBinaryValue "bool" shape vs $ putInt8 . boolToInt+  put (Int8Value shape vs) = putBinaryValue "  i8" shape vs+  put (Int16Value shape vs) = putBinaryValue " i16" shape vs+  put (Int32Value shape vs) = putBinaryValue " i32" shape vs+  put (Int64Value shape vs) = putBinaryValue " i64" shape vs+  put (Word8Value shape vs) = putBinaryValue "  u8" shape vs+  put (Word16Value shape vs) = putBinaryValue " u16" shape vs+  put (Word32Value shape vs) = putBinaryValue " u32" shape vs+  put (Word64Value shape vs) = putBinaryValue " u64" shape vs+  put (Float32Value shape vs) = putBinaryValue " f32" shape vs+  put (Float64Value shape vs) = putBinaryValue " f64" shape vs+  -- Bool must be treated specially because the Storable instance+  -- uses four bytes.+  put (BoolValue shape vs) = putBinaryValue "bool" shape $ SVec.map boolToInt8 vs     where-      boolToInt True = 1-      boolToInt False = 0+      boolToInt8 True = 1 :: Int8+      boolToInt8 False = 0    get = do     first <- getInt8@@ -106,41 +124,45 @@      shape <- replicateM (fromIntegral rank) $ fromIntegral <$> getInt64le     let num_elems = product shape-        shape' = UVec.fromList shape+        shape' = SVec.fromList shape -    case BS.unpack type_f of-      "  i8" -> get' (Int8Value shape') getInt8 num_elems-      " i16" -> get' (Int16Value shape') getInt16le num_elems-      " i32" -> get' (Int32Value shape') getInt32le num_elems-      " i64" -> get' (Int64Value shape') getInt64le num_elems-      "  u8" -> get' (Word8Value shape') getWord8 num_elems-      " u16" -> get' (Word16Value shape') getWord16le num_elems-      " u32" -> get' (Word32Value shape') getWord32le num_elems-      " u64" -> get' (Word64Value shape') getWord64le num_elems-      " f32" -> get' (Float32Value shape') getFloatle num_elems-      " f64" -> get' (Float64Value shape') getDoublele num_elems-      "bool" -> get' (BoolValue shape') getBool num_elems+    case LBS.unpack type_f of+      "  i8" -> get' (Int8Value shape') num_elems 1+      " i16" -> get' (Int16Value shape') num_elems 2+      " i32" -> get' (Int32Value shape') num_elems 4+      " i64" -> get' (Int64Value shape') num_elems 8+      "  u8" -> get' (Word8Value shape') num_elems 1+      " u16" -> get' (Word16Value shape') num_elems 2+      " u32" -> get' (Word32Value shape') num_elems 4+      " u64" -> get' (Word64Value shape') num_elems 8+      " f32" -> get' (Float32Value shape') num_elems 4+      " f64" -> get' (Float64Value shape') num_elems 8+      -- Bool must be treated specially because the Storable instance+      -- uses four bytes.+      "bool" -> BoolValue shape' . SVec.map int8ToBool . byteStringToVector . BS.copy <$> getByteString num_elems       s -> fail $ "Cannot parse binary values of type " ++ show s     where-      getBool = (/= 0) <$> getWord8+      -- The copy is to ensure that the bytestring is properly+      -- aligned.+      get' mk num_elems elem_size =+        mk . byteStringToVector . BS.copy <$> getByteString (num_elems * elem_size) -      get' mk get_elem num_elems =-        mk <$> genericGetVectorWith (pure num_elems) get_elem+      int8ToBool :: Int8 -> Bool+      int8ToBool = (/= 0)  putBinaryValue ::-  UVec.Unbox a =>+  SVec.Storable a =>   String ->   Vector Int ->   Vector a ->-  (a -> Put) ->   Put-putBinaryValue tstr shape vs putv = do+putBinaryValue tstr shape vs = do   putInt8 $ fromIntegral $ ord 'b'   putWord8 binaryFormatVersion-  putWord8 $ fromIntegral $ UVec.length shape+  putWord8 $ fromIntegral $ SVec.length shape   mapM_ (putInt8 . fromIntegral . ord) tstr-  mapM_ (putInt64le . fromIntegral) $ UVec.toList shape-  mapM_ putv $ UVec.toList vs+  putByteString $ vectorToByteString shape+  putByteString $ vectorToByteString vs  instance PP.Pretty Value where   ppr v@@ -149,41 +171,77 @@         <> parens (dims <> ppr (valueElemType v))     where       dims = mconcat $ map (brackets . ppr) $ valueShape v-  ppr (Int8Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Int16Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Int32Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Int64Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Word8Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Word16Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Word32Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Word64Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Float32Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (Float64Value shape vs) = pprArray (UVec.toList shape) vs-  ppr (BoolValue shape vs) = pprArray (UVec.toList shape) vs+  ppr (Int8Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Int16Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Int32Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Int64Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Word8Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Word16Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Word32Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Word64Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Float32Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (Float64Value shape vs) = pprArray (SVec.toList shape) vs+  ppr (BoolValue shape vs) = pprArray (SVec.toList shape) vs -pprArray :: (UVec.Unbox a, F.IsPrimValue a) => [Int] -> UVec.Vector a -> Doc+pprArray :: (SVec.Storable a, F.IsPrimValue a) => [Int] -> SVec.Vector a -> Doc pprArray [] vs =-  ppr $ F.primValue $ UVec.head vs+  ppr $ F.primValue $ SVec.head vs pprArray (d : ds) vs =   brackets $ cat $ punctuate separator $ map (pprArray ds . slice) [0 .. d -1]   where     slice_size = product ds-    slice i = UVec.slice (i * slice_size) slice_size vs+    slice i = SVec.slice (i * slice_size) slice_size vs     separator       | null ds = comma <> space       | otherwise = comma <> line +-- | The structure of a compound value, parameterised over the actual+-- values.  For most cases you probably want 'CompoundValue'.+data Compound v+  = ValueRecord (M.Map T.Text (Compound v))+  | -- | Must not be single value.+    ValueTuple [Compound v]+  | ValueAtom v+  deriving (Eq, Ord, Show)++instance Functor Compound where+  fmap = fmapDefault++instance Foldable Compound where+  foldMap = foldMapDefault++instance Traversable Compound where+  traverse f (ValueAtom v) = ValueAtom <$> f v+  traverse f (ValueTuple vs) = ValueTuple <$> traverse (traverse f) vs+  traverse f (ValueRecord m) = ValueRecord <$> traverse (traverse f) m++instance Pretty v => Pretty (Compound v) where+  ppr (ValueAtom v) = ppr v+  ppr (ValueTuple vs) = parens $ commasep $ map ppr vs+  ppr (ValueRecord m) = braces $ commasep $ map field $ M.toList m+    where+      field (k, v) = ppr k <> equals <> ppr v++-- | Create a tuple for a non-unit list, and otherwise a 'ValueAtom'+mkCompound :: [v] -> Compound v+mkCompound [v] = ValueAtom v+mkCompound vs = ValueTuple $ map ValueAtom vs++-- | Like a 'Value', but also grouped in compound ways that are not+-- supported by raw values.  You cannot parse or read these in+-- standard ways, and they cannot be elements of arrays.+type CompoundValue = Compound Value+ -- | A representation of the simple values we represent in this module. data ValueType = ValueType [Int] F.PrimType-  deriving (Show)+  deriving (Eq, Ord, Show)  instance PP.Pretty ValueType where   ppr (ValueType ds t) = mconcat (map pprDim ds) <> ppr t     where       pprDim d = brackets $ ppr d --- | A textual description of the type of a value.  Follows Futhark--- type notation, and contains the exact dimension sizes if an array.+-- | Get the type of a value. valueType :: Value -> ValueType valueType v = ValueType (valueShape v) $ valueElemType v @@ -200,42 +258,71 @@ valueElemType Float64Value {} = F.FloatType F.Float64 valueElemType BoolValue {} = F.Bool +-- | The shape of a value.  Empty list in case of a scalar. valueShape :: Value -> [Int]-valueShape (Int8Value shape _) = UVec.toList shape-valueShape (Int16Value shape _) = UVec.toList shape-valueShape (Int32Value shape _) = UVec.toList shape-valueShape (Int64Value shape _) = UVec.toList shape-valueShape (Word8Value shape _) = UVec.toList shape-valueShape (Word16Value shape _) = UVec.toList shape-valueShape (Word32Value shape _) = UVec.toList shape-valueShape (Word64Value shape _) = UVec.toList shape-valueShape (Float32Value shape _) = UVec.toList shape-valueShape (Float64Value shape _) = UVec.toList shape-valueShape (BoolValue shape _) = UVec.toList shape+valueShape (Int8Value shape _) = SVec.toList shape+valueShape (Int16Value shape _) = SVec.toList shape+valueShape (Int32Value shape _) = SVec.toList shape+valueShape (Int64Value shape _) = SVec.toList shape+valueShape (Word8Value shape _) = SVec.toList shape+valueShape (Word16Value shape _) = SVec.toList shape+valueShape (Word32Value shape _) = SVec.toList shape+valueShape (Word64Value shape _) = SVec.toList shape+valueShape (Float32Value shape _) = SVec.toList shape+valueShape (Float64Value shape _) = SVec.toList shape+valueShape (BoolValue shape _) = SVec.toList shape +-- | Produce a list of the immediate elements of the value.  That is,+-- a 2D array will produce a list of 1D values.  While lists are of+-- course inefficient, the actual values are just slices of the+-- original value, which makes them fairly efficient.+valueElems :: Value -> [Value]+valueElems v+  | n : ns <- valueShape v =+    let k = product ns+        slices mk vs =+          [ mk (SVec.fromList ns) $+              SVec.slice (k * i) k vs+            | i <- [0 .. n -1]+          ]+     in case v of+          Int8Value _ vs -> slices Int8Value vs+          Int16Value _ vs -> slices Int16Value vs+          Int32Value _ vs -> slices Int32Value vs+          Int64Value _ vs -> slices Int64Value vs+          Word8Value _ vs -> slices Word8Value vs+          Word16Value _ vs -> slices Word16Value vs+          Word32Value _ vs -> slices Word32Value vs+          Word64Value _ vs -> slices Word64Value vs+          Float32Value _ vs -> slices Float32Value vs+          Float64Value _ vs -> slices Float64Value vs+          BoolValue _ vs -> slices BoolValue vs+  | otherwise =+    []+ -- The parser -dropRestOfLine, dropSpaces :: BS.ByteString -> BS.ByteString-dropRestOfLine = BS.drop 1 . BS.dropWhile (/= '\n')-dropSpaces t = case BS.dropWhile isSpace t of+dropRestOfLine, dropSpaces :: LBS.ByteString -> LBS.ByteString+dropRestOfLine = LBS.drop 1 . LBS.dropWhile (/= '\n')+dropSpaces t = case LBS.dropWhile isSpace t of   t'-    | "--" `BS.isPrefixOf` t' -> dropSpaces $ dropRestOfLine t'+    | "--" `LBS.isPrefixOf` t' -> dropSpaces $ dropRestOfLine t'     | otherwise -> t' -type ReadValue v = BS.ByteString -> Maybe (v, BS.ByteString)+type ReadValue v = LBS.ByteString -> Maybe (v, LBS.ByteString) -symbol :: Char -> BS.ByteString -> Maybe BS.ByteString+symbol :: Char -> LBS.ByteString -> Maybe LBS.ByteString symbol c t-  | Just (c', t') <- BS.uncons t, c' == c = Just $ dropSpaces t'+  | Just (c', t') <- LBS.uncons t, c' == c = Just $ dropSpaces t'   | otherwise = Nothing -lexeme :: BS.ByteString -> BS.ByteString -> Maybe BS.ByteString+lexeme :: LBS.ByteString -> LBS.ByteString -> Maybe LBS.ByteString lexeme l t-  | l `BS.isPrefixOf` t = Just $ dropSpaces $ BS.drop (BS.length l) t+  | l `LBS.isPrefixOf` t = Just $ dropSpaces $ LBS.drop (LBS.length l) t   | otherwise = Nothing  -- (Used elements, shape, elements, remaining input)-type State s v = (Int, Vector Int, STVector s v, BS.ByteString)+type State s v = (Int, Vector Int, STVector s v, LBS.ByteString)  readArrayElemsST ::   UMVec.Unbox v =>@@ -260,12 +347,12 @@  updateShape :: Int -> Int -> Vector Int -> Maybe (Vector Int) updateShape d n shape-  | old_n < 0 = Just $ shape UVec.// [(r - d, n)]+  | old_n < 0 = Just $ shape SVec.// [(r - d, n)]   | old_n == n = Just shape   | otherwise = Nothing   where-    r = UVec.length shape-    old_n = shape UVec.! (r - d)+    r = SVec.length shape+    old_n = shape SVec.! (r - d)  growIfFilled :: UVec.Unbox v => Int -> STVector s v -> ST s (STVector s v) growIfFilled i arr =@@ -302,18 +389,18 @@   return (i, shape', arr, t')  readRankedArrayOf ::-  UMVec.Unbox v =>+  (UMVec.Unbox v, SVec.Storable v) =>   Int ->   ReadValue v ->-  BS.ByteString ->-  Maybe (Vector Int, Vector v, BS.ByteString)+  LBS.ByteString ->+  Maybe (Vector Int, Vector v, LBS.ByteString) readRankedArrayOf r rv t = runST $ do   arr <- UMVec.new 1024-  ms <- readRankedArrayOfST r rv (0, UVec.replicate r (-1), arr, t)+  ms <- readRankedArrayOfST r rv (0, SVec.replicate r (-1), arr, t)   case ms of     Just (i, shape, arr', t') -> do       arr'' <- freeze (UMVec.slice 0 i arr')-      return $ Just (shape, arr'', t')+      return $ Just (shape, UVec.convert arr'', t')     Nothing ->       return Nothing @@ -335,7 +422,7 @@     _ -> Nothing   return (v, dropSpaces b)   where-    (a, b) = BS.span constituent t+    (a, b) = LBS.span constituent t  readInt8 :: ReadValue Int8 readInt8 = readIntegral f@@ -395,7 +482,7 @@     _ -> Nothing   return (v, dropSpaces b)   where-    (a, b) = BS.span constituent t+    (a, b) = LBS.span constituent t     fromDouble = uncurry encodeFloat . decodeFloat     unLoc (L _ x) = x @@ -423,7 +510,7 @@     _ -> Nothing   return (v, dropSpaces b)   where-    (a, b) = BS.span constituent t+    (a, b) = LBS.span constituent t  readPrimType :: ReadValue String readPrimType t = do@@ -432,9 +519,9 @@     _ -> Nothing   return (pt, dropSpaces b)   where-    (a, b) = BS.span constituent t+    (a, b) = LBS.span constituent t -readEmptyArrayOfShape :: [Int] -> BS.ByteString -> Maybe (Value, BS.ByteString)+readEmptyArrayOfShape :: [Int] -> LBS.ByteString -> Maybe (Value, LBS.ByteString) readEmptyArrayOfShape shape t   | Just t' <- symbol '[' t,     Just (d, t'') <- readIntegral (const Nothing) t',@@ -444,28 +531,28 @@     (pt, t') <- readPrimType t     guard $ elem 0 shape     v <- case pt of-      "i8" -> Just $ Int8Value (UVec.fromList shape) UVec.empty-      "i16" -> Just $ Int16Value (UVec.fromList shape) UVec.empty-      "i32" -> Just $ Int32Value (UVec.fromList shape) UVec.empty-      "i64" -> Just $ Int64Value (UVec.fromList shape) UVec.empty-      "u8" -> Just $ Word8Value (UVec.fromList shape) UVec.empty-      "u16" -> Just $ Word16Value (UVec.fromList shape) UVec.empty-      "u32" -> Just $ Word32Value (UVec.fromList shape) UVec.empty-      "u64" -> Just $ Word64Value (UVec.fromList shape) UVec.empty-      "f32" -> Just $ Float32Value (UVec.fromList shape) UVec.empty-      "f64" -> Just $ Float64Value (UVec.fromList shape) UVec.empty-      "bool" -> Just $ BoolValue (UVec.fromList shape) UVec.empty+      "i8" -> Just $ Int8Value (SVec.fromList shape) SVec.empty+      "i16" -> Just $ Int16Value (SVec.fromList shape) SVec.empty+      "i32" -> Just $ Int32Value (SVec.fromList shape) SVec.empty+      "i64" -> Just $ Int64Value (SVec.fromList shape) SVec.empty+      "u8" -> Just $ Word8Value (SVec.fromList shape) SVec.empty+      "u16" -> Just $ Word16Value (SVec.fromList shape) SVec.empty+      "u32" -> Just $ Word32Value (SVec.fromList shape) SVec.empty+      "u64" -> Just $ Word64Value (SVec.fromList shape) SVec.empty+      "f32" -> Just $ Float32Value (SVec.fromList shape) SVec.empty+      "f64" -> Just $ Float64Value (SVec.fromList shape) SVec.empty+      "bool" -> Just $ BoolValue (SVec.fromList shape) SVec.empty       _ -> Nothing     return (v, t') -readEmptyArray :: BS.ByteString -> Maybe (Value, BS.ByteString)+readEmptyArray :: LBS.ByteString -> Maybe (Value, LBS.ByteString) readEmptyArray t = do   t' <- symbol '(' =<< lexeme "empty" t   (v, t'') <- readEmptyArrayOfShape [] t'   t''' <- symbol ')' t''   return (v, t''') -readValue :: BS.ByteString -> Maybe (Value, BS.ByteString)+readValue :: LBS.ByteString -> Maybe (Value, LBS.ByteString) readValue full_t   | Right (t', _, v) <- decodeOrFail full_t =     Just (v, dropSpaces t')@@ -493,11 +580,11 @@         `mplus` tryWith readBool BoolValue r t  -- | Parse Futhark values from the given bytestring.-readValues :: BS.ByteString -> Maybe [Value]+readValues :: LBS.ByteString -> Maybe [Value] readValues = readValues' . dropSpaces   where     readValues' t-      | BS.null t = Just []+      | LBS.null t = Just []       | otherwise = do         (a, t') <- readValue t         (a :) <$> readValues' t'@@ -574,28 +661,45 @@   | otherwise =     [ArrayShapeMismatch i (valueShape got_v) (valueShape expected_v)]   where+    {-# INLINE compareGen #-}+    {-# INLINE compareNum #-}+    {-# INLINE compareFloat #-}+    {-# INLINE compareFloatElement #-}+    {-# INLINE compareElement #-}     compareNum tol = compareGen $ compareElement tol     compareFloat tol = compareGen $ compareFloatElement tol      compareGen cmp got expected =-      concat $-        zipWith cmp (UVec.toList $ UVec.indexed got) (UVec.toList expected)+      let l = SVec.length got+          check acc j+            | j < l =+              case cmp j (got SVec.! j) (expected SVec.! j) of+                Just mismatch ->+                  check (mismatch : acc) (j + 1)+                Nothing ->+                  check acc (j + 1)+            | otherwise =+              acc+       in reverse $ check [] 0 -    compareElement tol (j, got) expected-      | comparePrimValue tol got expected = []-      | otherwise = [PrimValueMismatch (i, j) (value got) (value expected)]+    compareElement tol j got expected+      | comparePrimValue tol got expected = Nothing+      | otherwise = Just $ PrimValueMismatch (i, j) (value got) (value expected) -    compareFloatElement tol (j, got) expected-      | isNaN got, isNaN expected = []+    compareFloatElement tol j got expected+      | isNaN got,+        isNaN expected =+        Nothing       | isInfinite got,         isInfinite expected,         signum got == signum expected =-        []-      | otherwise = compareElement tol (j, got) expected+        Nothing+      | otherwise =+        compareElement tol j got expected -    compareBool (j, got) expected-      | got == expected = []-      | otherwise = [PrimValueMismatch (i, j) (value got) (value expected)]+    compareBool j got expected+      | got == expected = Nothing+      | otherwise = Just $ PrimValueMismatch (i, j) (value got) (value expected)  comparePrimValue ::   (Ord num, Num num) =>@@ -611,8 +715,103 @@ minTolerance :: Fractional a => a minTolerance = 0.002 -- 0.2% -tolerance :: (RealFloat a, UVec.Unbox a) => Vector a -> a-tolerance = UVec.foldl tolerance' minTolerance . UVec.filter (not . nanOrInf)+tolerance :: (RealFloat a, SVec.Storable a) => Vector a -> a+tolerance = SVec.foldl tolerance' minTolerance . SVec.filter (not . nanOrInf)   where     tolerance' t v = max t $ minTolerance * v     nanOrInf x = isInfinite x || isNaN x++-- | A class for Haskell values that can be retrieved from 'Value'.+-- This is a convenience facility - don't expect it to be fast.+class GetValue t where+  getValue :: Value -> Maybe t++instance GetValue Bool where+  getValue (BoolValue shape vs)+    | [] <- SVec.toList shape =+      Just $ vs SVec.! 0+  getValue _ = Nothing++instance GetValue Int8 where+  getValue (Int8Value shape vs)+    | [] <- SVec.toList shape =+      Just $ vs SVec.! 0+  getValue _ = Nothing++instance GetValue Int16 where+  getValue (Int16Value shape vs)+    | [] <- SVec.toList shape =+      Just $ vs SVec.! 0+  getValue _ = Nothing++instance GetValue Int32 where+  getValue (Int32Value shape vs)+    | [] <- SVec.toList shape =+      Just $ vs SVec.! 0+  getValue _ = Nothing++instance GetValue Int64 where+  getValue (Int64Value shape vs)+    | [] <- SVec.toList shape =+      Just $ vs SVec.! 0+  getValue _ = Nothing++-- | A class for Haskell values that can be converted to 'Value'.+-- This is a convenience facility - don't expect it to be fast.+class PutValue t where+  -- | This may fail for cases such as irregular arrays.+  putValue :: t -> Maybe Value++instance PutValue F.PrimValue where+  putValue (F.SignedValue (F.Int8Value x)) =+    Just $ Int8Value mempty $ SVec.singleton x+  putValue (F.SignedValue (F.Int16Value x)) =+    Just $ Int16Value mempty $ SVec.singleton x+  putValue (F.SignedValue (F.Int32Value x)) =+    Just $ Int32Value mempty $ SVec.singleton x+  putValue (F.SignedValue (F.Int64Value x)) =+    Just $ Int64Value mempty $ SVec.singleton x+  putValue (F.UnsignedValue (F.Int8Value x)) =+    Just $ Word8Value mempty $ SVec.singleton $ fromIntegral x+  putValue (F.UnsignedValue (F.Int16Value x)) =+    Just $ Word16Value mempty $ SVec.singleton $ fromIntegral x+  putValue (F.UnsignedValue (F.Int32Value x)) =+    Just $ Word32Value mempty $ SVec.singleton $ fromIntegral x+  putValue (F.UnsignedValue (F.Int64Value x)) =+    Just $ Word64Value mempty $ SVec.singleton $ fromIntegral x+  putValue (F.FloatValue (F.Float32Value x)) =+    Just $ Float32Value mempty $ SVec.singleton x+  putValue (F.FloatValue (F.Float64Value x)) =+    Just $ Float64Value mempty $ SVec.singleton x+  putValue (F.BoolValue b) =+    Just $ BoolValue mempty $ SVec.singleton b++instance PutValue [Value] where+  putValue [] = Nothing+  putValue (x : xs) = do+    let res_shape = SVec.fromList $ length (x : xs) : valueShape x+    guard $ all ((== valueType x) . valueType) xs+    Just $ case x of+      Int8Value {} -> Int8Value res_shape $ foldMap getVec (x : xs)+      Int16Value {} -> Int16Value res_shape $ foldMap getVec (x : xs)+      Int32Value {} -> Int32Value res_shape $ foldMap getVec (x : xs)+      Int64Value {} -> Int64Value res_shape $ foldMap getVec (x : xs)+      Word8Value {} -> Word8Value res_shape $ foldMap getVec (x : xs)+      Word16Value {} -> Word16Value res_shape $ foldMap getVec (x : xs)+      Word32Value {} -> Word32Value res_shape $ foldMap getVec (x : xs)+      Word64Value {} -> Word64Value res_shape $ foldMap getVec (x : xs)+      Float32Value {} -> Float32Value res_shape $ foldMap getVec (x : xs)+      Float64Value {} -> Float64Value res_shape $ foldMap getVec (x : xs)+      BoolValue {} -> BoolValue res_shape $ foldMap getVec (x : xs)+    where+      getVec (Int8Value _ vec) = SVec.unsafeCast vec+      getVec (Int16Value _ vec) = SVec.unsafeCast vec+      getVec (Int32Value _ vec) = SVec.unsafeCast vec+      getVec (Int64Value _ vec) = SVec.unsafeCast vec+      getVec (Word8Value _ vec) = SVec.unsafeCast vec+      getVec (Word16Value _ vec) = SVec.unsafeCast vec+      getVec (Word32Value _ vec) = SVec.unsafeCast vec+      getVec (Word64Value _ vec) = SVec.unsafeCast vec+      getVec (Float32Value _ vec) = SVec.unsafeCast vec+      getVec (Float64Value _ vec) = SVec.unsafeCast vec+      getVec (BoolValue _ vec) = SVec.unsafeCast vec
+ src/Futhark/Test/Values/Parser.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Megaparsec-based parser for primitive 'Value's.  The difference+-- between this and the parser defined in "Futhark.Test.Values" is+-- that we don't try to handle both the textual and binary format -+-- only the former.  On the other hand, this parser has (much) better+-- error messages and can be easily used by other parsers (like the+-- ones for FutharkScript or test blocks.+module Futhark.Test.Values.Parser+  ( parsePrimType,+    parseType,+    parsePrimValue,+    parseValue,+  )+where++import Control.Monad.Except+import Data.Functor+import qualified Data.Set as S+import qualified Data.Text as T+import qualified Data.Vector.Storable as SVec+import Data.Void+import Futhark.Test.Values+import qualified Language.Futhark.Syntax as F+import Text.Megaparsec+import Text.Megaparsec.Char.Lexer+  ( binary,+    decimal,+    float,+    hexadecimal,+    signed,+  )++type Parser = Parsec Void T.Text++-- | Parse the name of a primitive type.  Does *not* consume any+-- trailing whitespace, nor does it permit any internal whitespace.+parsePrimType :: Parser F.PrimType+parsePrimType =+  choice+    [ "i8" $> F.Signed F.Int8,+      "i16" $> F.Signed F.Int16,+      "i32" $> F.Signed F.Int32,+      "i64" $> F.Signed F.Int64,+      "u8" $> F.Unsigned F.Int8,+      "u16" $> F.Unsigned F.Int16,+      "u32" $> F.Unsigned F.Int32,+      "u64" $> F.Unsigned F.Int64,+      "f32" $> F.FloatType F.Float32,+      "f64" $> F.FloatType F.Float64,+      "bool" $> F.Bool+    ]++parseInteger :: Parser Integer+parseInteger =+  signed (pure ()) $+    choice+      [ "0b" *> binary,+        "0x" *> hexadecimal,+        decimal+      ]++parseIntConst :: Parser F.PrimValue+parseIntConst = do+  x <- parseInteger+  notFollowedBy $ "f32" <|> "f64" <|> "."+  choice+    [ signedV F.Int8Value x "i8",+      signedV F.Int16Value x "i16",+      signedV F.Int32Value x "i32",+      signedV F.Int64Value x "i64",+      unsignedV F.Int8Value x "u8",+      unsignedV F.Int16Value x "u16",+      unsignedV F.Int32Value x "u32",+      unsignedV F.Int64Value x "u64",+      signedV F.Int32Value x ""+    ]+  where+    signedV mk x suffix =+      suffix $> F.SignedValue (mk (fromInteger x))+    unsignedV mk x suffix =+      suffix $> F.UnsignedValue (mk (fromInteger x))++parseFloatConst :: Parser F.PrimValue+parseFloatConst =+  choice+    [ "f32.nan" $> F.FloatValue (F.Float32Value (0 / 0)),+      "f64.nan" $> F.FloatValue (F.Float64Value (0 / 0)),+      "f32.inf" $> F.FloatValue (F.Float32Value (1 / 0)),+      "f64.inf" $> F.FloatValue (F.Float64Value (1 / 0)),+      "-f32.inf" $> F.FloatValue (F.Float32Value (-1 / 0)),+      "-f64.inf" $> F.FloatValue (F.Float64Value (-1 / 0)),+      numeric+    ]+  where+    numeric = do+      x <-+        signed (pure ()) $ choice [try float, fromInteger <$> decimal]+      choice+        [ floatV F.Float32Value x "f32",+          floatV F.Float64Value x "f64",+          floatV F.Float64Value x ""+        ]++    floatV mk x suffix =+      suffix $> F.FloatValue (mk (realToFrac (x :: Double)))++-- | Parse a primitive value.  Does *not* consume any trailing+-- whitespace, nor does it permit any internal whitespace.+parsePrimValue :: Parser F.PrimValue+parsePrimValue =+  choice+    [ try parseIntConst,+      parseFloatConst,+      "true" $> F.BoolValue True,+      "false" $> F.BoolValue False+    ]++lexeme :: Parser () -> Parser a -> Parser a+lexeme sep p = p <* sep++inBrackets :: Parser () -> Parser a -> Parser a+inBrackets sep = between (lexeme sep "[") (lexeme sep "]")++-- | Parse a type.  Does *not* consume any trailing whitespace, nor+-- does it permit any internal whitespace.+parseType :: Parser ValueType+parseType = ValueType <$> many parseDim <*> parsePrimType+  where+    parseDim = fromInteger <$> ("[" *> parseInteger <* "]")++parseEmpty :: Parser Value+parseEmpty = do+  ValueType dims t <- parseType+  unless (product dims == 0) $ fail "Expected at least one empty dimension"+  pure $ case t of+    F.Signed F.Int8 -> Int8Value (SVec.fromList dims) mempty+    F.Signed F.Int16 -> Int16Value (SVec.fromList dims) mempty+    F.Signed F.Int32 -> Int32Value (SVec.fromList dims) mempty+    F.Signed F.Int64 -> Int64Value (SVec.fromList dims) mempty+    F.Unsigned F.Int8 -> Word8Value (SVec.fromList dims) mempty+    F.Unsigned F.Int16 -> Word16Value (SVec.fromList dims) mempty+    F.Unsigned F.Int32 -> Word32Value (SVec.fromList dims) mempty+    F.Unsigned F.Int64 -> Word64Value (SVec.fromList dims) mempty+    F.FloatType F.Float32 -> Float32Value (SVec.fromList dims) mempty+    F.FloatType F.Float64 -> Float64Value (SVec.fromList dims) mempty+    F.Bool -> BoolValue (SVec.fromList dims) mempty++-- | Parse a value, given a post-lexeme parser for whitespace.+parseValue :: Parser () -> Parser Value+parseValue sep =+  choice+    [ putValue' $ lexeme sep parsePrimValue,+      putValue' $ inBrackets sep (parseValue sep `sepBy` lexeme sep ","),+      lexeme sep $ "empty(" *> parseEmpty <* ")"+    ]+  where+    putValue' :: PutValue v => Parser v -> Parser Value+    putValue' p = do+      o <- getOffset+      x <- p+      case putValue x of+        Nothing ->+          parseError . FancyError o . S.singleton $+            ErrorFail "array is irregular or has elements of multiple types."+        Just v ->+          pure v
src/Futhark/Transform/Substitute.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE UndecidableInstances #-}  -- |@@ -64,7 +63,7 @@   substituteNames substs = fmap $ substituteNames substs  instance Substitute Bool where-  substituteNames = flip const+  substituteNames = const id  instance Substitute VName where   substituteNames substs k = M.findWithDefault k k substs
src/Futhark/Util/Options.hs view
@@ -3,6 +3,7 @@   ( FunOptDescr,     mainWithOptions,     commonOptions,+    module System.Console.GetOpt,   ) where 
src/Futhark/Util/Pretty.hs view
@@ -9,6 +9,7 @@     prettyDoc,     prettyTuple,     prettyText,+    prettyTextOneLine,     prettyOneLine,     apply,     oneLine,@@ -32,6 +33,10 @@ -- | Prettyprint a value to a 'Text', wrapped to 80 characters. prettyText :: Pretty a => a -> Text prettyText = LT.toStrict . PP.prettyLazyText 80 . ppr++-- | Prettyprint a value to a 'Text' without any width restriction.+prettyTextOneLine :: Pretty a => a -> Text+prettyTextOneLine = LT.toStrict . PP.prettyLazyText 80 . oneLine . ppr  -- | Prettyprint a value without any width restriction. prettyOneLine :: Pretty a => a -> String
src/Language/Futhark.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE Safe #-}- -- | Re-export the external Futhark modules for convenience. module Language.Futhark   ( module Language.Futhark.Syntax,
src/Language/Futhark/Interpreter.hs view
@@ -1626,13 +1626,14 @@       fun2t $ \i xs -> do         let (shape, xs') = fromArray xs         return $-          if asInt i > 0-            then-              let (bef, aft) = splitAt (asInt i) xs'-               in toArray shape $ aft ++ bef-            else-              let (bef, aft) = splitFromEnd (- asInt i) xs'-               in toArray shape $ aft ++ bef+          let idx = if null xs' then 0 else rem (asInt i) (length xs')+           in if idx > 0+                then+                  let (bef, aft) = splitAt idx xs'+                   in toArray shape $ aft ++ bef+                else+                  let (bef, aft) = splitFromEnd (- idx) xs'+                   in toArray shape $ aft ++ bef     def "flatten" = Just $       fun1 $ \xs -> do         let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs
src/Language/Futhark/Parser.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE Safe #-}- -- | Interface to the Futhark parser. module Language.Futhark.Parser   ( parseFuthark,
src/Language/Futhark/Prop.hs view
@@ -118,6 +118,7 @@ import Data.Maybe import Data.Ord import qualified Data.Set as S+import qualified Data.Text as T import qualified Futhark.IR.Primitive as Primitive import Futhark.Util (maxinum, nubOrd) import Futhark.Util.Pretty@@ -386,9 +387,11 @@ sortFields l = map snd $ sortOn fst $ zip (map (fieldish . fst) l') l'   where     l' = M.toList l-    fieldish s = case reads $ nameToString s of-      [(x, "")] -> Left (x :: Int)-      _ -> Right s+    onDigit Nothing _ = Nothing+    onDigit (Just d) c+      | isDigit c = Just $ d * 10 + ord c - ord '0'+      | otherwise = Nothing+    fieldish s = maybe (Right s) Left $ T.foldl' onDigit (Just 0) $ nameToText s  -- | Sort the constructors of a sum type in some well-defined (but not -- otherwise significant) manner.
src/Language/Futhark/Semantic.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE Safe #-}- -- | Definitions of various semantic objects (*not* the Futhark -- semantics themselves). module Language.Futhark.Semantic
src/Language/Futhark/Syntax.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE Strict #-} 
src/Language/Futhark/TypeChecker.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-}  -- | The type checker checks whether the program is type-consistent -- and adds type annotations and various other elaborations.  The@@ -43,7 +42,7 @@ -- | Type check a program containing no type information, yielding -- either a type error or a program with complete type information. -- Accepts a mapping from file names (excluding extension) to--- previously type checker results.  The 'FilePath' is used to resolve+-- previously type checked results.  The 'ImportName' is used to resolve -- relative @import@s. checkProg ::   Imports ->@@ -699,20 +698,15 @@   return (mempty, env, ValDec vb')  checkDecs :: [DecBase NoInfo Name] -> TypeM (TySet, Env, [DecBase Info VName])-checkDecs (LocalDec d loc : ds) = do-  (d_abstypes, d_env, d') <- checkOneDec d-  (ds_abstypes, ds_env, ds') <- localEnv d_env $ checkDecs ds-  return-    ( d_abstypes <> ds_abstypes,-      ds_env,-      LocalDec d' loc : ds'-    ) checkDecs (d : ds) = do   (d_abstypes, d_env, d') <- checkOneDec d   (ds_abstypes, ds_env, ds') <- localEnv d_env $ checkDecs ds   return     ( d_abstypes <> ds_abstypes,-      ds_env <> d_env,+      case d' of+        LocalDec {} -> ds_env+        ImportDec {} -> ds_env+        _ -> ds_env <> d_env,       d' : ds'     ) checkDecs [] =
src/Language/Futhark/TypeChecker/Match.hs view
@@ -162,6 +162,8 @@ findUnmatched _ _ = []  {-# NOINLINE unmatched #-}++-- | Find the unmatched cases. unmatched :: [Pattern] -> [Match] unmatched orig_ps =   -- The algorithm may find duplicate example, which we filter away
src/Language/Futhark/TypeChecker/Modules.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE TupleSections #-}  -- | Implementation of the Futhark module system (at least most of it;
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -412,7 +412,7 @@       | reachable (qs ++ orig_qs) name outer_env = QualName (qs ++ orig_qs) name       | otherwise = case rem_qs of         q : rem_qs' -> prependAsNecessary (qs ++ [q]) rem_qs' (QualName orig_qs name)-        [] -> QualName (qs ++ orig_qs) name+        [] -> QualName orig_qs name      reachable [] name env =       name `M.member` envVtable env
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -36,7 +36,7 @@ import Futhark.Util (nubOrd) import Futhark.Util.Pretty hiding (bool, group, space) import Language.Futhark hiding (unscopeType)-import Language.Futhark.Semantic (includeToString)+import Language.Futhark.Semantic (includeToFilePath) import Language.Futhark.Traversals import Language.Futhark.TypeChecker.Match import Language.Futhark.TypeChecker.Monad hiding (BoundV)@@ -621,7 +621,7 @@ checkIntrinsic space qn@(QualName _ name) loc   | Just v <- M.lookup (space, name) intrinsicsNameMap = do     me <- liftTypeM askImportName-    unless ("/prelude" `isPrefixOf` includeToString me) $+    unless ("/prelude" `isPrefixOf` includeToFilePath me) $       warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation."     scope <- asks termScope     return (scope, v)
src/Language/Futhark/TypeChecker/Types.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Safe #-}  -- | Type checker building blocks that do not involve unification. module Language.Futhark.TypeChecker.Types
src/Language/Futhark/Warnings.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE Safe #-}- -- | A very simple representation of collections of warnings. -- Warnings have a position (so they can be ordered), and their -- 'Show'-instance produces a human-readable string.
src/futhark.hs view
@@ -18,6 +18,7 @@ import qualified Futhark.CLI.Dataset as Dataset import qualified Futhark.CLI.Dev as Dev import qualified Futhark.CLI.Doc as Doc+import qualified Futhark.CLI.Literate as Literate import qualified Futhark.CLI.Misc as Misc import qualified Futhark.CLI.Multicore as Multicore import qualified Futhark.CLI.OpenCL as OpenCL@@ -63,7 +64,8 @@       ("check", (Check.main, "Type check a program.")),       ("imports", (Misc.mainImports, "Print all non-builtin imported Futhark files.")),       ("autotune", (Autotune.main, "Autotune threshold parameters.")),-      ("query", (Query.main, "Query semantic information about program."))+      ("query", (Query.main, "Query semantic information about program.")),+      ("literate", (Literate.main, "Process a literate Futhark program."))     ]  msg :: String
unittests/Futhark/BenchTests.hs view
@@ -2,6 +2,7 @@  module Futhark.BenchTests (tests) where +import qualified Data.Map as M import qualified Data.Text as T import Futhark.Bench import Test.Tasty@@ -19,10 +20,11 @@       <$> printable       <*> oneof         [ Left <$> arbText,-          Right <$> ((,) <$> arbitrary <*> arbText)+          Right <$> (Result <$> arbitrary <*> arbMap <*> arbText)         ]     where       arbText = T.pack <$> printable+      arbMap = M.fromList <$> listOf ((,) <$> arbText <*> arbitrary)  -- XXX: we restrict this generator to single datasets to we don't have -- to worry about duplicates.