the as-if rule
When you ask the compiler to make your program fast, you are giving it permission to do something surprising: it may throw away the exact instructions implied by your source and substitute completely different ones — as long as you cannot tell the difference by watching the program run. That permission is the as-if rule, and it is the legal foundation underneath every optimization in this field.
The C and C++ standards describe an abstract machine: a hypothetical, idealized computer that executes your program step by step exactly as written. The as-if rule says the real compiler need not faithfully reproduce those steps; it only has to produce a program that behaves AS IF it had run on the abstract machine — that is, it must preserve the program's observable behavior. Observable behavior is a precise, limited list: the contents and ordering of input and output (reads and writes to volatile objects, data written to files, interactive I/O), and program termination as specified. Everything else is fair game. The compiler may delete computations, reorder operations, fold constants, keep values only in registers, and skip work entirely, provided the observable behavior on the abstract machine is the same.
It matters because it explains, all at once, why the optimizer is allowed to do its most startling tricks — the loop that never runs, the variable that has no memory location, the reordered statements. They are all legal precisely because they do not change what an observer can see. Two crucial caveats keep this honest. First, the rule preserves behavior only on programs that are well-defined: if your program has undefined behavior, the abstract machine has NO defined behavior to preserve, so the as-if rule constrains nothing and the optimizer may do anything (this is the heart of UB-driven optimization). Second, 'observable' is narrower than you might hope — ordinary memory writes that no one observes are not protected, which is exactly why volatile exists to mark a write as observable and why timing and energy use are not part of observable behavior at all.
int f(void) { int x = 0; for (int i = 0; i < 1000000; i++) x += 1; // observable? no one watches x or i return x; } // the compiler may emit, as-if-equivalently: int f(void) { return 1000000; } // no loop runs; same observable behavior
The loop is deleted because no observable behavior depends on it running — only the returned value matters.
The as-if rule only protects WELL-DEFINED programs: undefined behavior means the abstract machine has no behavior to preserve, so the rule constrains nothing — and 'observable' excludes timing, energy, and plain unobserved memory writes (which is why volatile exists).