Code straight out of a
The whole method is a set of local rewrite rules plus one crucial control loop: keep sweeping until nothing changes — a fixed point. It is the compiler's cleanup crew: cheap, dumb, and remarkably effective, catching the leftover clumsiness that global optimizations don't bother with and that the code generator couldn't see one statement at a time.
Picture the generated instructions in a column and a little frame covering a couple of them at a time. At each position the optimizer asks: "does what I can see match a rule?" If so, it rewrites; then it slides on. Reveal the sweep — each stop finds a different idiom to fix:
Every rewrite is justified purely by what is inside the window — no global analysis, no dataflow — which is what keeps peephole optimization fast and simple. The power comes not from any single clever rule but from having many small ones and applying them relentlessly.
Peephole rules cluster into a handful of recurring categories. A representative rule from each:
| Category | Before | After |
|---|---|---|
| Redundant load/store | LD R1, x ; ST x, R1 | LD R1, x |
| Algebraic identity | ADD R2, R2, 0 | (deleted) |
| Strength reduction | MUL R3, R3, 2 | SHL R3, R3, 1 |
| Constant folding | ADD R5, 2, 3 | LD R5, 5 |
| Dead / useless code | MOV R4, R4 | (deleted) |
| Jump-to-jump (control flow) | goto L1 ; … ; L1: goto L2 | goto L2 |
| Machine idiom | SUB R6, R6, R6 | XOR R6, R6, R6 |
Redundant load/store deletes a store whose value provably already sits in the
register. Algebraic identities (XOR R,R,R is
a genuine x86 idiom). None of them needs to understand the program — only the window.
The single most important property of peephole optimization is that one rewrite can enable
another. Delete an
The optimizer below carries a few rules — redundant store, add/sub of zero, multiply-by-two, useless
move, and jump-to-jump — and sweeps to a fixed point. The input is deliberately arranged so that
deleting an ADD …, 0 in the first pass makes a load and store adjacent, which the
second pass then collapses — the enabling cascade in action:
Pass 1 folds four idioms at once but cannot yet touch the store; pass 2 collapses the now-adjacent load/store; pass 3 finds nothing and the sweep halts. That "run again because a rewrite opened a new opportunity" is the entire discipline of peephole optimization.
Classic peephole optimizers work on a linear window and stop at block boundaries, but the idea generalises. Superoptimizers take the peephole concept to its logical extreme: given a short instruction sequence, search all possible shorter sequences and keep any that is provably equivalent — finding rewrites no human rule-writer anticipated. Peephole rules can also be learned or verified with an SMT solver to guarantee each rewrite preserves semantics on every input. But the humble sliding-window optimizer with a hand-written rule set remains a fixture of every back end precisely because it is so cheap and catches so much of the low-hanging fruit that earlier phases leave behind.
The dangerous peephole bug is rewriting across a label. If some instruction inside
your window is the target of a jump from elsewhere, control can enter the window in the
middle — so a rule that assumes the window runs top-to-bottom (like "delete this store because
the value is still in the register") is simply wrong: a branch might reach the
store without ever executing the load. Never delete or merge instructions across a label unless you
have confirmed nothing branches into the region. This is the peephole echo of the
The second discipline is iteration. Because rewrites enable one another, a single pass leaves easy wins on the table. Always loop to a fixed point — but bound the loop (each rewrite must make measurable progress) so a badly-written pair of rules that undo each other can't spin forever.