Quartz v5.25

Handoff — SYS.5 closed, KERN.1 unblocked

Head: 063c8961 on trunk. Fixpoint stable at 2138 functions.

Session outcome: first freestanding Quartz source → ELF pipeline for BOTH aarch64 and x86_64. SYS.5 epic closed. The whole language surface needed for unikernel / kernel work is now present and verified end-to-end by five Quake tasks.

Session commits

SHAScope
503813baParser defensive-advance — two pre-existing hangs (@bogus + multi-line extern) fixed with 5-test spec wrapped in timeout 5.
13f2d372SYS.5 infrastructure twin — x86_64-multiboot.ld + smoke_x86.s + baremetal:verify_x86_64.
33361b36MIR: fuel_check skip for freestanding targets. Module-level _mir_is_freestanding flag.
b86991a3Codegen: panic helper stubs (overflow/bounds/map_key/backtrace + @abort define) + link-time extern declarations (malloc/free/memcpy/memset/qsort). Rust no_std + alloc pattern.
afa1f790Mid-session handoff (now superseded by this one).
21bd3af7Freestanding end-to-end for aarch64 — prelude panic path skip, libc-free sort/reverse helpers hoisted, @realloc declared, baremetal:verify_hello_aarch64 Quake task + libc_stubs.c.
063c8961Freestanding end-to-end for x86_64 — hello_x86.qz inline-asm fix ($$ escape + drop stack-pointer ref), baremetal:verify_hello_x86_64 Quake task.

What the tree has now

Five baremetal Quake tasks (all green)

TaskWhat it proves
baremetal:verifyaarch64 asm → ELF via link_baremetal.
baremetal:verify_hello_aarch64Quartz source → aarch64 ELF end-to-end.
baremetal:verify_x86_64x86_64 asm + Multiboot2 header → ELF.
baremetal:verify_x86_64_knobsLLC kernel knobs flow through (-mattr=-sse etc.).
baremetal:verify_hello_x86_64Quartz source → x86_64 ELF end-to-end.

Freestanding codegen

Freestanding targets (aarch64-unknown-none, x86_64-unknown-none-elf) now emit:

  • Target triple + datalayout.
  • Link-time extern declarations for malloc/realloc/free/memcpy/memset/ qsort (user supplies at link time — Rust no_std + alloc pattern).
  • Stubbed panic helpers (qz_overflow_panic / qz_bounds_panic / qz_map_key_panic / qz_print_backtrace): unreachable / ret void bodies, no libc refs.
  • Defined @abort as noreturn nounwind { unreachable }.
  • Libc-free runtime helpers needed by prelude (@qz_sort_cmp_asc + @qz_vec_sort + @qz_vec_reverse).

Freestanding codegen skips:

  • fuel_check instrumentation (no userspace scheduler in a kernel).
  • Main wrapper (no libc @main(argc, argv); user defines _start).
  • panic() intrinsic libc path when no @panic_handler registered (plain unreachable).

Language surface for kernel work — complete

Every item from SYS.1’s original “Tier 1 ABSENT” list plus everything needed by SYS.5:

  • Atomics: all 5 orderings + and/or/xor/min/max RMW.
  • Calling conventions: C, x86-interrupt, preserve_all, preserve_most.
  • @weak, @panic_handler, @align(N), @naked, @packed, @repr(C).
  • TLS intrinsics: x86 fs/gs + arm64 TPIDR_EL0/EL1.
  • Quake primitives: link_baremetal, assemble, compile_llc_to_obj.
  • Freestanding compile chain: aarch64 + x86_64 both pipelined to ELF.

Next-session scope (KERN.1)

With SYS.5 closed, KERN.1 (Unikernel Synchronous Parts) is fully unblocked on language/infrastructure. What’s needed:

A. x86_64 port-I/O intrinsics (blocker for hello_x86.qz boot)

Small, foundational. Legacy COM1 UART sits at I/O port 0x3F8 and needs outb / inb (+ outw / inw / outl / inl) to read/write. Quartz has no port-I/O intrinsics today. Add:

  • port_out8(port: Int, val: Int)call void asm sideeffect "outb %al, %dx", ...
  • port_in8(port: Int): Intcall i8 asm sideeffect "inb %dx, %al", ...
  • 16-bit + 32-bit variants.

