ADR-018 — Structured IR: optimize inside nested control-flow buffers

Status: Accepted Date: 2026-06-16 Supersedes (in practice): parts of ADR-012

Context

ADR-012 accepted a multi-stage IR with a high-level semantic IR and a separate lowered IR, bridged by analysis passes. In practice that split was never built. What exists (src/ir/) is a single typed IR that carries control flow in two competing representations:

The cost of maintaining both halves was real: hygiene passes only ever optimized top-level block.instrs. Constant folding seeded only top-level consts and punted on control-flow arms; DCE filtered only top-level instrs. Loop and conditional bodies — the code where folding and dead-code removal actually pay — were never optimized. Worse, each pass independently special-cased ~10 buffer-bearing kinds, the duplication that produced the silent while-loop DCE demotion fixed in #1922.

Two ways forward (the 2026-06 compiler quality review):

Decision

Adopt Option A. Nested instruction buffers are the canonical control-flow representation of this IR; the hygiene passes optimize through them.

The block-arg/phi CFG machinery is kept but frozen: it is not deleted (the linear backend and dominance checks still reference the block model), but from-ast and the hygiene passes do not introduce block args, and no new pass should rely on a phi-based CFG. Loops/ifs stay in buffers.

Option B remains the open long-term question; this ADR does not foreclose it, but commits the near term to one representation so we stop paying for both.

Consequences