Scalar Optimization
Folding, elimination, propagation, inlining and devirtualization — each with the precondition that makes it legal and the budget that makes it wise.
Evaluate at build time what would otherwise be evaluated at run time — but only when the operands are literals, the operation cannot fault, and the compiler computes exactly the value the machine would have computed.
Delete an instruction only when it has no side effect AND no user. Both halves are required, and removing an effectful instruction because its value happens to be unused is a miscompilation rather than an optimization.
Compute `a * b` once and reuse it — but only when the earlier computation dominates the later one, so the value is guaranteed available on every path that reaches the reuse. Over registers this is easy; over memory it needs alias analysis, which is why the two are different problems.
If `a` is a copy of `b`, use `b` directly and let the copy die. In SSA this is legal by construction; outside SSA it needs a reaching-definitions analysis, and that difference is one of the clearest arguments for SSA there is.
Replace an operation with a cheaper one that computes the identical value. The real content is not that shifts beat multiplies on some 1990s CPU — it is that `x + 0` is unconditionally `x` for integers and is not valid for IEEE-754 floats, which is why `-ffast-math` exists and why it changes what a program means.
Replace a call with the callee's body. The direct saving — a call and a return — is the least interesting part; the value is that every other optimization can now see across a boundary it could not cross. The cost is code size, compile time and instruction-cache pressure, and it is a budget rather than a rule.
Turn an indirect call through a dispatch table into a direct call to a known function — and then, because the target is known, inline it. The whole value is in that second step; a direct call on its own is barely cheaper than an indirect one.
When some inputs are known and others are not, a program can be specialized with respect to the known ones — producing a smaller, faster program that takes only the remaining inputs. It is the idea behind constant folding, template instantiation, JIT specialization and monomorphization, and it explains why they behave alike.