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:
@@ -7,17 +7,22 @@
|
||||
|
||||
use crate::cc::ConditionCode;
|
||||
use crate::integer_interner::{IntegerId, IntegerInterner};
|
||||
use crate::operator::{Operator, UnquoteOperator};
|
||||
use crate::paths::{PathId, PathInterner};
|
||||
use crate::r#type::{BitWidth, Type};
|
||||
use crate::unquote::UnquoteOperator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// A set of linear optimizations.
|
||||
#[derive(Debug)]
|
||||
pub struct Optimizations {
|
||||
pub struct Optimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The linear optimizations.
|
||||
pub optimizations: Vec<Optimization>,
|
||||
pub optimizations: Vec<Optimization<TOperator>>,
|
||||
|
||||
/// The de-duplicated paths referenced by these optimizations.
|
||||
pub paths: PathInterner,
|
||||
@@ -28,9 +33,12 @@ pub struct Optimizations {
|
||||
|
||||
/// A linearized optimization.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Optimization {
|
||||
pub struct Optimization<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The chain of increments for this optimization.
|
||||
pub increments: Vec<Increment>,
|
||||
pub increments: Vec<Increment<TOperator>>,
|
||||
}
|
||||
|
||||
/// Match any value.
|
||||
@@ -63,7 +71,10 @@ pub fn bool_to_match_result(b: bool) -> MatchResult {
|
||||
/// basically become a state and a transition edge out of that state in the
|
||||
/// final automata.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Increment {
|
||||
pub struct Increment<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The matching operation to perform.
|
||||
pub operation: MatchOp,
|
||||
|
||||
@@ -74,7 +85,7 @@ pub struct Increment {
|
||||
|
||||
/// Actions to perform, given that the operation resulted in the expected
|
||||
/// value.
|
||||
pub actions: Vec<Action>,
|
||||
pub actions: Vec<Action<TOperator>>,
|
||||
}
|
||||
|
||||
/// A matching operation to be performed on some Cranelift instruction as part
|
||||
@@ -163,7 +174,7 @@ pub struct RhsId(pub u16);
|
||||
/// When evaluating actions, the `i^th` action implicitly defines the
|
||||
/// `RhsId(i)`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
pub enum Action<TOperator> {
|
||||
/// Reuse something from the left-hand side.
|
||||
GetLhs {
|
||||
/// The path to the instruction or value.
|
||||
@@ -215,13 +226,13 @@ pub enum Action {
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operator for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
},
|
||||
|
||||
/// Make a binary instruction.
|
||||
MakeBinaryInst {
|
||||
/// The opcode for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operands for this instruction.
|
||||
@@ -231,7 +242,7 @@ pub enum Action {
|
||||
/// Make a ternary instruction.
|
||||
MakeTernaryInst {
|
||||
/// The opcode for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operands for this instruction.
|
||||
@@ -242,6 +253,7 @@ pub enum Action {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
|
||||
// These types all end up in the automaton, so we should take care that they
|
||||
// are small and don't fill up the data cache (or take up too much
|
||||
@@ -259,6 +271,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn action_size() {
|
||||
assert_eq!(std::mem::size_of::<Action>(), 16);
|
||||
assert_eq!(std::mem::size_of::<Action<TestOperator>>(), 16);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user