Runtime
runtime/ is the C platform layer. Generated programs link against it. bin/giz builds
it as libgizlang_runtime.a and libgizlang_runtime.so. A program an installed toolchain builds on Linux or macOS loads
the release's lib/gizlang/TARGET/libgizlang_runtime.so (.dylib on macOS: the runtime, libuv and OpenSSL) from its versions folder,
so a runtime fix reaches it without a relink and the programs on a host share one copy; a fast (-O0) source build
loads the build folder's. GIZLANG_RUNTIME_STATIC=1 links the archive, for a program copied to a machine without
the toolchain. The toolchain's own programs link the archive (tools/cli/build.giz runtimeShared). Windows: see Windows.
The runtime owns no language semantics. The compiler checks types, effects and borrows. The
runtime gives storage, handles and I/O through a narrow gizlang_* C ABI.
Files
| File | Owns |
|---|---|
resources.c | One handle table of files, sockets and processes: handles checked by generation, leases, borrows, deferred disposal, owner thread. |
heap.c | The precise, non-moving managed heap of each World, for Ref<T>: its own handles, typed roots, holds for values owned outside frames (task boxes, channel items, live state), collection. MEMORY owns the model. |
value_layout.c | Walks the layout descriptors that the compiler emits (scalars, Refs, slices, products, variants): traces Refs, counts buffers as generated dup and drop do, and encodes a value independently of its position, for replay. |
replay.c | Records the inputs of a run (declared I/O results, Task.now, batches of woken tasks) and replays them with no I/O (giz run --record/--replay LOG). A different call is a divergence (status 76). |
services.c | Binds one heap, one handle table and one I/O loop to a call context. |
refs.c | Ref create, load, store and collect. Holds and roots over the heap of the calling context (gizlang_services_heap). |
io.c | The only event loop, on pinned libuv 1.53.1 (commit eb497c58). Timers, files, TCP connect, listen and accept, DNS, cancellation tokens. |
fs.c | Files scoped by capability: attenuating grants, directory-relative opens that do not follow links, file size and time, directory creation, remove and list. |
text.c | Console output (with the console grant of the call). |
base64.c | Base64. It is freestanding: the native and the Wasm runtime both link it. Refer to Base64 in C. |
isolate.c | A process spawned isolated confines itself at start: namespaces, its folder as root, no capabilities, seccomp, no new machine code. |
uring.c | The C part of System.ring: one io_uring of 128-byte entries that carries URING_CMD (the ublk driver of fs/ublk.giz), and the read-only map of a descriptor area (System.mapShared). It is the only io_uring of the runtime. Refer to SYSTEM. |
levels.c | gizlang_cpu_level: the x86-64 level (baseline, v3 or v4) that the vector functions of a program run at (tools/cli/levels.giz gives each one a copy for each level). It reads the CPU once, or the level that GIZLANG_CPU names, and stops a program with a wrong GIZLANG_CPU before main. |
machine.c | gizlang:machine, the one owner of the host's CPU facts: the instruction-set features, capped at the levels.c level (no AVX-512 under GIZLANG_CPU=v3), the CPUs of the process's affinity, each cache instance and the CPUs that share it, and the memory nodes. It reads them once a process (sysfs on Linux, sysctl on macOS). |
glibc235.c | Linked into every program, so that it runs on glibc 2.35 and later. It gives the __isoc23_* functions that glibc 2.38 headers call under _GNU_SOURCE. It is compiled without _GNU_SOURCE, so it calls the older functions. tests/tools/levels checks that no symbol is newer than 2.35. On Windows it holds the thread's marked bytes instead (a DLL cannot share a thread-local variable). |
win32.c | Windows only (empty elsewhere): the POSIX file, directory, descriptor and process calls that win32.h declares, on Win32 handles, so the other files keep one code path. Refer to Windows. |
format.c | Value text, and UTF-8 validation of the C strings that native calls return. It uses memory only, so Wasm shares it. string(bytes) validation and byte search are Gizlang SIMD (utf8Valid in sdk/core/runtime.giz, bytes.indexOf and indexOfAny in sdk/core/text.giz). gizlang_text_utf8, _find and _find_any stay only for a CLI that the seed builds, until the next new seed. |
task.c | Structured tasks: stackful coroutines (x86-64 and AArch64 switch, guarded MAP_NORESERVE stacks, ASan fiber hooks), one ready queue, the io.c loop when nothing is ready, cancellation and deadlock faults, and bounded channels whose values are checked by layout key. |
system.c | The services behind gizlang:system: one handle table of directories, files, TCP and pipe streams, listeners, datagram sockets, Ready sets and child processes. The task that opened each one owns it. It operates over fs.c and io.c. |
state.c | Named live state (gizlang:live): objects held by World and name, migrated by exact schema keys. Process and thread teardown release only the bindings of that World. |
process.c | Runs an entry. The process main, the test runners and the live host share the World, grants, memory limit and leak check. |
live.c | The giz live host. A builder builds generations when sources change. A forked host loads a complete immutable function table and publishes it through the execution owner. |
compiler_driver.c, compiler_transport.c | The file transport of the compiler executable, linked with memory.c and format.c (string conversion and search). |
wasm.c, wasm/, wasm.mjs | The Wasm platform layer (wasm64, no C library). wasm/ declares the C functions that it defines. format.c, refs.c, state.c, heap.c, resources.c and value_layout.c are built for it. Refer to Wasm. |
Base64 in C
Base64 stays in C. Three Gizlang versions were measured against the C version (the limit is 1.3 times):
| Date | Gizlang version | Result |
|---|---|---|
| 2026-10-10 | Scalar | 33 ms (encode) and 38 ms (decode) for 30 MB, against 9 ms and 7 ms for C with AVX2. |
| 2026-10-11 | SIMD types | 2.0 times (encode) and 1.8 times (decode) the time of C. string() validates its output, and a new buffer starts with zeros. |
| 2026-10-11 | The C algorithm with shuffle, mul_high and lookup16 | 1.15 times at 30 MB, but 2.2 times (encode) and 1.4 times (decode) at 64 KB. There, the copy and check of string() on output that is ASCII by construction took 13 of the 31 ms of the encoder. |
Wasm
Besides a process main, the Wasm layer runs host calls. Each entry-module function
export function NAME(input: bytes): bytes (the table that compiler/entry.giz emits) runs
in one World that is kept between calls. Thus gizlang:live state persists. wasm.mjs is the
host for Node and browsers. It grants only console output.
Contracts
One owner for each concern
There is one handle table, one heap for each World and one event loop. Language adapters never get their own file, socket or scheduler stack.
Handles, not pointers
Every resource is a handle that is checked by generation and bound to a World and an owner thread. A stale or foreign handle fails. Pointers never leave the runtime.
Owned values
- A string, bytes or Vec value is
{address, length}. A nonzero address points immediately after a 16-byte header{count, capacity}(Gizlang_BufferHeader). - Generated code owns the counting. It allocates, shares, appends in place when the count is 1, and frees.
- A negative count marks an immortal buffer. Literals are immortal, and so is any owned value that a host passes in. The compiler driver does this for its input.
- Every allocation is charged to
memory_remainingof the context. Every free returns it.
Managed memory
MEMORY owns the model: the owners, the budget, roots and collection.
- Layouts come from the compiler as LLVM constant expressions. Thus there is no second layout calculator in C.
- Collection validates every edge before it sweeps.
- A root slot of an expression temporary ends where the temporary ends (
dropTemporariesincompiler/emit.giz). An if, while or for condition drops its temporaries before the body runs. - A dead or foreign handle in a root, hold or traced edge fails collection with
-40207(GIZLANG_HEAP_ROOT). This is a defect of the compiler or runtime, not of the Ref that the caller gave. - Trace callbacks cannot allocate or run user code.
- A
Refload gives the caller one more owner of the value's buffers (copy on write). Later stores or collections cannot change it. A staleReffails with-40208. GIZLANG_GC_STRESS=1collects at everyRefallocation and store while 64 objects or fewer live, and at one in live/64 past that. ARefthat generated code holds without a root then fails at once. It is the default ofgiz testfor programs and suites (GIZLANG_GC_STRESS=0turns it off). The compiler itself always runs without it. A test pass under stress is stored apart from a normal pass.
Tasks
- The scheduler is in context word 3. It runs on the owner thread.
- Each task keeps its own root chain. While the task is suspended, the chain is parked in the heap, so collection traces every task.
- Each task keeps its own stack floor: the end of its stack plus the reserve
(
gizlang_stack_floorfor the own stack of a thread). - The box of a task holds its closure, then its result. The layout key of the result is checked when the result is taken.
- When a task ends, it cancels and waits for its unfinished children. The process root does this before the leak check.
Pure parallel frames
A pool worker owns a shared computation budget and no World.
- Before dispatch, the initialized capture is checked one time through the same typed transfer owner as threads and crossing channels. This includes erased callable environments.
- Nonzero Refs are refused. The check does not borrow the heap of the caller.
- Frame registration alone cannot certify slots that are filled later.
- Vectors without references, including recursive types, are skipped by their immutable element layout. The cost of tracing does not increase with their population.
- Retirement requires the original frame and context. Ordinary unbound calls still refuse registration.
- The worker never borrows a World.
Verification:
- Twelve callable-transfer fixtures pass without cached verdicts, with one core and with two cores, in an isolated compiler build at a fixed point. They include owned BLOB rows built in pure parallel bodies, and owned BLOB temporaries in scalar fill bodies, also with a short final chunk.
- Three repeated parallel and fill failures keep fault 4, and a later dispatch in the same process succeeds.
- Ordinary recursive copy-on-write page directories keep captured BLOB versions across a parallel transfer. A callable that hides a Ref-bearing page payload still refuses with fault 14.
- The checks include rejection of an erased Ref. Compiler SHA-256:
3dee5edae234d94386c2ea5019e214c11bb72ff9a49e37e5500e74bdff5daf73. Runtime archive:711ddf6d94701e2235e864f7276f1801861bb1aef1d6be54e333317d008559a8. tests/runtime/tasks/owned-blob-kernelsuses the same fixtures as a scenario that always runs, at-O2. It passed with two-core admission, independently of the-O0checks of the normal test-file runner. The two-core admission of the runtime was checked independently.- These fixtures do not prove multicore SQL throughput.
I/O lifetime
- A buffer lives longer than its operation.
- Cancellation is a request. Only the completion callback releases the storage of the operation.
- Cancellation from a different thread goes through retained tokens and one wakeup queue on the owner loop.
Live code swap
- One publication shows the complete function table.
- Named dispatch and indirect callable invocation pin one table for their execution boundary.
- The scheduler saves the table across suspension, and gives it to spawned tasks and threads.
- Pure parallel workers inherit it without a World and without a change to their allocation budget.
- Dispatch restores the previous token before a fault propagates.
- This contract does not include transactional migration of state or schema, or safe early reclamation. Libraries that loaded successfully stay loaded, for their data layouts, callbacks and literals.
Explicit durability
- A write is durable only after
Stream.syncreturns.Stream.syncruns fdatasync on the thread pool of the loop. - The file calls of a process (read, write, sync) share one pool of 64 threads (libuv's
default is 4: four fsyncs that wait stopped every other file call).
UV_THREADPOOL_SIZEsets another size. A process whose cgroup allows fewer than 1024 tasks keeps 4 (runtime/io.c). - A created, renamed or removed entry is durable only after
Dir.syncof its directory. Dir.syncFilesystemmakes all data written to the file system of a directory durable, with the entries of its directories, in one syncfs on the thread pool (sync where there is no syncfs). It needs write authority. Use it for many files: 950 kept blobs took 3 s of fsyncs 4 at a time on backup.readAt,writeAtandtruncateaddress a file by offset.readManyreads many offsets in one io_uring batch (runtime/uring.c).
Teardown never guesses
- A World with active calls, pending operations, roots or borrows refuses release, and reports the reason.
- It never drains, cancels or rolls back implicitly.
- Nothing undoes completed external effects (files written, bytes sent).
Limits
| Limit | Value | Fault |
|---|---|---|
| Owned values and heap objects of a World | GIZLANG_MEMORY_LIMIT bytes. The default is 80% of the machine's memory, or of its cgroup's memory.max when that is lower (4 GiB where neither is known). Tests run with 256 MiB. | R011 |
| Call depth | 10,000 | R009 |
| Wall-clock time | No limit. |
When main returns, every buffer must be free. If bytes are still taken, the process entry
reports them on stderr and exits with status 70. Thus each test run is also a leak check.
Warning: Isolation is per process (
SpawnOptions.isolated, SYSTEM), not in the process. Do not run code that you do not trust in the process and call it isolated. The compiler, LLVM and the C runtime are trusted. None of them has had an independent security review.
Windows
x86_64-pc-windows-gnu (Windows 10 1709+ x64, UCRT) builds with llvm-mingw:
bin/giz tools/windows/runtime.giz build ARCHIVES WORK OUT writes libgizlang_runtime.a,
libgizlang_runtime.dll with its import library libgizlang_runtime.dll.a, and target.o from pinned archives.
The DLL holds the runtime, libuv, OpenSSL and winpthreads, and imports only Windows' own DLLs (the tool refuses
anything else). The compiler's IR has no target triple and passes only scalars to the runtime, so the same IR is
compiled for Windows. A program links target.o and the import library, and finds the DLL beside it or on PATH.
There is no native construction yet (tools/distribution/builder.giz), so no Windows toolchain release links it.
- One code path:
win32.hgives the other files the POSIX names with their POSIX meaning (openatunder a directory handle,unlinkatandrenameatwith POSIX semantics,flock,posix_spawnphanding the child only its three standard handles);win32.cimplements them. Paths the runtime returns separate with/, and a capability path with\or:is refused (fs.c valid_path). TLS trusts the system ROOT store (org.openssl.winstore://). - The task switch also saves
rdi,rsi,xmm6–xmm15and the thread block's stack bounds (task.c). - Not on Windows (
UV_ENOSYS, or exit 2 forGIZLANG_WORKERS):exec, stream pairs, descriptor handoff over Unix sockets, the parent lifeline and worker processes. Tests run one after another in the process. Signals areINTandTERMonly. - Error codes: the SDK names Linux numbers (
sdk/core/system.giz). An error from the runtime goes throughosError(runtime/system.c gizlang_error_portable), which maps libuv's codes (UV_E*, such as -4058) and the C runtime's-errno(ENOTEMPTYis 41, not 39) to those numbers, soNOT_FOUNDand the other names match on every OS. - Host and process facts (memory, CPUs, caches, NUMA nodes, environment, usage), terminals, users, priorities and network
interfaces come from libuv or the OS API in the runtime (
memory.c,machine.c,system.c), not from/procor Linux record layouts. User and group ids are -1, and setting them isUV_ENOSYS, on Windows. - Open: before a Windows release ships the DLL,
licenses/third-partyneeds its rows: libuv, OpenSSL 3.6.5 (MSYS2 clang64, not the Linux 3.0.13), winpthreads and the mingw-w64 runtime. - Checked on the
win11guest: hello, files, directories, rename, removal, refused escapes, temporary folders, sleep, tasks, child processes and TLS to a public host.
Status
- Source programs reach
heap.c,value_layout.candrefs.cthroughgizlang:memory(tests/language/programs/program-memory*.giz). - They reach
io.candfs.cthroughgizlang:system: files, directories, TCP, UDP,Ready, timers, standard streams and child processes (tests/runtime/system/).