feat(asm): Phase E — multi-output asm returns tuples
Replaces the N>1 "Phase E" bail with a shared asmResultType helper (lowering +
inferType) that derives the result type from the out_value operands: 0→void,
1→T, N→a named tuple (fields named via the §II.5 effective-name rule).
Key realization: toLLVMType(tuple) already produces a literal struct {T1,…,Tn} —
exactly what LLVM's multi-output inline asm returns — so emit needs NO change.
Building the op with a tuple result type makes the asm call return the struct,
which IS sx's tuple value (destructured by the normal tuple_get path).
inferType's .asm_expr arm now also delegates to asmResultType (single owner), so
`return asm`, `x := asm`, and `q, r := asm` all agree on the type.
Verified end-to-end on aarch64: split(0x1234)→(lo=52,hi=18), a udiv/msub
divmod→(3,2). IR: `call { i64, i64 } asm "divq ${4}",
"={rax},={rdx},{rax},{rdx},r,~{cc}"(…)` → extractvalue → tuple.
1640 → the x86_64 multi-output IR lock (ir-only); 1647 → a multi-output example
that runs on aarch64.
zig build test green (655 corpus, 446 unit).
This commit is contained in:
@@ -6,7 +6,25 @@ commit, one step at a time per the cadence rule (no commit may both add a test
|
||||
and make it pass).
|
||||
|
||||
## Last completed step
|
||||
**C.1 + D** — inline asm CODEGEN (lowering builds the op + LLVM emit). **Inline
|
||||
**E** — multi-output tuples. **Inline asm now returns tuples.** Replaced the
|
||||
N>1 bail with a shared `asmResultType` helper (`src/ir/lower/expr.zig`, mixed
|
||||
into `Lowering`) that derives the result type from the `out_value` operands
|
||||
(0→void, 1→T, N→named tuple, named via the §II.5 effective-name rule). The key
|
||||
realization: `toLLVMType(tuple)` already produces a literal struct `{T1,…,Tn}` —
|
||||
exactly LLVM's multi-output asm return — so **emit needed NO change**; building
|
||||
the op with a tuple result type makes the asm call return the struct, which IS
|
||||
sx's tuple value (destructured by the normal `tuple_get` path). `inferType`'s
|
||||
`.asm_expr` arm now also delegates to `asmResultType` (single owner), so
|
||||
`return asm`, `x := asm`, and `q, r := asm` all agree on the type. Verified
|
||||
end-to-end on aarch64: `split(0x1234)`→`(lo=52, hi=18)`, a udiv/msub divmod→
|
||||
`(3, 2)`. IR is textbook: `call { i64, i64 } asm "divq ${4}",
|
||||
"={rax},={rdx},{rax},{rdx},r,~{cc}"(…)` → extractvalue → tuple. Converted 1640 to
|
||||
the x86_64 multi-output IR lock (ir-only) + added `1647-platform-asm-aarch64-multi`
|
||||
(runs on aarch64). `zig build test` green (655 corpus, 446 unit). Files:
|
||||
`src/ir/lower/expr.zig`, `src/ir/lower.zig`, `src/ir/expr_typer.zig`,
|
||||
`examples/164{0,7}-*`.
|
||||
|
||||
Prior: **C.1 + D** — inline asm CODEGEN (lowering builds the op + LLVM emit). **Inline
|
||||
assembly now runs end-to-end.** `lowerAsmExpr` (`src/ir/lower/expr.zig`) stops
|
||||
bailing: it resolves each operand's effective name (§II.5 auto-naming), interns
|
||||
template/constraints/clobbers, lowers input `Ref`s, derives the result `TypeId`
|
||||
@@ -112,14 +130,15 @@ guards fire: corrupting the `.ir` → IR mismatch; deleting it → the require-f
|
||||
`src/corpus_run.test.zig`, `examples/1639-*`.
|
||||
|
||||
## Current state
|
||||
**Inline assembly works end-to-end for 0/1 value outputs.** Pipeline complete:
|
||||
lex (A.0) → parse (A.1) → validate (B.0/B.1 + the `%[name]` check) → IR op (C.0)
|
||||
→ lower-builds-op + LLVM emit + JIT asm-parser init (C.1/D). Single-value-output
|
||||
and no-output `volatile` asm assemble and execute on the host JIT; the auto-naming
|
||||
rule (§II.5) is live (effective name = explicit `[name]` else `{reg}`). **Phase E
|
||||
(multi-output tuples) is the remaining feature gap** — N>1 value outputs bail with
|
||||
a named "Phase E" diagnostic (1640). `-> @place` write-through outputs are still
|
||||
rejected at parse (Phase 2). Global asm (Phase F) not started.
|
||||
**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. **Remaining feature gaps:** `-> @place`
|
||||
write-through / read-write / indirect-memory outputs (rejected at parse — Phase 2)
|
||||
and global `asm { … }` + `extern` call-into-asm (Phase F). `readme.md` has no
|
||||
inline-asm section yet (docs-track-changes follow-up).
|
||||
|
||||
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,
|
||||
@@ -131,21 +150,21 @@ Phase E–F feasibility already confirmed against the live tree
|
||||
`extern`, 60 sites; `--target` a global CLI flag).
|
||||
|
||||
## Next step
|
||||
**Phase E** (multi-output tuples) — replace the N>1 "Phase E" bail in
|
||||
`lowerAsmExpr`: build a tuple `TypeId` from the `out_value` types (named via the
|
||||
effective-name rule), set it as the op result, and in `emitInlineAsm` make the
|
||||
LLVM return type an anonymous struct `{T1,…,Tn}`, then `extractvalue i` per
|
||||
`out_value` → assemble the sx tuple. Lock with `divmod`→`(quot,rem)` (reuse 1640's
|
||||
shape, now running) + `cpuid`→4-tuple, arch-pinned. See `PLAN-ASM.md` Phase E +
|
||||
design §II.6 (multi-return). Also worth adding: the x86_64-linux syscall-write
|
||||
example (ir-only on this host via `.build { "target": "x86_64-linux" }` + `.ir`)
|
||||
to lock the cross-target lowering, per the plan's D verification.
|
||||
Two independent directions (pick either):
|
||||
- **Phase F — global asm** (smaller; the plan calls it "Small"): top-level
|
||||
`asm { … }` decl (template only — reject operands/`volatile`) → lower to
|
||||
`c.LLVMAppendModuleInlineAsm`; the call-INTO-asm direction reuses the existing
|
||||
lib-less `extern` (no new surface). Parser: recognize `asm {` at decl scope →
|
||||
an `asm_global` decl. Plus the comptime-call guard (a global-asm symbol isn't
|
||||
in the JIT host — dlsym-miss must be loud). See `PLAN-ASM.md` Phase F.
|
||||
- **Phase 2 — `-> @place` outputs** (write-through, read-write `"+r" -> @place`,
|
||||
indirect-memory `"=*m"`): currently rejected at parse. Needs place-expr
|
||||
lowering for the output target + the indirect-constraint handling, plus
|
||||
output-to-`const` rejection.
|
||||
|
||||
Then Phase 2 (`-> @place` write-through / read-write / indirect-memory) and Phase
|
||||
F (global asm + `extern` call into asm symbols). Result-type derivation for the
|
||||
0/1 cases now lives in BOTH `lowerAsmExpr` (the op's `Inst.ty`) and
|
||||
`expr_typer.zig`'s `inferType` (for `:=`/value-position typing); Phase E extends
|
||||
both to the tuple case.
|
||||
Also worth doing soon: the **x86_64 syscall-write** ir-only example (plan's D
|
||||
verification) and a **readme.md** inline-asm section (docs-track-changes). And the
|
||||
orthogonal **issue 0137** (no-`main` segfault) whenever.
|
||||
|
||||
## Log
|
||||
- (init) Plan + design doc written; ASM stream opened.
|
||||
@@ -181,6 +200,11 @@ both to the tuple case.
|
||||
`inferType` arm; `LLVMInitializeNativeAsmParser` for the JIT. **Inline asm runs
|
||||
end-to-end.** N>1 bails (Phase E). Locked with 1645 (aarch64 add, runs) + 1646
|
||||
(`:=` binding); updated 1640/1642. `zig build test` green (654 corpus, 446 unit).
|
||||
- (E) multi-output tuples — `asmResultType` helper (0→void/1→T/N→named tuple),
|
||||
shared by lowering + `inferType`. `toLLVMType(tuple)` == LLVM multi-output
|
||||
struct, so emit unchanged; the asm struct return IS the sx tuple. Runs on
|
||||
aarch64 (1647: `split`→`(lo,hi)`); 1640 → x86 multi-output IR lock (ir-only).
|
||||
`zig build test` green (655 corpus, 446 unit).
|
||||
|
||||
## Known issues
|
||||
- **0137** — `sx run` on a program with no `main` segfaults (unguarded JIT entry
|
||||
|
||||
Reference in New Issue
Block a user