2026-07-02
Stack Overflow: View Question
Tags: compiler-construction, control-flow-graph, ssa
Score: 1 | Views: 99
The asker is implementing SSA (Static Single Assignment) construction using the classic Cytron et al. algorithm based on dominance frontiers. They have a tiny CFG: an entry block that assigns a = 1, and a self-looping block that reads b = a, writes a = 2, and jumps back to itself. After running the textbook algorithm, they end up with what looks like an unnecessary phi function for b — a variable that is only defined inside the loop and never read outside it. Why does the "vanilla" Cytron construction plant a phi for a variable that clearly doesn't need one?
Why it's interesting: This is the classic gap between minimal SSA, semi-pruned SSA, and pruned SSA. Cytron's original algorithm places a phi at every node in the iterated dominance frontier (IDF) of every variable's definitions — regardless of whether the variable is actually live at that point. For a self-loop, the loop header is in the dominance frontier of the block that defines a (and b, if b is written in the same block), so the algorithm dutifully inserts a phi. That phi is "superfluous" only in the sense that later passes (dead code elimination) would remove it — the algorithm itself is behaving correctly per its specification.
Direction toward a solution:
v at block B if v is live-in to B. For b, which is dead immediately after its assignment (never used elsewhere), no phi gets placed. This is what the asker likely wants.Gotchas:
a at the loop header is not superfluous even in a self-loop: a is live-in (used by b = a), and its value comes from two edges (entry block and back-edge). Don't accidentally prune that one.b as escaping the loop (no observers), a subsequent DCE pass will eliminate the whole assignment anyway, making the phi doubly redundant. This is fine and expected — the algorithm didn't lie, it just wasn't optimal.