Compilers & Code Generation

common-subexpression elimination

/ CSE /

If a recipe says 'add (flour times 2) cups, then later add another (flour times 2) cups', you would compute flour times 2 once and reuse it, not multiply twice. Common-subexpression elimination, or CSE, is the compiler doing exactly that: spotting an expression it has already computed, whose inputs have not changed, and reusing the earlier result instead of computing it again.

The compiler scans the IR for two or more occurrences of the same expression — same operation, same operands — where the operands are guaranteed to hold the same values at both points (none of them was reassigned in between). When it finds such a pair, it computes the expression once into a temporary and replaces the later occurrence with that temporary. So a = (x + y) * z; b = (x + y) * w; becomes t = x + y; a = t * z; b = t * w;, saving one addition. Modern compilers usually implement this via global value numbering, which assigns the same internal 'number' to expressions that are provably equal across the whole function, catching redundancies even across branches, not just within one straight line.

It matters because redundant computation is everywhere once macros, inlining, and array-index arithmetic expand — the same address calculation or subexpression appears repeatedly, and CSE collapses it. A caveat: CSE is only valid when the two computations genuinely give the same result, so the compiler must respect possible side effects and aliasing. If something between the two could change an operand (a store through a pointer that might alias x, a function call that might modify it), the subexpressions are not 'common' and CSE must not fire. And reusing a value sometimes lengthens its lifetime, which can increase register pressure — eliminating a cheap recompute is not unconditionally a win.

a = (x + y) * z; b = (x + y) * w; // CSE computes x + y once: t = x + y; a = t * z; b = t * w; // the second x + y is gone

x + y is computed once into t and reused, provided neither x nor y changes between the two uses.

CSE only fires when the operands provably did not change between the two computations: an intervening store through a possibly-aliasing pointer or a side-effecting call blocks it, so weak alias analysis can leave obvious redundancy in place.

Also called
CSEredundancy eliminationglobal value numbering (a related technique)公共子式消除