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.
This commit is contained in:
Nick Fitzgerald
2020-06-30 11:50:10 -07:00
parent ae95ad8733
commit ee5982fd16
46 changed files with 1945 additions and 1387 deletions

View File

@@ -1,6 +1,8 @@
//! Traversals over the AST.
use crate::ast::*;
use std::fmt::Debug;
use std::hash::Hash;
/// A low-level DFS traversal event: either entering or exiting the traversal of
/// an AST node.
@@ -32,13 +34,16 @@ pub enum TraversalEvent {
/// the AST, because the `new` constructor takes anything that can convert into
/// a `DynAstRef`.
#[derive(Debug, Clone)]
pub struct Dfs<'a> {
stack: Vec<(TraversalEvent, DynAstRef<'a>)>,
pub struct Dfs<'a, TOperator> {
stack: Vec<(TraversalEvent, DynAstRef<'a, TOperator>)>,
}
impl<'a> Dfs<'a> {
impl<'a, TOperator> Dfs<'a, TOperator>
where
TOperator: Copy + Debug + Eq + Hash,
{
/// Construct a new `Dfs` traversal starting at the given `start` AST node.
pub fn new(start: impl Into<DynAstRef<'a>>) -> Self {
pub fn new(start: impl Into<DynAstRef<'a, TOperator>>) -> Self {
let start = start.into();
Dfs {
stack: vec![
@@ -49,15 +54,18 @@ impl<'a> Dfs<'a> {
}
/// Peek at the next traversal event and AST node pair, if any.
pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a>)> {
self.stack.last().cloned()
pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> {
self.stack.last().copied()
}
}
impl<'a> Iterator for Dfs<'a> {
type Item = (TraversalEvent, DynAstRef<'a>);
impl<'a, TOperator> Iterator for Dfs<'a, TOperator>
where
TOperator: Copy,
{
type Item = (TraversalEvent, DynAstRef<'a, TOperator>);
fn next(&mut self) -> Option<(TraversalEvent, DynAstRef<'a>)> {
fn next(&mut self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> {
let (event, node) = self.stack.pop()?;
if let TraversalEvent::Enter = event {
let mut enqueue_children = EnqueueChildren(self);
@@ -65,15 +73,16 @@ impl<'a> Iterator for Dfs<'a> {
}
return Some((event, node));
struct EnqueueChildren<'a, 'b>(&'b mut Dfs<'a>)
struct EnqueueChildren<'a, 'b, TOperator>(&'b mut Dfs<'a, TOperator>)
where
'a: 'b;
impl<'a, 'b> Extend<DynAstRef<'a>> for EnqueueChildren<'a, 'b>
impl<'a, 'b, TOperator> Extend<DynAstRef<'a, TOperator>> for EnqueueChildren<'a, 'b, TOperator>
where
'a: 'b,
TOperator: Copy,
{
fn extend<T: IntoIterator<Item = DynAstRef<'a>>>(&mut self, iter: T) {
fn extend<T: IntoIterator<Item = DynAstRef<'a, TOperator>>>(&mut self, iter: T) {
let iter = iter.into_iter();
let (min, max) = iter.size_hint();
@@ -97,6 +106,7 @@ impl<'a> Iterator for Dfs<'a> {
#[cfg(test)]
mod tests {
use super::*;
use peepmatic_test_operator::TestOperator;
use DynAstRef::*;
#[test]
@@ -107,7 +117,7 @@ mod tests {
(ishl $x $(log2 $C)))
";
let buf = wast::parser::ParseBuffer::new(input).expect("input should lex OK");
let ast = match wast::parser::parse::<crate::ast::Optimizations>(&buf) {
let ast = match wast::parser::parse::<crate::ast::Optimizations<TestOperator>>(&buf) {
Ok(ast) => ast,
Err(e) => panic!("expected to parse OK, got error:\n\n{}", e),
};