docs(asm): document symbol operands ("s") + checkpoint
Adds a 'Symbol inputs — "s" = fn' section to docs/inline-assembly.md (direct bl/call, portability, the export-vs-callconv linkage point) and logs the symbol-operand + round-trip work in CHECKPOINT-ASM.
This commit is contained in:
@@ -216,9 +216,10 @@ guards fire: corrupting the `.ir` → IR mismatch; deleting it → the require-f
|
||||
**Inline assembly works end-to-end: 0, 1, and N value outputs (tuples).** Full
|
||||
pipeline: lex (A.0) → parse (A.1) → validate (B.0/B.1 + `%[name]` check) → IR op
|
||||
(C.0) → lower-builds-op + LLVM emit + JIT asm-parser init (C.1/D) → multi-output
|
||||
tuples (E). Register-class + register-pinned operands, inputs, clobbers, `#string`
|
||||
multi-instruction templates, `%[name]`/`%%` rewriting, and the §II.5 auto-naming
|
||||
rule all work and execute on the host JIT. Global `asm { … }` (Phase F) works via
|
||||
tuples (E). Register-class + register-pinned operands, inputs, **symbol operands
|
||||
(`"s"` → direct `bl`/`call` to a function/global by mangled name)**, clobbers,
|
||||
`#string` multi-instruction templates, `%[name]`/`%%` rewriting, and the §II.5
|
||||
auto-naming rule all work and execute on the host JIT. Global `asm { … }` (Phase F) works via
|
||||
lib-less `extern` under BOTH the JIT (`sx run` → 1653) and AOT (1648) — `sx run`
|
||||
compiles to an object, so the integrated assembler bakes the `module asm` symbol
|
||||
in and ORC resolves it. All three `-> @place` output forms now work and execute
|
||||
@@ -352,6 +353,21 @@ Orthogonal: **issue 0137** (no-`main` segfault).
|
||||
symbol fails loud (`comptime extern call: symbol not found via dlsym`) because
|
||||
the interpreter resolves externs via host dlsym before link. Arch-independent
|
||||
(no `.build`). `zig build test` green (663 corpus, 446 unit).
|
||||
- (round trip) `examples/1655-platform-asm-callback-into-sx.sx` — global-asm
|
||||
trampoline that `bl _cb` back into an `export`ed sx function (sx→asm→sx, → 42).
|
||||
Documented that `export` (external linkage + C symbol + C ABI) is what makes
|
||||
the callback resolvable; `callconv(.c)` alone leaves it `internal` (DCE'd).
|
||||
`zig build test` green (664 corpus, 446 unit).
|
||||
- (symbol ops) symbol operands (`"s"`) — feed a function/global symbol; the
|
||||
template emits its platform-mangled name so `bl %[fn]` is a DIRECT branch (one
|
||||
fewer indirection than register-indirect `blr`, portable — no hardcoded `_`).
|
||||
Emit passes the operand with its own llvm type (LLVMTypeOf), no coercion
|
||||
(`asmIsSymbol` helper); lowering lowers the function RHS to `ptr @fn`. Decided
|
||||
AGAINST mirroring Zig (which has no symbol operand — 483 std asm sites, none
|
||||
call a function) because the direct `bl` matters. Two commits (cadence): 1656
|
||||
locked the rejection (replacing an LLVM-verifier crash), then implemented +
|
||||
flipped to a runnable aarch64 example (objdump-confirmed direct `bl <_cb>`).
|
||||
`zig build test` green (665 corpus, 446 unit).
|
||||
|
||||
## Known issues
|
||||
- **0138** — RESOLVED. `@const` (address-of a `::` comptime constant) yielded a
|
||||
|
||||
@@ -55,6 +55,44 @@ does.
|
||||
"{rdi}" = fd // pinned to a specific register (x86_64 rdi)
|
||||
```
|
||||
|
||||
### Symbol inputs — `"s" = fn`
|
||||
|
||||
A `"s"` input feeds a **function or global symbol** (not a runtime value).
|
||||
In the template, `%[name]` expands to the symbol's **platform-mangled
|
||||
name**, so you can branch or call straight to it:
|
||||
|
||||
```sx
|
||||
cb :: (n: i64) -> i64 export "cb" { return n + 1; }
|
||||
|
||||
trampoline :: (n: i64) -> i64 {
|
||||
return asm volatile {
|
||||
#string ASM
|
||||
mov x0, %[arg]
|
||||
bl %[fn] // DIRECT call — `bl _cb` on macOS, `bl cb` on Linux
|
||||
mov %[res], x0
|
||||
ASM,
|
||||
[res] "=r" -> i64,
|
||||
[arg] "r" = n,
|
||||
[fn] "s" = cb, // symbol operand
|
||||
clobbers(.x0, .x30, .memory),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Two reasons to prefer this over passing a function *pointer* in a plain
|
||||
`"r"` register and using an indirect `blr`/`call *`:
|
||||
|
||||
- **One fewer indirection** — a direct PC-relative branch, no pointer
|
||||
load into a register, and a predictable (non-indirect) branch.
|
||||
- **Portable** — the backend emits the correctly-mangled name, so you
|
||||
don't hardcode the macOS leading underscore.
|
||||
|
||||
The callee needs a stable, externally-linked symbol — i.e. `export`
|
||||
(which also gives it the C ABI). A plain or `callconv(.c)`-only function
|
||||
is `internal` and gets dead-code-eliminated, so the symbol won't link.
|
||||
(A global-scope `asm { … }` routine has no operand list, so it can't use
|
||||
a symbol operand — it references the literal symbol in its text.)
|
||||
|
||||
### Value outputs — `-> Type`
|
||||
|
||||
`-> Type` produces a value that becomes (part of) the block's result:
|
||||
|
||||
Reference in New Issue
Block a user