System Primitives and Runtime

This page describes the operational clean LLVM compiler. Low-level operations are part of seed itself, not an addon language, but ordinary applications should consume narrow audited safe wrappers.

Runtime profiles

Profile Meaning
hosted platform runtime services selected by the target
freestanding libc-free image with explicitly selected services
kernel kernel ABI/service restrictions and no hosted assumptions
none no runtime services beyond target/compiler primitives

Effects (alloc, blocking, panic, I/O) are checked against the selected profile/service set. Profiles do not change ownership/type semantics.

Linux-native hosted execution has no mandatory libc. linux-libc is an explicit interoperability runtime choice. Freestanding/kernel targets remain libc-free.

Typed raw memory

unsafe fn use_buffer() -> Result<i64, alloc_error> {
    let pointer = raw_try_alloc<i64>(4)?
    raw_write<i64>(pointer, 0, 42)
    let value = raw_read<i64>(pointer, 0)
    raw_free<i64>(pointer)
    Ok(value)
}

An extern ABI that returns process/static-lifetime view storage may declare @static_view. This is a trusted provenance assertion restricted to extern; the call remains unsafe and an ordinary safe library wrapper must audit the foreign lifetime guarantee.

The clean unsafe substrate is raw_try_alloc<t>, raw_null<t>, raw_is_null<t>, raw_read<t>, raw_write<t>, raw_free<t>, and raw_slice<t>. Allocation rejects negative counts, multiplication overflow, and allocator failure with alloc_error. Unsafe callers remain responsible for extent, alignment, initialization, provenance, aliasing, and exact-once element release.

raw_null<t>() produces the internal null sentinel used by audited unsafe owners, and raw_is_null<t>(pointer) tests it. The standard box<t> confines those operations behind its constructor, consuming extraction, and destructor.

Linux-native allocations whose payload plus header fits 8 KiB and whose alignment is at most 16 bytes use precise slab classes (16 bytes through 8 KiB). Empty 1 MiB slabs share a process-wide 32-entry cache; excess empty slabs are unmapped. Larger or more strictly aligned allocations remain page-rounded direct mappings and are unmapped immediately.

The native runtime also supplies optimizer-introduced memcpy, memmove, and memset entry points without libc. Its sqrt(f64) runtime symbol lowers to the LLVM square-root intrinsic, allowing strict floating-point workloads to remain libc-free.

Legacy alloc(type,n), integer-address read/write, and free examples belong to archived backends and are not the clean compiler API.

Address spaces and devices

Raw pointer types may identify user, kernel, physical, mmio, or dma address spaces. They remain distinct through .sdi and LLVM lowering. Typed volatile operations and explicit unsafe mapping/cast transitions are used for device memory. compiler/llvm/libs/system builds safe nominal wrappers for MMIO, pages, pin/DMA, initialization, and CPU-local mechanisms.

Atomics

The compiler validates acquire/release helpers and explicit ordered load, store, fetch-add, exchange, compare-exchange, and fence operations. Invalid load/store/CAS ordering combinations are rejected. Atomics are distinct from volatile device access. A safe wrapper must establish allocation lifetime, alignment, shared access, and a consistent atomic protocol.

FFI and ABI

The target/ABI layer assigns explicit internal seed, public seed, platform-C, compiler-builtin, entry, interrupt, and naked ABI classes. .sdi includes target, ABI, build-profile, runtime-profile, and runtime-feature identity; known seed artifact mismatches fail closed with a rebuild diagnostic. Foreign C/assembly objects have no seed profile metadata and remain explicitly linkable.

Covered FFI boundaries include scalars, raw pointers, non-capturing callbacks, opaque handles, and pointer-based scalar C-struct wrappers. By-value C structs and richer platform ABI modeling remain limited. Foreign calls are unsafe until wrapped by a checked safe API.

Panic and cleanup

Safe check failure invokes the target panic/trap policy. Panic, trap, and abort never unwind across seed, FFI, dynamic-library, interrupt, kernel/user, or task boundaries. No lexical cleanup or user destructor runs on those edges.

Deferred durability cohort (V3-G48)

Bulk loaders can defer file durability to explicit boundaries. With the cohort active, path_file.sync() (and any other seed_runtime_file_sync caller) records a fcntl(F_DUPFD) duplicate of the descriptor instead of fsyncing; closing the boundary syncs every pending file once:

extern "c" fn seed_runtime_sync_defer(enabled: i64) -> i64;
extern "c" fn seed_runtime_sync_flush() -> i64;

Heap debugging

Setting SEED_HEAP_FENCE=1 in the hosted process environment switches the native runtime allocator to an electric-fence mode: every heap allocation is backed by private page mappings with a guard page placed immediately after the payload, so out-of-bounds writes and use-after-free accesses fault at the offending instruction instead of corrupting neighbors. It is a diagnostic mode (roughly two mmap calls per allocation), not a production configuration. Fence-mode size arithmetic (payload + alignment overhead + page rounding + guard page) is overflow-checked with uadd.with.overflow and fails the allocation cleanly on overflow, mirroring the normal large-allocation path.

Backend trust invariants

Some projections carry no runtime guard in the emitted LLVM IR — iterator element extraction (ITERATOR_ELEMENT) and ADT payload projection. This is deliberate: the front end proves dominance before the backend sees the program. A payload place is only well-formed inside the match arm whose pattern bound it, and an iterator element place is only well-formed between the iterator's next check and its advance, so the tag/bounds check has already been proven true by construction. The backend trusts these proofs and emits no redundant tag or bounds test; adding one would tax every match and loop for a condition that cannot be false. The contract is maintained by the mandatory HIR verification and cleanup re-verification passes (C900 on divergence), not by emitted guards.

Separate runtime and libraries

Compiler-builtins use stable runtime symbol IDs rather than source-name string inspection. Hosted executable runtime services include allocation/resource operations, process entry/args/environment, output/files, time/randomness, network descriptors, scheduler hooks, regions, panic, and abort at the level implemented by each target row.

seed_runtime_format_write consumes the stable immediate format_args descriptor and writes each {pointer, byte_length} piece without building a contiguous buffer. format_args.to_string() instead uses the selected allocator and reports failure as alloc_error; neither path requires libc.

Dynamic libraries declare required runtime symbols and resolve against the executable/runtime domain. Installed libraries are target/ABI qualified; dynamic linkage is default and --static selects static seed archives. Process libraries use the public seed_runtime_process_* boundary. Native entry-vector primitives such as seed_process_argc remain hidden inside the executable and are reached only through exported runtime wrappers.

Target levels

The declarative target matrix separates LLVM/object emission from static link, dynamic link, executable, and run levels.

See seed/compiler/llvm/tests/GATE14_TARGET_MATRIX.tsv rather than inferring capabilities from the presence of an LLVM triple.

Boot meaning

For Gate 7, “boot” means the compiler emits a target image, QEMU system emulation loads it through the declared firmware/machine path, target entry code executes, writes the expected success evidence through the target mechanism, and exits or reaches the expected terminal state. It does not mean that a full operating system, driver stack, userspace, or libc is present.

Release profiles

--release and --release-small preserve safe checks. LLVM may remove checks only when proved redundant. ThinLTO, PGO, static linkage, code/relocation model, red-zone policy, entry, linker script, target, runtime profile, and sysroot are explicit independent controls.