A `"s"` input operand feeds a function/global symbol; the template's %[name] emits the platform-mangled name, so `bl %[fn]` / `call %[fn]` branches DIRECTLY to it (PC-relative, no register load — one fewer indirection than register-indirect `blr`). Lowering: an `"s"` input lowers its RHS normally (a function name → `ptr @fn`); the rejection added last commit is removed. Emit: a symbol operand is passed with its OWN llvm type (LLVMTypeOf) and no coercion — the function value is a `ptr`, and the old coerce-to-register-int path mistyped it and failed the verifier. New asmIsSymbol helper. Verified on aarch64: examples/1656 (sx → asm → bl _cb → sx → 42); the emitted asm is a direct `bl <_cb>` (objdump-confirmed), IR constraint `...,s,...`(ptr @cb). Flipped 1656 from the rejection lock to a runnable aarch64 example. zig build test green (665 corpus, 446 unit).
26 lines
1.0 KiB
Plaintext
26 lines
1.0 KiB
Plaintext
// ASM stream — symbol operand (`"s"`): feed a function/global SYMBOL into the
|
|
// template so a DIRECT `bl %[fn]` (PC-relative — one fewer indirection than a
|
|
// register-indirect `blr`: no pointer load, a relative reloc, a predictable
|
|
// branch) goes straight to it. The backend emits the platform-mangled name
|
|
// (`_cb` on macOS, `cb` on Linux), so the template stays portable — no hardcoded
|
|
// underscore. Round trip: sx → asm → `bl _cb` → sx → 42. aarch64-macos-pinned;
|
|
// runs under the JIT here, ir-only elsewhere (the `.ir` locks `"s"`/`ptr @cb`).
|
|
cb :: (n: i64) -> i64 export "cb" { return n + 1; }
|
|
|
|
tramp :: (n: i64) -> i64 {
|
|
return asm volatile {
|
|
#string ASM
|
|
stp x29, x30, [sp, #-16]!
|
|
mov x0, %[arg]
|
|
bl %[fn]
|
|
mov %[res], x0
|
|
ldp x29, x30, [sp], #16
|
|
ASM,
|
|
[res] "=r" -> i64,
|
|
[arg] "r" = n,
|
|
[fn] "s" = cb, // symbol operand → direct `bl _cb`
|
|
clobbers(.x0, .x30, .memory),
|
|
};
|
|
}
|
|
main :: () -> i64 { return tramp(41); }
|