Files
sx/current/PLAN-ASM.md
agra c562fe236d docs(plans): inline-asm design + ASM and FFI-linkage plans/checkpoints
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.
2026-06-14 12:16:10 +03:00

5.5 KiB
Raw Blame History

sx Inline Assembly — Implementation Plan (ASM stream)

Design source of truth: 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 @cImports llvm-c/Core.h, so llvm_api.c.* already exposes LLVMGetInlineAsm (9-arg), LLVMInlineAsmDialectATT, LLVMBuildCall2, LLVMAppendModuleInlineAsm. No shim.

Relationship to other streams:

  • Phases AE (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 Refs, 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_globalc.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.