Carve the async-runtime fibers stream off PLAN-POST-METATYPE Stream B, mirroring the atomics carve. Grounds the B1 compiler floor against the tree: - abi(.pure) exists in the ABI enum but is inert (type_resolver maps it to .default CC, emit emits no naked attr) -> B1.0 makes it emit LLVM naked + skip prologue/ctx. Corrected the design's callconv(.naked) spelling to the real abi(.pure). - context is already an implicit *Context param (slot 0) + push Context is a stack alloca -> fiber-local for free; only shared root is the __sx_default_context global. B1.1 grounded as likely library-only (probe-first). - B1.0 snapshot story corrected: naked body is raw per-arch asm -> two arch-gated examples (aarch64 + x86_64), not one host .ir. Full xfail->green step detail + a B1.0a kickoff prompt. Baseline green (721/0). No code change; first implementation step is B1.0a.
5.4 KiB
CHECKPOINT-FIBERS — Stream B1 (fibers + Io + M:1 scheduler)
Companion to PLAN-FIBERS.md. Update after every step (one step at a time,
per the cadence rule). New corpus category: 18xx concurrency.
Last completed step
Carve — wrote PLAN-FIBERS.md + this checkpoint. Grounded the B1 compiler floor against
the tree (see Decisions). Baseline verified green: zig build && zig build test → 721
ran, 0 failed (one Android-SDK-gated example skipped; the trailing "failed command:" line
is the zig listen-protocol echo, not a failure). HEAD 3fad2d5, tree clean.
Current state
Stream A (atomics) is feature-complete (✅) and unblocks B2-channels. Stream B1 is carved, not started. No fibers/Io/scheduler code exists yet. The compiler floor for B1 is grounded:
abi(.pure)exists in theABIenum but is inert — maps to.defaultCC, emits no naked attribute. B1.0 makes it actually emit LLVMnaked.contextis already an implicit*Contextparam (slot 0) +push Contextis a stackalloca⇒ fiber-local for free. The only shared root is the__sx_default_contextglobal (entry-point bind). B1.1 is therefore expected to be a library convention (spawn trampoline snapshots the spawner's ctx into slot 0), likely zero compiler change — confirm by probe first.- Inline asm works end-to-end (lower→emit→JIT, aarch64 + x86_64) — the naked body reuses it.
Next step
B1.0a (naked-ABI lock commit) — per PLAN-FIBERS.md "Phases → B1.0 → B1.0a" and the
kickoff prompt at the bottom of that file. Add Function.is_naked, thread abi == .pure
through decl.zig (skip implicit-ctx like .c), make emit_llvm BAIL loudly on a naked
fn, add the two arch-gated examples (1800 aarch64 / 1801 x86_64), lock to the bail
diagnostic. STOP before B1.0b (real emission) — separate commit (cadence rule).
Known issues / capability gaps
- Orthogonal (not a B1 blocker): default VALUES for comptime params don't bind on generic-struct methods (free-fn defaults DO work) — inherited from Stream A. Only matters if a B2 lib type wants a defaulted comptime param; atomics/fibers require explicit, so unaffected.
- Issue 0144 (open, independent): calling an unrecognized bodiless
#builtinsilently returns 0 / exit 0 — a silent-fallback footgun in the generic builtin-call path. Filed; leave for its own fix session unless prioritized. Not a B1 blocker. - Deferred design gap (documented): the B1.4 event-loop
Iodoes not yet cooperate with a platform UI run loop (CFRunLoop/NSRunLoop/ALooper); pinning gives thread-affinity, not run-loop integration — a §6 app-target concern, out of B1 scope.
Decisions (Stream B1 specifics; surface locked in design §4 / §4.6)
- The async runtime is sx LIBRARY code. The compiler provides only: the general
primitives (inline asm ✅,
abi(.pure)naked [B1.0], atomics ✅) + fiber-safe codegen (contextalready fiber-local — B1.1). Schedulers, fibers, channels, futures,Iovtables,mmapstacks are all sx. abi(.pure)is the real spelling of the design'scallconv(.naked)— postfix slot,name :: (sig) -> Ret abi(.pure) { asm { … }; }. B1.0 = carry it into IR + emit LLVMnaked+ skip prologue/ctx (mirror the existing.cskip), NOT extend the enum (it's already there, just inert)..pure≠.c: a.cepilogue would restore SP from the wrong stack across a context switch (SP-in ≠ SP-out by design). naked = no prologue/epilogue/frame; the asm emits its ownret. This is why the switch must be naked.- B1.0 snapshot scope: the
nakedattr text is arch-invariant, but a naked body is raw per-arch asm — so B1.0 needs two arch-gated examples (aarch64 + x86_64,.buildtarget-gated, ir-only on mismatch), unlike atomics' single host.ir. The.irprovesnaked+ asm emitted, NOT register-save correctness (that's B1.3's stress harness). - B1.1 grounded as library-only (pending probe): push frames are stack-
alloca'd and the implicit ctx rides slot 0, so a spawn trampoline can pass a snapshotted ctx with no compiler change. The design doc's "never raw TLS" guards a non-problem (context is not TLS). Probe to confirm before sizing any compiler work. - Test keystones (design §10): the B1.3 switch-stress harness gates the
context-switch (the one piece the deterministic
Iocan't test — §8.1.1, §10.7); the B1.4 deterministic-simIo(calibrated against blockingIo— §8.1.3) gates all scheduling tests. Both must exist + be calibrated before the async tests they gate are trusted.18xxasserts program-emitted ordering contracts, not raw interleaving.
Log
- carve — wrote PLAN-FIBERS.md + CHECKPOINT-FIBERS.md. Grounded the B1 compiler floor:
ABI.pureinert (type_resolver.zig:237), IRFunctionhas no naked flag (inst.zig:605), attribute API pattern (emit_llvm.zig:1339 nounwind),.cctx-skip precedent (decl.zig:515),push Contextstack-alloca + slot-0 implicit ctx (stmt.zig:1263, lower.zig:259),__sx_default_contextroot (decl.zig:2667/2815), inline-asm corpus (1645/1651). Corrected the design'scallconv(.naked)→ realabi(.pure)spelling and the B1.0 snapshot story (two arch-gated examples, not one host.ir). B1.1 grounded as likely library-only. Baseline green (721/0). Stream ready; B1.0a is the first implementation step.