Master the fundamental concepts of compiler optimization techniques through this focused micro-challenge.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksGCC and Clang -O0 through -O3 and -Os change inlining, vectorization, and alias assumptions. Production servers compile hot paths at -O2 or -O3; debug builds stay at -O0 so breakpoints line up with source.
-O2: standard optimizations without aggressive size blowup. -O3: more inlining and vectorization. -ffast-math: relaxes IEEE rules for speed. -g does not disable optimizations.
bashLoading…
size and nm when code bloat appears-fomit-frame-pointer speeds x86 but breaks naive stack walks-S to inspect assembly when a loop did not vectorizeKeep the relevant documentation open while you implement. When your output disagrees with the reference, trace one failing case by hand before changing random lines.
You will compile the same program at -O0, -O2, and -O3 and compare runtime and binary size. This exercise asks you to attribute one speedup to a specific optimization visible in assembly.
What does -O2 actually do that -O0 does not? Build a miniature optimiser for straight-line three-address code in SSA form, and run it at four optimisation levels. Each level enables more passes, and you will see the instruction count fall the way it does in gcc -S output.
cLoading…
An OPERAND is a decimal integer (possibly negative) or a variable name. Variables that are never assigned are function parameters. Every variable may be assigned only once (SSA).
| Level | Passes (repeat all enabled passes until nothing changes) |
|---|---|
| -O0 | none |
| -O1 | constant folding, copy/constant propagation (a v = operand copy replaces every use of v, including ret), dead code elimination (keep only what the return value needs) |
| -O2 | -O1 plus algebraic identities (`x+0 0+x x |
| -O3 | -O2, then strength reduction: x * 2^k (k >= 1, constant on either side) becomes x << k |
Folding uses 64-bit C semantics, and never folds division or modulo by zero, or shifts outside 0..62. In one sweep, process instructions in order: fold, apply identities, propagate if the instruction is now a copy, then run CSE against earlier live instructions. After each sweep, run DCE. A folded or simplified instruction becomes a copy, which is then propagated and removed.
cLoading…
Surviving instructions keep their original order, and ret is not counted. Errors: VAR assigned twice (the input must be SSA) and cannot parse: LINE (both lines are ignored).
Input:
cLoading…
Output:
cLoading…
x - x, which feeds DCE).Hidden tests cover division by zero (left alone), commutative CSE, every algebraic identity, strength reduction with the constant on the left, multiplications that are not by a power of two, chains of dead code, negative constants and shifts, SSA violations, and unparsable lines.