Handoff — SYS.1 Complete (8/8 items landed)
Head: 0e5d7bde on trunk. Fixpoint stable at 2135 functions.
SYS.1 is substantively done. Every line-item from docs/KERNEL_EPIC.md’s
SYS.1 table (“Bare-metal completeness”) has a landed commit. The compiler
now exposes the full kernel-adjacent surface: atomic RMW completeness,
Quake linker-script + target-knob passthrough, @weak / @panic_handler
/ @align(N) attributes, x86 fs/gs and arm64 TPIDR TLS intrinsics, and
four custom calling conventions (C + x86-interrupt + preserve_all +
preserve_most). A kernel-capable freestanding Quartz binary is now a
matter of writing the kernel code, not adding language features — which
is the forcing-function the Kernel Epic was designed around.
SYS.1 scorecard — what shipped this session
| SHA | Item | Scope |
|---|---|---|
3f396d6e | — | Planning artifacts: ROADMAP Tier 6, KERNEL_EPIC.md, research/EFFECTS_IMPLEMENTATION_PLAN.md, research/NOTATION_PRIMER.md. |
98da3295 | 1 | atomic_and / or / xor / min / max with all 5 orderings. 19-test spec. Rebased a parallel-session worktree onto trunk. |
7c6a3a84 | — | Discovery: UFCS method-alias collision (.and / .or / .min / .max clash with lexer keywords / prior Array builtins). |
c1ad8f23 | 2 | link_baremetal(objs, out, script, flags) primitive in std/quake.qz. New baremetal:verify Quake task wired to hand-written aarch64 asm + aarch64-virt.ld. |
5f51a2a4 | 3 | assemble(src, out, target, flags) + compile_llc_to_obj(ll, out, flags) in std/quake.qz. baremetal:verify_x86_64_knobs task proves -mattr=-sse,-mmx,-avx -relocation-model=static -code-model=kernel flow through (negative check: 2 xmm instructions WITHOUT knobs, 0 WITH). |
a24b7d96 | 5 | @weak function attribute. Emits LLVM define weak. 6-test spec. |
790c2242 | — | Mid-session handoff (items 1, 2, 3, 5 state). |
145a308b | 6 | @panic_handler attribute. Codegen routes every panic site (user + prelude unwrap paths) to registered handler, bypassing libc write/longjmp/exit. The unblocker for freestanding .qz compilation. |
57386a19 | 8a | extern "x86-interrupt" cconv → LLVM x86_intrcc. Parser enforces Void return; rejects bodyless. |
e31aba4e | 7a | x86 TLS intrinsics: tls_read_gs / fs, tls_write_gs / fs. Codegen uses LLVM addrspace(256) = gs, addrspace(257) = fs. |
5fb3f7e3 | 7b | arm64 TPIDR intrinsics: tls_read_tpidr_el1 / el0, tls_write_tpidr_el1 / el0. Codegen uses single-instruction mrs / msr inline asm marked sideeffect. |
73781f47 | 8b | extern "preserve_all" + extern "preserve_most" cconvs → LLVM preserve_allcc / preserve_mostcc. |
0e5d7bde | 4 | Struct-level @align(N) attribute. Power-of-2 validation at parse. Plumbed parser → AST → MIR → codegen. Codegen emits align N on hoisted @value alloca. |
Recovery event mid-session. Between a24b7d96 and 790c2242 the
user’s machine hard-reset — almost certainly from sustained CPU load
plus subprocess leaks from an exploratory compiler probe that hit the
pre-existing ps_error-non-advance infinite loop. Uncommitted
@panic_handler WIP was stashed, known-good binary was restored from
backup, and the slice was replayed cleanly with adjusted discipline
(see “Workflow rules that worked” below). No data loss.
What’s actually in the compiler now
@weak # LLVM weak linkage on a function
@panic_handler # replace prelude panic path with user handler
@align(N) # struct-level alignment (power of 2)
@naked # no prologue/epilogue (pre-existing)
@packed / @repr(C) # struct layout (pre-existing)
extern "C" # default
extern "x86-interrupt" # → x86_intrcc; void return required; needs body
extern "preserve_all" # → preserve_allcc
extern "preserve_most" # → preserve_mostcc
# New intrinsics (system category)
tls_read_gs(offset) / tls_write_gs(offset, val)
tls_read_fs(offset) / tls_write_fs(offset, val)
tls_read_tpidr_el1() / tls_write_tpidr_el1(val)
tls_read_tpidr_el0() / tls_write_tpidr_el0(val)
# Quake (std/quake.qz) primitives
assemble(src, output, target, flags) # clang-as-assembler
compile_llc_to_obj(ll, output, flags) # llc with arbitrary knobs
link_baremetal(objs, output, linker_script, flags) # ld.lld -T
Verification targets added this session:
quake baremetal:verify— aarch64 ELF, entry 0x40000000.quake baremetal:verify_x86_64_knobs— x86_64 kernel-knob IR passes with zero SSE instructions.
Discoveries filed (not yet acted on)
All live in docs/KERNEL_EPIC.md under “Discoveries During Implementation”
plus commit bodies. Ranked rough impact first:
-
@value struct escape-analysis promotion defeats
@align(N)in practice. Evenspec/qspec/stack_alloc_return_spec.qz’s @value-struct-returning functions emitcall @malloc(i64 16)at the MIR_ALLOC_STACK site instead of a realalloca. Pre-existing — verified against the pre-align compiler backup. The@align(N)plumbing is ready; its observable effect widens the moment the escape analyzer leaves more @value structs on the stack. Recommended next session: investigatemir.qz’s escape-analysis pass (Compute which MIR_ALLOC_STACK registers escape their defining scope) for why simple @value struct-return patterns get flagged as escaped. -
cg_emit_int+cg_emit_line("")emission pattern miscompiles after self-compile. My first-draft @align codegen usedcg_emit_int(out, _h_align)+cg_emit_line(out, "")to write the digit and newline — mirroring idiomatic emission elsewhere. The rebuilt compiler then emitted malformed IR with a dangling%pushreference insocket$pipe_create. Reverted toN.to_s()+cg_emit_line(str)which produces identical output but went through a different SB path. Root cause unresolved — could be a subtle interaction betweensb_append_intand the surroundingsb_appendcalls, or a self-compile quirk. File as a low-priority compiler-internals bug for a debugging session.Closed Apr 19, 2026. Not an
sb_append_intinteraction. The%pushregression was the incremental-cache Tier 2 skip bug: the warm-cache path was skipping TC+MIR for modules with no cached fragment, so UFCS rewrite (vec.push(x)→vec_pushintrinsic) never fired. Different emission patterns happened to shiftchanged_modulesenough to shuffle what got skipped, which is why theto_s()workaround made it “go away” — it never actually was the sb_append_int pattern. Three-part fix inself-hosted/quartz.qz+dep_graph.qz: per-module fragment check before invalidation, short-name normalization ofrecompile_set, and TC-skip gate resolving owning module viaast_storerather than function-name prefix. Full diagnosis indocs/handoff/pipe-create-push-cache-bug.md; ROADMAP entry flipped to fixed. -
Parser hang on multi-line
extern "X"<NL>def ...header. Thehas_bodyindentation heuristic confuses itself whenextern "..."is on its own line above thedef. Hangs the compiler with a malformed program. Confirmed present in both current and pre-x86intr compiler backups — pre-existing, not introduced by this slice. Workaround in all new specs: single- line headers. -
Parser
ps_errornon-advance causes infinite loop on unknown attributes (e.g.@bogus). Same pre-existing pattern already noted in session memory — hangs on malformed input because error recovery doesn’t consume the offending token. Contributed to the Mac hard-reset earlier this session via sustained CPU + subprocess accumulation. -
UFCS method-alias collision on atomic RMW (filed in
7c6a3a84)..and/.orcollide withTOK_AND/TOK_OR;.min/.maxshadow Array builtins. Free-function form works for all five; the.xoralias shipped. Cosmetic, non-blocking.
Workflow rules that worked (after the crash)
These changed mid-session and every commit after has been clean:
- Write the QSpec test before rebuilding the compiler. One build invocation gets to verify both the feature AND the spec. Avoids build / probe / build / probe churn.
- Never run
timeout N ./self-hosted/bin/quartz <file>on a file that might hang the parser. Use fixtures that either compile cleanly or fail fast (known error messages). - One
quake buildand onequake guardper slice. Do not chain. Each is ~90 seconds at peak CPU; chaining five of them was how the session got in trouble earlier. pgrep -af "quartz|llc|clang"before every heavy step. Confirm no leaked subprocesses from prior timeouts. Kill stragglers withpkill -9before proceeding.- Fix-specific backups before compiler changes.
cp self-hosted/bin/quartz self-hosted/bin/backups/quartz-pre-<slice>-golden. Used twice this session (quartz-pre-panic-handler-golden,quartz-pre-align-golden) — both saved debugging cycles. - Smoke tests after every guard.
brainfuck.qz+style_demo.qz. Fixpoint alone is insufficient (CLAUDE.md Rule 2) — I learned firsthand that a self-consistent broken compiler can miscompile end-users of the change (the%push/ socket bug) while still passing fixpoint.
Where to point next session
Two productive threads, independent:
Thread A — tighten the escape analyzer (unblocks @align + other @value benefits)
See discovery #1. The escape analyzer is in self-hosted/backend/mir.qz
around the line noted in earlier exploration (“Compute which
MIR_ALLOC_STACK registers escape their defining scope”). Even
trivially-non-escaping @value struct returns like make_point(x, y, z)
are getting promoted. Investigating this would unlock:
- Real stack allocation for the alignment work just shipped.
- Better runtime performance on @value struct-heavy kernel code.
- Accurate MIR escape metadata for other optimizations.
Start by compiling spec/qspec/stack_alloc_return_spec.qz with
--dump-mir and inspecting which MIR_ALLOC_STACK registers get
flagged as escaped. The inputs are hand-crafted to be non-escaping;
if they’re getting flagged anyway, the analyzer is over-conservative.
Thread B — start KERN.1 (unikernel synchronous parts)
Per docs/KERNEL_EPIC.md’s KERN table, KERN.1 is blocked on SYS.1
and SYS.5 (x86_64-unknown-none parity with aarch64-virt). SYS.1 is
now unblocked. SYS.5 is a 2-3 day dependency: write
tools/baremetal/x86_64-multiboot.ld + tools/baremetal/hello_x86.qz,
verify boot on qemu-system-x86_64. Essentially the x86_64 twin of
the aarch64-virt infrastructure already in the tree.
After SYS.5, KERN.1 proper: GDT / IDT setup, exception handlers, timer driver, serial console, physical memory manager, paging setup. Big epic, ~1.5-2 quartz-weeks per the roadmap estimate. The language features needed are all now in place. Rust / Zig / C kernel implementations make good prior-art reference.
Smaller one-session follow-ups (if the big threads aren’t appealing)
- Fix the
ps_errorinfinite-loop on unknown attributes (discovery #4). Root cause known —ps_errordoesn’t advance. Recovery loop should explicit-break. Small parser patch, prevents future hangs / crashes. - Fix the multi-line
externheader hang (discovery #3).has_bodyindentation heuristic needs a smarter check. - Field-level
@[align(N)]. The deferred half of SYS.1 item 4. Requires changing the flat i64-slot field-layout model in MIR to insert padding between fields. Medium-large scope — not this-session-sized. - Investigate the
cg_emit_intself-compile quirk (discovery #2). Low-priority but satisfies curiosity.
State of the tree (for quick re-orientation)
- Branch:
trunk - HEAD:
0e5d7bde— SYS.1 item 4 (partial): struct-level @align(N) - Fixpoint: 2135 functions, gen1 == gen2 byte-identical, verified
- Smokes: brainfuck + style_demo both PASS
- Backup binaries saved this session:
self-hosted/bin/backups/quartz-pre-panic-handler-goldenself-hosted/bin/backups/quartz-pre-x86intr-goldenself-hosted/bin/backups/quartz-pre-tls-intrinsics-goldenself-hosted/bin/backups/quartz-pre-arm64-tls-goldenself-hosted/bin/backups/quartz-pre-preserve-cconv-goldenself-hosted/bin/backups/quartz-pre-align-golden
All SYS.1 spec files green:
spec/qspec/atomic_rmw_spec.qz— 19/19spec/qspec/memory_ordering_spec.qz— 25/25 (regression check)spec/qspec/weak_attribute_spec.qz— 6/6spec/qspec/panic_handler_spec.qz— 6/6spec/qspec/x86_intr_cconv_spec.qz— 5/5spec/qspec/tls_intrinsics_spec.qz— 6/6spec/qspec/tls_arm64_spec.qz— 6/6spec/qspec/preserve_cconv_spec.qz— 5/5spec/qspec/align_attr_spec.qz— 6/6
Baremetal Quake tasks both green:
quake baremetal:verify— aarch64 ELF at entry 0x40000000quake baremetal:verify_x86_64_knobs— x86-64, no SSE, kernel knobs applied
Safety rails (unchanged from mid-session handoff)
quake guardmandatory before any commit that touchesself-hosted/*.qz— pre-commit hook enforces. Don’t--no-verify.- Full QSpec suite NOT safe in Claude Code PTY — use
quake qspec_file FILE=...for targeted runs. - Smokes (brainfuck + style_demo) after every guard. Fixpoint alone is insufficient.
- Never ignore a subprocess leak from a timed-out
quartzrun — the May 18 hard-reset was caused by sustained CPU + leaked quartz processes fromps_errorinfinite loops. Kill stragglers before escalating to the next heavy op.