23 Canonicalization
Canonicalization rewrites equivalent IR into a preferred normal form. Its goal is not to perform every optimization; it removes representational variation so later analyses and patterns see fewer cases.
23.1 A Canonical Form Is A Convention
There is usually more than one valid way to express a computation. A compiler may prefer constants on one side of a commutative operation, remove identity casts, collapse nested reshapes when safe, or select one spelling for an empty operation. The preferred form is a convention shared by dialects and passes.
A canonicalization pattern must preserve all relevant semantics. The familiar identity add x, 0 is only canonical when overflow, floating behavior, flags, and types allow it. A reshape chain is only collapsible when shape and encoding information agree. Canonicalization is semantic normalization, not algebraic wishful thinking.
23.2 Greedy Rewriting Needs Convergence
MLIR commonly applies canonicalization patterns with a greedy driver. Patterns must make progress toward a stable form and avoid ping-pong rewrites. A rewrite that changes between two equivalent spellings depending on traversal order is a bad canonicalization rule even if each direction is correct in isolation.
Keep canonicalization local and inexpensive. Global scheduling, expensive cost models, and interprocedural decisions belong in dedicated passes. A canonicalizer should make subsequent work simpler, not hide important high-level structure just because a lower-level representation exists.
23.3 Folds And Patterns
Operation folding handles small local simplifications, especially constants, through a dedicated operation hook. Canonicalization patterns can match and rewrite larger local structures. Both may run near each other, but folding is not a replacement for a pattern and a pattern should not assume folding has already run unless its driver guarantees that ordering.