How Optimizers Work: O0–O3, Inlining, DCE
This chapter explains what a compiler optimizer does, the -O0 through -O3 levels, and two fundamental passes: inlining and dead-code elimination.
Why This Matters
You cannot reason about performance or "release-only" bugs without knowing what the optimizer is doing to your code. This is the foundation for the performance and UB-optimization chapters.
Prerequisites
c.build.8— compiler flags.
Core Concept
An optimizer transforms the compiler's intermediate representation to make the program faster or smaller, while preserving observable behavior. It runs a series of passes — each a specific transformation.
| Level | Meaning |
|---|---|
-O0 | no optimization |
-O1 | basic optimizations, quick compile |
-O2 | aggressive, standard for release |
-O3 | -O2 + loop/vectorization, may increase size |
-Os | optimize for size |
-Ofast | -O3 + non-standard floating-point relaxations |
Fundamental Passes
Inlining
Replaces a function call with the function body, eliminating call overhead and enabling further optimization. The compiler uses heuristics (function size, call frequency) to decide.
Dead-code elimination (DCE)
Removes code whose results are never used. This includes unreachable code and stores to variables that are never read.
Constant folding and propagation
Computes constant expressions at compile time and replaces variables with their known constant values.
Common subexpression elimination
Reuses a computation already performed instead of recomputing it.
Examples
A function the optimizer can simplify
int f(int x)
{
int y = x * 2;
int z = y + 1;
if (0) { return z; } /* dead branch */
return x * 2 + 1; /* constant-foldable to same as z */
}
At -O2, this collapses to a single expression.
How It Works
The compiler builds an IR, applies passes repeatedly (often until a fixed point), and emits optimized machine code. Each pass must preserve the abstract machine's observable behavior — assuming no UB.
Variations
Link-time optimization (LTO)
LTO runs optimization across translation units, enabling cross-module inlining (c.opt.3).
Profile-guided optimization (PGO)
PGO uses run-time profiles to guide inlining and branch layout (c.opt.3).
Common Mistakes
- Assuming
-O3is always faster (it can be slower due to code size). - Using
-Ofastwithout understanding its floating-point relaxations. - Micro-optimizing by hand what the compiler already does.
Undefined Behavior
- Optimizers assume UB never occurs. Code with UB can be transformed in
surprising ways (c.opt.2).
Portability
- Optimization levels are compiler concepts; the exact passes and results
differ between GCC, Clang, and MSVC.
Under the Hood
GCC uses GIMPLE/RTL; Clang uses LLVM IR. Passes operate on SSA form, which makes dataflow analysis (DCE, constant propagation) efficient.
Practical Usage
- Use
-O2for release;-O0/-Ogfor debugging. - Trust the optimizer for micro-optimizations; focus on algorithms and layout.
- Benchmark at the level you ship.
Exercises
1. Compile a small function at -O0 and -O2 and compare the assembly. 2. Identify inlining and DCE in the generated code. 3. Experiment with -Os vs. -O3 on size and speed.
Deep Challenge
Write a function where -O2 produces dramatically different assembly than -O0 (e.g., a loop that gets vectorized or a switch that becomes a jump table), and explain each transformation you observe.
Related Concepts
c.opt.2— UB-based optimization.c.opt.3— LTO/PGO/vectorization.c.cpu.6— C-to-assembly.
References
- GCC optimization options, LLVM pass documentation.
Verification
- Optimization level semantics are documented compiler behavior.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Optimization levels O0–O3
- [ ] Inlining
- [ ] Dead-code elimination
- [ ] Constant folding
- [ ] Common subexpression elimination
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.opt.levels | 0 | 6 |
| c.opt.inlining | 0 | 6 |
| c.opt.dce | 0 | 5 |