smlfut(1)

πŸ“¦ version 1.6.4, in nixos 26.05, nixpkgs unstable

πŸ’– Name πŸ’–

Okay so this is <pink>smlfut(1)</pink> from the General Commands Manual, bestie β€” and <pink>smlfut</pink> means call Futhark from Standard ML. That's the whole vibe. πŸ’…

✨ Synopsis ✨

smlfut [options] MANIFEST.json

πŸŽ€ Description πŸŽ€

<pink>smlfut</pink> lets SML programs invoke Futhark programs through Futhark's C API, by generating appropriate wrapper code, which is then compiled as normal with an SML compiler. It's literally the glue between your SML and your Futhark, no cap. πŸ¦‹

πŸ› οΈ Options πŸ› οΈ

  • --mono-arrays β€” Equivalent to --target=mlton-mono.
  • --poly-arrays β€” Equivalent to --target=mlton-poly.
  • --output-directory=NAME β€” Write output files to this directory.
  • --signature-name=NAME β€” Use this name for the SML signature. Defaults to the uppercased basename of the manifest file.
  • --structure-name=NAME β€” Use this name for the SML structure. Defaults to the basename of the manifest file.
  • --target=TARGET β€” Target this SML compiler. Must be one of mlkit, mlton-poly, or mlton-mono (the default).
  • -V, --version β€” Print version information.

🧚 Usage 🧚

First compile a Futhark program foo.fut with futhark(1) using one of the C backends, then pass the generated JSON manifest file foo.json to smlfut. This will produce three files: foo.sig, foo.sml, and foo.smlfut.c. The first two files contain definitions of SML signatures and structures, while the last contains C code. The name of the structure will be taken from the file basename (in this case 'foo') and the signature name will be the structure name in all caps ('FOO'). The generated SML files can be passed to the SML compiler, and the final executable must be linked with foo.smlfut.c, and of course the C file generated by the Futhark compiler itself.

The generated interface directly exposes the C API, so it is low level. The only concession is that the C error codes are turned into SML exceptions.

The generated signatures are designed to be human readable and should be mostly straightforward. The general principle is that you construct a context by passing a configuration record. The context maintains Futhark state and is passed to entry points and various other functions.

Different context objects are not interchangeable, despite having the same type, and Futhark values obtained via one context may not be passed to another. Like, do not mix those contexts, bestie. 😭

πŸ‘‘ Top level signature πŸ‘‘

The generated structure implements the following signature (here using the name FUTHARK, but this is taken from the program file name or the --signature-name option).

             signature FUTHARK =
             sig
               val backend : string
               val version : string

               type ctx
               exception Error of string
               exception Free
               type cfg = { logging:bool,
                          , debugging:bool
                          , profiling:bool
                          , cache:string option
                          , tuning: (string * int) list
                            (* Possibly more *)
                          }

               structure Config : sig
                 (* See below *)
               end

               structure Context : sig
                 (* See below *)
               end

               (* Array specs... *)

               structure Opaque : sig
                 (* Opaque type specs... *)
               end

               structure Entry : sig
                 (* Entry points... *)
               end
             end

🌸 Description 🌸

  • backend β€” The Futhark compiler backend used to compile the program.
  • version β€” The Futhark compiler version used to compile the program.
  • exception Error β€” Raised when Futhark execution fails. The string is the error message reported by Futhark.
  • exception Free β€” Raised when an attempt is made to use an object that has been freed.
  • type ctx β€” Contains Futhark runtime state.
  • type cfg β€” Configuration record used to create the context. Depending on the backend, this record may have more fields.
  • structure Config/Context/Opaque/Entry β€” See below.

πŸ› οΈ The Config Structure πŸ› οΈ

Facilities for easily constructing or modifying configuration records. In particular, the structure exposes a setter for each field in the configuration record, as SML's built-in facilities for updating records are rather cumbersome. It's giving "setter slay." πŸ’…

             structure Config : sig
               val default : cfg
               val logging : bool -> cfg -> cfg
               val debugging : bool -> cfg -> cfg
               val profiling : bool -> cfg -> cfg
               val cache : string option -> cfg -> cfg
               val tuning : (string * int) list -> cfg -> cfg
               (* Possibly more *)
             end

🌈 The Context Structure 🌈

The context is essentially the Futhark runtime state. Main character energy, honestly. πŸ‘‘

             structure Context : sig
               val new : cfg -> ctx
               val free : ctx -> unit
               val sync : ctx -> unit
               val report : ctx -> string
               val pauseProfiling : ctx -> unit
               val unpauseProfiling : ctx -> unit
               val clearCaches : ctx -> unit
             end

