Companion of #574 (EPIC-017) — the cross-block/locals half of the IR representation work, made visible upstream. Dates refer to the internal iteration log.
Summary
Block-args are a low-level accommodation in the shared IR: the stack VM lacks native locals, so every cross-block value must be threaded through branch-target args, and lower.lg rejects any direct cross-block ref (check-cross-block!). That rejection is why the AOT inline splice is unusable on the bytecode path (fold-unroll ships an outline-to-sibling workaround instead), and it forces the DUP-heavy branch-arg materialization that dominates lowered-code size (see #574's 2× finding).
The epic builds shared liveness metadata on the block interface, then gives the stack VM real locals, then erases block-args where a single-source value makes them redundant.
Story ledger
Done (foundation, backend-independent):
- STORY-0068 —
ir.passes.liveness: shared per-block liveness (in/out/kill) as the substrate for everything below. (done, ITER-0066)
- STORY-0069 — relocate/deduplicate the existing ad-hoc reachability helpers onto it (
lower_go's live-nids was the one genuine duplicate). (done, ITER-0067)
- STORY-0070 — single-value resolver: trace a block-arg to its unique source value when all in-edges agree (scope refined from single-source to single-value). Retiring
check-cross-block! rides on this. (done, ITER-0068)
- STORY-0071 — block-arg classification + census over the corpus: how many args are erasable, what the win is. (done, ITER-0069)
Pending (backend-coupled; sequencing guard: hold until STORY-0060 / EPIC-016 re-check verdict — if that flips to GO for an index-RPN mainline VM, the stack-VM half here is superseded):
- STORY-0072 —
OP_LOAD_LOCAL/OP_STORE_LOCAL in the stack VM: frame locals so cross-block values stop living on the operand stack.
- STORY-0073 — resolver lowering:
lower.lg consumes cross-block refs via locals; retires check-cross-block! and the junk-below accounting class with it.
- STORY-0074 — retire the fold-OUTLINE workaround: the general inliner (not just fold-unroll) unblocks on the bytecode path.
Why this matters now
The 2026-07-18 lowering investigation put numbers on the cost of not having this: branch-arg DUP shuffle is ~67% of the 2× instruction bloat in IR-lowered hot functions, and the junk-below bookkeeping that locals would eliminate is the source of a class of latent miscompiles (now guarded by fallback, at the cost of IR coverage).
Relationships
Companion of #574 (EPIC-017) — the cross-block/locals half of the IR representation work, made visible upstream. Dates refer to the internal iteration log.
Summary
Block-args are a low-level accommodation in the shared IR: the stack VM lacks native locals, so every cross-block value must be threaded through branch-target args, and
lower.lgrejects any direct cross-block ref (check-cross-block!). That rejection is why the AOT inline splice is unusable on the bytecode path (fold-unroll ships an outline-to-sibling workaround instead), and it forces the DUP-heavy branch-arg materialization that dominates lowered-code size (see #574's 2× finding).The epic builds shared liveness metadata on the block interface, then gives the stack VM real locals, then erases block-args where a single-source value makes them redundant.
Story ledger
Done (foundation, backend-independent):
ir.passes.liveness: shared per-block liveness (in/out/kill) as the substrate for everything below. (done, ITER-0066)lower_go'slive-nidswas the one genuine duplicate). (done, ITER-0067)check-cross-block!rides on this. (done, ITER-0068)Pending (backend-coupled; sequencing guard: hold until STORY-0060 / EPIC-016 re-check verdict — if that flips to GO for an index-RPN mainline VM, the stack-VM half here is superseded):
OP_LOAD_LOCAL/OP_STORE_LOCALin the stack VM: frame locals so cross-block values stop living on the operand stack.lower.lgconsumes cross-block refs via locals; retirescheck-cross-block!and the junk-below accounting class with it.Why this matters now
The 2026-07-18 lowering investigation put numbers on the cost of not having this: branch-arg DUP shuffle is ~67% of the 2× instruction bloat in IR-lowered hot functions, and the junk-below bookkeeping that locals would eliminate is the source of a class of latent miscompiles (now guarded by fallback, at the cost of IR coverage).
Relationships