docs(asm): symbol refs are portable — explain the auto-:c mechanism

Updates the symbol-operand guide: x86 now uses the same plain %[fn] as
aarch64, and a 'How the portability works' note explains the mechanism
(compiler auto-injects LLVM's :c modifier for "s" operands, equivalent
to GCC :P/%P0 for x86 calls, no-op on aarch64, overridable). Drops the
stale per-arch :P guidance; checkpoint updated.
This commit is contained in:
agra
2026-06-16 09:05:15 +03:00
parent 066ba54346
commit 0e0ee40528
2 changed files with 30 additions and 15 deletions

View File

@@ -372,9 +372,16 @@ Orthogonal: **issue 0137** (no-`main` segfault).
arches: 1657 read-write (`"incq ${0}","=r,0"`), 1658 indirect (`"movq $$42, arches: 1657 read-write (`"incq ${0}","=r,0"`), 1658 indirect (`"movq $$42,
${0}","=*m"`(ptr elementtype)), 1659 symbol (`"call ${2:P}"`, direct call). x86 ${0}","=*m"`(ptr elementtype)), 1659 symbol (`"call ${2:P}"`, direct call). x86
templates validated by cross-emitting an object (integrated assembler accepts; templates validated by cross-emitting an object (integrated assembler accepts;
objdump confirms 1659's direct `call` reloc). **Note:** x86 direct calls need objdump confirms 1659's direct `call` reloc). Pure additive locks. `zig build
the `P` operand modifier (`%[fn:P]`); aarch64 `bl %[fn]` needs none. Pure test` green (668 corpus, 446 unit).
additive locks. `zig build test` green (668 corpus, 446 unit). - (symbol portability) made `%[fn]` portable across arches — `renderAsmTemplate`
auto-injects LLVM's `:c` modifier (`${N}``${N:c}`) for symbol (`"s"`) operands
lacking an explicit modifier (`asmNamedIsSymbol` helper). Without it x86 renders
`$cb` (a bad `call` target needing a hand-written `:P`); aarch64 unaffected.
Verified `:c``:P` for x86-64 calls (both → `R_X86_64_PLT32`). Explicit
`%[fn:X]` still wins (escape hatch). 1659 dropped its `:P` → same plain `%[fn]`
as aarch64 1656; both IRs regen to `${N:c}`. `zig build test` green (668 corpus,
446 unit).
## Known issues ## Known issues
- **0138** — RESOLVED. `@const` (address-of a `::` comptime constant) yielded a - **0138** — RESOLVED. `@const` (address-of a `::` comptime constant) yielded a

View File

@@ -79,20 +79,11 @@ ASM,
} }
``` ```
Two reasons to prefer this over passing a function *pointer* in a plain The same `%[fn]` works on **x86_64** — just the branch mnemonic differs:
`"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.
On **x86_64**, a direct `call` to a symbol operand needs the `P`
("call-target") operand modifier — `%[name:P]` (the GCC `%P0` idiom):
```sx ```sx
return asm volatile { return asm volatile {
"call %[fn:P]", // x86_64 — note the :P modifier "call %[fn]", // x86_64 — same portable %[fn]
[ret] "={rax}" -> i64, [ret] "={rax}" -> i64,
"{rdi}" = n, "{rdi}" = n,
[fn] "s" = cb, [fn] "s" = cb,
@@ -100,7 +91,24 @@ return asm volatile {
}; };
``` ```
aarch64 `bl %[fn]` needs no modifier. 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** — `%[fn]` is the same on every target; the backend emits
the correctly-mangled name, so you never hardcode the macOS leading
underscore *or* a per-arch operand modifier.
**How the portability works.** A bare `%[fn]` would render differently
per target — on x86 the symbol prints as `$cb` (an immediate `$`-prefix
that `call` rejects), while aarch64 prints it bare. So for a symbol (`"s"`)
operand the compiler **auto-injects LLVM's `:c` operand modifier** (`%[fn]`
`${N:c}`, "print the constant with no punctuation"). `:c` prints the
plain symbol on every target — equivalent to the GCC `:P`/`%P0` call-target
idiom on x86 (both emit the same `R_X86_64_PLT32` relocation) and a no-op
on aarch64. You can still override it with an explicit `%[fn:X]` if you
ever need a different rendering, but for a call/branch you never should.
The callee needs a stable, externally-linked symbol — i.e. `export` 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 (which also gives it the C ABI). A plain or `callconv(.c)`-only function