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

@@ -84,7 +84,10 @@ mod tok {
custom_keyword!(nof);
}
impl<'a> Parse<'a> for Optimizations<'a> {
impl<'a, TOperator> Parse<'a> for Optimizations<'a, TOperator>
where
TOperator: Parse<'a>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
let mut optimizations = vec![];
@@ -98,7 +101,10 @@ impl<'a> Parse<'a> for Optimizations<'a> {
}
}
impl<'a> Parse<'a> for Optimization<'a> {
impl<'a, TOperator> Parse<'a> for Optimization<'a, TOperator>
where
TOperator: Parse<'a>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
p.parens(|p| {
@@ -110,7 +116,10 @@ impl<'a> Parse<'a> for Optimization<'a> {
}
}
impl<'a> Parse<'a> for Lhs<'a> {
impl<'a, TOperator> Parse<'a> for Lhs<'a, TOperator>
where
TOperator: Parse<'a>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
let mut preconditions = vec![];
@@ -139,30 +148,36 @@ impl<'a> Parse<'a> for Lhs<'a> {
}
}
impl<'a> Parse<'a> for Pattern<'a> {
impl<'a, TOperator> Parse<'a> for Pattern<'a, TOperator>
where
TOperator: Parse<'a>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
if p.peek::<ValueLiteral>() {
if p.peek::<ValueLiteral<TOperator>>() {
return Ok(Pattern::ValueLiteral(p.parse()?));
}
if p.peek::<Constant>() {
if p.peek::<Constant<TOperator>>() {
return Ok(Pattern::Constant(p.parse()?));
}
if p.peek::<Operation<Self>>() {
if p.peek::<Operation<TOperator, Self>>() {
return Ok(Pattern::Operation(p.parse()?));
}
if p.peek::<Variable>() {
if p.peek::<Variable<TOperator>>() {
return Ok(Pattern::Variable(p.parse()?));
}
Err(p.error("expected a left-hand side pattern"))
}
}
impl<'a> Peek for Pattern<'a> {
impl<'a, TOperator> Peek for Pattern<'a, TOperator>
where
TOperator: 'a,
{
fn peek(c: Cursor) -> bool {
ValueLiteral::peek(c)
|| Constant::peek(c)
|| Variable::peek(c)
|| Operation::<Self>::peek(c)
ValueLiteral::<TOperator>::peek(c)
|| Constant::<TOperator>::peek(c)
|| Variable::<TOperator>::peek(c)
|| Operation::<TOperator, Self>::peek(c)
}
fn display() -> &'static str {
@@ -170,24 +185,26 @@ impl<'a> Peek for Pattern<'a> {
}
}
impl<'a> Parse<'a> for ValueLiteral<'a> {
impl<'a, TOperator> Parse<'a> for ValueLiteral<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
if let Ok(b) = p.parse::<Boolean>() {
if let Ok(b) = p.parse::<Boolean<TOperator>>() {
return Ok(ValueLiteral::Boolean(b));
}
if let Ok(i) = p.parse::<Integer>() {
if let Ok(i) = p.parse::<Integer<TOperator>>() {
return Ok(ValueLiteral::Integer(i));
}
if let Ok(cc) = p.parse::<ConditionCode>() {
if let Ok(cc) = p.parse::<ConditionCode<TOperator>>() {
return Ok(ValueLiteral::ConditionCode(cc));
}
Err(p.error("expected an integer or boolean or condition code literal"))
}
}
impl<'a> Peek for ValueLiteral<'a> {
impl<'a, TOperator> Peek for ValueLiteral<'a, TOperator> {
fn peek(c: Cursor) -> bool {
c.integer().is_some() || Boolean::peek(c) || ConditionCode::peek(c)
c.integer().is_some()
|| Boolean::<TOperator>::peek(c)
|| ConditionCode::<TOperator>::peek(c)
}
fn display() -> &'static str {
@@ -195,7 +212,7 @@ impl<'a> Peek for ValueLiteral<'a> {
}
}
impl<'a> Parse<'a> for Integer<'a> {
impl<'a, TOperator> Parse<'a> for Integer<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
p.step(|c| {
@@ -221,7 +238,7 @@ impl<'a> Parse<'a> for Integer<'a> {
}
}
impl<'a> Parse<'a> for Boolean<'a> {
impl<'a, TOperator> Parse<'a> for Boolean<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
if p.parse::<tok::r#true>().is_ok() {
@@ -244,7 +261,7 @@ impl<'a> Parse<'a> for Boolean<'a> {
}
}
impl<'a> Peek for Boolean<'a> {
impl<'a, TOperator> Peek for Boolean<'a, TOperator> {
fn peek(c: Cursor) -> bool {
<tok::r#true as Peek>::peek(c) || <tok::r#false as Peek>::peek(c)
}
@@ -254,7 +271,7 @@ impl<'a> Peek for Boolean<'a> {
}
}
impl<'a> Parse<'a> for ConditionCode<'a> {
impl<'a, TOperator> Parse<'a> for ConditionCode<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
@@ -292,7 +309,7 @@ impl<'a> Parse<'a> for ConditionCode<'a> {
}
}
impl<'a> Peek for ConditionCode<'a> {
impl<'a, TOperator> Peek for ConditionCode<'a, TOperator> {
fn peek(c: Cursor) -> bool {
macro_rules! peek_cc {
( $( $token:ident, )* ) => {
@@ -321,7 +338,7 @@ impl<'a> Peek for ConditionCode<'a> {
}
}
impl<'a> Parse<'a> for Constant<'a> {
impl<'a, TOperator> Parse<'a> for Constant<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
let id = Id::parse(p)?;
@@ -330,7 +347,11 @@ impl<'a> Parse<'a> for Constant<'a> {
.chars()
.all(|c| !c.is_alphabetic() || c.is_uppercase())
{
Ok(Constant { span, id })
Ok(Constant {
span,
id,
marker: PhantomData,
})
} else {
let upper = id
.name()
@@ -345,7 +366,7 @@ impl<'a> Parse<'a> for Constant<'a> {
}
}
impl<'a> Peek for Constant<'a> {
impl<'a, TOperator> Peek for Constant<'a, TOperator> {
fn peek(c: Cursor) -> bool {
if let Some((id, _rest)) = c.id() {
id.chars().all(|c| !c.is_alphabetic() || c.is_uppercase())
@@ -359,7 +380,7 @@ impl<'a> Peek for Constant<'a> {
}
}
impl<'a> Parse<'a> for Variable<'a> {
impl<'a, TOperator> Parse<'a> for Variable<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
let id = Id::parse(p)?;
@@ -368,7 +389,11 @@ impl<'a> Parse<'a> for Variable<'a> {
.chars()
.all(|c| !c.is_alphabetic() || c.is_lowercase())
{
Ok(Variable { span, id })
Ok(Variable {
span,
id,
marker: PhantomData,
})
} else {
let lower = id
.name()
@@ -383,7 +408,7 @@ impl<'a> Parse<'a> for Variable<'a> {
}
}
impl<'a> Peek for Variable<'a> {
impl<'a, TOperator> Peek for Variable<'a, TOperator> {
fn peek(c: Cursor) -> bool {
if let Some((id, _rest)) = c.id() {
id.chars().all(|c| !c.is_alphabetic() || c.is_lowercase())
@@ -397,10 +422,11 @@ impl<'a> Peek for Variable<'a> {
}
}
impl<'a, T> Parse<'a> for Operation<'a, T>
impl<'a, TOperator, TOperand> Parse<'a> for Operation<'a, TOperator, TOperand>
where
T: 'a + Ast<'a> + Peek + Parse<'a>,
DynAstRef<'a>: From<&'a T>,
TOperator: Parse<'a>,
TOperand: 'a + Ast<'a, TOperator> + Peek + Parse<'a>,
DynAstRef<'a, TOperator>: From<&'a TOperand>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
@@ -417,7 +443,7 @@ where
});
let mut operands = vec![];
while p.peek::<T>() {
while p.peek::<TOperand>() {
operands.push(p.parse()?);
}
Ok(Operation {
@@ -431,10 +457,10 @@ where
}
}
impl<'a, T> Peek for Operation<'a, T>
impl<'a, TOperator, TOperand> Peek for Operation<'a, TOperator, TOperand>
where
T: 'a + Ast<'a>,
DynAstRef<'a>: From<&'a T>,
TOperand: 'a + Ast<'a, TOperator>,
DynAstRef<'a, TOperator>: From<&'a TOperand>,
{
fn peek(c: Cursor) -> bool {
wast::LParen::peek(c)
@@ -445,19 +471,20 @@ where
}
}
impl<'a> Parse<'a> for Precondition<'a> {
impl<'a, TOperator> Parse<'a> for Precondition<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
p.parens(|p| {
let constraint = p.parse()?;
let mut operands = vec![];
while p.peek::<ConstraintOperand>() {
while p.peek::<ConstraintOperand<TOperator>>() {
operands.push(p.parse()?);
}
Ok(Precondition {
span,
constraint,
operands,
marker: PhantomData,
})
})
}
@@ -481,24 +508,26 @@ impl<'a> Parse<'a> for Constraint {
}
}
impl<'a> Parse<'a> for ConstraintOperand<'a> {
impl<'a, TOperator> Parse<'a> for ConstraintOperand<'a, TOperator> {
fn parse(p: Parser<'a>) -> ParseResult<Self> {
if p.peek::<ValueLiteral>() {
if p.peek::<ValueLiteral<TOperator>>() {
return Ok(ConstraintOperand::ValueLiteral(p.parse()?));
}
if p.peek::<Constant>() {
if p.peek::<Constant<TOperator>>() {
return Ok(ConstraintOperand::Constant(p.parse()?));
}
if p.peek::<Variable>() {
if p.peek::<Variable<TOperator>>() {
return Ok(ConstraintOperand::Variable(p.parse()?));
}
Err(p.error("expected an operand for precondition constraint"))
}
}
impl<'a> Peek for ConstraintOperand<'a> {
impl<'a, TOperator> Peek for ConstraintOperand<'a, TOperator> {
fn peek(c: Cursor) -> bool {
ValueLiteral::peek(c) || Constant::peek(c) || Variable::peek(c)
ValueLiteral::<TOperator>::peek(c)
|| Constant::<TOperator>::peek(c)
|| Variable::<TOperator>::peek(c)
}
fn display() -> &'static str {
@@ -506,34 +535,40 @@ impl<'a> Peek for ConstraintOperand<'a> {
}
}
impl<'a> Parse<'a> for Rhs<'a> {
impl<'a, TOperator> Parse<'a> for Rhs<'a, TOperator>
where
TOperator: Parse<'a>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
if p.peek::<ValueLiteral>() {
if p.peek::<ValueLiteral<TOperator>>() {
return Ok(Rhs::ValueLiteral(p.parse()?));
}
if p.peek::<Constant>() {
if p.peek::<Constant<TOperator>>() {
return Ok(Rhs::Constant(p.parse()?));
}
if p.peek::<Variable>() {
if p.peek::<Variable<TOperator>>() {
return Ok(Rhs::Variable(p.parse()?));
}
if p.peek::<Unquote>() {
if p.peek::<Unquote<TOperator>>() {
return Ok(Rhs::Unquote(p.parse()?));
}
if p.peek::<Operation<Self>>() {
if p.peek::<Operation<TOperator, Self>>() {
return Ok(Rhs::Operation(p.parse()?));
}
Err(p.error("expected a right-hand side replacement"))
}
}
impl<'a> Peek for Rhs<'a> {
impl<'a, TOperator> Peek for Rhs<'a, TOperator>
where
TOperator: 'a,
{
fn peek(c: Cursor) -> bool {
ValueLiteral::peek(c)
|| Constant::peek(c)
|| Variable::peek(c)
|| Unquote::peek(c)
|| Operation::<Self>::peek(c)
ValueLiteral::<TOperator>::peek(c)
|| Constant::<TOperator>::peek(c)
|| Variable::<TOperator>::peek(c)
|| Unquote::<TOperator>::peek(c)
|| Operation::<TOperator, Self>::peek(c)
}
fn display() -> &'static str {
@@ -541,26 +576,30 @@ impl<'a> Peek for Rhs<'a> {
}
}
impl<'a> Parse<'a> for Unquote<'a> {
impl<'a, TOperator> Parse<'a> for Unquote<'a, TOperator>
where
TOperator: Parse<'a>,
{
fn parse(p: Parser<'a>) -> ParseResult<Self> {
let span = p.cur_span();
p.parse::<tok::dollar>()?;
p.parens(|p| {
let operator = p.parse()?;
let mut operands = vec![];
while p.peek::<Rhs>() {
while p.peek::<Rhs<TOperator>>() {
operands.push(p.parse()?);
}
Ok(Unquote {
span,
operator,
operands,
marker: PhantomData,
})
})
}
}
impl<'a> Peek for Unquote<'a> {
impl<'a, TOperator> Peek for Unquote<'a, TOperator> {
fn peek(c: Cursor) -> bool {
tok::dollar::peek(c)
}
@@ -573,7 +612,7 @@ impl<'a> Peek for Unquote<'a> {
#[cfg(test)]
mod test {
use super::*;
use peepmatic_runtime::operator::Operator;
use peepmatic_test_operator::TestOperator;
macro_rules! test_parse {
(
@@ -623,7 +662,7 @@ mod test {
}
test_parse! {
parse_boolean<Boolean> {
parse_boolean<Boolean<TestOperator>> {
ok {
"true",
"false",
@@ -641,7 +680,7 @@ mod test {
"falsezzz",
}
}
parse_cc<ConditionCode> {
parse_cc<ConditionCode<TestOperator>> {
ok {
"eq",
"ne",
@@ -661,7 +700,7 @@ mod test {
"neq",
}
}
parse_constant<Constant> {
parse_constant<Constant<TestOperator>> {
ok {
"$C",
"$C1",
@@ -693,7 +732,7 @@ mod test {
"imul",
}
}
parse_constraint_operand<ConstraintOperand> {
parse_constraint_operand<ConstraintOperand<TestOperator>> {
ok {
"1234",
"true",
@@ -707,7 +746,7 @@ mod test {
"(iadd 1 2)",
}
}
parse_integer<Integer> {
parse_integer<Integer<TestOperator>> {
ok {
"0",
"1",
@@ -746,7 +785,7 @@ mod test {
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
}
}
parse_lhs<Lhs> {
parse_lhs<Lhs<TestOperator>> {
ok {
"(when (imul $C1 $C2) (is-power-of-two $C1) (is-power-of-two $C2))",
"(when (imul $x $C) (is-power-of-two $C))",
@@ -762,7 +801,7 @@ mod test {
"abc",
}
}
parse_operation_pattern<Operation<Pattern>> {
parse_operation_pattern<Operation<TestOperator, Pattern<TestOperator>>> {
ok {
"(iadd)",
"(iadd 1)",
@@ -779,7 +818,7 @@ mod test {
"(ishl $x $(log2 $C))",
}
}
parse_operation_rhs<Operation<Rhs>> {
parse_operation_rhs<Operation<TestOperator, Rhs<TestOperator>>> {
ok {
"(iadd)",
"(iadd 1)",
@@ -793,7 +832,7 @@ mod test {
"$CONST",
}
}
parse_operator<Operator> {
parse_operator<TestOperator> {
ok {
"bor",
"iadd",
@@ -812,7 +851,7 @@ mod test {
"iadd{i32}",
}
}
parse_optimization<Optimization> {
parse_optimization<Optimization<TestOperator>> {
ok {
"(=> (when (iadd $x $C) (is-power-of-two $C) (is-power-of-two $C)) (iadd $C $x))",
"(=> (when (iadd $x $C)) (iadd $C $x))",
@@ -825,7 +864,7 @@ mod test {
"(=> () ())",
}
}
parse_optimizations<Optimizations> {
parse_optimizations<Optimizations<TestOperator>> {
ok {
"",
r#"
@@ -844,7 +883,7 @@ mod test {
"#,
}
}
parse_pattern<Pattern> {
parse_pattern<Pattern<TestOperator>> {
ok {
"1234",
"$C",
@@ -857,7 +896,7 @@ mod test {
"abc",
}
}
parse_precondition<Precondition> {
parse_precondition<Precondition<TestOperator>> {
ok {
"(is-power-of-two)",
"(is-power-of-two $C)",
@@ -871,7 +910,7 @@ mod test {
"$CONST",
}
}
parse_rhs<Rhs> {
parse_rhs<Rhs<TestOperator>> {
ok {
"5",
"$C",
@@ -884,7 +923,7 @@ mod test {
"()",
}
}
parse_unquote<Unquote> {
parse_unquote<Unquote<TestOperator>> {
ok {
"$(log2)",
"$(log2 $C)",
@@ -899,7 +938,7 @@ mod test {
"$()",
}
}
parse_value_literal<ValueLiteral> {
parse_value_literal<ValueLiteral<TestOperator>> {
ok {
"12345",
"true",
@@ -911,7 +950,7 @@ mod test {
"12.34",
}
}
parse_variable<Variable> {
parse_variable<Variable<TestOperator>> {
ok {
"$v",
"$v1",