The keyword-cond→switch lowering (structurize) currently gates absorption with a maximally-conservative shape test: a chain is absorbed only when every absorbed test block has zero block params and exactly the const+eq instruction shape. This is sound by construction, and real corpus chains do absorb under it (ir/dump, ir/passes/typeinfer, ir/lattice, ir/ops) — but the common canonical form (cond (= x :a) 1 (= x :b) 2 …) threads the discriminant through block params in build.lg's SSA, so those chains fall back to nested :if.
Expanding coverage to param-carrying chains requires the boundary analysis the conservative gate deliberately avoids:
- a param of an absorbed block is only safe when every use is inside the absorbed set or rewritten via
:absorbed-arg-subst at emission (coverage-in-map ≠ rewritten-in-output);
- values defined by absorbed instructions must not be consumed outside the set (
declared and not used / undefined: step_* are the two Go-compile failure signatures when this goes wrong);
- multiple chains in one function must each be checked against their own absorbed set (cross-chain blindness).
Several boundary-analysis attempts oscillated between under-rejection (uncompilable lowered Go in for_emit-class functions) and over-rejection (canonical chains dead); the conservative gate was chosen to land soundness first. This issue tracks doing the analysis properly, with the failure signatures above as the adversarial test set.
Related: #268 (op catalog parent). The IR structural test's positive fixture asserts a param-less chain today; this issue's acceptance would restore a param-carrying canonical-COND positive case.
The keyword-cond→switch lowering (structurize) currently gates absorption with a maximally-conservative shape test: a chain is absorbed only when every absorbed test block has zero block params and exactly the const+eq instruction shape. This is sound by construction, and real corpus chains do absorb under it (ir/dump, ir/passes/typeinfer, ir/lattice, ir/ops) — but the common canonical form
(cond (= x :a) 1 (= x :b) 2 …)threads the discriminant through block params in build.lg's SSA, so those chains fall back to nested:if.Expanding coverage to param-carrying chains requires the boundary analysis the conservative gate deliberately avoids:
:absorbed-arg-substat emission (coverage-in-map ≠ rewritten-in-output);declared and not used/undefined: step_*are the two Go-compile failure signatures when this goes wrong);Several boundary-analysis attempts oscillated between under-rejection (uncompilable lowered Go in
for_emit-class functions) and over-rejection (canonical chains dead); the conservative gate was chosen to land soundness first. This issue tracks doing the analysis properly, with the failure signatures above as the adversarial test set.Related: #268 (op catalog parent). The IR structural test's positive fixture asserts a param-less chain today; this issue's acceptance would restore a param-carrying canonical-COND positive case.