Location: self-hosted/backend/cg_intrinsic_system.qz (sibling to existing TLS intrinsics from this session). Signatures go in typecheck_builtins.qz. Pattern is well-established — mirror tls_read_gs / tls_write_gs.

Estimate: half-session. Unblocks hello_x86.qz writing to serial under QEMU -serial stdio.

B. Multiboot2 header injection into Quartz source

hello_x86.qz today compiles to ELF but has no Multiboot2 header. QEMU -kernel won’t load it. Two approaches:

  1. Compile-time global: extern const for the header, placed via @[section(".multiboot")]. Quartz already has @[section(...)] per the existing SYS.1 audit. Check if the section attribute is wired to LLVM IR emission.

  2. Link smoke_x86.o (provides the header) alongside hello_x86.o. Simpler but less flexible.

Estimate: half-session for either.

C. 32 → 64 long-mode trampoline

x86_64 kernels entered in 32-bit protected mode by Multiboot2 must build PML4 + set PAE + LME + PG + far-jump to 64-bit code. This is the first kernel code — GDT/IDT/etc. follow.

Estimate: 1-2 sessions following the Philipp Oppermann Blog OS pattern.

D. Real kernel (the rest of KERN.1)

GDT, IDT, exception handlers, APIC/Generic Timer driver, UART driver (prototype exists in hello.qz / hello_x86.qz), physical memory manager, paging setup. Per the epic estimate, ~1.5-2 quartz-weeks for the full set. Start with A + B + C above as the on-ramp.

What did NOT ship this session

  • Panic routing through @panic_handler for internal panic helpers (qz_bounds_panic / qz_overflow_panic / qz_map_key_panic) — they stub to unreachable instead. A real kernel registers a handler via the user-visible panic() path but internal bounds violations currently just trap. Follow-up: wire @panic_handler through the panic-helper emission so bounds violations in kernel code trigger the user’s kernel panic routine.

  • Prelude internal-linkage + globaldce pass. The prelude functions (unwrap, sorted, etc.) are emitted unconditionally, which means their bodies pull in runtime helpers that have to be emitted for freestanding. Works today but bigger binaries than needed. A DCE pass would shrink them. Low priority — functional correctness is here, binary-size optimization can wait.

  • Empirical QEMU boot. Verified ELF structural validity via llvm-readelf; actual qemu-system-aarch64 -kernel / -serial stdio boot not exercised because the stub libc doesn’t provide a working allocator (hello.qz calls vec_new via prelude in places, mallocs land in do-nothing stubs, would crash). First real boot requires KERN.1 work providing a bump allocator.

State of the tree

  • Branch: trunk
  • HEAD: 063c8961 — SYS.5 x86_64 twin shipped
  • Fixpoint: 2138 functions, gen1 == gen2 byte-identical
  • Smokes: brainfuck + style_demo both PASS
  • QSpec coverage: freestanding_spec.qz 17/17, parser_hang_recovery_spec.qz 5/5
  • Backup binaries this session:
    • backups/quartz-pre-parser-hang-fix-golden
    • backups/quartz-pre-fuel-freestanding-golden
    • backups/quartz-pre-freestanding-stubs-golden
    • backups/quartz-pre-freestanding-e2e-golden

Prime-directive scorecard

  • PD1 (impact): SYS.5 closure is THE unblocker for KERN.1 — highest-impact move available. Picked freestanding codegen work (medium-sized compiler changes) over the smaller escape-analyzer investigation, correctly.
  • PD2 (design): Rust no_std + alloc pattern adopted for link-time extern declarations; Zig callconv(.Naked) + Blog OS for x86 boot structure. Not reinvented.
  • PD3 (pragmatism vs shortcut): Stubbed panic helpers to unreachable is pragmatic (unblocks the link today); full @panic_handler routing is named as a follow-up. Honest about the gap.
  • PD4 (work spans sessions): Session started from a clean SYS.1-complete handoff; ended with SYS.5 closed; clean handoff forward to KERN.1. Three-session arc executing cleanly.
  • PD5 (report reality): “What did NOT ship” section names the exact remaining gaps. End-to-end QEMU boot NOT verified — said so.
  • PD6 (holes filled or filed): Every discovery this session is either fixed in-commit or tracked in KERNEL_EPIC Discoveries + this handoff.
  • PD7 (delete dead code): Superseded afa1f790 handoff stays in place as a historical record of the partial state between b86991a3 and 21bd3af7 — not a compat shim, just accurate archival. This handoff supersedes it in content.