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-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]

View File

@@ -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>,

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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,

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;

View 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");
}