Compiler optimization 2026-11-05
Compiler optimization transforms a program representation to improve a chosen resource objective while preserving the behavior required by its programming language semantics. Objectives include execution time, code size and memory traffic. A transformation need not find a globally best program, and improving one objective can worsen another.
For example, constant folding replaces 2+3 by 5, while common subexpression elimination may compute a repeated expression once. Reusing a load across a store is valid only if the store cannot change the loaded location; alias analysis helps establish that condition. Similarly, replacing floating-point (a+b)+c by a+(b+c) can change rounding, so an optimizer needs an applicable permission or proof. Data-flow analysis supplies facts used by transformations, and undefined behavior affects which executions the language requires an implementation to preserve.
Control-flow graph 2026-11-05
A control-flow graph is a directed graph whose vertices represent program regions, commonly basic blocks, and whose edges represent possible immediate control transfers. It describes which execution paths the representation permits, rather than which path one particular run takes. Compiler optimization and data-flow analysis use this structure.
For if c then x=1 else x=2; return x, use an entry vertex with edges to separate true and false basic blocks, each leading to one join basic block. The graph has a diamond shape. The join needs the incoming value selected by the executed path; static single-assignment form represents that choice using a phi node. LLVM's tutorial constructs precisely this branch-and-join structure.[ref] Loops add backward edges, so a control-flow graph need not be acyclic.
Data-flow analysis 2026-11-05
Data-flow analysis propagates facts about values or variable uses through a control-flow graph. Each program operation transforms incoming facts, and a join combines information from possible predecessor paths. A compiler can use the result to justify compiler optimization or issue warnings. Clang's introduction develops this method using sets of possible values and iteration to a fixed point.[ref]
For example, live-variable analysis asks which current values might be read later before being overwritten. For a basic block , with uses before any local definition in and definitions in , the backward equations are
A use requires the incoming value; an intervening definition removes that requirement. Iterating from empty sets terminates for a finite graph and finite variable set because these equations only add facts. The result guides register allocation.
Live-variable analysis 2026-11-05
Live-variable analysis computes which program variables may be read in the future before their current values are overwritten. It is a backward data-flow analysis: future uses make information flow toward earlier instructions. For a basic block , let contain values read before being defined in that block and let contain its definitions. Then
A value is needed on entry if the block itself first reads it, or if a successor needs it and the block does not replace it.[ref]
For a block x = y + 1; return x;, y is live on entry but the old x is not. Iterating the equations over a finite control-flow graph yields a conservative answer useful for register allocation. Syntactically possible paths may include paths that execution can never take, so computed liveness need not prove that a use actually occurs.
Static program analysis 2026-11-05
Static program analysis derives information about a computer program from its representation and programming language semantics, without executing each concrete input case. An analysis commonly approximates many possible executions at once. Data-flow analysis propagates such information between locations in a control-flow graph.[ref]
For example, after if c then x=1 else x=2, an analysis can infer that x belongs to at the join and hence is nonzero. More complicated paths may force a less precise answer. A conservative warning can therefore flag a possible failure that no actual input reaches. The guarantee depends on what the approximation includes: failing to model an external call or integer overflow can invalidate an apparent proof.