Files
wasmtime/cranelift/peepmatic/src/lib.rs
Nick Fitzgerald ee5982fd16 peepmatic: Be generic over the operator type
This lets us avoid the cost of `cranelift_codegen::ir::Opcode` to
`peepmatic_runtime::Operator` conversion overhead, and paves the way for
allowing Peepmatic to support non-clif optimizations (e.g. vcode optimizations).

Rather than defining our own `peepmatic::Operator` type like we used to, now the
whole `peepmatic` crate is effectively generic over a `TOperator` type
parameter. For the Cranelift integration, we use `cranelift_codegen::ir::Opcode`
as the concrete type for our `TOperator` type parameter. For testing, we also
define a `TestOperator` type, so that we can test Peepmatic code without
building all of Cranelift, and we can keep them somewhat isolated from each
other.

The methods that `peepmatic::Operator` had are now translated into trait bounds
on the `TOperator` type. These traits need to be shared between all of
`peepmatic`, `peepmatic-runtime`, and `cranelift-codegen`'s Peepmatic
integration. Therefore, these new traits live in a new crate:
`peepmatic-traits`. This crate acts as a header file of sorts for shared
trait/type/macro definitions.

Additionally, the `peepmatic-runtime` crate no longer depends on the
`peepmatic-macro` procedural macro crate, which should lead to faster build
times for Cranelift when it is using pre-built peephole optimizers.
2020-07-17 16:16:49 -07:00

195 lines
5.0 KiB
Rust

/*!
`peepmatic` is a DSL and compiler for generating peephole optimizers.
The user writes a set of optimizations in the DSL, and then `peepmatic` compiles
the set of optimizations into an efficient peephole optimizer.
*/
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
mod ast;
mod automatize;
mod dot_fmt;
mod linear_passes;
mod linearize;
mod parser;
mod traversals;
mod verify;
pub use self::{
ast::*, automatize::*, linear_passes::*, linearize::*, parser::*, traversals::*, verify::*,
};
use peepmatic_runtime::PeepholeOptimizations;
use peepmatic_traits::TypingRules;
use std::convert::TryFrom;
use std::fmt::Debug;
use std::fs;
use std::hash::Hash;
use std::num::NonZeroU32;
use std::path::Path;
/// Compile the given DSL file into a compact peephole optimizations automaton!
///
/// ## Example
///
/// ```ignore
/// # fn main() -> anyhow::Result<()> {
/// use std::path::Path;
///
/// let peep_opts = peepmatic::compile_file::<cranelift_codegen::ir::Opcode>(
/// Path::new("path/to/optimizations.peepmatic")
/// )?;
///
/// // Use the peephole optimizations or serialize them into bytes here...
/// # Ok(())
/// # }
/// ```
///
/// ## Visualizing the Peephole Optimizer's Automaton
///
/// To visualize (or debug) the peephole optimizer's automaton, set the
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
/// peephole optimizer's automaton will be written to that file path.
pub fn compile_file<TOperator>(filename: &Path) -> anyhow::Result<PeepholeOptimizations<TOperator>>
where
TOperator: Copy
+ Debug
+ Eq
+ Hash
+ for<'a> wast::parser::Parse<'a>
+ TypingRules
+ Into<NonZeroU32>
+ TryFrom<NonZeroU32>,
{
let source = fs::read_to_string(filename)?;
compile_str::<TOperator>(&source, filename)
}
/// Compile the given DSL source text down into a compact peephole optimizations
/// automaton.
///
/// This is like [compile_file][crate::compile_file] but you bring your own file
/// I/O.
///
/// The `filename` parameter is used to provide better error messages.
///
/// ## Example
///
/// ```ignore
/// # fn main() -> anyhow::Result<()> {
/// use std::path::Path;
///
/// let peep_opts = peepmatic::compile_str::<cranelift_codegen::ir::Opcode>(
/// "
/// (=> (iadd $x 0) $x)
/// (=> (imul $x 0) 0)
/// (=> (imul $x 1) $x)
/// ",
/// Path::new("my-optimizations"),
/// )?;
///
/// // Use the peephole optimizations or serialize them into bytes here...
/// # Ok(())
/// # }
/// ```
///
/// ## Visualizing the Peephole Optimizer's Automaton
///
/// To visualize (or debug) the peephole optimizer's automaton, set the
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
/// peephole optimizer's automaton will be written to that file path.
pub fn compile_str<TOperator>(
source: &str,
filename: &Path,
) -> anyhow::Result<PeepholeOptimizations<TOperator>>
where
TOperator: Copy
+ Debug
+ Eq
+ Hash
+ for<'a> wast::parser::Parse<'a>
+ TypingRules
+ Into<NonZeroU32>
+ TryFrom<NonZeroU32>,
{
let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| {
e.set_path(filename);
e.set_text(source);
e
})?;
let opts = wast::parser::parse::<Optimizations<'_, TOperator>>(&buf).map_err(|mut e| {
e.set_path(filename);
e.set_text(source);
e
})?;
verify(&opts).map_err(|mut e| {
e.set_path(filename);
e.set_text(source);
e
})?;
let mut opts = crate::linearize(&opts);
sort_least_to_most_general(&mut opts);
remove_unnecessary_nops(&mut opts);
match_in_same_order(&mut opts);
sort_lexicographically(&mut opts);
let automata = automatize(&opts);
let paths = opts.paths;
let integers = opts.integers;
if let Ok(path) = std::env::var("PEEPMATIC_DOT") {
let f = dot_fmt::PeepholeDotFmt(&paths, &integers);
if let Err(e) = automata.write_dot_file(&f, &path) {
panic!(
"failed to write GraphViz Dot file to PEEPMATIC_DOT={}; error: {}",
path, e
);
}
}
Ok(PeepholeOptimizations {
paths,
integers,
automata,
})
}
#[cfg(test)]
mod tests {
use super::*;
use peepmatic_test_operator::TestOperator;
fn assert_compiles(path: &str) {
match compile_file::<TestOperator>(Path::new(path)) {
Ok(_) => return,
Err(e) => {
eprintln!("error: {}", e);
panic!("error: {}", e);
}
}
}
#[test]
fn compile_redundant_bor() {
assert_compiles("examples/redundant-bor.peepmatic");
}
#[test]
fn mul_by_pow2() {
assert_compiles("examples/mul-by-pow2.peepmatic");
}
#[test]
fn compile_preopt() {
assert_compiles("../codegen/src/preopt.peepmatic");
}
}