21 Analysis
An analysis is a computed fact about IR that helps a compiler make a safe or profitable decision. It does not change the program. Dominance, liveness, aliasing, side effects, call graphs, loop structure, and shape facts are all analyses, though their precision and cost vary widely.
21.1 Analyses Answer Specific Questions
There is no single object called “the analysis.” Each one has a contract. A dominance analysis asks whether one definition is guaranteed to precede a use. An alias analysis asks whether two references may denote the same memory. A liveness analysis asks whether a value may be used in the future. A call graph asks which functions may invoke which others.
Use the narrowest analysis that justifies a rewrite. If a transformation only needs to know whether a value has one use, inspect its use list. Do not build an expensive interprocedural alias analysis merely because it exists. Conversely, do not replace a load or move a store based only on local syntax when alias and effect information is required.
21.2 Precision Is A Tradeoff
An analysis can report a conservative answer such as “may alias” or “unknown.” That is not a failure. Conservatism protects correctness: a rewrite can proceed only when the analysis proves its precondition, and otherwise leaves the IR unchanged. More precision may enable more optimization but takes more compile time and often needs stronger dialect interfaces.
A common mistake is treating unknown as no. Unknown memory effects do not mean an operation is pure. May-alias does not mean two accesses are independent. Compiler transformations should be optimistic only when an explicit semantic contract allows it.
21.3 Caching And Invalidation
The pass manager may cache analyses for an operation. A transformation that changes the IR must declare which cached results remain valid. Most structural rewrites invalidate dominance, liveness, alias information, and operation walks. Marking them preserved without proof turns stale facts into compiler miscompilations.
This makes analysis design inseparable from pass design. An analysis needs a clear operation scope, a defined invalidation boundary, and a way to be recomputed. When a pass relies on a module-wide fact while transforming a function, it must state that relationship rather than retain an ad hoc global cache.
21.4 Example: Eliminating An Unused Computation
Given an effect-free operation with an unused result, dead-code elimination may erase it:
%unused = arith.addi %x, %y : i32
The use list proves the result is unused. The operation’s effects prove removing it is unobservable. Both facts are necessary. Repeating this reasoning for a store fails because memory effects make the operation observable even with zero results.