No GC.
No tail calls.
Fixed bump heaps die at quota, while every cross-module call grows the native stack. A server loop survives neither its garbage nor its iterations.
Lifetime boundaryGarbage collection and proper tail calls make Lux programs long-lived. Raw ports let them own devices. A GPU transport proves the kernel never needed to know the protocol.
M0–M10 plus SMP are green. The TCP stack proved the thesis once; M11 makes it sustainable and general.
Fixed bump heaps die at quota, while every cross-module call grows the native stack. A server loop survives neither its garbage nor its iterations.
Lifetime boundaryThe bytecode and assembler know PORT_OPEN and PORT_SUBMIT, but the language exposes no builtins. The TCP demo still needs a native NIC adapter.
A Lux virgl-class driver needs direct control of a virtio queue. Today there is no GPU and no generic transport surface through which to reach one.
Protocol boundaryThe kernel moves buffers. Lux implements protocols.
The command bytes stay opaque—from virtio-gpu 2D today to virgl command streams tomorrow.
Lux compiles one function per content-addressed module, so every Lux-level call is
already cross-module. One tail variant of CALL_EXT gives the language full
tail-call coverage—and creates an exact, tiny root set for moving collection.
TAIL_CALL_EXT
The interpreter or JIT stashes (module_atom, fname_atom, args) per process,
then returns an otherwise-unused tag-7 sentinel. modload::invoke loops,
resolves the current module version, and runs again on a constant native stack. The
trampoline remains the hot-code migration point.
ProcHeap wraps the existing bump arena with multiple physical spans.
Allocation failure grows geometrically up to a per-process cap. Old spans remain
alive, so JIT-held terms never move during ordinary execution.
At a trampoline hop, the only live terms are the stashed arguments. Cheney evacuation compacts them into a fresh span, installs forwarding pointers, preserves sharing, and frees every old span—without JIT stack maps.
[ok] lux loop: 100k tail-recursive iterations in bounded memory
A Lux loop allocates a list and map on every turn with a deliberately small heap cap. It must outlive both the old heap and the old native stack by orders of magnitude.
PORT_SUBMIT2The runtime already delivers completions as ordinary messages. M11 fills the missing language surface and adds the all-register submission form that real devices need.
(rport, rop, rarg0, rarg1, rtag)
Dynamic operation, two buffer arguments, and a caller-chosen completion tag.
port_open(kind)→ PortAcquire a process-owned device queue.
port_submit(port, op, arg0, arg1, tag)→ IntSubmit opaque work and name its completion.
buf_to_bin(id)→ StringBring a kernel buffer into the term world.
bin_to_buf(bin)→ IntMove a binary out to a device-ready buffer.
LUX-PORT-OK
A Lux module opens the serial port and writes the marker byte by byte—proving the complete language-to-port path with no new kernel driver.
The kernel discovers the GPU and owns queue zero. It does not learn rectangles, pixel
formats, scanouts, or virgl. Lux builds those little-endian command structures itself
and sends them as opaque buffers through KIND_GPU = 3.
command buffer → response buffer
Submit any control-queue command and return its response as a new buffer ID.
prefix + backing buffer
The kernel appends the one physical-address entry bytecode must never see, then pins the backing until close.
gpu_demo.lux asks for display information, creates a modest 320×200 XRGB
resource, attaches memory, sets the scanout, and paints three color bands with a centered
rectangle. A tail call keeps the render loop bounded. The kernel sees only bytes.
QEMU screendump writes a P6 PPM; the harness probes known coordinates for the expected colors.
[ok] lux gpu: scene rendered via virtio-gpu
If headless screendump is unreliable, the fallback still proves display-info and resource-flush responses across the full queue path.
CTX_CREATE, SUBMIT_3D, and capset queries are simply more OP_CTRL buffers. M11 creates the surface an independent Lux 3D driver needs.
Every step returns the full suite to green before the next boundary moves.
Cheney evacuation, forwarding tests, segmented ProcHeap, grow-retry paths, spawn limits.
TAIL_CALL_EXT end to end, per-process stash, trampoline GC, Lux tail analysis, bounded loop.
PORT_SUBMIT2, four Lux builtins, serial marker through the existing completion path.
Add virtio-gpu and QEMU monitor; prove headless screendump before depending on it.
GPU transport, pinning, gpu_demo.lux, PPM assertion, marker, and virgl documentation.
Term evacuation, bytecode and verifier, interpreter, JIT, process heap, module trampoline, ports, virtio, self-tests, host runner, and QEMU harness.
crates/ygg-termcrates/ygg-bytecodecrates/ygg-interpcrates/ygg-jitkernel/{proc,modload,jit,ports,virtio,selftest}.rstools/{ygg-run,xtask}
Tail-position threading, port and buffer builtins, BEAM compatibility stubs, and three acceptance programs.
src/codegen/yggdrasil.rsexamples/lux_loop.luxexamples/port_hello.luxexamples/gpu_demo.luxtests / SystemApi harnesses
Start in the pure crate with diamond sharing, deep lists, maps, and explicit forwarding tests.
Begin conservatively. A missed tail call only keeps today’s stack growth; it cannot change a result.
Assert that tag-7 words are intercepted by the trampoline and never escape into the term world.
Spike the QEMU path in C1, with a protocol-response fallback already defined.
If the public API fights transport ownership, replace it with a minimal split virtqueue we control.
The new demonstrations extend the regression net; they do not replace it.
cargo xtask testTwo boots, four cores, TCP echo, pcap, persistence, bounded loop, serial port, and GPU render.
cargo testCheney, segmented heap, tail semantics, interpreter/JIT parity, and mutation coverage.
ygg-run --interp / --jitRun the loop and GPU luxpacks under both engines with host-side device stubs.
-display gtkBoot once with a window and confirm that the deterministic Lux scene is actually on screen.