🌟 Description 🌟

  • new β€” Create a context from a configuration. May be quite slow β€” don't call it too often. May throw Error if initialisation of the Futhark context fails, or if an invalid tuning parameter is provided in the configuration.
  • free β€” Free a context. It may not be used again. There is no guarantee that this frees any Futhark values (e.g. arrays) associated with the context. To avoid memory leaks, make sure to free all values before freeing the context.
  • sync β€” Wait for all asynchronous operations to finish. Depending on the Futhark backend, entry points may execute asynchronously, and errors may not be reported until the next function call. Use Context.sync to ensure all errors have been detected.
  • report β€” Retrieve (and reset) current profiling information. Contains much more interesting information if the configuration had its profiling field set to true.
  • pauseProfiling β€” Temporarily suspend the collection of profiling information. Has no effect if profiling was not enabled in the configuration, or if profiling is already paused.
  • unpauseProfiling β€” Resume the collection of profiling information. Has no effect if profiling was not enabled in the configuration, or if profiling is not currently paused.
  • clearCaches β€” Release any context-internal caches and buffers that may otherwise use computer resources. Will not invalidate any values or ongoing computation.

πŸ¦‹ The Entry Structure πŸ¦‹

This structure contains an SML function for each corresponding Futhark entry point. They all have the type

             val foo : ctx -> args -> result

where args and result may be tuples. Futhark's scalar types are mapped directly to the corresponding SML types, e.g. f64 to Real64. Each non-primitive type is represented by an appropriate abstract type, which presents an interface to the underlying Futhark value, as discussed below. While execution of an entry point can in principle be asynchronous, this is not semantically visible. The result(s) returned by an entry point can be used immediately. You only need to call Context.sync if you wish to check for errors.

πŸ’œ Array Structures πŸ’œ

Depending on the target compiler, each Futhark array type is represented by a structure implementing one of the signatures below. The mlton-poly target uses polymorphic arrays, while all other targets use monomorphic arrays.

             signature FUTHARK_MONO_ARRAY =
             sig
               type array
               type ctx
               type shape
               type index
               type raw
               structure Array : MONO_ARRAY
               structure Slice : MONO_ARRAY_SLICE
               val new: ctx -> Slice.slice -> shape -> array
               val free: array -> unit
               val shape: array -> shape
               val index: array -> index -> Array.elem
               val values: array -> native.array
               val values_into: array -> Slice.slice -> unit
               val new_raw: ctx -> raw -> shape -> array
               val values_raw: array -> raw
             end
             signature FUTHARK_POLY_ARRAY =
             sig
               type array
               type ctx
               type shape
               type index
               type elem
               type raw
               val new: ctx -> elem ArraySlice.slice -> shape -> array
               val free: array -> unit
               val shape: array -> shape
               val index: array -> index -> elem
               val values: array -> elem Array.array
               val values_into: array -> elem ArraySlice.slice -> unit
               val new_raw: ctx -> raw -> shape -> array
               val values_raw: array -> raw
             end

The name of an array structure is similar to the naming of monomorphic arrays in the SML basis library, e.g., Int32Array1 for []i32 and Real32Array32 for [][]f32.

⭐ Description ⭐

  • type array β€” The abstract array type.
  • type ctx β€” The Futhark context type. Each array structure refines this to be identical to the type ctx type of the top level structure.
  • type shape β€” A type for the shape of the array. This is int for single dimensional arrays, and otherwise a tuple of integers.
  • type index β€” A type for valid indexes into the array. This is identical to shape and is only present to make the types more readable.
  • index β€” Read a single element from the array. Raises Subscript on out of bounds.
  • new β€” Construct a Futhark array from an SML array slice. Raises Size if the length of the array slice is not equal to the product of the provided shape.
  • raw β€” The type of the raw underlying pointer corresponding to the array memory.
  • free β€” Frees the array. Do not use it again. If you do not manually call this function when you are done with the array, then you leak memory.
  • values β€” Synchronously copy the Futhark array into a freshly constructed SML array.
  • values_into β€” Synchronously copy the Futhark array into an existing SML array slice. Raises Size if the length of the slice is not equal to the product of the array shape.
  • new_raw β€” Construct an array from a raw pointer, without copying. Futhark does not take ownership of this memory, and it must not be freed until Futhark no longer uses it.
  • values_raw β€” Retrieve a pointer to the raw data underlying this array.

πŸ’Ž OPAQUE TYPES πŸ’Ž

Each non-record opaque type is represented by a structure implementing the signature below.

signature FUTHARK_OPAQUE =
sig
  type t
  type ctx
  val free    : t -> unit
  val store   : t -> Word8Array.array
  val restore : ctx -> Word8ArraySlice.slice -> t
end

🫧 Description 🫧

  • free β€” frees the object. Do not use it again. If you don’t manually call this function when you’re done with the object, then memory will be leaked. fr fr, leak city bestie πŸ’€
  • store β€” serialise the value to a byte array. clean, simple, iconic ✨
  • restore β€” restore a value from its serialised representation.
Warning bestie: you should not pass untrusted data to restore, as this can result in memory corruption. like, do not do it πŸ™ˆ

