Two new workstreams:
- ASM: inline assembly — asm { "tmpl", "=r" -> T, "r" = expr, clobbers(.…) },
multi-return tuples; lowers via the existing llvm_api.c (no shim).
- FFI-linkage: add extern/export postfix keywords, migrate every #foreign onto
them, then purge 'foreign' from the tree (end-state invariant).
Drop current/ from .gitignore so plans + checkpoints are tracked normally
(the dir was ignored; only checkpoints had been force-added). Includes
docs/inline-asm-design.md. specs.md change left uncommitted.
83 lines
5.5 KiB
Markdown
83 lines
5.5 KiB
Markdown
# sx Inline Assembly — Implementation Plan (ASM stream)
|
||
|
||
**Design source of truth:** [docs/inline-asm-design.md](../docs/inline-asm-design.md).
|
||
This plan turns that doc's §II.7 stage-map + §II.8 phasing into ordered,
|
||
commit-sized, testable steps. Read the design doc first — this file is the
|
||
*how/when*, not the *what/why*.
|
||
|
||
**Surface (decided):**
|
||
`asm volatile { "template", "=r" -> T, "r" = expr, clobbers(.cc, .memory) }`
|
||
— brace block; `->` output / `=` input; `clobbers(.…)` dot-name list; N `-> Type`
|
||
outputs return a tuple; templates are pure AT&T (via LLVM).
|
||
|
||
**Feasibility (confirmed):** sx links LLVM@19; `src/llvm_api.zig` `@cImport`s
|
||
`llvm-c/Core.h`, so `llvm_api.c.*` already exposes `LLVMGetInlineAsm` (9-arg),
|
||
`LLVMInlineAsmDialectATT`, `LLVMBuildCall2`, `LLVMAppendModuleInlineAsm`. No shim.
|
||
|
||
**Relationship to other streams:**
|
||
- Phases A–E (the inline-asm *expression*) are independent of EXTERN-EXPORT.
|
||
- Phase F (global asm) consumes `extern`/`export` to import/expose asm symbols —
|
||
do it **after** `PLAN-EXTERN-EXPORT.md` Phase 2.
|
||
|
||
## Cadence (IMPASSIBLE)
|
||
No commit may both add a test AND make it pass. Each feature step is either a
|
||
behavior-locking PASSING test, or an xfail test the *next* commit turns green.
|
||
Arch-pinned tests live in `examples/16xx-platform-asm-*` (must declare `target=`).
|
||
Never regenerate snapshots while red.
|
||
|
||
## Phase A — keyword + AST + parser (parses; no codegen)
|
||
| Step | Commit | What | Files |
|
||
|---|---|---|---|
|
||
| A.0 | lock | add `kw_asm` keyword + map entry; unit lex test `asm → kw_asm` | `src/token.zig`, `src/lexer.zig` + `.test.zig` |
|
||
| A.1 | xfail | parse `asm { … }` → `AsmExpr`/`AsmOperand` in `parsePrimary`; pin an AST/`sx ir` parse snapshot; lowering still `bailDetail("inline asm codegen unimplemented")` | `src/ast.zig` (:85 union arm, :721 structs), `src/parser.zig` (parsePrimary), `src/ir/interp.zig` |
|
||
| A.2 | green | parse-shape snapshot lands green; the unimplemented bail is loud + named | — |
|
||
|
||
## Phase B — sema / typing
|
||
| Step | Commit | What | Files |
|
||
|---|---|---|---|
|
||
| B.0 | xfail | result-type rule (0→`void` / 1→`T` / N→named-or-positional tuple) + checklist (no-output⇒`volatile`, layout, comptime-string template) — pin error messages | `src/ir/expr_typer.zig` |
|
||
| B.1 | green | typing + diagnostics implemented; `.unresolved` sentinel on failure (no silent default) | `src/ir/expr_typer.zig`, `src/ir/semantic_diagnostics.zig` |
|
||
|
||
## Phase C — IR op + lowering
|
||
| Step | Commit | What | Files |
|
||
|---|---|---|---|
|
||
| C.0 | lock | add `inline_asm: InlineAsm` to `Op` + `AsmOperand` (role/name/constraint/operand) + interp `bailDetail` arm; unit tests for the IR shape | `src/ir/inst.zig` (:80), `src/ir/interp.zig` |
|
||
| C.1 | xfail→green | `lowerAsmExpr` in `lowerExpr` dispatch — interns template/constraints/clobber-names, lowers input `Ref`s, sets result `TypeId` | `src/ir/lower/expr.zig` |
|
||
|
||
## Phase D — LLVM emit (single value-output; the core)
|
||
| Step | Commit | What | Files |
|
||
|---|---|---|---|
|
||
| D.0 | xfail | `examples/16xx-platform-asm-syscall-write.sx` + `…-register-read.sx` + `…-no-output-volatile.sx` + `…-missing-volatile.sx` (expected compile error) — all red | examples + `expected/` markers |
|
||
| D.1 | green | `emitInlineAsm`: **port `FuncGen.airAssembly`** — constraint-string assembler (outputs `=`/`+`, inputs, `clobbers(.name)`→`~{name}`), `%[name]`→`${N}` / `%%` / `%=` template rewriter, `LLVMGetInlineAsm`+`LLVMBuildCall2`, `sideeffect=volatile`, AT&T dialect | `src/ir/emit_llvm.zig` (emitInst dispatch + handler) |
|
||
| D.2 | green | lock the template-rewrite + constraint string via an `expected/*.ir` snapshot on `…-template-subst.sx` | examples |
|
||
|
||
**Phase D verification:** `zig build test`; the syscall example runs on
|
||
`x86_64-linux`; IR snapshot matches the design doc's worked `sys_write` lowering.
|
||
|
||
## Phase E — multi-return tuples + `clobbers(.…)`
|
||
| Step | Commit | What | Files |
|
||
|---|---|---|---|
|
||
| E.0 | xfail | `…-asm-multi-return.sx` (`divmod`→`(quot,rem)`, `cpuid`→4-tuple) red | examples |
|
||
| E.1 | green | N `out_value` → LLVM struct return + `extractvalue i` → sx tuple (named when operands named); `clobbers(.name)` dot-name lowering finalized | `src/ir/emit_llvm.zig`, `src/ir/lower/expr.zig` |
|
||
|
||
## Phase F — global asm (needs EXTERN-EXPORT Phase 2)
|
||
| Step | Commit | What | Files |
|
||
|---|---|---|---|
|
||
| F.0 | xfail | top-level `asm { … }` decl parsed (reject operands/`volatile`); `…-asm-global.sx` (defines a symbol, imported via `extern`) red | `src/parser.zig`, `src/ast.zig` |
|
||
| F.1 | green | lower `asm_global` → `c.LLVMAppendModuleInlineAsm`; comptime-call guard (dlsym-miss is loud); blocks concatenate in source order | `src/ir/lower/decl.zig`, `src/ir/emit_llvm.zig`, `src/ir/interp.zig` |
|
||
|
||
## Phase G — later (own steps when scheduled)
|
||
`-> @place` write-through + read-write (`"+r" -> @place`) + indirect-memory
|
||
(`"=*m"`) outputs · `%=` unique-id · output-to-const rejection · Intel-dialect
|
||
opt-in · naked functions (`callconv(.naked)`, coordinate with EXTERN-EXPORT).
|
||
|
||
## Open decisions (design doc §II.10)
|
||
Dialect (AT&T-only v1, recommended) · `volatile` contextual-keyword (recommended)
|
||
· brace separator comma (recommended) · `clobbers(.name)` dot-name sugar now →
|
||
checked per-arch `Clobber` enum later (Phase 4 of the design doc).
|
||
|
||
## End-to-end verification (per phase)
|
||
`zig build && zig build test`; for arch-pinned examples confirm they run on a
|
||
matching host or assert on `sx ir`/`.s` snapshots. After intentional output
|
||
changes only: `zig build test -Dupdate-goldens`, then review the diff.
|