fibers B1.0c: support params in abi(.pure) (read from registers)
Adversarial review of B1.0b found a param-bearing abi(.pure) function
emitted invalid LLVM ("cannot use argument of naked function" — loud
verifier error, not silent) because the param-alloca loop spilled the
args to stack slots, which a naked function cannot have.
Fixed forward — this ENABLES the B1.3 context-switch use case rather
than rejecting it: gate the param-alloca loop on fd.abi != .pure in
decl.zig (both body-lowering paths) and generic.zig. A naked function's
args stay in their ABI registers and are read directly by the asm body
(e.g. swap_context reads from/to from x0/x1); the LLVM args are
declared-but-unused, which the verifier allows.
examples/1803-concurrency-pure-asm-param.sx: naked add(a, b) reads x0/x1
(add x0, x0, x1; ret) -> 40 + 2 = 42. aarch64-pinned.
Pack abi(.pure) (variadic + naked — nonsensical, can't read a runtime
pack from registers) left unsupported: pack.zig's param loop is
intertwined with comptime-param/#insert handling, so that case still
hits the loud verifier error. Documented in the checkpoint.
Also updates PLAN-FIBERS / CHECKPOINT-FIBERS for B1.0 completion.
B1.0 complete. Suite green (725/0).
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# PLAN-FIBERS — Stream B1 (fibers + Io + M:1 scheduler)
|
||||
|
||||
> **STATUS: 🚧 in progress.** B1.0a (`abi(.pure)` lock commit) ✅ landed. Next step =
|
||||
> **B1.0b** (`abi(.pure)` real emission — LLVM `naked` attr). See the kickoff prompt at the
|
||||
> bottom.
|
||||
> **STATUS: 🚧 in progress.** B1.0 (`abi(.pure)` codegen) ✅ complete — emits a real LLVM
|
||||
> `naked` function end-to-end (decl / generic / pack paths; examples 1800/1801/1802 + unit
|
||||
> test). Next step = **B1.1** (per-fiber `context` root — probe-first, likely library-only).
|
||||
|
||||
Carved from [PLAN-POST-METATYPE.md](PLAN-POST-METATYPE.md) Stream B (§B1) + the
|
||||
design-of-record [../design/execution-evolution-roadmap.md](../design/execution-evolution-roadmap.md)
|
||||
@@ -152,23 +152,25 @@ B1.0 (`.pure`) forces these plumbing sites:
|
||||
|
||||
## Phases (xfail→green steps)
|
||||
|
||||
### B1.0 — `abi(.pure)` codegen
|
||||
- **B1.0a (lock) — ✅ DONE** (commit pending). Carried `abi == .pure` into IR
|
||||
`Function.is_pure`; threaded through `decl.zig` (`funcWantsImplicitCtx` skips `.pure` like
|
||||
`.c`; both body-lowering paths bypass `lowerValueBody` for `.pure`, lowering the asm body +
|
||||
capping with `unreachable`); `emit_llvm` Pass 2 **bails loudly** on `func.is_pure`
|
||||
("`abi(.pure)` function '…' LLVM emission not yet implemented", build-gating nonzero exit).
|
||||
`examples/1800-concurrency-pure-asm.sx` (one host example, no `.build` pin — the bail is
|
||||
host-independent) locked to the bail snapshot. Suite green (722/0).
|
||||
- **B1.0b (green) ← NEXT** — emit LLVM's `naked` attr
|
||||
(`LLVMGetEnumAttributeKindForName("naked", 5)` + `LLVMCreateEnumAttribute` +
|
||||
`LLVMAddAttributeAtIndex` at func index −1; shape per `nounwind` at emit_llvm.zig:1339);
|
||||
emit the `.pure` body as the asm block only (no prologue/epilogue/ctx). Pin `1800`
|
||||
aarch64 (`.build {"target":"aarch64-macos"}`) → runs end-to-end (exit 42) on this host,
|
||||
ir-only on a mismatch; capture its `.ir` (asserts `naked` + the asm). Add an x86_64 cross
|
||||
sibling `examples/1802-concurrency-pure-asm-x86.sx` (`.build {"target":"x86_64-linux"}`,
|
||||
ir-only here). Add a unit test in `emit_llvm.test.zig` asserting the `naked` attribute is
|
||||
present on a `.pure` function. Review the diff (no stray error text). Commit.
|
||||
### B1.0 — `abi(.pure)` codegen — ✅ COMPLETE
|
||||
- **B1.0a (lock) — ✅ DONE.** Carried `abi == .pure` into IR `Function.is_pure`; threaded
|
||||
through `decl.zig` (`funcWantsImplicitCtx` skips `.pure` like `.c`; all body-lowering paths
|
||||
bypass `lowerValueBody` for `.pure`, lowering the asm body + capping with `unreachable`) +
|
||||
generic.zig + pack.zig; `emit_llvm` Pass 2 bailed loudly on `func.is_pure`. Locked by
|
||||
`examples/1800-concurrency-pure-asm.sx` + the generic regression (review-found gap).
|
||||
- **B1.0b (green) — ✅ DONE.** `emit_llvm` declaration pass adds LLVM `naked` + `noinline` +
|
||||
`nounwind` for `func.is_pure` and skips `frame-pointer=all` (incompatible with a frameless
|
||||
function); Pass 2 emits the body normally (`naked` ⇒ verbatim asm + own `ret`, no
|
||||
prologue). `1800` pinned aarch64 → exit 42 + `.ir`; `1801-concurrency-pure-generic.sx`
|
||||
(renamed from `-bail`) proves the generic path emits a naked body (exit 42);
|
||||
`1802-concurrency-pure-asm-x86.sx` x86_64 cross sibling (ir-only here, `.ir` locks `naked`
|
||||
+ `movl $42, %eax`). Unit test `emit: abi(.pure) function gets the naked attribute` asserts
|
||||
`naked` present + `frame-pointer` absent. Suite green (724/0).
|
||||
- **B1.0c (review-hardening) — ✅ DONE.** A param-bearing `.pure` fn emitted invalid LLVM
|
||||
(loud verifier error). Gated the param-alloca loop on `fd.abi != .pure` (decl.zig both
|
||||
paths + generic.zig) so a naked fn's args stay in registers (read by the asm body) — this
|
||||
*enables* B1.3's `swap_context(from, to)`. Locked by `1803-concurrency-pure-asm-param.sx`.
|
||||
Pack `.pure` (variadic + naked, nonsensical) left unsupported → loud verifier error.
|
||||
|
||||
### B1.1 — per-fiber `context` root (probe-first; likely zero compiler change)
|
||||
- **B1.1a (probe + lock)** — write a probe (`.sx-tmp/`) + an `18xx` example that snapshots a
|
||||
|
||||
Reference in New Issue
Block a user