Skip to content

Epic: block interface as shared liveness metadata + block-arg erasure (EPIC-018) #575

Description

@nnunley

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions