peepmatic
peepmatic is a DSL and compiler for peephole optimizers for
Cranelift.
- [About](#about)
- [Example](#example)
- [A DSL for Optimizations](#a-dsl-for-optimizations)
- [Variables](#variables)
- [Constants](#constants)
- [Nested Patterns](#nested-patterns)
- [Preconditions and Unquoting](#preconditions-and-unquoting)
- [Bit Widths](#bit-widths)
- [Implementation](#implementation)
- [Parsing](#parsing)
- [Type Checking](#type-checking)
- [Linearization](#linearization)
- [Automatization](#automatization)
- [References](#references)
- [Acknowledgments](#acknowledgments)
## About
Peepmatic is a DSL for peephole optimizations and compiler for generating
peephole optimizers from them. The user writes a set of optimizations in the
DSL, and then `peepmatic` compiles the set of optimizations into an efficient
peephole optimizer:
```
DSL ----peepmatic----> Peephole Optimizer
```
The generated peephole optimizer has all of its optimizations' left-hand sides
collapsed into a compact automata that makes matching candidate instruction
sequences fast.
The DSL's optimizations may be written by hand or discovered mechanically with a
superoptimizer like [Souper][]. Eventually, `peepmatic` should have a verifier
that ensures that the DSL's optimizations are sound, similar to what [Alive][]
does for LLVM optimizations.
Currently, `peepmatic` is targeting peephole optimizers that operate on
Cranelift's clif intermediate representation. The intended next target is
Cranelift's new backend's "vcode" intermediate representation. Supporting
non-Cranelift targets is not a goal.
[Cranelift]: https://github.com/bytecodealliance/wasmtime/tree/main/cranelift#readme
[Souper]: https://github.com/google/souper
[Alive]: https://github.com/AliveToolkit/alive2
## Example
This snippet of our DSL describes optimizations for removing redundant
bitwise-or instructions that are no-ops:
```lisp
(=> (bor $x (bor $x $y))
(bor $x $y))
(=> (bor $y (bor $x $y))
(bor $x $y))
(=> (bor (bor $x $y) $x)
(bor $x $y))
(=> (bor (bor $x $y) $y)
(bor $x $y))
```
When compiled into a peephole optimizer automaton, they look like this:

## A DSL for Optimizations
A single peephole optimization has two parts:
1. A **left-hand side** that describes candidate instruction sequences that the
optimization applies to.
2. A **right-hand side** that contains the new instruction sequence that
replaces old instruction sequences that the left-hand side matched.
A left-hand side may bind sub-expressions to variables and the right-hand side
may contain those bound variables to reuse the sub-expressions. The operations
inside the left-hand and right-hand sides are a subset of clif operations.
Let's take a look at an example:
```lisp
(=> (imul $x 2)
(ishl $x 1))
```
As you can see, the DSL uses S-expressions. (S-expressions are easy to parse and
we also have a bunch of nice parsing infrastructure for S-expressions already
for our [`wat`][wat] and [`wast`][wast] crates.)
[wat]: https://crates.io/crates/wat
[wast]: https://crates.io/crates/wast
The left-hand side of this optimization is `(imul $x 2)`. It matches integer
multiplication operations where a value is multiplied by the constant two. The
value multiplied by two is bound to the variable `$x`.
The right-hand side of this optimization is `(ishl $x 1)`. It reuses the `$x`
variable that was bound in the left-hand side.
This optimization replaces expressions of the form `x * 2` with `x << 1`. This
is sound because multiplication by two is the same as shifting left by one for
binary integers, and it is desirable because a shift-left instruction executes
in fewer cycles than a multiplication.
The general form of an optimization is:
```lisp
(=>