The serialisation format is stable across compiler backends and recompilation, but not necessarily across different compiler versions β€” although in practice compatibility is broken very rarely. slay for stability πŸ’…


🧾 RECORD TYPES 🧾

Each record type is represented by a structure implementing the signature below.

signature FUTHARK_RECORD =
sig
  include FUTHARK_OPAQUE
  type record
  val values : t -> record
  val new : ctx -> record -> t
end

Any struct that implements this signature will refine the record type such that the Futhark value can be constructed from an appropriate SML record. As a special case, a Futhark tuple (0-indexed) is associated with a properly 1-indexed SML tuple. iykyk πŸŒ€

Note that if you convert a Futhark record into an SML record with values, you must still also free the original Futhark record at some point. don’t forget the cleanup era 🧹


πŸŒ€ SUM TYPES πŸŒ€

Each sum type is represented by a structure implementing the signature below.

signature FUTHARK_SUM =
sig
  include FUTHARK_OPAQUE
  type sum
  val values: t -> sum
  val new: ctx -> sum -> t
end

Any struct that implements this signature will define the type sum to be an SML mirror of the Futhark-level sum type. Note that if you convert a Futhark sum type value into an SML value with values, you must still also free the original Futhark object at some point. same energy as records, bestie β€” free the original πŸ’–


πŸ—‚οΈ ARRAYS OF RECORDS πŸ—‚οΈ

Each array of records is represented by a structure implementing the signature below.

signature FUTHARK_RECORD_ARRAY =
sig
  include FUTHARK_OPAQUE
  type shape
  type index
  type elem
  type fields
  val shape: t -> shape
  val index: t -> index -> elem
  val zip : fields -> t
end

The shape, index, and elem types are similar to the corresponding types in the FUTHARK_ARRAY signature. The fields type will be refined to a tuple of Futhark array types, which can be zipped together (using zip) to produce an array of records. Raises Size if the arrays do not have matching shapes (up to the rank of the record array type). shape check or it’s a no from me βœ‹


🧊 ARRAYS OF OPAQUES 🧊

Each array of opaque non-records is represented by a structure implementing the signature below.

signature FUTHARK_OPAQUE_ARRAY =
sig
  include FUTHARK_OPAQUE
  type shape
  type index
  type elem
  val shape: t -> shape
  val index: t -> index -> elem
end

The shape, index, and elem types are similar to the corresponding types in the FUTHARK_ARRAY signature. In practice, this signature is used only for arrays comprised of sum type values. niche but iconic 🫢


πŸšͺ ENTRY POINTS πŸšͺ

Each Futhark entry point becomes a function with two parameters: the context object, and a tuple with one element per parameter of the original Futhark function. main character function energy πŸ‘‘


πŸ”— LINKING FUTHARK WITH MLTON πŸ”—

The Futhark compiler produces C code, which must be passed to mlton(1) in order to produce an executable. This can be done simply by passing the .c file to mlton(1) on the command line, along with the .sml and/or .mlb file. However, if you want to use a GPU backend, you must also pass the appropriate linker flags, e.g.

mlton foo.mlb foo.c foo.smlfut.c -link-opt '-lcuda -lnvrtc -lcudart'

for CUDA. GPU girlies rise πŸŽ€

Note that foo.c is the file produced by futhark(1), while foo.smlfut.c is the file generated by smlfut. See the Futhark documentation for the appropriate linker flags needed for a given backend. read the docs, bestie β€” it’s giving necessary πŸ’…


🧩 LINKING FUTHARK WITH MLKIT 🧩

The same general concerns apply with MLKit as with MLton. To link with MLKit, you must compile the C file generated by the Futhark compiler, foo.c, and the C file produced by smlfut foo.smlfut.c, to object files foo.o and foo.smlfut.o, and then combine them to a single archive file with

ar r libfoo.a test.o test.smlfut.o

The libfoo.a file can then be supplied to MLKit by passing

-libs m,c,dl,test

on the command line (along with other relevant linker flags). archive and deliver ✨


πŸ› BUGS πŸ›

  • The generated code currently supports only MLton, MPL, and MLKit.
  • The generated code directly exposes the manual memory management required by Futhark’s C interface. If you neglect to free the values built using constructor functions or entry points, you will leak memory. Freeing the context before freeing all associated values will also leak memory. memory leak jumpscare 😭
  • Be careful not to mix values associated with different contexts. They have the same type, so this is an easy mistake to make. same type does NOT mean same context, bestie β€” stay safe πŸ™

πŸ“œ LICENSE πŸ“œ

smlfut itself is released under the GPLv3 or later. The code generated by smlfut may be used for any purpose, without restrictions or requirements, without any need for attribution. generated code says free to use, no attribution needed β€” we love a generous queen 🌈


πŸ’Œ okay that’s the whole page, bestie β€” brought to you by DIKU Β· 2024 Β· smlfut(1). mwah πŸ’‹

πŸ“¦ where this page comes from