19. Compilation and runtime
19.1 Pipeline#
Source is tokenised, laid out, parsed, resolved, and typed into the same typed intermediate representation as full Luce, then lowered to the canonical machine representation every backend consumes. A Base module is checked by the same passes with the Base profile: the grammar additions of Chapter 21 are admitted, runtime-dependent constructs are rejected with a diagnostic naming the tier they belong to, and the freestanding property is proven by reachability.
19.2 What Base adds to the shared representation#
All target-neutral: a pointer-width integer type; a nullable pointer type, the one recorded exception to full Luce's uniform tagged optional, justified by C-representability of struct fields; a two-word unmanaged interface-view type with a witness-table-address instruction; a union type; a memory-zeroing instruction; SizeOf, AlignOf, and OffsetOf instructions the backend folds; pointer difference, pointer-integer conversion, and pointer ordering; a volatile flag on loads and stores; atomic load, store, read-modify-write, compare-exchange, and fence with an ordering; an asm region carrying every architecture variant; and a variadic call form. The verifier checks each.
19.3 Backends and bridges#
The stage-1 backend is QBE, through which every Base construct compiles today. Where QBE lacks a native form, the backend bridges, at a stated cost:
- Atomics lower to calls of the size-suffixed
__atomic_*library functions (__atomic_load_4,__atomic_fetch_add_8,__atomic_compare_exchange_4, with C's memory-order encoding), which compiler-rt supplies on macOS and libatomic supplies on Linux, where the driver adds-latomic. A call is an opaque barrier to QBE, so ordering is preserved.compare_exchange_weaklowers to the strong call. One call per operation. - Fences have no library function; they lower to a tiny out-of-line assembly helper per target (
mfenceorlock addl $0, (%rsp)on x86-64,dmb ishon ARM64). volatilelowers to the relaxed__atomic_load_Nand__atomic_store_Ncalls, because QBE's load optimiser forwards stores to loads and removes repeated loads of one address. Widths 1, 2, 4, and 8.asmblocks are emitted as out-of-line assembly functions with the C calling convention: register inputs arrive in argument registers, register outputs are materialised in the block and stored to caller slots by a generated epilogue, memory operands address those slots, and callee-saved registers named inclobberare saved and restored. A block cannot observe the caller's frame or flags. One call per block.- Variadic calls and by-value aggregates use QBE's native support.
- WebAssembly supports atomics where the host enables threads, rejects
asm, and passes variadic arguments through a shadow-stack buffer as Clang does.
The Luce-owned native backends scheduled after stage 1 implement all of these natively with no source change.
19.4 Artifacts and profiles#
A Base executable links a startup shim and a trap reporter. By default the shim uses the host C library for process start, output, and exit; --freestanding drops it and the program supplies _start through a module-level asm block or a C sidecar. No build profile changes overflow, bounds, evaluation order, or error behaviour. A diagnostic profile additionally fills --- storage, quarantines freed blocks in the standard allocators, and records allocation sites.
19.5 Tooling#
luce fmt, luce check, luce build, luce test, and luce bind apply to Base modules. luce build --lib produces a library and header. luce build --freestanding drops the shim. A --target listing shows which asm architectures a package covers.