Module Sarek_ppx_intrinsic

type parsed_type =
  1. | PTSimple of string
  2. | PTArray of parsed_type
  3. | PTVec of parsed_type
  4. | PTArrow of parsed_type * parsed_type

Parsed type representation for intrinsic declarations

val parsed_type_of_core_type : Ppxlib.core_type -> parsed_type

Extract parsed type from a core_type

val type_name_of_core_type : Ppxlib.core_type -> string

Extract type name from a core_type for registry registration (string representation)

val library_name : string option Stdlib.ref

Name of the dune library whose module is being preprocessed, from the library-name cookie dune passes to every ppx driver it runs on a library's sources (--cookie 'library-name="foo"'). None when the file is not part of a library (an executable or test stanza) or when the driver is run outside dune.

val module_path_of_loc : Ppxlib.location -> string list

The OCaml path under which the module being preprocessed is reachable from a consumer, e.g. ["Sarek_stdlib"; "Float32"] for sarek/Sarek_stdlib/Float32.ml. Both registries key the intrinsic on it, and the kernel PPX emits it verbatim as the existence witness and as the Native backend's call target, so it must be the REAL path.

It comes from the library name dune hands the driver, under dune's default (wrapped true): module M of library lib is Lib.M, and the library's main module lib.ml is Lib itself. Outside a library it is [M].

It used to be guessed from the source DIRECTORY: a directory name containing an underscore was taken to be the library name, any other was dropped. That held for sarek's own trees, whose directories are named after their libraries, and nowhere else: a downstream library foo_intrinsics living in src/intrinsics/ registered Bits.f instead of Foo_intrinsics.Bits.f, and renaming the directory changed the path.

A (wrapped false) library is not detectable from the driver: its modules are top-level, but the path computed here still carries the library prefix, and the witness then fails to compile ("Unbound module"), loudly. Declare intrinsics in a wrapped library.

val sarek_type_of_simple_name : loc:Ppxlib.location -> string -> Ppxlib.expression

Convert simple type name to Sarek_types representation

val flatten_arrow : parsed_type -> parsed_type list * parsed_type

Flatten a parsed arrow type into (arg_types, return_type). E.g., PTArrow(a, PTArrow(b, c)) becomes (a; b, c)

val sarek_type_of_simple_parsed : loc:Ppxlib.location -> parsed_type -> Ppxlib.expression

Convert a non-arrow parsed type to Sarek_types representation

val sarek_type_of_parsed : loc:Ppxlib__.Location.t -> parsed_type -> Ppxlib.expression

Convert parsed type to Sarek_types representation. Flattens arrow types so a -> b -> c becomes TFun(a; b, c).

val sarek_type_of_name : loc:Ppxlib.location -> string -> Ppxlib.expression

Convert type name string to Sarek_types representation (for backward compat)

val build_sarek_fun_type : loc:Ppxlib.location -> string list -> string -> Ppxlib.expression

Build a function type from argument types and return type

val size_of_type_name : loc:Ppxlib.Location.t -> string -> int

Derive the size (in bytes) of a sarek type from its name alone. This avoids the Ctypes dependency for the pure metadata path. Sizes match the Ctypes.sizeof values for the corresponding C types: float32=4, float64=8, int32=4, int64=8, bool=4, char=1, int=4 (GPU int).

val expand_sarek_intrinsic_type : ctxt:Ppxlib.Expansion_context.Extension.t -> Ppxlib.payload -> Ppxlib_ast.Ast.structure_item list

Extension for type%%sarek_intrinsic - handles type registration. The ctype field is OPTIONAL.

  • When present: emits both Sarek_registry (JIT/FFI) and Sarek_ppx_registry registrations using Ctypes.sizeof for the authoritative byte-size.
  • When absent: emits only Sarek_ppx_registry registration using a pure size derived from the type name. No Ctypes dependency.
val extract_fun_types : Ppxlib.core_type -> string list * string

Extract argument types and return type from a function type

val host_ctor_of_type_name : string -> string option

The Sarek_registry.host_value constructor carrying a scalar of the given declared type, or None when the type is not a scalar the registry's host path can carry (int, arrays, vectors, registered custom types).

val host_impl_registration : loc:Ppxlib.location -> module_path_expr:Ppxlib_ast.Ast.expression -> fun_name:string -> string list -> string -> Ppxlib_ast.Ast.structure_item option

Registration of the host implementation fun_name (already bound) for evaluators without a built-in entry for it (the interpreter). None when the signature has no argument or a non-scalar type: such an intrinsic is left to the evaluator's own table, as before.

val expand_sarek_intrinsic_fun : ctxt:Ppxlib.Expansion_context.Extension.t -> Ppxlib.payload -> Ppxlib_ast.Ast.structure_item list

Extension for let%sarek_intrinsic - handles function registration

val expand_sarek_intrinsic : ctxt:Ppxlib.Expansion_context.Extension.t -> Ppxlib.payload -> Ppxlib_ast.Ast.structure_item list

Combined extension for %sarek_intrinsic - handles both types and functions

val sarek_intrinsic_extension : Ppxlib.Extension.V3.t
val expand_sarek_extend : ctxt:Ppxlib.Expansion_context.Extension.t -> Ppxlib.payload -> Ppxlib_ast.Ast.structure_item

Extension for %sarek_extend - allows extending intrinsics for new backends.

Syntax: let%sarek_extend Module.func = fun dev -> if ... then "new_code" else Module.func_device dev

This updates the Module.func_device_ref to point to the new function, which chains to the original via Module.func_device.

val sarek_extend_extension : Ppxlib.Extension.V3.t