Files
sx/current/CHECKPOINT-FIBERS.md
agra 7044b8133b fibers: carve Stream B1 (PLAN-FIBERS + CHECKPOINT-FIBERS)
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.
2026-06-20 14:16:39 +03:00

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 test721 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 the ABI enum but is inert — maps to .default CC, emits no naked attribute. B1.0 makes it actually emit LLVM naked.
  • context is already an implicit *Context param (slot 0) + push Context is a stack allocafiber-local for free. The only shared root is the __sx_default_context global (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 #builtin silently 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 Io does 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 (context already fiber-local — B1.1). Schedulers, fibers, channels, futures, Io vtables, mmap stacks are all sx.
  • abi(.pure) is the real spelling of the design's callconv(.naked) — postfix slot, name :: (sig) -> Ret abi(.pure) { asm { … }; }. B1.0 = carry it into IR + emit LLVM naked + skip prologue/ctx (mirror the existing .c skip), NOT extend the enum (it's already there, just inert).
  • .pure.c: a .c epilogue 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 own ret. This is why the switch must be naked.
  • B1.0 snapshot scope: the naked attr text is arch-invariant, but a naked body is raw per-arch asm — so B1.0 needs two arch-gated examples (aarch64 + x86_64, .build target-gated, ir-only on mismatch), unlike atomics' single host .ir. The .ir proves naked + 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 Io can't test — §8.1.1, §10.7); the B1.4 deterministic-sim Io (calibrated against blocking Io — §8.1.3) gates all scheduling tests. Both must exist + be calibrated before the async tests they gate are trusted. 18xx asserts program-emitted ordering contracts, not raw interleaving.

Log

  • carve — wrote PLAN-FIBERS.md + CHECKPOINT-FIBERS.md. Grounded the B1 compiler floor: ABI.pure inert (type_resolver.zig:237), IR Function has no naked flag (inst.zig:605), attribute API pattern (emit_llvm.zig:1339 nounwind), .c ctx-skip precedent (decl.zig:515), push Context stack-alloca + slot-0 implicit ctx (stmt.zig:1263, lower.zig:259), __sx_default_context root (decl.zig:2667/2815), inline-asm corpus (1645/1651). Corrected the design's callconv(.naked) → real abi(.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.