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

@@ -2,10 +2,10 @@
use crate::instruction_set::InstructionSet;
use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult};
use crate::operator::UnquoteOperator;
use crate::optimizations::PeepholeOptimizations;
use crate::part::{Constant, Part};
use crate::r#type::{BitWidth, Type};
use crate::unquote::UnquoteOperator;
use peepmatic_automata::State;
use std::convert::TryFrom;
use std::fmt::{self, Debug};
@@ -21,20 +21,20 @@ use std::num::NonZeroU32;
/// Reusing an instance when applying peephole optimizations to different
/// instruction sequences means that you reuse internal allocations that are
/// used to match left-hand sides and build up right-hand sides.
pub struct PeepholeOptimizer<'peep, 'ctx, I>
pub struct PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
where
I: InstructionSet<'ctx>,
TInstructionSet: InstructionSet<'ctx>,
{
pub(crate) peep_opt: &'peep PeepholeOptimizations,
pub(crate) instr_set: I,
pub(crate) right_hand_sides: Vec<Part<I::Instruction>>,
pub(crate) actions: Vec<Action>,
pub(crate) peep_opt: &'peep PeepholeOptimizations<TInstructionSet::Operator>,
pub(crate) instr_set: TInstructionSet,
pub(crate) right_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
pub(crate) actions: Vec<Action<TInstructionSet::Operator>>,
pub(crate) backtracking_states: Vec<(State, usize)>,
}
impl<'peep, 'ctx, I> Debug for PeepholeOptimizer<'peep, 'ctx, I>
impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
where
I: InstructionSet<'ctx>,
TInstructionSet: InstructionSet<'ctx>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let PeepholeOptimizer {
@@ -54,9 +54,9 @@ where
}
}
impl<'peep, 'ctx, I> PeepholeOptimizer<'peep, 'ctx, I>
impl<'peep, 'ctx, TInstructionSet> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
where
I: InstructionSet<'ctx>,
TInstructionSet: InstructionSet<'ctx>,
{
fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant {
use Constant::*;
@@ -107,7 +107,11 @@ where
}
}
fn eval_actions(&mut self, context: &mut I::Context, root: I::Instruction) {
fn eval_actions(
&mut self,
context: &mut TInstructionSet::Context,
root: TInstructionSet::Instruction,
) {
let mut actions = mem::replace(&mut self.actions, vec![]);
for action in actions.drain(..) {
@@ -272,8 +276,8 @@ where
fn eval_match_op(
&mut self,
context: &mut I::Context,
root: I::Instruction,
context: &mut TInstructionSet::Context,
root: TInstructionSet::Instruction,
match_op: MatchOp,
) -> MatchResult {
use crate::linear::MatchOp::*;
@@ -288,13 +292,7 @@ where
.ok_or(Else)?;
let inst = part.as_instruction().ok_or(Else)?;
let op = self.instr_set.operator(context, inst).ok_or(Else)?;
let op = op as u32;
debug_assert!(
op != 0,
"`Operator` doesn't have any variant represented
with zero"
);
Ok(unsafe { NonZeroU32::new_unchecked(op as u32) })
Ok(op.into())
}
IsConst { path } => {
let path = self.peep_opt.paths.lookup(path);
@@ -477,9 +475,9 @@ where
/// untouched and `None` is returned.
pub fn apply_one(
&mut self,
context: &mut I::Context,
root: I::Instruction,
) -> Option<I::Instruction> {
context: &mut TInstructionSet::Context,
root: TInstructionSet::Instruction,
) -> Option<TInstructionSet::Instruction> {
log::trace!("PeepholeOptimizer::apply_one");
self.backtracking_states.clear();
@@ -566,7 +564,11 @@ where
/// Keep applying peephole optimizations to the given instruction until none
/// can be applied anymore.
pub fn apply_all(&mut self, context: &mut I::Context, mut inst: I::Instruction) {
pub fn apply_all(
&mut self,
context: &mut TInstructionSet::Context,
mut inst: TInstructionSet::Instruction,
) {
loop {
if let Some(new_inst) = self.apply_one(context, inst) {
inst = new_inst;