Give every expression a value number so that two expressions get the same number exactly when they are provably equal — not textually equal, congruent. Then a second a+b, or a d+b where d is a copy of a, is recognised as already computed and reused. It catches redundancy that textual matching cannot.
Hash each expression by its operator and the value numbers of its operands. Equal hash → equal value number → redundant. Watch a+b, a second a+b, and d+b (with d=a) all collapse to one number: live demo
“GVN is just CSE.” — related but not equal. Common-subexpression elimination matches expressions by an available-expressions dataflow (roughly textual); GVN partitions by congruence of value numbers, so it sees equalities through copies and φs that textual CSE misses. cited
Congruence is a fixed point: two φs are congruent if their operands are pairwise congruent — a mutual recursion resolved by partition refinement (optimistic) rather than pessimistic worklist. Alpern-Wegman-Zadeck, 1988
The equalities GVN must prove — the second a+b, and d+b through the copy d=a — hold on the canonical compiler: