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:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-runtime"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -12,13 +12,14 @@ description = "Runtime support for peepmatic peephole optimizers"
|
||||
bincode = "1.2.1"
|
||||
bumpalo = "3.2.0"
|
||||
log = "0.4.8"
|
||||
peepmatic-automata = { version = "0.2.0", path = "../automata", features = ["serde"] }
|
||||
peepmatic-macro = { version = "0.2.0", path = "../macro" }
|
||||
peepmatic-automata = { version = "0.66.0", path = "../automata", features = ["serde"] }
|
||||
peepmatic-traits = { version = "0.66.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
thiserror = "1.0.15"
|
||||
wast = { version = "15.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic-test-operator = { version = "0.66.0", path = "../test-operator" }
|
||||
serde_test = "1.0.114"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! Interfacing with actual instructions.
|
||||
|
||||
use crate::operator::Operator;
|
||||
use crate::part::{Constant, Part};
|
||||
use crate::paths::Path;
|
||||
use crate::r#type::Type;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// A trait for interfacing with actual instruction sequences.
|
||||
///
|
||||
@@ -32,6 +33,9 @@ pub unsafe trait InstructionSet<'a> {
|
||||
/// implementation.
|
||||
type Context;
|
||||
|
||||
/// An operator.
|
||||
type Operator: 'static + Copy + Debug + Eq + Hash + Into<NonZeroU32>;
|
||||
|
||||
/// An instruction (or identifier for an instruction).
|
||||
type Instruction: Copy + Debug + Eq;
|
||||
|
||||
@@ -64,10 +68,12 @@ pub unsafe trait InstructionSet<'a> {
|
||||
|
||||
/// Get the given instruction's operator.
|
||||
///
|
||||
/// If the instruction's opcode does not have an associated
|
||||
/// `peepmatic_runtime::operator::Operator` variant (i.e. that instruction
|
||||
/// isn't supported by `peepmatic` yet) then `None` should be returned.
|
||||
fn operator(&self, context: &mut Self::Context, instr: Self::Instruction) -> Option<Operator>;
|
||||
/// If the instruction isn't supported, then `None` should be returned.
|
||||
fn operator(
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
instr: Self::Instruction,
|
||||
) -> Option<Self::Operator>;
|
||||
|
||||
/// Make a unary instruction.
|
||||
///
|
||||
@@ -76,7 +82,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
) -> Self::Instruction;
|
||||
@@ -92,7 +98,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
b: Part<Self::Instruction>,
|
||||
@@ -108,7 +114,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
b: Part<Self::Instruction>,
|
||||
|
||||
@@ -22,12 +22,12 @@ pub mod error;
|
||||
pub mod instruction_set;
|
||||
pub mod integer_interner;
|
||||
pub mod linear;
|
||||
pub mod operator;
|
||||
pub mod optimizations;
|
||||
pub mod optimizer;
|
||||
pub mod part;
|
||||
pub mod paths;
|
||||
pub mod r#type;
|
||||
pub mod unquote;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use optimizations::PeepholeOptimizations;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
//! Operator definitions.
|
||||
|
||||
use peepmatic_macro::PeepmaticOperator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An operator.
|
||||
///
|
||||
/// These are a subset of Cranelift IR's operators.
|
||||
///
|
||||
/// ## Caveats for Branching and Trapping Operators
|
||||
///
|
||||
/// Branching operators are not fully modeled: we do not represent their label
|
||||
/// and jump arguments. It is up to the interpreter doing the instruction
|
||||
/// replacement to recognize when we are replacing one branch with another, and
|
||||
/// copy over the extra information.
|
||||
///
|
||||
/// Affected operations: `brz`, `brnz`, `trapz`, `trapnz`.
|
||||
#[derive(PeepmaticOperator, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum Operator {
|
||||
/// `adjust_sp_down`
|
||||
#[peepmatic(params(iNN), result(void))]
|
||||
// NB: We convert `Operator`s into `NonZeroU32`s with unchecked casts;
|
||||
// memory safety relies on `Operator` starting at `1` and no variant ever
|
||||
// being zero.
|
||||
AdjustSpDown = 1,
|
||||
|
||||
/// `adjust_sp_down_imm`
|
||||
#[peepmatic(immediates(iNN), result(void))]
|
||||
AdjustSpDownImm,
|
||||
|
||||
/// `band`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Band,
|
||||
|
||||
/// `band_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BandImm,
|
||||
|
||||
/// `bconst`
|
||||
#[peepmatic(immediates(b1), result(bNN))]
|
||||
Bconst,
|
||||
|
||||
/// `bint`
|
||||
#[peepmatic(params(bNN), result(iNN))]
|
||||
Bint,
|
||||
|
||||
/// `bnot`
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Bnot,
|
||||
|
||||
/// `bor`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bor,
|
||||
|
||||
/// `bor_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BorImm,
|
||||
|
||||
/// `brnz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Brnz,
|
||||
|
||||
/// `brz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Brz,
|
||||
|
||||
/// `bxor`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bxor,
|
||||
|
||||
/// `bxor_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BxorImm,
|
||||
|
||||
/// `iadd`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Iadd,
|
||||
|
||||
/// `iadd_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IaddImm,
|
||||
|
||||
/// `icmp`
|
||||
#[peepmatic(immediates(cc), params(iNN, iNN), result(b1))]
|
||||
Icmp,
|
||||
|
||||
/// `icmp_imm`
|
||||
#[peepmatic(immediates(cc, iNN), params(iNN), result(b1))]
|
||||
IcmpImm,
|
||||
|
||||
/// `iconst`
|
||||
#[peepmatic(immediates(iNN), result(iNN))]
|
||||
Iconst,
|
||||
|
||||
/// `ifcmp`
|
||||
#[peepmatic(params(iNN, iNN), result(cpu_flags))]
|
||||
Ifcmp,
|
||||
|
||||
/// `ifcmp_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(cpu_flags))]
|
||||
IfcmpImm,
|
||||
|
||||
/// `imul`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Imul,
|
||||
|
||||
/// `imul_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
ImulImm,
|
||||
|
||||
/// `ireduce`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Ireduce,
|
||||
|
||||
/// `irsub_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IrsubImm,
|
||||
|
||||
/// `ishl`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Ishl,
|
||||
|
||||
/// `ishl_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IshlImm,
|
||||
|
||||
/// `isub`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Isub,
|
||||
|
||||
/// `rotl`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Rotl,
|
||||
|
||||
/// `rotl_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
RotlImm,
|
||||
|
||||
/// `rotr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Rotr,
|
||||
|
||||
/// `rotr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
RotrImm,
|
||||
|
||||
/// `sdiv`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Sdiv,
|
||||
|
||||
/// `sdiv_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SdivImm,
|
||||
|
||||
/// `select`
|
||||
#[peepmatic(params(bool_or_int, any_t, any_t), result(any_t))]
|
||||
Select,
|
||||
|
||||
/// `sextend`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Sextend,
|
||||
|
||||
/// `srem`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Srem,
|
||||
|
||||
/// `srem_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SremImm,
|
||||
|
||||
/// `sshr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Sshr,
|
||||
|
||||
/// `sshr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SshrImm,
|
||||
|
||||
/// `trapnz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Trapnz,
|
||||
|
||||
/// `trapz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Trapz,
|
||||
|
||||
/// `udiv`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Udiv,
|
||||
|
||||
/// `udiv_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UdivImm,
|
||||
|
||||
/// `uextend`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Uextend,
|
||||
|
||||
/// `urem`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Urem,
|
||||
|
||||
/// `urem_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UremImm,
|
||||
|
||||
/// `ushr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Ushr,
|
||||
|
||||
/// `ushr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UshrImm,
|
||||
}
|
||||
|
||||
/// Compile-time unquote operators.
|
||||
///
|
||||
/// These are used in the right-hand side to perform compile-time evaluation of
|
||||
/// constants matched on the left-hand side.
|
||||
#[derive(PeepmaticOperator, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum UnquoteOperator {
|
||||
/// Compile-time `band` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Band,
|
||||
|
||||
/// Compile-time `bor` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bor,
|
||||
|
||||
/// Compile-time `bxor` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bxor,
|
||||
|
||||
/// Compile-time `iadd` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Iadd,
|
||||
|
||||
/// Compile-time `imul` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Imul,
|
||||
|
||||
/// Compile-time `isub` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Isub,
|
||||
|
||||
/// Take the base-2 log of a power of two integer.
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Log2,
|
||||
|
||||
/// Wrapping negation of an integer.
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Neg,
|
||||
}
|
||||
|
||||
/// A trait to represent a typing context.
|
||||
///
|
||||
/// This is used by the macro-generated operator methods that create the type
|
||||
/// variables for their immediates, parameters, and results. This trait is
|
||||
/// implemented by the concrete typing context in `peepmatic/src/verify.rs`.
|
||||
#[cfg(feature = "construct")]
|
||||
pub trait TypingContext<'a> {
|
||||
/// A type variable.
|
||||
type TypeVariable;
|
||||
|
||||
/// Create a condition code type.
|
||||
fn cc(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `bNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn bNN(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iNN(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iMM` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iMM(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the CPU flags type variable.
|
||||
fn cpu_flags(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type of size one bit.
|
||||
fn b1(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the void type, used as the result of operators that branch away,
|
||||
/// or do not return anything.
|
||||
fn void(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that may be either a boolean or an integer.
|
||||
fn bool_or_int(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that can be any type T.
|
||||
///
|
||||
/// Each use of `any_t` by the same operator refers to the same type
|
||||
/// variable.
|
||||
fn any_t(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ use crate::optimizer::PeepholeOptimizer;
|
||||
use crate::paths::PathInterner;
|
||||
use peepmatic_automata::Automaton;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[cfg(feature = "construct")]
|
||||
use std::fs;
|
||||
@@ -19,7 +21,10 @@ use std::path::Path;
|
||||
/// This is the compilation result of the `peepmatic` crate, after its taken a
|
||||
/// bunch of optimizations written in the DSL and lowered and combined them.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PeepholeOptimizations {
|
||||
pub struct PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The instruction paths referenced by the peephole optimizations.
|
||||
pub paths: PathInterner,
|
||||
|
||||
@@ -29,12 +34,18 @@ pub struct PeepholeOptimizations {
|
||||
|
||||
/// The underlying automata for matching optimizations' left-hand sides, and
|
||||
/// building up the corresponding right-hand side.
|
||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action]>>,
|
||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action<TOperator>]>>,
|
||||
}
|
||||
|
||||
impl PeepholeOptimizations {
|
||||
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Deserialize a `PeepholeOptimizations` from bytes.
|
||||
pub fn deserialize(serialized: &[u8]) -> Result<Self> {
|
||||
pub fn deserialize<'a>(serialized: &'a [u8]) -> Result<Self>
|
||||
where
|
||||
TOperator: serde::Deserialize<'a>,
|
||||
{
|
||||
let peep_opt: Self = bincode::deserialize(serialized)?;
|
||||
Ok(peep_opt)
|
||||
}
|
||||
@@ -43,12 +54,20 @@ impl PeepholeOptimizations {
|
||||
///
|
||||
/// Requires that the `"construct"` cargo feature is enabled.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()> {
|
||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()>
|
||||
where
|
||||
TOperator: serde::Serialize,
|
||||
{
|
||||
let file = fs::File::create(path)?;
|
||||
bincode::serialize_into(file, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Create a new peephole optimizer instance from this set of peephole
|
||||
/// optimizations.
|
||||
///
|
||||
@@ -58,9 +77,13 @@ impl PeepholeOptimizations {
|
||||
/// instance, rather than create a new one for each instruction. Reusing the
|
||||
/// peephole optimizer instance allows the reuse of a few internal
|
||||
/// allocations.
|
||||
pub fn optimizer<'peep, 'ctx, I>(&'peep self, instr_set: I) -> PeepholeOptimizer<'peep, 'ctx, I>
|
||||
pub fn optimizer<'peep, 'ctx, TInstructionSet>(
|
||||
&'peep self,
|
||||
instr_set: TInstructionSet,
|
||||
) -> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx, Operator = TOperator>,
|
||||
TOperator: Into<std::num::NonZeroU32>,
|
||||
{
|
||||
PeepholeOptimizer {
|
||||
peep_opt: self,
|
||||
|
||||
@@ -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;
|
||||
|
||||
44
cranelift/peepmatic/crates/runtime/src/unquote.rs
Normal file
44
cranelift/peepmatic/crates/runtime/src/unquote.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
//! Unquote operator definition.
|
||||
|
||||
peepmatic_traits::define_operator! {
|
||||
/// Compile-time unquote operators.
|
||||
///
|
||||
/// These are used in the right-hand side to perform compile-time evaluation of
|
||||
/// constants matched on the left-hand side.
|
||||
#[allow(missing_docs)]
|
||||
UnquoteOperator {
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor => Bor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bxor => Bxor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd => Iadd {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
imul => Imul {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
isub => Isub {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
log2 => Log2 {
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
neg => Neg {
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
}
|
||||
parse_cfg(feature = "construct");
|
||||
}
|
||||
Reference in New Issue
Block a user