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,6 @@
[package]
name = "peepmatic-fuzzing"
version = "0.2.0"
version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018"
publish = false
@@ -17,6 +17,8 @@ peepmatic = { path = "../.." }
peepmatic-automata = { path = "../automata", features = ["serde"] }
peepmatic-runtime = { path = "../runtime", features = ["construct"] }
peepmatic-test = { path = "../test" }
peepmatic-test-operator = { path = "../test-operator" }
peepmatic-traits = { path = "../traits" }
rand = { version = "0.7.3", features = ["small_rng"] }
serde = "1.0.106"
wast = "15.0.0"

View File

@@ -1,6 +1,7 @@
//! Fuzz testing utilities related to AST pattern matching.
use peepmatic_runtime::PeepholeOptimizations;
use peepmatic_test_operator::TestOperator;
use std::path::Path;
use std::str;
@@ -19,18 +20,18 @@ pub fn compile(data: &[u8]) {
Ok(s) => s,
};
let opt = match peepmatic::compile_str(source, Path::new("fuzz")) {
let opt = match peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz")) {
Err(_) => return,
Ok(o) => o,
};
// Should be able to serialize and deserialize the peephole optimizer.
let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK");
let _: PeepholeOptimizations =
let _: PeepholeOptimizations<TestOperator> =
bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK");
// Compiling the same source text again should be deterministic.
let opt2 = peepmatic::compile_str(source, Path::new("fuzz"))
let opt2 = peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz"))
.expect("should be able to compile source text again, if it compiled OK the first time");
let opt2_bytes =
bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK");

View File

@@ -6,12 +6,13 @@ use peepmatic::{
};
use peepmatic_runtime::{
cc::ConditionCode,
operator::TypingContext as TypingContextTrait,
part::Constant,
r#type::BitWidth,
r#type::{Kind, Type},
};
use peepmatic_test::{Program, TestIsa};
use peepmatic_test_operator::TestOperator;
use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules};
use std::collections::{BTreeMap, HashMap};
use std::path::Path;
use std::str;
@@ -37,7 +38,7 @@ pub fn interp(data: &[u8]) {
// Okay, we know it compiles and verifies alright, so (re)parse the AST.
let buf = wast::parser::ParseBuffer::new(&source).unwrap();
let ast = wast::parser::parse::<Optimizations>(&buf).unwrap();
let ast = wast::parser::parse::<Optimizations<TestOperator>>(&buf).unwrap();
// And we need access to the assigned types, so re-verify it as well.
peepmatic::verify(&ast).unwrap();
@@ -87,7 +88,7 @@ pub fn interp(data: &[u8]) {
// Generate this operation's immediates.
let mut imm_tys = vec![];
op.operator
.immediate_types(&mut TypingContext, op.span(), &mut imm_tys);
.immediate_types((), &mut TypingContext, &mut imm_tys);
let imms: Vec<_> = op
.operands
.iter()
@@ -121,7 +122,7 @@ pub fn interp(data: &[u8]) {
// this operation's arguments.
let mut arg_tys = vec![];
op.operator
.param_types(&mut TypingContext, op.span(), &mut arg_tys);
.parameter_types((), &mut TypingContext, &mut arg_tys);
let args: Vec<_> = op
.operands
.iter()
@@ -165,7 +166,7 @@ pub fn interp(data: &[u8]) {
})
.collect();
let ty = match op.operator.result_type(&mut TypingContext, op.span()) {
let ty = match op.operator.result_type((), &mut TypingContext) {
TypeOrConditionCode::Type(ty) => ty,
TypeOrConditionCode::ConditionCode => {
unreachable!("condition codes cannot be operation results")
@@ -206,41 +207,42 @@ enum TypeOrConditionCode {
struct TypingContext;
impl<'a> TypingContextTrait<'a> for TypingContext {
type Span = ();
type TypeVariable = TypeOrConditionCode;
fn cc(&mut self, _: wast::Span) -> Self::TypeVariable {
fn cc(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::ConditionCode
}
fn bNN(&mut self, _: wast::Span) -> Self::TypeVariable {
fn bNN(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::b1())
}
fn iNN(&mut self, _: wast::Span) -> Self::TypeVariable {
fn iNN(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::i32())
}
fn iMM(&mut self, _: wast::Span) -> Self::TypeVariable {
fn iMM(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::i32())
}
fn cpu_flags(&mut self, _: wast::Span) -> Self::TypeVariable {
fn cpu_flags(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::cpu_flags())
}
fn b1(&mut self, _: wast::Span) -> Self::TypeVariable {
fn b1(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::b1())
}
fn void(&mut self, _: wast::Span) -> Self::TypeVariable {
fn void(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::void())
}
fn bool_or_int(&mut self, _: wast::Span) -> Self::TypeVariable {
fn bool_or_int(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::b1())
}
fn any_t(&mut self, _: wast::Span) -> Self::TypeVariable {
fn any_t(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::i32())
}
}

View File

@@ -1,6 +1,7 @@
//! Utilities for fuzzing our DSL's parser.
use peepmatic::Optimizations;
use peepmatic_test_operator::TestOperator;
use std::str;
/// Attempt to parse the given string as if it were a snippet of our DSL.
@@ -15,7 +16,7 @@ pub fn parse(data: &[u8]) {
Err(_) => return,
};
let _ = wast::parser::parse::<Optimizations>(&buf);
let _ = wast::parser::parse::<Optimizations<TestOperator>>(&buf);
}
#[cfg(test)]