Data-Flow Analysis
One framework — facts, transfer functions, a meet operator, iterate to a fixed point — and the four classic analyses that are all instances of it.
Four slots — a lattice of facts, a transfer function per instruction, a meet operator at joins, and iteration to a fixed point. Fill them in four different ways and you get reaching definitions, liveness, available expressions and constant propagation. There is only one algorithm here.
Apply the equations until nothing changes. It terminates because the transfer functions are monotone over a lattice of finite height, so a fact can only move one way and only so far. Worklist order changes how many rounds it takes and never what it converges to.
Liveness runs backward because "is this value needed?" is a question about the future. Reaching definitions runs forward because "where did this value come from?" is a question about the past. The direction is dictated by the question, and choosing it is not a design decision.
Which assignments may have produced the value I am reading here? A forward, may analysis with union at merges — and the analysis SSA was invented to make unnecessary, because in SSA the answer is the operand name.
Is this value needed in the future? A backward, may analysis whose answer is the direct input to `[[register-allocation]]` — and the reason it must iterate is the back edge, where a loop-carried value has to stay live around a body that never mentions it.
Has this expression already been computed on *every* path to here, with no operand changed since? A forward, must analysis with intersection at merges — and the precondition without which `[[common-subexpression-elimination]]` is a miscompilation.
`x = 5; y = x + 3` becomes `y = 8`. A forward analysis over a three-level lattice — unknown, one specific constant, not constant — and its SSA-based descendant SCCP does something the dense version cannot: it kills unreachable branches while it propagates.