docs(asm): checkpoint indirect-memory =*m — inline asm feature-complete
This commit is contained in:
@@ -6,8 +6,29 @@ commit, one step at a time per the cadence rule (no commit may both add a test
|
||||
and make it pass).
|
||||
|
||||
## Last completed step
|
||||
**G (read-write `+` place outputs)** — a `+r` / `+{reg}` `-> @place` output is now
|
||||
implemented (the last substantive feature). LLVM has no `+` constraint, so a
|
||||
**G (indirect-memory `=*m` place outputs)** — the LAST substantive asm feature.
|
||||
Unlike a write-through `=` output (which returns a value then stored), an
|
||||
indirect output passes the place ADDRESS to the asm and the asm writes through
|
||||
it — no return slot. `emitInlineAsm` (`src/backend/llvm/ops.zig`): indirect
|
||||
outputs are excluded from the LLVM return type; their pointer is an opaque `ptr`
|
||||
call arg placed **first** (arg-consuming constraint order = output-section
|
||||
indirect pointers → inputs → read-write tied seeds); each gets an
|
||||
`elementtype(T)` call-site attribute (required in the opaque-pointer era) via
|
||||
`LLVMCreateTypeAttribute`/`LLVMAddCallSiteAttribute`; the store-back loop skips
|
||||
them. New `asmIsIndirect(e, op)` helper. Lowering (`lowerAsmExpr`) stops
|
||||
rejecting `*` (constraint kept verbatim, `=*m` reaches the constraint string
|
||||
as-is). `asmOperandIndex` unchanged — indirect outputs still count as operands,
|
||||
so `%[name]`→`${N}` holds. Verified by **running** on aarch64: store-through-
|
||||
pointer (`str x9, %[out]` → 42, IR `"=*m,~{x9}"(ptr elementtype(i64) …)`) and a
|
||||
mixed case (indirect + value output + input → `"=*m,=r,r"`, indirect ptr arg
|
||||
first, `${0}/${1}/${2}` correct). Two commits per cadence: (1)
|
||||
`examples/1652-platform-asm-indirect-mem.sx` locked the rejection; (2) implemented
|
||||
+ flipped 1652 to a runnable aarch64-pinned example (`{ "target": "macos" }`,
|
||||
ir-only elsewhere). `zig build test` green (661 corpus, 446 unit). Files:
|
||||
`src/ir/lower/expr.zig`, `src/backend/llvm/ops.zig`, `examples/1652-*`.
|
||||
|
||||
Prior: **G (read-write `+` place outputs)** — a `+r` / `+{reg}` `-> @place` output is now
|
||||
implemented. LLVM has no `+` constraint, so a
|
||||
read-write place lowers to: an output **`=`** constraint (return slot, stored back
|
||||
through the place after the call; the leading `+` rewritten to `=` in
|
||||
`appendAsmConstraints`), **plus** a **tied input** (the decimal index of that
|
||||
@@ -194,15 +215,15 @@ pipeline: lex (A.0) → parse (A.1) → validate (B.0/B.1 + `%[name]` check) →
|
||||
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 AOT (call-into-asm
|
||||
via lib-less `extern`). `-> @place` **write-through** outputs work (Phase 2) and
|
||||
**read-write (`+`)** place outputs work (Phase G — tied-input lowering, runs on
|
||||
aarch64). Indirect-memory (`*`) place outputs are still rejected loudly as
|
||||
not-yet-implemented — the only remaining substantive feature. The x86_64
|
||||
syscall-write ir-only example is DONE (1651). Smaller follow-ups: the
|
||||
comptime-call guard for global asm (`#run` into a module-asm symbol should fail
|
||||
loud via dlsym-miss — pin a test) and a JIT-vs-global-asm note (`sx run` silently
|
||||
mishandles module-asm symbols; AOT is correct). `readme.md` now has an "Inline
|
||||
Assembly" section.
|
||||
via lib-less `extern`). All three `-> @place` output forms now work and execute
|
||||
on aarch64: **write-through** `=` (Phase 2), **read-write** `+` (tied input), and
|
||||
**indirect-memory** `=*m` (pointer arg + `elementtype`, asm writes through it).
|
||||
**Inline assembly is now feature-complete — no substantive features remain.** The
|
||||
x86_64 syscall-write ir-only example is DONE (1651). Only polish follow-ups
|
||||
remain: the comptime-call guard for global asm (`#run` into a module-asm symbol
|
||||
should fail loud via dlsym-miss — pin a test) and a JIT-vs-global-asm note
|
||||
(`sx run` silently mishandles module-asm symbols; AOT is correct). `readme.md`
|
||||
now has an "Inline Assembly" section.
|
||||
|
||||
Known orthogonal bug: **issue 0137** — `sx run` on a program with no `main`
|
||||
segfaults (`src/target.zig:256-273`, unguarded JIT entry lookup). Pre-existing,
|
||||
@@ -214,22 +235,21 @@ Phase E–F feasibility already confirmed against the live tree
|
||||
`extern`, 60 sites; `--target` a global CLI flag).
|
||||
|
||||
## Next step
|
||||
The output-to-`const` rejection item is **DONE** (via issue 0138, now RESOLVED).
|
||||
The general `@const` address-of bug — `@scalar_const` reinterpreting the folded
|
||||
value as a pointer (`inttoptr (i64 40 to ptr)`) → segfault on deref / invalid
|
||||
store for asm `-> @const` — was fixed in `src/ir/lower/expr.zig`'s `.address_of`
|
||||
path (scalar `::` consts now diagnose "no storage"; array/struct consts keep real
|
||||
storage). Because asm `-> @place` lowers `@place` through that same path, asm
|
||||
`-> @const` now reports the clean diagnostic for free — no asm-specific code
|
||||
needed. Regression: `examples/1177-diagnostics-addr-of-const-rejected.sx`.
|
||||
**Inline assembly is feature-complete.** All substantive features are done:
|
||||
0/1/N value outputs (tuples), register-class + pinned operands, inputs, clobbers,
|
||||
`#string` templates, `%[name]`/`%%`/`$`/`%=` rewriting, §II.5 auto-naming, global
|
||||
`asm { … }` (AOT), and all three `-> @place` output forms — write-through (`=`),
|
||||
read-write (`+`), and indirect-memory (`=*m`). The x86_64 syscall-write ir-only
|
||||
example (1651) and the output-to-`const` rejection (issue 0138) are also done.
|
||||
|
||||
Remaining work, all optional / additive:
|
||||
- **Indirect-memory (`"=*m"`) outputs**: pass the place address as an arg, asm
|
||||
writes through it (no return slot). Currently rejected. The last substantive
|
||||
feature; needs `elementtype` type-attribute plumbing on the indirect arg
|
||||
(precedent: `sret` in `emit_llvm.zig`/`ops.zig`).
|
||||
- **Polish**: comptime-call guard test for global asm; make `sx run` error (not
|
||||
silently mishandle) a module-asm symbol.
|
||||
Remaining work, all **polish** (optional):
|
||||
- **Global-asm comptime-call guard**: pin a test that `#run` into a module-asm
|
||||
symbol fails loud via the dlsym-miss path (module-asm symbols don't exist until
|
||||
the final link).
|
||||
- **`sx run` module-asm**: make it *error* (not silently mishandle) a module-asm
|
||||
symbol — the ORC JIT doesn't link `module asm`, so AOT is the only correct path.
|
||||
|
||||
Orthogonal: **issue 0137** (no-`main` JIT segfault).
|
||||
|
||||
Done since last: output-to-`const` rejection (issue 0138), x86_64 syscall-write
|
||||
ir-only example (1651).
|
||||
@@ -305,6 +325,14 @@ Orthogonal: **issue 0137** (no-`main` segfault).
|
||||
(`.ir` asserted), runs on x86_64-linux (hand-authored `"ok\n"` stdout).
|
||||
`examples/1651-platform-asm-x86-syscall-write.sx`. Pure additive lock, no
|
||||
compiler change. `zig build test` green (660 corpus, 446 unit).
|
||||
- (G indirect) indirect-memory `=*m` place outputs — the place address is passed
|
||||
as an opaque `ptr` arg (with an `elementtype(T)` call-site attr), placed before
|
||||
inputs; asm writes through it; no return slot; store-back skips it.
|
||||
`asmIsIndirect` helper; lowering stops rejecting `*`. Verified by running on
|
||||
aarch64 (store-through → 42; mixed indirect+value+input → `"=*m,=r,r"`). Two
|
||||
commits (cadence): 1652 locked the rejection, then flipped to a runnable aarch64
|
||||
example. **Inline asm now feature-complete.** `zig build test` green (661 corpus,
|
||||
446 unit).
|
||||
|
||||
## Known issues
|
||||
- **0138** — RESOLVED. `@const` (address-of a `::` comptime constant) yielded a
|
||||
|
||||
Reference in New Issue
Block a user