Expand description
Peephole rewrites: a small pattern of instructions becomes a smaller one.
The third pass, and the one that will eventually not exist. Section 9.3 of
spec/09-optimizer.md says the value level optimizer is an acyclic e-graph, and that an
e-graph replaces what would otherwise be a folding pass, a peephole pass, a GVN pass, a
reassociation pass and an instcombine pass, all with a pass ordering problem between them.
This is the peephole pass, written now because the e-graph is a milestone away and because
there is a rewrite that unblocks twelve lowering rules today.
Every rewrite here has to survive being moved into the rule set later, so each one is stated as a pattern and a replacement in its own function and nothing shares state with anything.
§The rewrites
One so far: an exclusive or of a comparison with an i1 of all ones is that comparison with
the opposite predicate. That is issue 379, and it is worth more than the instruction it saves.
C spells eight of the sixteen floating point predicates. The six relational and equality
operators give the six ordered ones, != gives une, and __builtin_isunordered gives uno.
The other eight are what the negation of one of those means, and the front end writes a
negation as an exclusive or rather than as a flipped predicate, so !(x < y) lowers to an
fcmp olt and an xor where the machine has an fcmp uge. Twelve rules in the x86-64 rule
set are written on those predicates and none of them has ever fired, over the whole torture
suite at every optimization level, because no IR that reaches selection contains one.
The integer case comes with it. !(a < b) on integers is the same shape, the same rewrite and
the same saving, and leaving it out because the coverage report did not complain about it would
be picking the rewrite by what measures it rather than by what it does.
§Why it needs dead code elimination after it
The rewrite turns the xor into the comparison and leaves the original comparison where it
was, used by nothing when the negation was its only reader. Rewriting in place keeps the
result value, so every use of it is already correct and there is nothing to rewrite, and what
is left over is exactly what crate::dce takes out. That is why the pipeline runs the two in
this order, and it is why the pass before the dead code eliminator was written first.
Structs§
- Simplify
- The pass. It holds nothing, because a peephole needs to know nothing beyond the pattern.