Y Yggdrasil field notes
Field note 01016 Aug 2026M11 plan

The raw-device unlock.

Garbage 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.

Read the plan View verbatim source
M11 / dependency chain03 linked gaps
M11 dependency chain Tail calls and garbage collection enable long-running Lux programs, ports unlock raw devices, and a GPU transport proves the design. TAIL + GCbounded life RAW PORTopaque bytes LUX GPUscreen pixels RUN FOREVER OWN THE QUEUE DRAW THE PROOF
kernel moves buffersLux implements protocols
00 / Context

Three gaps between a demo and a driver.

M0–M10 plus SMP are green. The TCP stack proved the thesis once; M11 makes it sustainable and general.

01

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 boundary
02

Lux cannot
touch ports.

The bytecode and assembler know PORT_OPEN and PORT_SUBMIT, but the language exposes no builtins. The TCP demo still needs a native NIC adapter.

Ownership boundary
03

No display.
No raw device.

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 boundary
Design principle
The kernel moves buffers. Lux implements protocols.

The command bytes stay opaque—from virtio-gpu 2D today to virgl command streams tomorrow.

A
Make programs immortal

Tail calls + segmented heaps + trampoline GC

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.

Opcode 47TAIL_CALL_EXT
run functionstash targetreturn sentinelresolve latest

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.

Heap growth

Segment first. Never invalidate a pointer.

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.

  • Default: 16 initial pages, 64 maximum
  • Owner core grows without cross-core heap writes
  • Existing quota-death semantics stay intact
Collection

Move only when the stack is empty.

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.

  • All boxed kinds, including maps and binaries
  • Forwarding kind 7 preserves shared diamonds
  • Mailbox fragments and port buffers stay separate
Acceptance marker [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.

B
Give Lux the queue

Port builtins + PORT_SUBMIT2

The runtime already delivers completions as ordinary messages. M11 fills the missing language surface and adds the all-register submission form that real devices need.

Opcode 48 PORT_SUBMIT2
(rport, rop, rarg0, rarg1, rtag)

Dynamic operation, two buffer arguments, and a caller-chosen completion tag.

port_open(kind)→ Port

Acquire a process-owned device queue.

port_submit(port, op, arg0, arg1, tag)→ Int

Submit opaque work and name its completion.

buf_to_bin(id)→ String

Bring a kernel buffer into the term world.

bin_to_buf(bin)→ Int

Move a binary out to a device-ready buffer.

Completion {port_reply, Port, Tag, Result} Lux receive already handles this shape today.
Acceptance marker 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.

C
Draw the proof

A virtio-gpu transport, not a 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.

OP 01

CTRL

command buffer → response buffer

Submit any control-queue command and return its response as a new buffer ID.

OP 02

CTRL_ATTACH

prefix + backing buffer

The kernel appends the one physical-address entry bytecode must never see, then pins the backing until close.

Lux owns the protocol
GET_DISPLAY_INFO
RESOURCE_CREATE_2D
ATTACH_BACKING
SET_SCANOUT
TRANSFER_TO_HOST_2D
RESOURCE_FLUSH
Kernel owns transport
VirtQueue<KernelHal, 64>
physical address append
buffer pinning

The renderer is just another Lux process.

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.

Headless proof

QEMU screendump writes a P6 PPM; the harness probes known coordinates for the expected colors.

Acceptance marker [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.

What this unlocks next

Virgl without another kernel interface.

CTX_CREATE, SUBMIT_3D, and capset queries are simply more OP_CTRL buffers. M11 creates the surface an independent Lux 3D driver needs.

Known ceilings: QEMU needs a GL-capable display for real 3D, and completion pumping remains poll-based.
Execution order

Land one proof at a time.

Every step returns the full suite to green before the next boundary moves.

A1Heap foundations

Cheney evacuation, forwarding tests, segmented ProcHeap, grow-retry paths, spawn limits.

A2Tail trampoline

TAIL_CALL_EXT end to end, per-process stash, trampoline GC, Lux tail analysis, bounded loop.

B1Language ports

PORT_SUBMIT2, four Lux builtins, serial marker through the existing completion path.

C1Harness spike

Add virtio-gpu and QEMU monitor; prove headless screendump before depending on it.

C2Lux renders

GPU transport, pinning, gpu_demo.lux, PPM assertion, marker, and virgl documentation.

Blast radius

Two repositories. One vertical slice.

Yggdrasil

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}

Lux

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
Need the exact file list?Open the verbatim implementation plan
Risk register

Name the sharp edges first.

01

Cheney + sharing

Start in the pure crate with diamond sharing, deep lists, maps, and explicit forwarding tests.

Test first
02

Tail-position analysis

Begin conservatively. A missed tail call only keeps today’s stack growth; it cannot change a result.

Safe degrade
03

Sentinel discipline

Assert that tag-7 words are intercepted by the trampoline and never escape into the term world.

Hard assert
04

Headless screendump

Spike the QEMU path in C1, with a protocol-response fallback already defined.

Hedged
05

VirtQueue friction

If the public API fights transport ownership, replace it with a minimal split virtqueue we control.

Fallback ready
Verification

Keep the old world green.

The new demonstrations extend the regression net; they do not replace it.

01Kernel acceptancecargo xtask test

Two boots, four cores, TCP echo, pcap, persistence, bounded loop, serial port, and GPU render.

02Pure cratescargo test

Cheney, segmented heap, tail semantics, interpreter/JIT parity, and mutation coverage.

03Host enginesygg-run --interp / --jit

Run the loop and GPU luxpacks under both engines with host-side device stubs.

04One human look-display gtk

Boot once with a window and confirm that the deterministic Lux scene is actually on screen.