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:
25
examples/1803-concurrency-pure-asm-param.sx
Normal file
25
examples/1803-concurrency-pure-asm-param.sx
Normal file
@@ -0,0 +1,25 @@
|
||||
// Stream B1 (fibers) — an `abi(.pure)` function with PARAMETERS reads its args
|
||||
// from ABI registers (the shape the fiber context-switch needs: `swap_context`
|
||||
// reads `from`/`to` from x0/x1).
|
||||
//
|
||||
// A naked function has no frame, so params are NOT spilled to stack slots — they
|
||||
// stay in their ABI registers and the asm body reads them directly. Here `a` is
|
||||
// in x0, `b` in x1 (aarch64 AAPCS), and the result returns in x0: `add x0, x0,
|
||||
// x1`. The lowering skips the param-alloca loop for `.pure` (decl.zig /
|
||||
// generic.zig); the LLVM args are declared-but-unused, which the verifier allows
|
||||
// (spilling them would emit `store i64 %0, …` → "cannot use argument of naked
|
||||
// function"). aarch64-pinned; runs end-to-end (exit 42), ir-only on a mismatch.
|
||||
//
|
||||
// Regression for an adversarial-review finding: before the param-alloca guard, a
|
||||
// param-bearing `.pure` fn emitted invalid LLVM (loud verifier error) instead of
|
||||
// a working naked function.
|
||||
add :: (a: i64, b: i64) -> i64 abi(.pure) {
|
||||
asm volatile {
|
||||
#string A
|
||||
add x0, x0, x1
|
||||
ret
|
||||
A
|
||||
};
|
||||
}
|
||||
|
||||
main :: () -> i64 { return add(40, 2); }
|
||||
1
examples/expected/1803-concurrency-pure-asm-param.build
Normal file
1
examples/expected/1803-concurrency-pure-asm-param.build
Normal file
@@ -0,0 +1 @@
|
||||
{ "target": "macos" }
|
||||
1
examples/expected/1803-concurrency-pure-asm-param.exit
Normal file
1
examples/expected/1803-concurrency-pure-asm-param.exit
Normal file
@@ -0,0 +1 @@
|
||||
42
|
||||
15
examples/expected/1803-concurrency-pure-asm-param.ir
Normal file
15
examples/expected/1803-concurrency-pure-asm-param.ir
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
; Function Attrs: naked noinline nounwind
|
||||
define internal i64 @add(i64 %0, i64 %1) #0 {
|
||||
entry:
|
||||
call void asm sideeffect " add x0, x0, x1\0A ret\0A", ""()
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define i32 @main() #1 {
|
||||
entry:
|
||||
%call = call i64 @add(i64 40, i64 2)
|
||||
%ca.tr = trunc i64 %call to i32
|
||||
ret i32 %ca.tr
|
||||
}
|
||||
1
examples/expected/1803-concurrency-pure-asm-param.stderr
Normal file
1
examples/expected/1803-concurrency-pure-asm-param.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
examples/expected/1803-concurrency-pure-asm-param.stdout
Normal file
1
examples/expected/1803-concurrency-pure-asm-param.stdout
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user