Middle-end

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.

The Data-Flow Framework
▶ lab

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.

Q · Is there one thing called "data-flow analysis", or is every analysis its own algorithm?
Iterating to a Fixed Point
▶ lab

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.

Q · Why does "keep applying the equations until they stop changing" terminate, and why does the order I visit blocks in not change the answer?
Forward and Backward Analysis
▶ lab

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.

Q · How do I know whether an analysis should run forwards or backwards through the control-flow graph?
Reaching Definitions
▶ lab

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.

Q · Which assignments could have produced the value at this program point, and why does SSA make the question trivial?
Liveness Analysis
▶ lab

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.

Q · How does a compiler know which values still matter at a given point, and why can it not work that out in a single pass?
Available Expressions
▶ lab

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.

Q · When is it safe to reuse a value the program already computed, instead of computing it again?
Constant Propagation
▶ lab

`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.

Q · How does a compiler know a variable holds a specific value, and how far can it carry that knowledge?