loop-invariant code motion
/ LICM /
Imagine a chore you repeat 1000 times, and inside it you keep recomputing one fact that never changes between repetitions — like re-checking the date each time you stamp an envelope. The obvious fix is to compute it once before you start and reuse it. Loop-invariant code motion is the compiler doing precisely that: moving a computation whose result does not change across iterations out of the loop, to run it once before the loop instead of every time around.
A computation is loop-invariant if it produces the same value on every iteration — its inputs are not modified inside the loop. The compiler identifies such computations and hoists them into a preheader, a block that runs once just before the loop begins. For example, in for (i = 0; i < n; i++) a[i] = b * c + i;, the product b * c does not depend on i and is recomputed needlessly each pass; LICM lifts t = b * c above the loop and the body becomes a[i] = t + i. The analysis relies on proving the moved code's inputs really are constant across the loop and that hoisting it is safe (it does not, for instance, move a faulting or side-effecting operation to where it might run when the loop would have run zero times).
It matters because the body of a hot loop is the most valuable real estate in a program; removing even one operation there pays off n times. LICM is a standard part of -O2 loop optimization and often works together with strength reduction and common-subexpression elimination. A caveat: the compiler can only hoist what it can prove is invariant and safe. If a pointer write inside the loop might alias the inputs, or a called function might change them, the value is not provably invariant and stays put — weak alias information is a common reason an 'obviously' hoistable computation is left in the loop.
for (i = 0; i < n; i++) a[i] = b * c + i; // b*c recomputed every iteration // after LICM: t = b * c; // hoisted: computed once in the preheader for (i = 0; i < n; i++) a[i] = t + i;
b * c does not change with i, so it is computed once before the loop instead of n times inside it.
Hoisting requires PROVING invariance and safety: if a store through a possibly-aliasing pointer or a function call inside the loop might change the inputs, the compiler conservatively leaves the computation in place.