Port library/modules/std/sched.sx to run on aarch64-linux alongside aarch64-macOS, validated byte-identical on both via Apple `container`. Per-OS bits are comptime-branched: - MAP_AP (mmap MAP_ANON flag): linux 0x22 / macOS 0x1002. - fd-readiness backend: epoll on linux, kqueue on darwin (epoll import scoped to the linux branch). block_on_fd, the run-loop Mode-2 drain, and cancel_io_waiter_for each branch; the epoll paths EPOLL_CTL_DEL on fire and on early-wake (EPOLLONESHOT only disables a registration; kqueue EV_ONESHOT auto-removes it). - first-entry trampoline: a per-OS hand-written global-asm symbol becomes a naked sx fn fib_tramp (mov x0,x19; br x20) + register-indirect dispatch (spawn presets regs[1] == x20 == &fib_dispatch), dropping the per-OS .global symbol entirely. Fixes issue 0193 Bug A: the trampoline redesign bus-errored on the go/wait/sleep capstone (1817) because fib_dispatch was not pinned to the C ABI. Without an explicit ABI, fib_dispatch uses sx's internal calling convention (x0 = implicit context, first arg self shifted to x1) while the trampoline hands self over in x0 (C-ABI); on first entry the body runs (x1 happens to alias self) but the closure then loads regs[1] == &fib_dispatch as its first capture and re-invokes fib_dispatch forever -> stack overflow -> bus error. Annotating fib_dispatch `abi(.c)` pins it to the C-ABI (self in x0), matching the trampoline. `abi(.c)` rather than `export` because the fn is reached only by address through the trampoline, never by an external name -- so it needs the convention, not a public symbol (it stays a local symbol). Root cause found via lldb on an AOT build; confirmed against the compiler source. Bug B (a top-level asm block wrapped in inline-if is dropped during the comptime-conditional flatten) is carved out to issue 0194 (OPEN) -- no live trigger remains, since the naked-fn trampoline sidesteps it. 1811/1814/1816/1817 run byte-identical on the aarch64-macOS host and in an aarch64-linux container; full suite green (817/0). Documents the fiber runtime in readme.md.
4.7 KiB
Issue 0194 — a top-level global asm block wrapped in inline if / case is DROPPED
Status: OPEN. Carved out of issue 0193 (the linux fiber-runtime port). The port itself is
RESOLVED — it sidesteps this bug entirely by using a naked-sx-fn trampoline (fib_tramp) plus a
register-indirect br x20 instead of a hand-written global-asm symbol, so there is no live trigger
for this bug in the tree today. It is filed standalone so the compiler defect is not lost.
Symptom
A top-level global asm { … } block that defines a symbol (e.g. .global _foo / _foo: …) is
not emitted when it is wrapped in a comptime inline if OS == { case … } (or
inline if OS == .linux { asm } else { asm }). nm main.o shows the symbol as U (undefined) and
the link fails on both platforms. A PLAIN, unwrapped top-level asm { … } emits fine.
- Observed: symbol undefined, link error.
- Expected: the
asmblock in the taken comptime arm emits its template into the module's global asm exactly as an unwrapped block would (the comptime-conditional pre-pass already surfaces the taken arm's other top-level decls — fns, consts, imports — correctly; only theasm_globalnode is lost).
Reproduction
Not yet reproducible in isolation. During the 0193 port, minimal/medium repros ALL emitted +
linked correctly: a top-level asm in a single case; two case blocks; a case asm in an
imported module; a naked fn + case asm with bl to an exported fn; a one-sided
inline if .linux { #import } before the asm. Only the full library/modules/std/sched.sx
dropped it — so the trigger is an interaction with something else in that module, not the wrapped
asm alone.
The exact form that triggered it (now replaced on the branch, recoverable from history): the original global trampoline
asm {
#string T
.global _fib_tramp
_fib_tramp:
mov x0, x19
bl _fib_dispatch
brk #0
T,
};
fib_tramp :: () extern;
wrapped as
inline if OS == {
case .linux: asm { #string T
fib_tramp:
mov x0, x19
bl fib_dispatch
br x30
T, };
case .macos: asm { #string T
.global _fib_tramp
_fib_tramp:
mov x0, x19
bl _fib_dispatch
brk #0
T, };
}
dropped the asm in BOTH arms (whichever was taken). See issues/0193-linux-fiber-port.patch for the
full module context that triggers it, and the 0193 writeup for the larger investigation history.
Investigation prompt (ready to paste)
A top-level global
asmblock defining a symbol is dropped when wrapped in a comptimeinline if OS == { case … }— but only inside the fulllibrary/modules/std/sched.sx; it can't be reproduced in isolation. Find where the surfacedasm_globalnode is lost between the comptime-conditional flatten and IR lowering.Key files:
src/imports.zig—flattenComptimeConditionals(line ~38) +appendBranchDecls(line ~72): the pre-pass that surfaces a taken comptime arm's top-level decls. It appears correct — it appends every node of the taken branch's block,asm_globalincluded — so confirm the flattened slice actually carries theasm_globalnode (dumpflat_declsatsrc/imports.zig:932).src/ir/lower/decl.zig—lowerMainAndComptime(line ~1494), whose.asm_globalarm (line ~1503) appends the verbatim template toself.module.global_asm. Prime suspect: does the lowering entry point feedlowerMainAndComptimethe flattened decl list, or a pre-flattenroot.declsthat never contains the surfaced (formerly-nested)asm_global? If the asm-emission pass walks a different decl list than the one flattening wrote to, a surfacedasm_globalis silently skipped.src/ir/emit_llvm.zig:384— wheremodule.global_asmis concatenated into the LLVM module. If the node never reachedglobal_asm, it never emits.Steps: (1) build sched.sx's wrapped-asm variant (recover from
issues/0193-linux-fiber-port.patchor git history of branchfix/0192-qualified-import-const-comptime), (2) instrumentflattenComptimeConditionalsto log whether theasm_globalnode survives intoflat_decls, (3) instrumentlowerMainAndComptimeto log whether it ever sees anasm_global, (4) bisect what else in sched.sx must be present for the drop to occur (the isolation repros lacked it). Verification:nmthe object shows the wrapped-asm symbol DEFINED (notU); the wrapped form links and runs identically to a plain unwrappedasm.Verify it isn't a syntax issue first: it reproduces with both the
caseandif/elseforms, and plain unwrapped asm emits fine — so the wrapping, not the asm itself, is the trigger. That points to the flatten/lowering interaction, not user error.