Restructures the .asm_expr lowering arm into lowerAsmExpr, which validates the
asm shape with specific named diagnostics BEFORE the not-yet-implemented codegen
bail, so the user sees the real problem first. Two checklist items enforced:
- template must be a compile-time-known string ("..." or #string), not a
runtime expression;
- an asm with no value outputs must be `volatile` (else its effects could be
deleted) — mirrors Zig's rule.
Valid shapes still bail with the "codegen not yet implemented" message. Result-
type derivation + the operand auto-naming rule stay deferred to Phase C, where a
real IR op makes the result type observable/testable.
Locked with 1641-platform-asm-missing-volatile (the volatile error) and
1642-platform-asm-nop-volatile (no-output + volatile accepted → codegen bail).
zig build test green (650 corpus, 445 unit).
8.1 KiB
sx Inline Assembly — Checkpoint (ASM stream)
Companion to current/PLAN-ASM.md; design in
docs/inline-asm-design.md. Update after every
commit, one step at a time per the cadence rule (no commit may both add a test
and make it pass).
Last completed step
B.0 — asm shape validation (compile-path diagnostics). Restructured the
.asm_expr lowering arm into lowerAsmExpr (src/ir/lower/expr.zig, mixed into
Lowering in src/ir/lower.zig): it validates BEFORE the not-yet-implemented
codegen bail, so the user sees the real problem first. Two checklist items now
enforced with named diagnostics: (1) template must be a compile-time-known
string ("..." / #string); (2) no value outputs ⇒ must be volatile
(mirrors Zig — a result-less asm could be deleted). Valid shapes still bail with
the "codegen not yet implemented" message. Result-type derivation + auto-naming
stay deferred to a later step (observable only once Phase C produces a real IR
op). Locked with examples/1641-platform-asm-missing-volatile.sx (volatile
error) + 1642-platform-asm-nop-volatile.sx (volatile no-output accepted →
codegen bail). zig build test green (650 corpus, 0 failed; 445 unit). Files:
src/ir/lower/expr.zig, src/ir/lower.zig, examples/164{1,2}-*.
Prior: A.1 — parse asm { … } + loud lowering bail (folded A.1+A.2 into one honest
lock commit, since the loud bail IS current correct behavior — cadence option
(a)). Added AsmExpr/AsmOperand to src/ast.zig + the asm_expr Node.Data
arm; parseAsmExpr in src/parser.zig (parsePrimary .kw_asm dispatch) —
parses the template, flat operand list ([name]? "constraint" -> Type value
output / = expr input), and clobbers(.…); volatile/clobbers recognized
contextually via isContextualWord. The new asm_expr tag forced (and got)
arms in three exhaustive Node.Data switches: src/sema.zig analyzeNode +
findNodeAtOffset, src/ir/semantic_diagnostics.zig checkBindingNames (all
recurse into template + operand payloads). Lowering bails LOUD + named in
src/ir/lower/expr.zig ("inline assembly codegen is not yet implemented…") via
an explicit .asm_expr arm (not the generic unknown_expr else) returning
emitPlaceholder. -> @place write-through is rejected with a clear "Phase 2"
parse error. Locked with examples/1640-platform-asm-parse.sx (multi-output
divmod, named operands, register pins, clobbers — parses then bails; called
from main). zig build test green (648 corpus, 0 failed; 445 unit). Files:
src/ast.zig, src/parser.zig, src/sema.zig, src/ir/semantic_diagnostics.zig,
src/ir/lower/expr.zig, examples/1640-*.
Prior: A.0 — kw_asm keyword (first compiler code). Added the kw_asm Token.Tag
variant + .{ "asm", .kw_asm } keyword-map entry in src/token.zig; volatile /
clobbers deliberately stay OUT of the global table (contextual). New exhaustive
Tag switch in src/lsp/server.zig classifyToken flagged the missing arm (the
intended coverage tripwire) — added .kw_asm to the keyword group. Lock test in
new src/lexer.test.zig (asm→kw_asm, volatile/clobbers→identifier),
wired into the src/root.zig barrel as lexer_tests. zig build test green (648
corpus, 0 failed; 445 unit, 0 failed — +1). Files: src/token.zig,
src/lexer.test.zig, src/root.zig, src/lsp/server.zig.
Prior: 0.2 — CLAUDE.md docs for <name>.build; Phase 0 COMPLETE.
0.1 — corpus runner ir-only branch for cross-target examples. Replaced
0.0's loud placeholder bail: when cfg.target doesn't match the host (ir_only),
sweepRoot skips run/build/exec and verifies via sx ir --target only —
asserting .exit (ir cmd) + .ir (normalized stdout) + .stderr, never
.stdout (write skipped in update mode, assertion skipped in verify mode). An
.ir snapshot is required in ir-only mode — its absence is a loud failure
("needs an .ir snapshot for ir-only mode"). Locked with
examples/1639-platform-target-cross.sx (asm-free main :: () -> i64 { return 0; }), .build { "target": "x86_64-linux" }, + checked-in .ir. Verified both
guards fire: corrupting the .ir → IR mismatch; deleting it → the require-failure.
zig build test green (647 corpus, 0 failed; 444 unit). Files:
src/corpus_run.test.zig, examples/1639-*.
Current state
Phase A underway: asm { … } lexes (A.0) and parses into AsmExpr (A.1);
lowering bails LOUD + named (no IR op / emit yet). Result-type derivation, the
operand auto-naming rule, and the validation checklist are Phase B (not yet
implemented — any asm reaching lowering errors out). The adopted operand
auto-naming rule (design §II.5, decided this session): name auto-derived from a
{reg} pin; explicit [name] only when it differs or for register-class (=r)
operands; echo form [eax] "={eax}" rejected. Parser stores name: ?[]const u8;
the rule is a Phase-B (typing) concern, so the parser needs no change for it.
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,
asm-independent; does NOT block the ASM stream (every example has a main).
Phase B–E feasibility already confirmed against the live tree
(LLVMGetInlineAsm / LLVMBuildCall2 / LLVMAppendModuleInlineAsm in LLVM@19
Core.h; ERR-stream extractvalue→tuple in emit_llvm.zig:726-927; lib-less
extern, 60 sites; --target a global CLI flag).
Next step
B.1 / C.0 — two viable directions:
- B.1 (more validation, testable now): remaining checklist items that fire
pre-codegen — echo-name rejection (
[eax] "={eax}", §II.5 auto-naming), duplicate operand names,%[name]references that name no operand. Add tolowerAsmExpr(same compile-path pattern). Each is a pinnable error example. - C.0 (IR op, unlocks the rest): add
inline_asm: InlineAsmtoOp(src/ir/inst.zig) + interpbailDetail; thenlowerAsmExprstops bailing and builds the op, at which point result-type derivation becomes observable (so the auto-naming rule + tuple result typing inexpr_typer.zigcan be tested end-to-end). SeePLAN-ASM.mdPhase C + design §II.6.
Recommended: finish the cheap B.1 validation diagnostics, then move to C.0.
(Result-type derivation in expr_typer.zig is intentionally deferred until C
makes it observable — see Current state.)
Log
- (init) Plan + design doc written; ASM stream opened.
- (0.0) Corpus runner target-gating:
<name>.buildJSON config (replaces.aotmarker),--targetthreading,hostMatchesTargetexecute-gate, loud cross-target placeholder bail. Migrated 1226/1227.aot→.build; locked with 1638 fixture + unit tests.zig build testgreen. - (0.1) ir-only branch: cross-target examples verify via
sx ir --targetonly (exit+ir+stderr, no stdout;.irrequired). Locked with 1639 fixture; verified corrupt-.ir → mismatch and missing-.ir → loud failure.zig build testgreen. - (0.2) docs: CLAUDE.md documents
<name>.buildJSON sidecar (aot + target + ir-only gating), replacing stale.aotmarker prose. Phase 0 COMPLETE. - (A.0)
kw_asmkeyword in token.zig (+ map entry); LSPclassifyTokenswitch coverage; lock test in newlexer.test.zig(wired via root.zig).volatile/clobbersstay contextual identifiers.zig build testgreen (445 unit, +1). - (A.1) parse
asm { … }→AsmExpr+ loud lowering bail;asm_exprarms in 3 exhaustiveNode.Dataswitches;-> @placerejected (Phase 2). Adopted operand auto-naming rule (design §II.5). Locked with 1640 fixture. Filed orthogonal issue 0137 (no-mainJIT segfault).zig build testgreen (648 corpus, 445 unit). - (B.0) asm shape validation in
lowerAsmExpr: comptime-string template + no-output⇒volatile, with named diagnostics before the codegen bail. Locked with 1641 (volatile error) + 1642 (volatile accepted).zig build testgreen (650 corpus, 445 unit).
Known issues
- 0137 —
sx runon a program with nomainsegfaults (unguarded JIT entry lookup,src/target.zig:256-273). Pre-existing, asm-independent. Filedissues/0137-jit-run-no-main-segfault.md. Does not block A.1.