abi is a top-level attribute that controls the calling convention and worker entrypoints generated for a top-level function or typeclass instance method.
Syntax
;; Top-level attribute on declare or define
(abi ⟨convention⟩)
(declare ⟨name⟩ ⟨type⟩)
(abi ⟨convention⟩)
(define (⟨name⟩ ⟨arg⟩*) ⟨body⟩+)
;; Attribute on an instance method
(define-instance (...)
(abi ⟨convention⟩)
(define (⟨method-name⟩ ⟨arg⟩*) ⟨body⟩+))
;; ⟨convention⟩ := :auto | :lisp | :unpacked | :unboxed
Semantics
(abi ⟨convention⟩)must appear immediately before a top-leveldeclare, a top-leveldefine, or a method definition insidedefine-instance. At most oneabiattribute may be attached to a declaration or definition, and dynamic variables (*name*), types, and classes cannot carry anabiattribute.- Every top-level function
falways retains its canonical:lispentrypoint on the symbolf(both(cl:fdefinition 'f)and the global lexical valuef), so higher-order calls ((map f xs)), first-class function values,call-coalton-function, keyword-bearing calls, and REPL/FFI callers continue to use standard boxed Common Lisp calling conventions. - When a specialized convention is active and eligible, the compiler generates one or more worker entrypoints alongside
fand rewrites saturated direct calls(f a1 ... an)to invoke the specialized entrypoint directly::lisp:- Emits only the canonical boxed Common Lisp entrypoint
f. - This is the default in development mode when no
abiattribute is present.
- Emits only the canonical boxed Common Lisp entrypoint
:unpacked:- Generates a portable worker entrypoint
f%UNPACKEDover standard Common Lispcl:defunandcl:values:- Single-constructor product arguments (
define-structand single-constructordefine-typeADTs such asTuple) that are only destructured inside the function are flattened into separate positional parameters (up to 16 flattened inputs). - Single-constructor product return types are returned as multiple values via
cl:values(up to 16 flattened outputs). - Non-recursive sum return types with concrete payload fields (such as
OptionalandResult, up to 8 constructors) are returned viacl:valuesas a discriminant tag (Booleanfor 2-constructor sums,UFixfor 3–8-constructor sums) followed by the payload values.
- Single-constructor product arguments (
- Intermediate pack/unpack steps across chained direct calls
(f (g x))andmatchexpressions cancel out during optimization without allocating intermediate structs or sum wrappers.
- Generates a portable worker entrypoint
:unboxed:- Extends
:unpackedwith an additional worker entrypointf%UNBOXEDwhen supported by the host compiler and architecture (on SBCL:x86-64and:arm64when type annotations are enabled). - Passes flattened
U64,I64,F32, andF64arguments and return values directly in CPU general-purpose and floating-point registers across non-inlined and cross-file calls without heap boxing. - On Common Lisp implementations or architectures without
:unboxedsupport,(abi :unboxed)automatically falls back to:unpacked(for composite types) or:lisp.
- Extends
:auto:- Automatically selects the most specialized eligible convention (
:unboxed>:unpacked>:lisp). - This is the default for all eligible top-level functions and instance methods in release mode (
COALTON_ENV=release).
- Automatically selects the most specialized eligible convention (
- Whole-value parameters:
- A struct or tuple parameter is flattened in
f%UNPACKED/f%UNBOXEDonly when all uses of that parameter inside the function are field reads or pattern matches. If the parameter is also used as a whole value (for example, stored into a data structure, passed to a higher-order function, or captured in the variable list of alispform), it remains a single boxed parameter so the callee never re-allocates a duplicate copy.
- A struct or tuple parameter is flattened in
- Functions with keyword parameters (
&key) always use the:lispconvention.
Examples
(coalton-toplevel
(define-struct Vec3
(x F64)
(y F64)
(z F64))
;; Flatten Vec3 inputs/outputs and pass all F64 leaves in XMM registers on SBCL
(abi :unboxed)
(declare vec3-fma (Vec3 * F64 * Vec3 -> Vec3))
(define (vec3-fma a scale b)
(Vec3 (+ (* (.x a) scale) (.x b))
(+ (* (.y a) scale) (.y b))
(+ (* (.z a) scale) (.z b))))
;; Return Optional U64 as (values Boolean U64) in registers without heap allocation
(abi :unboxed)
(declare safe-div (U64 * U64 -> Optional U64))
(define (safe-div a b)
(if (== b 0)
None
(Some (coalton/math/integral:quot a b))))
;; Force the canonical boxed Common Lisp ABI
(abi :lisp)
(declare boxed-sum (Tuple U64 U64 -> U64))
(define (boxed-sum (Tuple x y))
(+ x y)))
Other Notes
- When a function with
:unpackedor:unboxedworker entrypoints is redefined with a compatible type signature, all of its entrypoints are updated in lockstep. When it is redefined with an incompatible signature, Coalton installs forwarding stubs on any retired worker symbols so existing compiled callers continue to route through the new definition. - See Performance and Optimization and
repr.