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.
195 lines
5.0 KiB
Rust
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");
|
|
}
|
|
}
|