Merge pull request #1960 from fitzgen/peepmatic-generic-over-ir

peepmatic: Be generic over the IR we are optimizing
This commit is contained in:
Nick Fitzgerald
2020-07-17 17:05:55 -07:00
committed by GitHub
47 changed files with 1982 additions and 1392 deletions

View File

@@ -162,16 +162,17 @@ jobs:
--package peepmatic-macro \ --package peepmatic-macro \
--package peepmatic-runtime \ --package peepmatic-runtime \
--package peepmatic-test --package peepmatic-test
- name: Rebuild Cranelift's peepmatic-based peephole optimizers - name: Rebuild Peepmatic-based peephole optimizers and test them
run: | run: |
cd cranelift/ cargo test \
cargo build --features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers' --features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers' \
- name: Check that peephole optimizers are up to date peepmatic
working-directory: ./cranelift
- name: Check that built peephole optimizers are up to date
run: git diff --exit-code run: git diff --exit-code
- name: Test `cranelift-codegen` with `peepmatic` enabled - name: Test with Peepmatic-based peephole optimizers
run: | run: cargo test --features 'enable-peepmatic'
cd cranelift/ working-directory: ./cranelift
cargo test --features 'enable-peepmatic'
# Perform all tests (debug mode) for `wasmtime`. This runs stable/beta/nightly # Perform all tests (debug mode) for `wasmtime`. This runs stable/beta/nightly
# channels of Rust as well as macOS/Linux/Windows. # channels of Rust as well as macOS/Linux/Windows.

33
Cargo.lock generated
View File

@@ -386,11 +386,13 @@ dependencies = [
"log", "log",
"peepmatic", "peepmatic",
"peepmatic-runtime", "peepmatic-runtime",
"peepmatic-traits",
"regalloc", "regalloc",
"serde", "serde",
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wast 15.0.0",
] ]
[[package]] [[package]]
@@ -1298,20 +1300,23 @@ dependencies = [
"peepmatic-automata", "peepmatic-automata",
"peepmatic-macro", "peepmatic-macro",
"peepmatic-runtime", "peepmatic-runtime",
"peepmatic-test-operator",
"peepmatic-traits",
"serde",
"wast 15.0.0", "wast 15.0.0",
"z3", "z3",
] ]
[[package]] [[package]]
name = "peepmatic-automata" name = "peepmatic-automata"
version = "0.2.0" version = "0.66.0"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "peepmatic-fuzzing" name = "peepmatic-fuzzing"
version = "0.2.0" version = "0.66.0"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"bincode", "bincode",
@@ -1322,6 +1327,8 @@ dependencies = [
"peepmatic-automata", "peepmatic-automata",
"peepmatic-runtime", "peepmatic-runtime",
"peepmatic-test", "peepmatic-test",
"peepmatic-test-operator",
"peepmatic-traits",
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
"wast 15.0.0", "wast 15.0.0",
@@ -1329,7 +1336,7 @@ dependencies = [
[[package]] [[package]]
name = "peepmatic-macro" name = "peepmatic-macro"
version = "0.2.0" version = "0.66.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1338,13 +1345,14 @@ dependencies = [
[[package]] [[package]]
name = "peepmatic-runtime" name = "peepmatic-runtime"
version = "0.2.0" version = "0.66.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"bumpalo", "bumpalo",
"log", "log",
"peepmatic-automata", "peepmatic-automata",
"peepmatic-macro", "peepmatic-test-operator",
"peepmatic-traits",
"serde", "serde",
"serde_test", "serde_test",
"thiserror", "thiserror",
@@ -1359,8 +1367,23 @@ dependencies = [
"log", "log",
"peepmatic", "peepmatic",
"peepmatic-runtime", "peepmatic-runtime",
"peepmatic-test-operator",
"peepmatic-traits",
] ]
[[package]]
name = "peepmatic-test-operator"
version = "0.66.0"
dependencies = [
"peepmatic-traits",
"serde",
"wast 15.0.0",
]
[[package]]
name = "peepmatic-traits"
version = "0.66.0"
[[package]] [[package]]
name = "plain" name = "plain"
version = "0.2.3" version = "0.2.3"

View File

@@ -25,8 +25,11 @@ gimli = { version = "0.21.0", default-features = false, features = ["write"], op
smallvec = { version = "1.0.0" } smallvec = { version = "1.0.0" }
thiserror = "1.0.4" thiserror = "1.0.4"
byteorder = { version = "1.3.2", default-features = false } byteorder = { version = "1.3.2", default-features = false }
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.2.0" } peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
regalloc = { version = "0.0.28" } peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, version = "0.66.0" }
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.66.0" }
regalloc = "0.0.28"
wast = { version = "15.0.0", optional = true }
# It is a goal of the cranelift-codegen crate to have minimal external dependencies. # It is a goal of the cranelift-codegen crate to have minimal external dependencies.
# Please don't add any unless they are essential to the task of creating binary # Please don't add any unless they are essential to the task of creating binary
# machine code. Integration tests that need external dependencies can be # machine code. Integration tests that need external dependencies can be
@@ -34,7 +37,6 @@ regalloc = { version = "0.0.28" }
[build-dependencies] [build-dependencies]
cranelift-codegen-meta = { path = "meta", version = "0.66.0" } cranelift-codegen-meta = { path = "meta", version = "0.66.0" }
peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
[features] [features]
default = ["std", "unwind"] default = ["std", "unwind"]
@@ -80,10 +82,10 @@ regalloc-snapshot = ["bincode", "regalloc/enable-serde"]
# Recompile our optimizations that are written in the `peepmatic` DSL into a # Recompile our optimizations that are written in the `peepmatic` DSL into a
# compact finite-state transducer automaton. # compact finite-state transducer automaton.
rebuild-peephole-optimizers = ["peepmatic"] rebuild-peephole-optimizers = ["peepmatic", "peepmatic-traits", "wast"]
# Enable the use of `peepmatic`-generated peephole optimizers. # Enable the use of `peepmatic`-generated peephole optimizers.
enable-peepmatic = ["peepmatic-runtime"] enable-peepmatic = ["peepmatic-runtime", "peepmatic-traits", "serde"]
[badges] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }

View File

@@ -90,20 +90,11 @@ fn main() {
} }
#[cfg(feature = "rebuild-peephole-optimizers")] #[cfg(feature = "rebuild-peephole-optimizers")]
rebuild_peephole_optimizers(); {
std::fs::write(
std::path::Path::new(&out_dir).join("CRANELIFT_CODEGEN_PATH"),
cur_dir.to_str().unwrap(),
)
.unwrap()
} }
#[cfg(feature = "rebuild-peephole-optimizers")]
fn rebuild_peephole_optimizers() {
use std::path::Path;
let source_path = Path::new("src").join("preopt.peepmatic");
println!("cargo:rerun-if-changed={}", source_path.display());
let preopt =
peepmatic::compile_file(&source_path).expect("failed to compile `src/preopt.peepmatic`");
preopt
.serialize_to_file(&Path::new("src").join("preopt.serialized"))
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
} }

View File

@@ -407,7 +407,11 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
All instructions from all supported ISAs are present. All instructions from all supported ISAs are present.
"#, "#,
); );
fmt.line("#[repr(u16)]");
fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]"); fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
fmt.line(
r#"#[cfg_attr(feature = "enable-peepmatic", derive(serde::Serialize, serde::Deserialize))]"#
);
// We explicitly set the discriminant of the first variant to 1, which allows us to take // We explicitly set the discriminant of the first variant to 1, which allows us to take
// advantage of the NonZero optimization, meaning that wrapping enums can use the 0 // advantage of the NonZero optimization, meaning that wrapping enums can use the 0
@@ -589,6 +593,24 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
fmt.empty_line(); fmt.empty_line();
} }
fn gen_try_from(all_inst: &AllInstructions, fmt: &mut Formatter) {
fmt.line("impl core::convert::TryFrom<u16> for Opcode {");
fmt.indent(|fmt| {
fmt.line("type Error = ();");
fmt.line("#[inline]");
fmt.line("fn try_from(x: u16) -> Result<Self, ()> {");
fmt.indent(|fmt| {
fmtln!(fmt, "if 0 < x && x <= {} {{", all_inst.len());
fmt.indent(|fmt| fmt.line("Ok(unsafe { core::mem::transmute(x) })"));
fmt.line("} else {");
fmt.indent(|fmt| fmt.line("Err(())"));
fmt.line("}");
});
fmt.line("}");
});
fmt.line("}");
}
/// Get the value type constraint for an SSA value operand, where /// Get the value type constraint for an SSA value operand, where
/// `ctrl_typevar` is the controlling type variable. /// `ctrl_typevar` is the controlling type variable.
/// ///
@@ -1147,7 +1169,10 @@ pub(crate) fn generate(
gen_instruction_data_impl(&formats, &mut fmt); gen_instruction_data_impl(&formats, &mut fmt);
fmt.empty_line(); fmt.empty_line();
gen_opcodes(all_inst, &mut fmt); gen_opcodes(all_inst, &mut fmt);
fmt.empty_line();
gen_type_constraints(all_inst, &mut fmt); gen_type_constraints(all_inst, &mut fmt);
fmt.empty_line();
gen_try_from(all_inst, &mut fmt);
fmt.update_file(opcode_filename, out_dir)?; fmt.update_file(opcode_filename, out_dir)?;
// Instruction builder. // Instruction builder.

View File

@@ -7,7 +7,9 @@
//! directory. //! directory.
use alloc::vec::Vec; use alloc::vec::Vec;
use core::convert::{TryFrom, TryInto};
use core::fmt::{self, Display, Formatter}; use core::fmt::{self, Display, Formatter};
use core::num::NonZeroU32;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::str::FromStr; use core::str::FromStr;
@@ -69,6 +71,24 @@ impl Opcode {
} }
} }
impl TryFrom<NonZeroU32> for Opcode {
type Error = ();
#[inline]
fn try_from(x: NonZeroU32) -> Result<Self, ()> {
let x: u16 = x.get().try_into().map_err(|_| ())?;
Self::try_from(x)
}
}
impl From<Opcode> for NonZeroU32 {
#[inline]
fn from(op: Opcode) -> NonZeroU32 {
let x = op as u8;
NonZeroU32::new(x as u32).unwrap()
}
}
// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since // This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since
// it critically depends on the `opcode_name()` function which is needed here anyway, it lives in // it critically depends on the `opcode_name()` function which is needed here anyway, it lives in
// this module. This also saves us from running the build script twice to generate code for the two // this module. This also saves us from running the build script twice to generate code for the two

View File

@@ -13,27 +13,287 @@ use cranelift_codegen_shared::condcodes::IntCC;
use peepmatic_runtime::{ use peepmatic_runtime::{
cc::ConditionCode, cc::ConditionCode,
instruction_set::InstructionSet, instruction_set::InstructionSet,
operator::Operator,
part::{Constant, Part}, part::{Constant, Part},
paths::Path, paths::Path,
r#type::{BitWidth, Kind, Type}, r#type::{BitWidth, Kind, Type},
PeepholeOptimizations, PeepholeOptimizer, PeepholeOptimizations, PeepholeOptimizer,
}; };
use peepmatic_traits::TypingRules;
use std::borrow::Cow;
use std::boxed::Box; use std::boxed::Box;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::ptr; use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::atomic::{AtomicPtr, Ordering};
peepmatic_traits::define_parse_and_typing_rules_for_operator! {
Opcode {
adjust_sp_down => AdjustSpDown {
parameters(iNN);
result(void);
}
adjust_sp_down_imm => AdjustSpDownImm {
immediates(iNN);
result(void);
}
band => Band {
parameters(iNN, iNN);
result(iNN);
}
band_imm => BandImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
bconst => Bconst {
immediates(b1);
result(bNN);
}
bint => Bint {
parameters(bNN);
result(iNN);
}
bor => Bor {
parameters(iNN, iNN);
result(iNN);
}
bor_imm => BorImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
brnz => Brnz {
parameters(bool_or_int);
result(void);
}
brz => Brz {
parameters(bool_or_int);
result(void);
}
bxor => Bxor {
parameters(iNN, iNN);
result(iNN);
}
bxor_imm => BxorImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
iadd => Iadd {
parameters(iNN, iNN);
result(iNN);
}
iadd_imm => IaddImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
icmp => Icmp {
immediates(cc);
parameters(iNN, iNN);
result(b1);
}
icmp_imm => IcmpImm {
immediates(cc, iNN);
parameters(iNN);
result(b1);
}
iconst => Iconst {
immediates(iNN);
result(iNN);
}
ifcmp => Ifcmp {
parameters(iNN, iNN);
result(cpu_flags);
}
ifcmp_imm => IfcmpImm {
immediates(iNN);
parameters(iNN);
result(cpu_flags);
}
imul => Imul {
parameters(iNN, iNN);
result(iNN);
}
imul_imm => ImulImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
ireduce => Ireduce {
parameters(iNN);
result(iMM);
is_reduce(true);
}
irsub_imm => IrsubImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
ishl => Ishl {
parameters(iNN, iNN);
result(iNN);
}
ishl_imm => IshlImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
isub => Isub {
parameters(iNN, iNN);
result(iNN);
}
rotl => Rotl {
parameters(iNN, iNN);
result(iNN);
}
rotl_imm => RotlImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
rotr => Rotr {
parameters(iNN, iNN);
result(iNN);
}
rotr_imm => RotrImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
sdiv => Sdiv {
parameters(iNN, iNN);
result(iNN);
}
sdiv_imm => SdivImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
select => Select {
parameters(bool_or_int, any_t, any_t);
result(any_t);
}
sextend => Sextend {
parameters(iNN);
result(iMM);
is_extend(true);
}
srem => Srem {
parameters(iNN, iNN);
result(iNN);
}
srem_imm => SremImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
sshr => Sshr {
parameters(iNN, iNN);
result(iNN);
}
sshr_imm => SshrImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
trapnz => Trapnz {
parameters(bool_or_int);
result(void);
}
trapz => Trapz {
parameters(bool_or_int);
result(void);
}
udiv => Udiv {
parameters(iNN, iNN);
result(iNN);
}
udiv_imm => UdivImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
uextend => Uextend {
parameters(iNN);
result(iMM);
is_extend(true);
}
urem => Urem {
parameters(iNN, iNN);
result(iNN);
}
urem_imm => UremImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
ushr => Ushr {
parameters(iNN, iNN);
result(iNN);
}
ushr_imm => UshrImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
}
parse_cfg(feature = "rebuild-peephole-optimizers");
}
/// Code required to rebuild Peepmatic-based peephole optimizers.
///
/// This module is used to scope imports and dependencies that are only required
/// for building peephole optimizers (as opposed to just using pre-built
/// peephole optimizers). This helps ensure that our regular builds using
/// pre-built peephole optimizers stay lean.
#[cfg(feature = "rebuild-peephole-optimizers")]
mod rebuild {
use super::*;
use alloc::vec::Vec;
use std::fs;
use std::path::Path;
/// Rebuild the `preopt.peepmatic` peephole optimizer.
///
/// Saves and overwrites the old `preopt.serialized` build and returns a
/// copy of the result.
pub fn rebuild_preopt() -> Vec<u8> {
let codegen_path = Path::new(include_str!(concat!(
env!("OUT_DIR"),
"/CRANELIFT_CODEGEN_PATH"
)));
let source_path = codegen_path.join("src").join("preopt.peepmatic");
let preopt = peepmatic::compile_file::<Opcode>(&source_path)
.expect("failed to compile `src/preopt.peepmatic`");
let serialized_path = codegen_path.join("src").join("preopt.serialized");
preopt
.serialize_to_file(&serialized_path)
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
fs::read(&serialized_path).expect("failed to read `src/preopt.serialized`")
}
}
/// Get the `preopt.peepmatic` peephole optimizer. /// Get the `preopt.peepmatic` peephole optimizer.
pub(crate) fn preopt<'a, 'b>( pub(crate) fn preopt<'a, 'b>(
isa: &'b dyn TargetIsa, isa: &'b dyn TargetIsa,
) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> { ) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> {
#[cfg(feature = "rebuild-peephole-optimizers")]
fn get_serialized() -> Cow<'static, [u8]> {
rebuild::rebuild_preopt().into()
}
#[cfg(not(feature = "rebuild-peephole-optimizers"))]
fn get_serialized() -> Cow<'static, [u8]> {
static SERIALIZED: &[u8] = include_bytes!("preopt.serialized"); static SERIALIZED: &[u8] = include_bytes!("preopt.serialized");
SERIALIZED.into()
}
// Once initialized, this must never be re-assigned. The initialized value // Once initialized, this must never be re-assigned. The initialized value
// is semantically "static data" and is intentionally leaked for the whole // is semantically "static data" and is intentionally leaked for the whole
// program's lifetime. // program's lifetime.
static DESERIALIZED: AtomicPtr<PeepholeOptimizations> = AtomicPtr::new(ptr::null_mut()); static DESERIALIZED: AtomicPtr<PeepholeOptimizations<Opcode>> = AtomicPtr::new(ptr::null_mut());
// If `DESERIALIZED` has already been initialized, then just use it. // If `DESERIALIZED` has already been initialized, then just use it.
let ptr = DESERIALIZED.load(Ordering::SeqCst); let ptr = DESERIALIZED.load(Ordering::SeqCst);
@@ -46,7 +306,7 @@ pub(crate) fn preopt<'a, 'b>(
// another thread could be doing the same thing concurrently, so there is a // another thread could be doing the same thing concurrently, so there is a
// race to see who initializes `DESERIALIZED` first, and we need to be // race to see who initializes `DESERIALIZED` first, and we need to be
// prepared to both win or lose that race. // prepared to both win or lose that race.
let peep_opts = PeepholeOptimizations::deserialize(SERIALIZED) let peep_opts = PeepholeOptimizations::deserialize(&get_serialized())
.expect("should always be able to deserialize `preopt.serialized`"); .expect("should always be able to deserialize `preopt.serialized`");
let peep_opts = Box::into_raw(Box::new(peep_opts)); let peep_opts = Box::into_raw(Box::new(peep_opts));
@@ -219,70 +479,6 @@ fn part_to_value(pos: &mut FuncCursor, root: Inst, part: Part<ValueOrInst>) -> O
} }
} }
impl Opcode {
fn to_peepmatic_operator(&self) -> Option<Operator> {
macro_rules! convert {
( $( $op:ident $(,)* )* ) => {
match self {
$( Self::$op => Some(Operator::$op), )*
_ => None,
}
}
}
convert!(
AdjustSpDown,
AdjustSpDownImm,
Band,
BandImm,
Bconst,
Bint,
Bnot,
Bor,
BorImm,
Brnz,
Brz,
Bxor,
BxorImm,
Iadd,
IaddImm,
Icmp,
IcmpImm,
Iconst,
Ifcmp,
IfcmpImm,
Imul,
ImulImm,
Ireduce,
IrsubImm,
Ishl,
IshlImm,
Isub,
Rotl,
RotlImm,
Rotr,
RotrImm,
Sdiv,
SdivImm,
Select,
Sextend,
Srem,
SremImm,
Sshr,
SshrImm,
Trapnz,
Trapz,
Udiv,
UdivImm,
Uextend,
Urem,
UremImm,
Ushr,
UshrImm,
)
}
}
impl TryFrom<Constant> for Imm64 { impl TryFrom<Constant> for Imm64 {
type Error = &'static str; type Error = &'static str;
@@ -429,6 +625,8 @@ fn peepmatic_ty_to_ir_ty(ty: Type, dfg: &DataFlowGraph, root: Inst) -> types::Ty
unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
type Context = FuncCursor<'b>; type Context = FuncCursor<'b>;
type Operator = Opcode;
type Instruction = ValueOrInst; type Instruction = ValueOrInst;
fn replace_instruction( fn replace_instruction(
@@ -496,7 +694,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
let mut part = Part::Instruction(root); let mut part = Part::Instruction(root);
for p in path.0[1..].iter().copied() { for p in path.0[1..].iter().copied() {
let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?; let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?;
let operator = pos.func.dfg[inst].opcode().to_peepmatic_operator()?; let operator = pos.func.dfg[inst].opcode();
if p < operator.immediates_arity() { if p < operator.immediates_arity() {
part = get_immediate(&pos.func.dfg, inst, p as usize); part = get_immediate(&pos.func.dfg, inst, p as usize);
@@ -513,16 +711,16 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
Some(part) Some(part)
} }
fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Operator> { fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Opcode> {
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?; let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
pos.func.dfg[inst].opcode().to_peepmatic_operator() Some(pos.func.dfg[inst].opcode())
} }
fn make_inst_1( fn make_inst_1(
&self, &self,
pos: &mut FuncCursor<'b>, pos: &mut FuncCursor<'b>,
root: ValueOrInst, root: ValueOrInst,
operator: Operator, operator: Opcode,
r#type: Type, r#type: Type,
a: Part<ValueOrInst>, a: Part<ValueOrInst>,
) -> ValueOrInst { ) -> ValueOrInst {
@@ -530,32 +728,32 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
let root = root.resolve_inst(&pos.func.dfg).unwrap(); let root = root.resolve_inst(&pos.func.dfg).unwrap();
match operator { match operator {
Operator::AdjustSpDown => { Opcode::AdjustSpDown => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
pos.ins().adjust_sp_down(a).into() pos.ins().adjust_sp_down(a).into()
} }
Operator::AdjustSpDownImm => { Opcode::AdjustSpDownImm => {
let c = a.unwrap_constant(); let c = a.unwrap_constant();
let imm = Imm64::try_from(c).unwrap(); let imm = Imm64::try_from(c).unwrap();
pos.ins().adjust_sp_down_imm(imm).into() pos.ins().adjust_sp_down_imm(imm).into()
} }
Operator::Bconst => { Opcode::Bconst => {
let c = a.unwrap_constant(); let c = a.unwrap_constant();
let val = const_to_value(pos.ins(), c, root); let val = const_to_value(pos.ins(), c, root);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Bint => { Opcode::Bint => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
let val = pos.ins().bint(ty, a); let val = pos.ins().bint(ty, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Bnot => { Opcode::Bnot => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let val = pos.ins().bnot(a); let val = pos.ins().bnot(a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Brnz => { Opcode::Brnz => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
// NB: branching instructions must be the root of an // NB: branching instructions must be the root of an
@@ -567,37 +765,37 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
pos.ins().brnz(a, block, &args).into() pos.ins().brnz(a, block, &args).into()
} }
Operator::Brz => { Opcode::Brz => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
// See the comment in the `Operator::Brnz` match argm. // See the comment in the `Opcode::Brnz` match argm.
let block = pos.func.dfg[root].branch_destination().unwrap(); let block = pos.func.dfg[root].branch_destination().unwrap();
let args = pos.func.dfg.inst_args(root)[1..].to_vec(); let args = pos.func.dfg.inst_args(root)[1..].to_vec();
pos.ins().brz(a, block, &args).into() pos.ins().brz(a, block, &args).into()
} }
Operator::Iconst => { Opcode::Iconst => {
let a = a.unwrap_constant(); let a = a.unwrap_constant();
let val = const_to_value(pos.ins(), a, root); let val = const_to_value(pos.ins(), a, root);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Ireduce => { Opcode::Ireduce => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
let val = pos.ins().ireduce(ty, a); let val = pos.ins().ireduce(ty, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Sextend => { Opcode::Sextend => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
let val = pos.ins().sextend(ty, a); let val = pos.ins().sextend(ty, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Trapnz => { Opcode::Trapnz => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
// NB: similar to branching instructions (see comment in the // NB: similar to branching instructions (see comment in the
// `Operator::Brnz` match arm) trapping instructions must be the // `Opcode::Brnz` match arm) trapping instructions must be the
// root of an optimization's right-hand side, and we get the // root of an optimization's right-hand side, and we get the
// trap code from the root of the left-hand side. Peepmatic // trap code from the root of the left-hand side. Peepmatic
// doesn't currently represent trap codes. // doesn't currently represent trap codes.
@@ -605,13 +803,13 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
pos.ins().trapnz(a, code).into() pos.ins().trapnz(a, code).into()
} }
Operator::Trapz => { Opcode::Trapz => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
// See comment in the `Operator::Trapnz` match arm. // See comment in the `Opcode::Trapnz` match arm.
let code = pos.func.dfg[root].trap_code().unwrap(); let code = pos.func.dfg[root].trap_code().unwrap();
pos.ins().trapz(a, code).into() pos.ins().trapz(a, code).into()
} }
Operator::Uextend => { Opcode::Uextend => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
let val = pos.ins().uextend(ty, a); let val = pos.ins().uextend(ty, a);
@@ -625,7 +823,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
&self, &self,
pos: &mut FuncCursor<'b>, pos: &mut FuncCursor<'b>,
root: ValueOrInst, root: ValueOrInst,
operator: Operator, operator: Opcode,
_: Type, _: Type,
a: Part<ValueOrInst>, a: Part<ValueOrInst>,
b: Part<ValueOrInst>, b: Part<ValueOrInst>,
@@ -634,193 +832,193 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
let root = root.resolve_inst(&pos.func.dfg).unwrap(); let root = root.resolve_inst(&pos.func.dfg).unwrap();
match operator { match operator {
Operator::Band => { Opcode::Band => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().band(a, b); let val = pos.ins().band(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::BandImm => { Opcode::BandImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().band_imm(b, a); let val = pos.ins().band_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Bor => { Opcode::Bor => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().bor(a, b); let val = pos.ins().bor(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::BorImm => { Opcode::BorImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().bor_imm(b, a); let val = pos.ins().bor_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Bxor => { Opcode::Bxor => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().bxor(a, b); let val = pos.ins().bxor(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::BxorImm => { Opcode::BxorImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().bxor_imm(b, a); let val = pos.ins().bxor_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Iadd => { Opcode::Iadd => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().iadd(a, b); let val = pos.ins().iadd(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::IaddImm => { Opcode::IaddImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().iadd_imm(b, a); let val = pos.ins().iadd_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Ifcmp => { Opcode::Ifcmp => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().ifcmp(a, b); let val = pos.ins().ifcmp(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::IfcmpImm => { Opcode::IfcmpImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().ifcmp_imm(b, a); let val = pos.ins().ifcmp_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Imul => { Opcode::Imul => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().imul(a, b); let val = pos.ins().imul(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::ImulImm => { Opcode::ImulImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().imul_imm(b, a); let val = pos.ins().imul_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::IrsubImm => { Opcode::IrsubImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().irsub_imm(b, a); let val = pos.ins().irsub_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Ishl => { Opcode::Ishl => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().ishl(a, b); let val = pos.ins().ishl(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::IshlImm => { Opcode::IshlImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().ishl_imm(b, a); let val = pos.ins().ishl_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Isub => { Opcode::Isub => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().isub(a, b); let val = pos.ins().isub(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Rotl => { Opcode::Rotl => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().rotl(a, b); let val = pos.ins().rotl(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::RotlImm => { Opcode::RotlImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().rotl_imm(b, a); let val = pos.ins().rotl_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Rotr => { Opcode::Rotr => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().rotr(a, b); let val = pos.ins().rotr(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::RotrImm => { Opcode::RotrImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().rotr_imm(b, a); let val = pos.ins().rotr_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Sdiv => { Opcode::Sdiv => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().sdiv(a, b); let val = pos.ins().sdiv(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::SdivImm => { Opcode::SdivImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().sdiv_imm(b, a); let val = pos.ins().sdiv_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Srem => { Opcode::Srem => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().srem(a, b); let val = pos.ins().srem(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::SremImm => { Opcode::SremImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().srem_imm(b, a); let val = pos.ins().srem_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Sshr => { Opcode::Sshr => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().sshr(a, b); let val = pos.ins().sshr(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::SshrImm => { Opcode::SshrImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().sshr_imm(b, a); let val = pos.ins().sshr_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Udiv => { Opcode::Udiv => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().udiv(a, b); let val = pos.ins().udiv(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::UdivImm => { Opcode::UdivImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().udiv_imm(b, a); let val = pos.ins().udiv_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Urem => { Opcode::Urem => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().urem(a, b); let val = pos.ins().urem(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::UremImm => { Opcode::UremImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().urem_imm(b, a); let val = pos.ins().urem_imm(b, a);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Ushr => { Opcode::Ushr => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().ushr(a, b); let val = pos.ins().ushr(a, b);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::UshrImm => { Opcode::UshrImm => {
let a = part_to_imm64(pos, a); let a = part_to_imm64(pos, a);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let val = pos.ins().ushr_imm(b, a); let val = pos.ins().ushr_imm(b, a);
@@ -834,7 +1032,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
&self, &self,
pos: &mut FuncCursor<'b>, pos: &mut FuncCursor<'b>,
root: ValueOrInst, root: ValueOrInst,
operator: Operator, operator: Opcode,
_: Type, _: Type,
a: Part<ValueOrInst>, a: Part<ValueOrInst>,
b: Part<ValueOrInst>, b: Part<ValueOrInst>,
@@ -844,7 +1042,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
let root = root.resolve_inst(&pos.func.dfg).unwrap(); let root = root.resolve_inst(&pos.func.dfg).unwrap();
match operator { match operator {
Operator::Icmp => { Opcode::Icmp => {
let cond = a.unwrap_condition_code(); let cond = a.unwrap_condition_code();
let cond = peepmatic_to_intcc(cond); let cond = peepmatic_to_intcc(cond);
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
@@ -852,7 +1050,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
let val = pos.ins().icmp(cond, b, c); let val = pos.ins().icmp(cond, b, c);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::IcmpImm => { Opcode::IcmpImm => {
let cond = a.unwrap_condition_code(); let cond = a.unwrap_condition_code();
let cond = peepmatic_to_intcc(cond); let cond = peepmatic_to_intcc(cond);
let imm = part_to_imm64(pos, b); let imm = part_to_imm64(pos, b);
@@ -860,7 +1058,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
let val = pos.ins().icmp_imm(cond, c, imm); let val = pos.ins().icmp_imm(cond, c, imm);
pos.func.dfg.value_def(val).unwrap_inst().into() pos.func.dfg.value_def(val).unwrap_inst().into()
} }
Operator::Select => { Opcode::Select => {
let a = part_to_value(pos, root, a).unwrap(); let a = part_to_value(pos, root, a).unwrap();
let b = part_to_value(pos, root, b).unwrap(); let b = part_to_value(pos, root, b).unwrap();
let c = part_to_value(pos, root, c).unwrap(); let c = part_to_value(pos, root, c).unwrap();
@@ -891,3 +1089,21 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
self.pointer_bits() self.pointer_bits()
} }
} }
#[cfg(test)]
#[cfg(feature = "x86")]
mod tests {
use super::*;
use crate::isa::lookup;
use crate::settings::{builder, Flags};
use std::str::FromStr;
use target_lexicon::triple;
#[test]
fn get_peepmatic_preopt() {
let isa = lookup(triple!("x86_64"))
.expect("expect x86 ISA")
.finish(Flags::new(builder()));
let _ = preopt(&*isa);
}
}

View File

@@ -10,8 +10,13 @@ description = "DSL and compiler for generating peephole optimizers"
[dependencies] [dependencies]
anyhow = "1.0.27" anyhow = "1.0.27"
peepmatic-automata = { version = "0.2.0", path = "crates/automata", features = ["dot"] } peepmatic-automata = { version = "0.66.0", path = "crates/automata", features = ["dot"] }
peepmatic-macro = { version = "0.2.0", path = "crates/macro" } peepmatic-macro = { version = "0.66.0", path = "crates/macro" }
peepmatic-runtime = { version = "0.2.0", path = "crates/runtime", features = ["construct"] } peepmatic-runtime = { version = "0.66.0", path = "crates/runtime", features = ["construct"] }
peepmatic-traits = { version = "0.66.0", path = "crates/traits" }
serde = { version = "1.0.105", features = ["derive"] }
wast = "15.0.0" wast = "15.0.0"
z3 = { version = "0.6.0", features = ["static-link-z3"] } z3 = { version = "0.6.0", features = ["static-link-z3"] }
[dev-dependencies]
peepmatic-test-operator = { version = "0.66.0", path = "crates/test-operator" }

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "peepmatic-automata" name = "peepmatic-automata"
version = "0.2.0" version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018" edition = "2018"
license = "Apache-2.0 WITH LLVM-exception" license = "Apache-2.0 WITH LLVM-exception"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "peepmatic-fuzzing" name = "peepmatic-fuzzing"
version = "0.2.0" version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018" edition = "2018"
publish = false publish = false
@@ -17,6 +17,8 @@ peepmatic = { path = "../.." }
peepmatic-automata = { path = "../automata", features = ["serde"] } peepmatic-automata = { path = "../automata", features = ["serde"] }
peepmatic-runtime = { path = "../runtime", features = ["construct"] } peepmatic-runtime = { path = "../runtime", features = ["construct"] }
peepmatic-test = { path = "../test" } peepmatic-test = { path = "../test" }
peepmatic-test-operator = { path = "../test-operator" }
peepmatic-traits = { path = "../traits" }
rand = { version = "0.7.3", features = ["small_rng"] } rand = { version = "0.7.3", features = ["small_rng"] }
serde = "1.0.106" serde = "1.0.106"
wast = "15.0.0" wast = "15.0.0"

View File

@@ -1,6 +1,7 @@
//! Fuzz testing utilities related to AST pattern matching. //! Fuzz testing utilities related to AST pattern matching.
use peepmatic_runtime::PeepholeOptimizations; use peepmatic_runtime::PeepholeOptimizations;
use peepmatic_test_operator::TestOperator;
use std::path::Path; use std::path::Path;
use std::str; use std::str;
@@ -19,18 +20,18 @@ pub fn compile(data: &[u8]) {
Ok(s) => s, Ok(s) => s,
}; };
let opt = match peepmatic::compile_str(source, Path::new("fuzz")) { let opt = match peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz")) {
Err(_) => return, Err(_) => return,
Ok(o) => o, Ok(o) => o,
}; };
// Should be able to serialize and deserialize the peephole optimizer. // Should be able to serialize and deserialize the peephole optimizer.
let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK"); let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK");
let _: PeepholeOptimizations = let _: PeepholeOptimizations<TestOperator> =
bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK"); bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK");
// Compiling the same source text again should be deterministic. // Compiling the same source text again should be deterministic.
let opt2 = peepmatic::compile_str(source, Path::new("fuzz")) let opt2 = peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz"))
.expect("should be able to compile source text again, if it compiled OK the first time"); .expect("should be able to compile source text again, if it compiled OK the first time");
let opt2_bytes = let opt2_bytes =
bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK"); bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK");

View File

@@ -6,12 +6,13 @@ use peepmatic::{
}; };
use peepmatic_runtime::{ use peepmatic_runtime::{
cc::ConditionCode, cc::ConditionCode,
operator::TypingContext as TypingContextTrait,
part::Constant, part::Constant,
r#type::BitWidth, r#type::BitWidth,
r#type::{Kind, Type}, r#type::{Kind, Type},
}; };
use peepmatic_test::{Program, TestIsa}; use peepmatic_test::{Program, TestIsa};
use peepmatic_test_operator::TestOperator;
use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::path::Path; use std::path::Path;
use std::str; use std::str;
@@ -37,7 +38,7 @@ pub fn interp(data: &[u8]) {
// Okay, we know it compiles and verifies alright, so (re)parse the AST. // Okay, we know it compiles and verifies alright, so (re)parse the AST.
let buf = wast::parser::ParseBuffer::new(&source).unwrap(); let buf = wast::parser::ParseBuffer::new(&source).unwrap();
let ast = wast::parser::parse::<Optimizations>(&buf).unwrap(); let ast = wast::parser::parse::<Optimizations<TestOperator>>(&buf).unwrap();
// And we need access to the assigned types, so re-verify it as well. // And we need access to the assigned types, so re-verify it as well.
peepmatic::verify(&ast).unwrap(); peepmatic::verify(&ast).unwrap();
@@ -87,7 +88,7 @@ pub fn interp(data: &[u8]) {
// Generate this operation's immediates. // Generate this operation's immediates.
let mut imm_tys = vec![]; let mut imm_tys = vec![];
op.operator op.operator
.immediate_types(&mut TypingContext, op.span(), &mut imm_tys); .immediate_types((), &mut TypingContext, &mut imm_tys);
let imms: Vec<_> = op let imms: Vec<_> = op
.operands .operands
.iter() .iter()
@@ -121,7 +122,7 @@ pub fn interp(data: &[u8]) {
// this operation's arguments. // this operation's arguments.
let mut arg_tys = vec![]; let mut arg_tys = vec![];
op.operator op.operator
.param_types(&mut TypingContext, op.span(), &mut arg_tys); .parameter_types((), &mut TypingContext, &mut arg_tys);
let args: Vec<_> = op let args: Vec<_> = op
.operands .operands
.iter() .iter()
@@ -165,7 +166,7 @@ pub fn interp(data: &[u8]) {
}) })
.collect(); .collect();
let ty = match op.operator.result_type(&mut TypingContext, op.span()) { let ty = match op.operator.result_type((), &mut TypingContext) {
TypeOrConditionCode::Type(ty) => ty, TypeOrConditionCode::Type(ty) => ty,
TypeOrConditionCode::ConditionCode => { TypeOrConditionCode::ConditionCode => {
unreachable!("condition codes cannot be operation results") unreachable!("condition codes cannot be operation results")
@@ -206,41 +207,42 @@ enum TypeOrConditionCode {
struct TypingContext; struct TypingContext;
impl<'a> TypingContextTrait<'a> for TypingContext { impl<'a> TypingContextTrait<'a> for TypingContext {
type Span = ();
type TypeVariable = TypeOrConditionCode; type TypeVariable = TypeOrConditionCode;
fn cc(&mut self, _: wast::Span) -> Self::TypeVariable { fn cc(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::ConditionCode TypeOrConditionCode::ConditionCode
} }
fn bNN(&mut self, _: wast::Span) -> Self::TypeVariable { fn bNN(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::b1()) TypeOrConditionCode::Type(Type::b1())
} }
fn iNN(&mut self, _: wast::Span) -> Self::TypeVariable { fn iNN(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::i32()) TypeOrConditionCode::Type(Type::i32())
} }
fn iMM(&mut self, _: wast::Span) -> Self::TypeVariable { fn iMM(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::i32()) TypeOrConditionCode::Type(Type::i32())
} }
fn cpu_flags(&mut self, _: wast::Span) -> Self::TypeVariable { fn cpu_flags(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::cpu_flags()) TypeOrConditionCode::Type(Type::cpu_flags())
} }
fn b1(&mut self, _: wast::Span) -> Self::TypeVariable { fn b1(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::b1()) TypeOrConditionCode::Type(Type::b1())
} }
fn void(&mut self, _: wast::Span) -> Self::TypeVariable { fn void(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::void()) TypeOrConditionCode::Type(Type::void())
} }
fn bool_or_int(&mut self, _: wast::Span) -> Self::TypeVariable { fn bool_or_int(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::b1()) TypeOrConditionCode::Type(Type::b1())
} }
fn any_t(&mut self, _: wast::Span) -> Self::TypeVariable { fn any_t(&mut self, _: ()) -> Self::TypeVariable {
TypeOrConditionCode::Type(Type::i32()) TypeOrConditionCode::Type(Type::i32())
} }
} }

View File

@@ -1,6 +1,7 @@
//! Utilities for fuzzing our DSL's parser. //! Utilities for fuzzing our DSL's parser.
use peepmatic::Optimizations; use peepmatic::Optimizations;
use peepmatic_test_operator::TestOperator;
use std::str; use std::str;
/// Attempt to parse the given string as if it were a snippet of our DSL. /// Attempt to parse the given string as if it were a snippet of our DSL.
@@ -15,7 +16,7 @@ pub fn parse(data: &[u8]) {
Err(_) => return, Err(_) => return,
}; };
let _ = wast::parser::parse::<Optimizations>(&buf); let _ = wast::parser::parse::<Optimizations<TestOperator>>(&buf);
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "peepmatic-macro" name = "peepmatic-macro"
version = "0.2.0" version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018" edition = "2018"
license = "Apache-2.0 WITH LLVM-exception" license = "Apache-2.0 WITH LLVM-exception"

View File

@@ -9,8 +9,8 @@ pub fn derive_child_nodes(input: &DeriveInput) -> Result<impl quote::ToTokens> {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
Ok(quote! { Ok(quote! {
impl #impl_generics ChildNodes<'a, 'a> for #name #ty_generics #where_clause { impl #impl_generics ChildNodes<'a, 'a, TOperator> for #name #ty_generics #where_clause {
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a>>) { fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a, TOperator>>) {
#children #children
} }
} }
@@ -103,7 +103,12 @@ fn get_child_nodes(data: &syn::Data) -> Result<impl quote::ToTokens> {
fn add_trait_bounds(mut generics: Generics) -> Generics { fn add_trait_bounds(mut generics: Generics) -> Generics {
for param in &mut generics.params { for param in &mut generics.params {
if let GenericParam::Type(type_param) = param { if let GenericParam::Type(type_param) = param {
type_param.bounds.push(parse_quote!(ChildNodes<'a, 'a>)); if type_param.ident == "TOperator" {
continue;
}
type_param
.bounds
.push(parse_quote!(ChildNodes<'a, 'a, TOperator>));
} }
} }
generics generics

View File

@@ -13,7 +13,7 @@ pub fn derive_into_dyn_ast_ref(input: &DeriveInput) -> Result<impl quote::ToToke
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
Ok(quote! { Ok(quote! {
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a> #where_clause { impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a, TOperator> #where_clause {
#[inline] #[inline]
fn from(x: &'a #ty #ty_generics) -> Self { fn from(x: &'a #ty #ty_generics) -> Self {
Self::#ty(x) Self::#ty(x)

View File

@@ -11,14 +11,8 @@ use syn::{parse_macro_input, Ident, Result};
mod child_nodes; mod child_nodes;
mod into_dyn_ast_ref; mod into_dyn_ast_ref;
mod operator;
mod span; mod span;
#[proc_macro_derive(PeepmaticOperator, attributes(peepmatic))]
pub fn operator(input: TokenStream) -> TokenStream {
operator::derive_operator(input)
}
#[proc_macro_derive(Ast, attributes(peepmatic))] #[proc_macro_derive(Ast, attributes(peepmatic))]
pub fn derive_ast(input: TokenStream) -> TokenStream { pub fn derive_ast(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);

View File

@@ -1,325 +0,0 @@
//! Implementation of the `#[peepmatic]` macro for the `Operator` AST node.
use crate::proc_macro::TokenStream;
use crate::PeepmaticOpts;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::DeriveInput;
use syn::Error;
use syn::{parse_macro_input, Result};
pub fn derive_operator(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let variants = match get_enum_variants(&input) {
Ok(v) => v,
Err(e) => return e.to_compile_error().into(),
};
let arity = match create_arity(&variants) {
Ok(a) => a,
Err(e) => return e.to_compile_error().into(),
};
let num_operators = variants.len();
let type_methods = create_type_methods(&variants);
let parse_impl = create_parse_impl(&input.ident, &variants);
let display_impl = create_display_impl(&input.ident, &variants);
let try_from_u32_impl = create_try_from_u32_impl(&input.ident, &variants);
let ident = &input.ident;
let expanded = quote! {
impl #ident {
#arity
#type_methods
/// Get the total number of different operators.
pub const fn num_operators() -> usize {
#num_operators
}
}
#display_impl
#try_from_u32_impl
#parse_impl
};
// eprintln!("{}", expanded);
TokenStream::from(expanded)
}
fn get_enum_variants(input: &DeriveInput) -> Result<Vec<OperatorVariant>> {
let en = match &input.data {
syn::Data::Enum(en) => en,
syn::Data::Struct(_) => {
panic!("can only put #[peepmatic] on an enum; found it on a struct")
}
syn::Data::Union(_) => panic!("can only put #[peepmatic] on an enum; found it on a union"),
};
en.variants
.iter()
.cloned()
.map(|mut variant| {
Ok(OperatorVariant {
opts: PeepmaticOpts::from_attrs(&mut variant.attrs)?,
syn: variant,
})
})
.collect()
}
struct OperatorVariant {
syn: syn::Variant,
opts: PeepmaticOpts,
}
fn create_arity(variants: &[OperatorVariant]) -> Result<impl quote::ToTokens> {
let mut imm_arities = vec![];
let mut params_arities = vec![];
for v in variants {
let variant = &v.syn.ident;
let imm_arity = v.opts.immediates.len();
if imm_arity > std::u8::MAX as usize {
return Err(Error::new(
v.opts.immediates_paren.span,
"cannot have more than u8::MAX immediates",
));
}
let imm_arity = imm_arity as u8;
imm_arities.push(quote! {
Self::#variant => #imm_arity,
});
let params_arity = v.opts.params.len();
if params_arity > std::u8::MAX as usize {
return Err(Error::new(
v.opts.params_paren.span,
"cannot have more than u8::MAX params",
));
}
let params_arity = params_arity as u8;
params_arities.push(quote! {
Self::#variant => #params_arity,
});
}
Ok(quote! {
/// Get the number of immediates that this operator has.
pub fn immediates_arity(&self) -> u8 {
match *self {
#( #imm_arities )*
}
}
/// Get the number of parameters that this operator takes.
pub fn params_arity(&self) -> u8 {
match *self {
#( #params_arities )*
}
}
})
}
fn create_type_methods(variants: &[OperatorVariant]) -> impl quote::ToTokens {
let mut result_types = vec![];
let mut imm_types = vec![];
let mut param_types = vec![];
for v in variants {
let variant = &v.syn.ident;
let result_ty = v.opts.result.as_ref().unwrap_or_else(|| {
panic!(
"must define #[peepmatic(result(..))] on operator `{}`",
variant
)
});
result_types.push(quote! {
Self::#variant => {
context.#result_ty(span)
}
});
let imm_tys = match &v.opts.immediates[..] {
[] => quote! {},
[ty, rest @ ..] => {
let rest = rest.iter().map(|ty| {
quote! { .chain(::std::iter::once(context.#ty(span))) }
});
quote! {
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
}
}
};
imm_types.push(quote! {
Self::#variant => {
#imm_tys
}
});
let param_tys = match &v.opts.params[..] {
[] => quote! {},
[ty, rest @ ..] => {
let rest = rest.iter().map(|ty| {
quote! { .chain(::std::iter::once(context.#ty(span))) }
});
quote! {
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
}
}
};
param_types.push(quote! {
Self::#variant => {
#param_tys
}
});
}
quote! {
/// Get the result type of this operator.
#[cfg(feature = "construct")]
pub fn result_type<'a, C>(
&self,
context: &mut C,
span: wast::Span,
) -> C::TypeVariable
where
C: 'a + TypingContext<'a>,
{
match *self {
#( #result_types )*
}
}
/// Get the immediate types of this operator.
#[cfg(feature = "construct")]
pub fn immediate_types<'a, C>(
&self,
context: &mut C,
span: wast::Span,
types: &mut impl Extend<C::TypeVariable>,
)
where
C: 'a + TypingContext<'a>,
{
match *self {
#( #imm_types )*
}
}
/// Get the parameter types of this operator.
#[cfg(feature = "construct")]
pub fn param_types<'a, C>(
&self,
context: &mut C,
span: wast::Span,
types: &mut impl Extend<C::TypeVariable>,
)
where
C: 'a + TypingContext<'a>,
{
match *self {
#( #param_types )*
}
}
}
}
fn snake_case(s: &str) -> String {
let mut t = String::with_capacity(s.len() + 1);
for (i, ch) in s.chars().enumerate() {
if i != 0 && ch.is_uppercase() {
t.push('_');
}
t.extend(ch.to_lowercase());
}
t
}
fn create_parse_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
let token_defs = variants.iter().map(|v| {
let tok = snake_case(&v.syn.ident.to_string());
let tok = Ident::new(&tok, Span::call_site());
quote! {
wast::custom_keyword!(#tok);
}
});
let parses = variants.iter().map(|v| {
let tok = snake_case(&v.syn.ident.to_string());
let tok = Ident::new(&tok, Span::call_site());
let ident = &v.syn.ident;
quote! {
if p.peek::<#tok>() {
p.parse::<#tok>()?;
return Ok(Self::#ident);
}
}
});
let expected = format!("expected {}", ident);
quote! {
#[cfg(feature = "construct")]
impl<'a> wast::parser::Parse<'a> for #ident {
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<Self> {
#( #token_defs )*
#( #parses )*
Err(p.error(#expected))
}
}
}
}
fn create_display_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
let displays = variants.iter().map(|v| {
let variant = &v.syn.ident;
let snake = snake_case(&v.syn.ident.to_string());
quote! {
Self::#variant => write!(f, #snake),
}
});
quote! {
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
#( #displays )*
}
}
}
}
}
fn create_try_from_u32_impl(
ident: &syn::Ident,
variants: &[OperatorVariant],
) -> impl quote::ToTokens {
let matches = variants.iter().map(|v| {
let variant = &v.syn.ident;
quote! {
x if Self::#variant as u32 == x => Ok(Self::#variant),
}
});
let error_msg = format!("value is not an `{}`", ident);
quote! {
impl std::convert::TryFrom<u32> for #ident {
type Error = &'static str;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
#( #matches )*
_ => Err(#error_msg)
}
}
}
}
}

View File

@@ -45,6 +45,9 @@ pub fn derive_span(input: &DeriveInput) -> Result<impl quote::ToTokens> {
fn add_span_trait_bounds(mut generics: Generics) -> Generics { fn add_span_trait_bounds(mut generics: Generics) -> Generics {
for param in &mut generics.params { for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param { if let GenericParam::Type(ref mut type_param) = *param {
if type_param.ident == "TOperator" {
continue;
}
type_param.bounds.push(parse_quote!(Span)); type_param.bounds.push(parse_quote!(Span));
} }
} }

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "peepmatic-runtime" name = "peepmatic-runtime"
version = "0.2.0" version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018" edition = "2018"
license = "Apache-2.0 WITH LLVM-exception" license = "Apache-2.0 WITH LLVM-exception"
@@ -12,13 +12,14 @@ description = "Runtime support for peepmatic peephole optimizers"
bincode = "1.2.1" bincode = "1.2.1"
bumpalo = "3.2.0" bumpalo = "3.2.0"
log = "0.4.8" log = "0.4.8"
peepmatic-automata = { version = "0.2.0", path = "../automata", features = ["serde"] } peepmatic-automata = { version = "0.66.0", path = "../automata", features = ["serde"] }
peepmatic-macro = { version = "0.2.0", path = "../macro" } peepmatic-traits = { version = "0.66.0", path = "../traits" }
serde = { version = "1.0.105", features = ["derive"] } serde = { version = "1.0.105", features = ["derive"] }
thiserror = "1.0.15" thiserror = "1.0.15"
wast = { version = "15.0.0", optional = true } wast = { version = "15.0.0", optional = true }
[dev-dependencies] [dev-dependencies]
peepmatic-test-operator = { version = "0.66.0", path = "../test-operator" }
serde_test = "1.0.114" serde_test = "1.0.114"
[features] [features]

View File

@@ -1,10 +1,11 @@
//! Interfacing with actual instructions. //! Interfacing with actual instructions.
use crate::operator::Operator;
use crate::part::{Constant, Part}; use crate::part::{Constant, Part};
use crate::paths::Path; use crate::paths::Path;
use crate::r#type::Type; use crate::r#type::Type;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::Hash;
use std::num::NonZeroU32;
/// A trait for interfacing with actual instruction sequences. /// A trait for interfacing with actual instruction sequences.
/// ///
@@ -32,6 +33,9 @@ pub unsafe trait InstructionSet<'a> {
/// implementation. /// implementation.
type Context; type Context;
/// An operator.
type Operator: 'static + Copy + Debug + Eq + Hash + Into<NonZeroU32>;
/// An instruction (or identifier for an instruction). /// An instruction (or identifier for an instruction).
type Instruction: Copy + Debug + Eq; type Instruction: Copy + Debug + Eq;
@@ -64,10 +68,12 @@ pub unsafe trait InstructionSet<'a> {
/// Get the given instruction's operator. /// Get the given instruction's operator.
/// ///
/// If the instruction's opcode does not have an associated /// If the instruction isn't supported, then `None` should be returned.
/// `peepmatic_runtime::operator::Operator` variant (i.e. that instruction fn operator(
/// isn't supported by `peepmatic` yet) then `None` should be returned. &self,
fn operator(&self, context: &mut Self::Context, instr: Self::Instruction) -> Option<Operator>; context: &mut Self::Context,
instr: Self::Instruction,
) -> Option<Self::Operator>;
/// Make a unary instruction. /// Make a unary instruction.
/// ///
@@ -76,7 +82,7 @@ pub unsafe trait InstructionSet<'a> {
&self, &self,
context: &mut Self::Context, context: &mut Self::Context,
root: Self::Instruction, root: Self::Instruction,
operator: Operator, operator: Self::Operator,
r#type: Type, r#type: Type,
a: Part<Self::Instruction>, a: Part<Self::Instruction>,
) -> Self::Instruction; ) -> Self::Instruction;
@@ -92,7 +98,7 @@ pub unsafe trait InstructionSet<'a> {
&self, &self,
context: &mut Self::Context, context: &mut Self::Context,
root: Self::Instruction, root: Self::Instruction,
operator: Operator, operator: Self::Operator,
r#type: Type, r#type: Type,
a: Part<Self::Instruction>, a: Part<Self::Instruction>,
b: Part<Self::Instruction>, b: Part<Self::Instruction>,
@@ -108,7 +114,7 @@ pub unsafe trait InstructionSet<'a> {
&self, &self,
context: &mut Self::Context, context: &mut Self::Context,
root: Self::Instruction, root: Self::Instruction,
operator: Operator, operator: Self::Operator,
r#type: Type, r#type: Type,
a: Part<Self::Instruction>, a: Part<Self::Instruction>,
b: Part<Self::Instruction>, b: Part<Self::Instruction>,

View File

@@ -22,12 +22,12 @@ pub mod error;
pub mod instruction_set; pub mod instruction_set;
pub mod integer_interner; pub mod integer_interner;
pub mod linear; pub mod linear;
pub mod operator;
pub mod optimizations; pub mod optimizations;
pub mod optimizer; pub mod optimizer;
pub mod part; pub mod part;
pub mod paths; pub mod paths;
pub mod r#type; pub mod r#type;
pub mod unquote;
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use optimizations::PeepholeOptimizations; pub use optimizations::PeepholeOptimizations;

View File

@@ -7,17 +7,22 @@
use crate::cc::ConditionCode; use crate::cc::ConditionCode;
use crate::integer_interner::{IntegerId, IntegerInterner}; use crate::integer_interner::{IntegerId, IntegerInterner};
use crate::operator::{Operator, UnquoteOperator};
use crate::paths::{PathId, PathInterner}; use crate::paths::{PathId, PathInterner};
use crate::r#type::{BitWidth, Type}; use crate::r#type::{BitWidth, Type};
use crate::unquote::UnquoteOperator;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::hash::Hash;
use std::num::NonZeroU32; use std::num::NonZeroU32;
/// A set of linear optimizations. /// A set of linear optimizations.
#[derive(Debug)] #[derive(Debug)]
pub struct Optimizations { pub struct Optimizations<TOperator>
where
TOperator: 'static + Copy + Debug + Eq + Hash,
{
/// The linear optimizations. /// The linear optimizations.
pub optimizations: Vec<Optimization>, pub optimizations: Vec<Optimization<TOperator>>,
/// The de-duplicated paths referenced by these optimizations. /// The de-duplicated paths referenced by these optimizations.
pub paths: PathInterner, pub paths: PathInterner,
@@ -28,9 +33,12 @@ pub struct Optimizations {
/// A linearized optimization. /// A linearized optimization.
#[derive(Clone, Debug, PartialEq, Eq)] #[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. /// The chain of increments for this optimization.
pub increments: Vec<Increment>, pub increments: Vec<Increment<TOperator>>,
} }
/// Match any value. /// 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 /// basically become a state and a transition edge out of that state in the
/// final automata. /// final automata.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Increment { pub struct Increment<TOperator>
where
TOperator: 'static + Copy + Debug + Eq + Hash,
{
/// The matching operation to perform. /// The matching operation to perform.
pub operation: MatchOp, pub operation: MatchOp,
@@ -74,7 +85,7 @@ pub struct Increment {
/// Actions to perform, given that the operation resulted in the expected /// Actions to perform, given that the operation resulted in the expected
/// value. /// value.
pub actions: Vec<Action>, pub actions: Vec<Action<TOperator>>,
} }
/// A matching operation to be performed on some Cranelift instruction as part /// 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 /// When evaluating actions, the `i^th` action implicitly defines the
/// `RhsId(i)`. /// `RhsId(i)`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Action { pub enum Action<TOperator> {
/// Reuse something from the left-hand side. /// Reuse something from the left-hand side.
GetLhs { GetLhs {
/// The path to the instruction or value. /// The path to the instruction or value.
@@ -215,13 +226,13 @@ pub enum Action {
/// The type of this instruction's result. /// The type of this instruction's result.
r#type: Type, r#type: Type,
/// The operator for this instruction. /// The operator for this instruction.
operator: Operator, operator: TOperator,
}, },
/// Make a binary instruction. /// Make a binary instruction.
MakeBinaryInst { MakeBinaryInst {
/// The opcode for this instruction. /// The opcode for this instruction.
operator: Operator, operator: TOperator,
/// The type of this instruction's result. /// The type of this instruction's result.
r#type: Type, r#type: Type,
/// The operands for this instruction. /// The operands for this instruction.
@@ -231,7 +242,7 @@ pub enum Action {
/// Make a ternary instruction. /// Make a ternary instruction.
MakeTernaryInst { MakeTernaryInst {
/// The opcode for this instruction. /// The opcode for this instruction.
operator: Operator, operator: TOperator,
/// The type of this instruction's result. /// The type of this instruction's result.
r#type: Type, r#type: Type,
/// The operands for this instruction. /// The operands for this instruction.
@@ -242,6 +253,7 @@ pub enum Action {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use peepmatic_test_operator::TestOperator;
// These types all end up in the automaton, so we should take care that they // 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 // are small and don't fill up the data cache (or take up too much
@@ -259,6 +271,6 @@ mod tests {
#[test] #[test]
fn action_size() { 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 crate::paths::PathInterner;
use peepmatic_automata::Automaton; use peepmatic_automata::Automaton;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::hash::Hash;
#[cfg(feature = "construct")] #[cfg(feature = "construct")]
use std::fs; use std::fs;
@@ -19,7 +21,10 @@ use std::path::Path;
/// This is the compilation result of the `peepmatic` crate, after its taken a /// 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. /// bunch of optimizations written in the DSL and lowered and combined them.
#[derive(Debug, Serialize, Deserialize)] #[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. /// The instruction paths referenced by the peephole optimizations.
pub paths: PathInterner, pub paths: PathInterner,
@@ -29,12 +34,18 @@ pub struct PeepholeOptimizations {
/// The underlying automata for matching optimizations' left-hand sides, and /// The underlying automata for matching optimizations' left-hand sides, and
/// building up the corresponding right-hand side. /// 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. /// 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)?; let peep_opt: Self = bincode::deserialize(serialized)?;
Ok(peep_opt) Ok(peep_opt)
} }
@@ -43,12 +54,20 @@ impl PeepholeOptimizations {
/// ///
/// Requires that the `"construct"` cargo feature is enabled. /// Requires that the `"construct"` cargo feature is enabled.
#[cfg(feature = "construct")] #[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)?; let file = fs::File::create(path)?;
bincode::serialize_into(file, self)?; bincode::serialize_into(file, self)?;
Ok(()) Ok(())
} }
}
impl<TOperator> PeepholeOptimizations<TOperator>
where
TOperator: 'static + Copy + Debug + Eq + Hash,
{
/// Create a new peephole optimizer instance from this set of peephole /// Create a new peephole optimizer instance from this set of peephole
/// optimizations. /// optimizations.
/// ///
@@ -58,9 +77,13 @@ impl PeepholeOptimizations {
/// instance, rather than create a new one for each instruction. Reusing the /// instance, rather than create a new one for each instruction. Reusing the
/// peephole optimizer instance allows the reuse of a few internal /// peephole optimizer instance allows the reuse of a few internal
/// allocations. /// 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 where
I: InstructionSet<'ctx>, TInstructionSet: InstructionSet<'ctx, Operator = TOperator>,
TOperator: Into<std::num::NonZeroU32>,
{ {
PeepholeOptimizer { PeepholeOptimizer {
peep_opt: self, peep_opt: self,

View File

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

View File

@@ -0,0 +1,12 @@
[package]
name = "peepmatic-test-operator"
version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
peepmatic-traits = { version = "0.66.0", path = "../traits" }
serde = { version = "1.0.105", features = ["derive"] }
wast = "15.0.0"

View File

@@ -0,0 +1,219 @@
//! This crate defines `TestOperator`: a `TOperator` type for usage in tests.
//!
//! This allows us to write Peepmatic-specific tests that do not depend on
//! building all of Cranelift.
peepmatic_traits::define_operator! {
/// A `TOperator` type for use inside tests.
TestOperator {
adjust_sp_down => AdjustSpDown {
parameters(iNN);
result(void);
}
adjust_sp_down_imm => AdjustSpDownImm {
immediates(iNN);
result(void);
}
band => Band {
parameters(iNN, iNN);
result(iNN);
}
band_imm => BandImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
bconst => Bconst {
immediates(b1);
result(bNN);
}
bint => Bint {
parameters(bNN);
result(iNN);
}
bor => Bor {
parameters(iNN, iNN);
result(iNN);
}
bor_imm => BorImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
brnz => Brnz {
parameters(bool_or_int);
result(void);
}
brz => Brz {
parameters(bool_or_int);
result(void);
}
bxor => Bxor {
parameters(iNN, iNN);
result(iNN);
}
bxor_imm => BxorImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
iadd => Iadd {
parameters(iNN, iNN);
result(iNN);
}
iadd_imm => IaddImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
icmp => Icmp {
immediates(cc);
parameters(iNN, iNN);
result(b1);
}
icmp_imm => IcmpImm {
immediates(cc, iNN);
parameters(iNN);
result(b1);
}
iconst => Iconst {
immediates(iNN);
result(iNN);
}
ifcmp => Ifcmp {
parameters(iNN, iNN);
result(cpu_flags);
}
ifcmp_imm => IfcmpImm {
immediates(iNN);
parameters(iNN);
result(cpu_flags);
}
imul => Imul {
parameters(iNN, iNN);
result(iNN);
}
imul_imm => ImulImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
ireduce => Ireduce {
parameters(iNN);
result(iMM);
is_reduce(true);
}
irsub_imm => IrsubImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
ishl => Ishl {
parameters(iNN, iNN);
result(iNN);
}
ishl_imm => IshlImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
isub => Isub {
parameters(iNN, iNN);
result(iNN);
}
rotl => Rotl {
parameters(iNN, iNN);
result(iNN);
}
rotl_imm => RotlImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
rotr => Rotr {
parameters(iNN, iNN);
result(iNN);
}
rotr_imm => RotrImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
sdiv => Sdiv {
parameters(iNN, iNN);
result(iNN);
}
sdiv_imm => SdivImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
select => Select {
parameters(bool_or_int, any_t, any_t);
result(any_t);
}
sextend => Sextend {
parameters(iNN);
result(iMM);
is_extend(true);
}
srem => Srem {
parameters(iNN, iNN);
result(iNN);
}
srem_imm => SremImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
sshr => Sshr {
parameters(iNN, iNN);
result(iNN);
}
sshr_imm => SshrImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
trapnz => Trapnz {
parameters(bool_or_int);
result(void);
}
trapz => Trapz {
parameters(bool_or_int);
result(void);
}
udiv => Udiv {
parameters(iNN, iNN);
result(iNN);
}
udiv_imm => UdivImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
uextend => Uextend {
parameters(iNN);
result(iMM);
is_extend(true);
}
urem => Urem {
parameters(iNN, iNN);
result(iNN);
}
urem_imm => UremImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
ushr => Ushr {
parameters(iNN, iNN);
result(iNN);
}
ushr_imm => UshrImm {
immediates(iNN);
parameters(iNN);
result(iNN);
}
}
}

View File

@@ -12,3 +12,5 @@ env_logger = "0.7.1"
log = "0.4.8" log = "0.4.8"
peepmatic = { path = "../.." } peepmatic = { path = "../.." }
peepmatic-runtime = { path = "../runtime" } peepmatic-runtime = { path = "../runtime" }
peepmatic-test-operator = { path = "../test-operator" }
peepmatic-traits = { path = "../traits" }

View File

@@ -5,11 +5,12 @@
use peepmatic_runtime::{ use peepmatic_runtime::{
cc::ConditionCode, cc::ConditionCode,
instruction_set::InstructionSet, instruction_set::InstructionSet,
operator::Operator,
part::{Constant, Part}, part::{Constant, Part},
paths::Path, paths::Path,
r#type::{BitWidth, Kind, Type}, r#type::{BitWidth, Kind, Type},
}; };
use peepmatic_test_operator::TestOperator;
use peepmatic_traits::TypingRules;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryFrom; use std::convert::TryFrom;
@@ -19,7 +20,7 @@ pub struct Instruction(pub usize);
#[derive(Debug)] #[derive(Debug)]
pub struct InstructionData { pub struct InstructionData {
pub operator: Operator, pub operator: TestOperator,
pub r#type: Type, pub r#type: Type,
pub immediates: Vec<Immediate>, pub immediates: Vec<Immediate>,
pub arguments: Vec<Instruction>, pub arguments: Vec<Instruction>,
@@ -174,7 +175,7 @@ impl Program {
pub fn new_instruction( pub fn new_instruction(
&mut self, &mut self,
operator: Operator, operator: TestOperator,
r#type: Type, r#type: Type,
immediates: Vec<Immediate>, immediates: Vec<Immediate>,
arguments: Vec<Instruction>, arguments: Vec<Instruction>,
@@ -188,11 +189,11 @@ impl Program {
immediates.len(), immediates.len(),
); );
assert_eq!( assert_eq!(
operator.params_arity() as usize, operator.parameters_arity() as usize,
arguments.len(), arguments.len(),
"wrong number of arguments for {:?}: expected {}, found {}", "wrong number of arguments for {:?}: expected {}, found {}",
operator, operator,
operator.params_arity(), operator.parameters_arity(),
arguments.len(), arguments.len(),
); );
@@ -222,7 +223,7 @@ impl Program {
assert!(!root_bit_width.is_polymorphic()); assert!(!root_bit_width.is_polymorphic());
match c { match c {
Constant::Bool(_, bit_width) => self.new_instruction( Constant::Bool(_, bit_width) => self.new_instruction(
Operator::Bconst, TestOperator::Bconst,
if bit_width.is_polymorphic() { if bit_width.is_polymorphic() {
Type { Type {
kind: Kind::Bool, kind: Kind::Bool,
@@ -238,7 +239,7 @@ impl Program {
vec![], vec![],
), ),
Constant::Int(_, bit_width) => self.new_instruction( Constant::Int(_, bit_width) => self.new_instruction(
Operator::Iconst, TestOperator::Iconst,
if bit_width.is_polymorphic() { if bit_width.is_polymorphic() {
Type { Type {
kind: Kind::Int, kind: Kind::Int,
@@ -259,12 +260,12 @@ impl Program {
fn instruction_to_constant(&mut self, inst: Instruction) -> Option<Constant> { fn instruction_to_constant(&mut self, inst: Instruction) -> Option<Constant> {
match self.data(inst) { match self.data(inst) {
InstructionData { InstructionData {
operator: Operator::Iconst, operator: TestOperator::Iconst,
immediates, immediates,
.. ..
} => Some(immediates[0].unwrap_constant()), } => Some(immediates[0].unwrap_constant()),
InstructionData { InstructionData {
operator: Operator::Bconst, operator: TestOperator::Bconst,
immediates, immediates,
.. ..
} => Some(immediates[0].unwrap_constant()), } => Some(immediates[0].unwrap_constant()),
@@ -310,6 +311,8 @@ pub struct TestIsa {
// Unsafe because we must ensure that `instruction_result_bit_width` never // Unsafe because we must ensure that `instruction_result_bit_width` never
// returns zero. // returns zero.
unsafe impl<'a> InstructionSet<'a> for TestIsa { unsafe impl<'a> InstructionSet<'a> for TestIsa {
type Operator = TestOperator;
type Context = Program; type Context = Program;
type Instruction = Instruction; type Instruction = Instruction;
@@ -360,7 +363,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
Some(part) Some(part)
} }
fn operator(&self, program: &mut Program, instr: Instruction) -> Option<Operator> { fn operator(&self, program: &mut Program, instr: Instruction) -> Option<TestOperator> {
log::debug!("operator({:?})", instr); log::debug!("operator({:?})", instr);
let data = program.data(instr); let data = program.data(instr);
Some(data.operator) Some(data.operator)
@@ -370,7 +373,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
&self, &self,
program: &mut Program, program: &mut Program,
root: Instruction, root: Instruction,
operator: Operator, operator: TestOperator,
r#type: Type, r#type: Type,
a: Part<Instruction>, a: Part<Instruction>,
) -> Instruction { ) -> Instruction {
@@ -383,11 +386,11 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
let (imms, args) = match operator.immediates_arity() { let (imms, args) = match operator.immediates_arity() {
0 => { 0 => {
assert_eq!(operator.params_arity(), 1); assert_eq!(operator.parameters_arity(), 1);
(vec![], vec![program.part_to_instruction(root, a).unwrap()]) (vec![], vec![program.part_to_instruction(root, a).unwrap()])
} }
1 => { 1 => {
assert_eq!(operator.params_arity(), 0); assert_eq!(operator.parameters_arity(), 0);
(vec![program.part_to_immediate(a).unwrap()], vec![]) (vec![program.part_to_immediate(a).unwrap()], vec![])
} }
_ => unreachable!(), _ => unreachable!(),
@@ -399,7 +402,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
&self, &self,
program: &mut Program, program: &mut Program,
root: Instruction, root: Instruction,
operator: Operator, operator: TestOperator,
r#type: Type, r#type: Type,
a: Part<Instruction>, a: Part<Instruction>,
b: Part<Instruction>, b: Part<Instruction>,
@@ -414,7 +417,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
let (imms, args) = match operator.immediates_arity() { let (imms, args) = match operator.immediates_arity() {
0 => { 0 => {
assert_eq!(operator.params_arity(), 2); assert_eq!(operator.parameters_arity(), 2);
( (
vec![], vec![],
vec![ vec![
@@ -424,14 +427,14 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
) )
} }
1 => { 1 => {
assert_eq!(operator.params_arity(), 1); assert_eq!(operator.parameters_arity(), 1);
( (
vec![program.part_to_immediate(a).unwrap()], vec![program.part_to_immediate(a).unwrap()],
vec![program.part_to_instruction(root, b).unwrap()], vec![program.part_to_instruction(root, b).unwrap()],
) )
} }
2 => { 2 => {
assert_eq!(operator.params_arity(), 0); assert_eq!(operator.parameters_arity(), 0);
( (
vec![ vec![
program.part_to_immediate(a).unwrap(), program.part_to_immediate(a).unwrap(),
@@ -449,7 +452,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
&self, &self,
program: &mut Program, program: &mut Program,
root: Instruction, root: Instruction,
operator: Operator, operator: TestOperator,
r#type: Type, r#type: Type,
a: Part<Instruction>, a: Part<Instruction>,
b: Part<Instruction>, b: Part<Instruction>,
@@ -465,7 +468,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
); );
let (imms, args) = match operator.immediates_arity() { let (imms, args) = match operator.immediates_arity() {
0 => { 0 => {
assert_eq!(operator.params_arity(), 3); assert_eq!(operator.parameters_arity(), 3);
( (
vec![], vec![],
vec![ vec![
@@ -476,7 +479,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
) )
} }
1 => { 1 => {
assert_eq!(operator.params_arity(), 2); assert_eq!(operator.parameters_arity(), 2);
( (
vec![program.part_to_immediate(a).unwrap()], vec![program.part_to_immediate(a).unwrap()],
vec![ vec![
@@ -486,7 +489,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
) )
} }
2 => { 2 => {
assert_eq!(operator.params_arity(), 1); assert_eq!(operator.parameters_arity(), 1);
( (
vec![ vec![
program.part_to_immediate(a).unwrap(), program.part_to_immediate(a).unwrap(),
@@ -496,7 +499,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
) )
} }
3 => { 3 => {
assert_eq!(operator.params_arity(), 0); assert_eq!(operator.parameters_arity(), 0);
( (
vec![ vec![
program.part_to_immediate(a).unwrap(), program.part_to_immediate(a).unwrap(),

View File

@@ -1,10 +1,10 @@
use peepmatic_runtime::{ use peepmatic_runtime::{
cc::ConditionCode, cc::ConditionCode,
operator::Operator,
part::Constant, part::Constant,
r#type::{BitWidth, Type}, r#type::{BitWidth, Type},
}; };
use peepmatic_test::*; use peepmatic_test::*;
use peepmatic_test_operator::TestOperator;
const TEST_ISA: TestIsa = TestIsa { const TEST_ISA: TestIsa = TestIsa {
native_word_size_in_bits: 32, native_word_size_in_bits: 32,
@@ -26,13 +26,13 @@ fn opcode() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, zero]); let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, zero]);
let new = optimizer.apply_one(&mut program, add); let new = optimizer.apply_one(&mut program, add);
let new = new.expect("optimization should have applied"); let new = new.expect("optimization should have applied");
assert!(program.structurally_eq(new, five)); assert!(program.structurally_eq(new, five));
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, five]); let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, five]);
let replacement = optimizer.apply_one(&mut program, add); let replacement = optimizer.apply_one(&mut program, add);
assert!(replacement.is_none()); assert!(replacement.is_none());
} }
@@ -45,10 +45,10 @@ fn constant() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, zero]); let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, zero]);
let expected = program.new_instruction( let expected = program.new_instruction(
Operator::IaddImm, TestOperator::IaddImm,
Type::i32(), Type::i32(),
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()], vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
vec![zero], vec![zero],
@@ -58,8 +58,8 @@ fn constant() {
let new = new.expect("optimization should have applied"); let new = new.expect("optimization should have applied");
assert!(program.structurally_eq(new, expected)); assert!(program.structurally_eq(new, expected));
let mul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, zero]); let mul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, zero]);
let add = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![mul, five]); let add = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![mul, five]);
let replacement = optimizer.apply_one(&mut program, add); let replacement = optimizer.apply_one(&mut program, add);
assert!(replacement.is_none()); assert!(replacement.is_none());
} }
@@ -71,7 +71,7 @@ fn boolean() {
let mut program = Program::default(); let mut program = Program::default();
let t = program.r#const(Constant::Bool(true, BitWidth::One), BitWidth::One); let t = program.r#const(Constant::Bool(true, BitWidth::One), BitWidth::One);
let bint = program.new_instruction(Operator::Bint, Type::i1(), vec![], vec![t]); let bint = program.new_instruction(TestOperator::Bint, Type::i1(), vec![], vec![t]);
let one = program.r#const(Constant::Int(1, BitWidth::One), BitWidth::ThirtyTwo); let one = program.r#const(Constant::Int(1, BitWidth::One), BitWidth::ThirtyTwo);
let new = optimizer.apply_one(&mut program, bint); let new = optimizer.apply_one(&mut program, bint);
@@ -79,7 +79,7 @@ fn boolean() {
assert!(program.structurally_eq(new, one)); assert!(program.structurally_eq(new, one));
let f = program.r#const(Constant::Bool(false, BitWidth::One), BitWidth::One); let f = program.r#const(Constant::Bool(false, BitWidth::One), BitWidth::One);
let bint = program.new_instruction(Operator::Bint, Type::i1(), vec![], vec![f]); let bint = program.new_instruction(TestOperator::Bint, Type::i1(), vec![], vec![f]);
let replacement = optimizer.apply_one(&mut program, bint); let replacement = optimizer.apply_one(&mut program, bint);
assert!(replacement.is_none()); assert!(replacement.is_none());
} }
@@ -92,7 +92,7 @@ fn condition_codes() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::One); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::One);
let icmp_eq = program.new_instruction( let icmp_eq = program.new_instruction(
Operator::Icmp, TestOperator::Icmp,
Type::b1(), Type::b1(),
vec![ConditionCode::Eq.into()], vec![ConditionCode::Eq.into()],
vec![five, five], vec![five, five],
@@ -104,7 +104,7 @@ fn condition_codes() {
assert!(program.structurally_eq(new, t)); assert!(program.structurally_eq(new, t));
let icmp_ne = program.new_instruction( let icmp_ne = program.new_instruction(
Operator::Icmp, TestOperator::Icmp,
Type::b1(), Type::b1(),
vec![ConditionCode::Ne.into()], vec![ConditionCode::Ne.into()],
vec![five, five], vec![five, five],
@@ -128,17 +128,17 @@ fn is_power_of_two() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]); let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
let one = program.r#const(Constant::Int(1, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let one = program.r#const(Constant::Int(1, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let ishl = program.new_instruction(Operator::Ishl, Type::i32(), vec![], vec![five, one]); let ishl = program.new_instruction(TestOperator::Ishl, Type::i32(), vec![], vec![five, one]);
let new = optimizer.apply_one(&mut program, imul); let new = optimizer.apply_one(&mut program, imul);
let new = new.expect("optimization should have applied"); let new = new.expect("optimization should have applied");
assert!(program.structurally_eq(new, ishl)); assert!(program.structurally_eq(new, ishl));
let three = program.r#const(Constant::Int(3, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let three = program.r#const(Constant::Int(3, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, three]); let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, three]);
let replacement = optimizer.apply_one(&mut program, imul); let replacement = optimizer.apply_one(&mut program, imul);
assert!(replacement.is_none()); assert!(replacement.is_none());
@@ -159,10 +159,10 @@ fn bit_width() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]); let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
let imul_imm = program.new_instruction( let imul_imm = program.new_instruction(
Operator::ImulImm, TestOperator::ImulImm,
Type::i32(), Type::i32(),
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()], vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
vec![two], vec![two],
@@ -174,7 +174,7 @@ fn bit_width() {
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour); let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour); let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]); let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
let replacement = optimizer.apply_one(&mut program, imul); let replacement = optimizer.apply_one(&mut program, imul);
assert!(replacement.is_none()); assert!(replacement.is_none());
@@ -195,10 +195,10 @@ fn fits_in_native_word() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]); let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
let imul_imm = program.new_instruction( let imul_imm = program.new_instruction(
Operator::ImulImm, TestOperator::ImulImm,
Type::i32(), Type::i32(),
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()], vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
vec![two], vec![two],
@@ -210,7 +210,7 @@ fn fits_in_native_word() {
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour); let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour); let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
let imul = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![five, two]); let imul = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![five, two]);
let replacement = optimizer.apply_one(&mut program, imul); let replacement = optimizer.apply_one(&mut program, imul);
assert!(replacement.is_none()); assert!(replacement.is_none());
@@ -230,10 +230,10 @@ fn unquote_neg() {
let mut program = Program::default(); let mut program = Program::default();
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour); let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour); let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
let isub = program.new_instruction(Operator::Isub, Type::i64(), vec![], vec![five, two]); let isub = program.new_instruction(TestOperator::Isub, Type::i64(), vec![], vec![five, two]);
let iadd_imm = program.new_instruction( let iadd_imm = program.new_instruction(
Operator::IaddImm, TestOperator::IaddImm,
Type::i64(), Type::i64(),
vec![Constant::Int(-2 as _, BitWidth::SixtyFour).into()], vec![Constant::Int(-2 as _, BitWidth::SixtyFour).into()],
vec![five], vec![five],
@@ -276,13 +276,13 @@ fn subsumption() {
log::debug!("(iadd (iadd (iadd w x) y) z) => (iadd (iadd w x) (iadd y z))"); log::debug!("(iadd (iadd (iadd w x) y) z) => (iadd (iadd w x) (iadd y z))");
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, y]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, y]);
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, z]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, z]);
let expected_lhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]); let expected_lhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
let expected_rhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]); let expected_rhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]);
let expected = program.new_instruction( let expected = program.new_instruction(
Operator::Iadd, TestOperator::Iadd,
Type::i64(), Type::i64(),
vec![], vec![],
vec![expected_lhs, expected_rhs], vec![expected_lhs, expected_rhs],
@@ -294,17 +294,17 @@ fn subsumption() {
log::debug!("(iadd w x) => y"); log::debug!("(iadd w x) => y");
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
let new = optimizer.apply_one(&mut program, iadd); let new = optimizer.apply_one(&mut program, iadd);
let new = new.expect("optimization should have applied"); let new = new.expect("optimization should have applied");
assert!(program.structurally_eq(new, y)); assert!(program.structurally_eq(new, y));
log::debug!("(iadd x (iadd y z)) => (iadd_imm x (iadd y z))"); log::debug!("(iadd x (iadd y z)) => (iadd_imm x (iadd y z))");
let iadd_y_z = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]); let iadd_y_z = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]);
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]);
let iadd_imm = program.new_instruction( let iadd_imm = program.new_instruction(
Operator::IaddImm, TestOperator::IaddImm,
Type::i64(), Type::i64(),
vec![Constant::Int(22, BitWidth::SixtyFour).into()], vec![Constant::Int(22, BitWidth::SixtyFour).into()],
vec![iadd_y_z], vec![iadd_y_z],
@@ -316,19 +316,19 @@ fn subsumption() {
log::debug!("(iadd (imul_imm x 1) (imul_imm x 1)) => (ishl_imm 1 (imul_imm x 1))"); log::debug!("(iadd (imul_imm x 1) (imul_imm x 1)) => (ishl_imm 1 (imul_imm x 1))");
let imul_imm = program.new_instruction( let imul_imm = program.new_instruction(
Operator::ImulImm, TestOperator::ImulImm,
Type::i64(), Type::i64(),
vec![Constant::Int(1, BitWidth::SixtyFour).into()], vec![Constant::Int(1, BitWidth::SixtyFour).into()],
vec![x], vec![x],
); );
let iadd = program.new_instruction( let iadd = program.new_instruction(
Operator::Iadd, TestOperator::Iadd,
Type::i64(), Type::i64(),
vec![], vec![],
vec![imul_imm, imul_imm], vec![imul_imm, imul_imm],
); );
let ishl_imm = program.new_instruction( let ishl_imm = program.new_instruction(
Operator::IshlImm, TestOperator::IshlImm,
Type::i64(), Type::i64(),
vec![Constant::Int(1, BitWidth::SixtyFour).into()], vec![Constant::Int(1, BitWidth::SixtyFour).into()],
vec![imul_imm], vec![imul_imm],
@@ -339,10 +339,10 @@ fn subsumption() {
log::debug!("(iadd (imul w x) (imul y z)) does not match any optimization."); log::debug!("(iadd (imul w x) (imul y z)) does not match any optimization.");
let imul_w_x = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![w, x]); let imul_w_x = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![w, x]);
let imul_y_z = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![y, z]); let imul_y_z = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![y, z]);
let iadd = program.new_instruction( let iadd = program.new_instruction(
Operator::Iadd, TestOperator::Iadd,
Type::i64(), Type::i64(),
vec![], vec![],
vec![imul_w_x, imul_y_z], vec![imul_w_x, imul_y_z],
@@ -363,9 +363,9 @@ fn polymorphic_bit_widths() {
let x = program.r#const(Constant::Int(42, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let x = program.r#const(Constant::Int(42, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let y = program.r#const(Constant::Int(420, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo); let y = program.r#const(Constant::Int(420, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
let iadd = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![x, y]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![x, y]);
let iadd_imm = program.new_instruction( let iadd_imm = program.new_instruction(
Operator::IaddImm, TestOperator::IaddImm,
Type::i32(), Type::i32(),
vec![Constant::Int(42, BitWidth::ThirtyTwo).into()], vec![Constant::Int(42, BitWidth::ThirtyTwo).into()],
vec![y], vec![y],
@@ -379,9 +379,9 @@ fn polymorphic_bit_widths() {
let x = program.r#const(Constant::Int(42, BitWidth::Sixteen), BitWidth::Sixteen); let x = program.r#const(Constant::Int(42, BitWidth::Sixteen), BitWidth::Sixteen);
let y = program.r#const(Constant::Int(420, BitWidth::Sixteen), BitWidth::Sixteen); let y = program.r#const(Constant::Int(420, BitWidth::Sixteen), BitWidth::Sixteen);
let iadd = program.new_instruction(Operator::Iadd, Type::i16(), vec![], vec![x, y]); let iadd = program.new_instruction(TestOperator::Iadd, Type::i16(), vec![], vec![x, y]);
let iadd_imm = program.new_instruction( let iadd_imm = program.new_instruction(
Operator::IaddImm, TestOperator::IaddImm,
Type::i16(), Type::i16(),
vec![Constant::Int(42, BitWidth::Sixteen).into()], vec![Constant::Int(42, BitWidth::Sixteen).into()],
vec![y], vec![y],

View File

@@ -0,0 +1,9 @@
[package]
name = "peepmatic-traits"
version = "0.66.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,26 @@
//! Shared traits, types, and macros for Peepmatic.
//!
//! This crate is used both at build time when constructing peephole optimizers
//! (i.e. in the `peepmatic` crate), and at run time when using pre-built
//! peephole optimizers (i.e. in the `peepmatic-runtime` crate and in
//! Cranelift's Peepmatic integration at `cranelift/codegen/src/peepmatic.rs`).
//!
//! This crate is similar to a header file: it should generally only contain
//! trait/type/macro definitions, not any code.
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#[macro_use]
mod operator;
pub use operator::*;
mod typing;
pub use typing::*;
/// Raise a panic about an unsupported operation.
#[cold]
#[inline(never)]
pub fn unsupported(msg: &str) -> ! {
panic!("unsupported: {}", msg)
}

View File

@@ -0,0 +1,317 @@
/// Define a `wast::parser::Parse` implementation for an operator type.
#[macro_export]
macro_rules! define_parse_impl_for_operator {
(
$operator:ident {
$(
$keyword:ident => $variant:ident;
)*
}
) => {
impl<'a> wast::parser::Parse<'a> for $operator {
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<$operator> {
/// Token definitions for our `Opcode` keywords.
mod tok {
$(
wast::custom_keyword!($keyword);
)*
}
// Peek at the next token, and if it is the variant's
// keyword, then consume it with `parse`, and finally return
// the `Opcode` variant.
$(
if p.peek::<tok::$keyword>() {
p.parse::<tok::$keyword>()?;
return Ok(Self::$variant);
}
)*
// If none of the keywords matched, then we get a parse error.
Err(p.error(concat!("expected `", stringify!($operator), "`")))
}
}
}
}
/// Define a `peepmatic_traits::TypingRules` implementation for the given
/// operator type.
#[macro_export]
macro_rules! define_typing_rules_impl_for_operator {
(
$operator:ident {
$(
$variant:ident {
$( immediates( $($immediate:ident),* ); )?
$( parameters( $($parameter:ident),* ); )?
result( $result:ident );
$( is_reduce($is_reduce:expr); )?
$( is_extend($is_extend:expr); )?
}
)*
}
) => {
impl $crate::TypingRules for $operator {
fn result_type<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
) -> C::TypeVariable
where
C: $crate::TypingContext<'a> {
match self {
$(
Self::$variant => typing_context.$result(span),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn immediates_arity(&self) -> u8 {
match self {
$(
Self::$variant => $crate::define_typing_rules_impl_for_operator!(
@arity;
$( $( $immediate, )* )?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn immediate_types<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
types: &mut impl Extend<C::TypeVariable>,
)
where
C: $crate::TypingContext<'a>
{
match self {
$(
Self::$variant => types.extend(
None.into_iter()
$(
$(
.chain(Some(typing_context.$immediate(span)))
)*
)?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn parameters_arity(&self) -> u8 {
match self {
$(
Self::$variant => $crate::define_typing_rules_impl_for_operator!(
@arity;
$( $( $parameter, )* )?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn parameter_types<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
types: &mut impl Extend<C::TypeVariable>,
)
where
C: $crate::TypingContext<'a>
{
match self {
$(
Self::$variant => types.extend(
None.into_iter()
$(
$(
.chain(Some(typing_context.$parameter(span)))
)*
)?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn is_reduce(&self) -> bool {
match self {
$(
Self::$variant if false $( || $is_reduce )? => false $( || $is_reduce )?,
)*
_ => false,
}
}
fn is_extend(&self) -> bool {
match self {
$(
Self::$variant if false $( || $is_extend )? => false $( || $is_extend )?,
)*
_ => false,
}
}
}
};
// Base case: zero arity.
(
@arity;
) => {
0
};
// Recursive case: count one for the head and add that to the arity of the
// rest.
(
@arity;
$head:ident,
$( $rest:ident, )*
) => {
1 + $crate::define_typing_rules_impl_for_operator!(
@arity;
$( $rest, )*
)
}
}
/// Define both a `wast::parser::Parse` implementation and a
/// `peepmatic_traits::TypingRules` implementation for the given operator type.
#[macro_export]
macro_rules! define_parse_and_typing_rules_for_operator {
(
$operator:ident {
$(
$keyword:ident => $variant:ident {
$( immediates( $($immediate:ident),* ); )?
$( parameters( $($parameter:ident),* ); )?
result( $result:ident );
$( is_reduce($is_reduce:expr); )?
$( is_extend($is_extend:expr); )?
}
)*
}
$( parse_cfg($parse_cfg:meta); )?
) => {
$( #[cfg($parse_cfg)] )?
$crate::define_parse_impl_for_operator! {
$operator {
$(
$keyword => $variant;
)*
}
}
$crate::define_typing_rules_impl_for_operator! {
$operator {
$(
$variant {
$( immediates( $($immediate),* ); )?
$( parameters( $($parameter),* ); )?
result( $result );
$( is_reduce($is_reduce); )?
$( is_extend($is_extend); )?
}
)*
}
}
}
}
/// Define an operator type, as well as its parsing and typing rules.
#[macro_export]
macro_rules! define_operator {
(
$( #[$attr:meta] )*
$operator:ident {
$(
$keywrord:ident => $variant:ident {
$( immediates( $($immediate:ident),* ); )?
$( parameters( $($parameter:ident),* ); )?
result( $result:ident );
$( is_reduce($is_reduce:expr); )?
$( is_extend($is_extend:expr); )?
}
)*
}
$( parse_cfg($parse_cfg:meta); )?
) => {
$( #[$attr] )*
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[repr(u32)]
pub enum $operator {
$(
$variant,
)*
}
impl From<$operator> for u32 {
#[inline]
fn from(x: $operator) -> u32 {
x as u32
}
}
impl From<$operator> for core::num::NonZeroU32 {
#[inline]
fn from(x: $operator) -> core::num::NonZeroU32 {
let x: u32 = x.into();
core::num::NonZeroU32::new(x.checked_add(1).unwrap()).unwrap()
}
}
impl core::convert::TryFrom<u32> for $operator {
type Error = ();
#[inline]
fn try_from(x: u32) -> Result<Self, ()> {
match x {
$(
x if x == Self::$variant.into() => Ok(Self::$variant),
)*
_ => Err(())
}
}
}
impl core::convert::TryFrom<core::num::NonZeroU32> for $operator {
type Error = ();
#[inline]
fn try_from(x: core::num::NonZeroU32) -> Result<Self, ()> {
let x = x.get().checked_sub(1).ok_or(())?;
Self::try_from(x)
}
}
$crate::define_parse_and_typing_rules_for_operator! {
$operator {
$(
$keywrord => $variant {
$( immediates( $($immediate),* ); )?
$( parameters( $($parameter),* ); )?
result( $result );
$( is_reduce($is_reduce); )?
$( is_extend($is_extend); )?
}
)*
}
$( parse_cfg($parse_cfg); )?
}
}
}

View File

@@ -0,0 +1,97 @@
/// 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`.
pub trait TypingContext<'a> {
/// A source span.
type Span: Copy;
/// A type variable.
type TypeVariable;
/// Create a condition code type.
fn cc(&mut self, span: Self::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: Self::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: Self::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: Self::Span) -> Self::TypeVariable;
/// Create the CPU flags type variable.
fn cpu_flags(&mut self, span: Self::Span) -> Self::TypeVariable;
/// Create a boolean type of size one bit.
fn b1(&mut self, span: Self::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: Self::Span) -> Self::TypeVariable;
/// Create a type variable that may be either a boolean or an integer.
fn bool_or_int(&mut self, span: Self::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: Self::Span) -> Self::TypeVariable;
}
/// The typing rules for a `TOperator` type.
///
/// This trait describes the types of immediates, parameters, and results of an
/// operator type, as well as their arity.
pub trait TypingRules {
/// Get the result type of this operator.
fn result_type<'a, C>(&self, span: C::Span, typing_context: &mut C) -> C::TypeVariable
where
C: TypingContext<'a>;
/// Get the number of immediates this operator has.
fn immediates_arity(&self) -> u8;
/// Get the types of this operator's immediates.
fn immediate_types<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
types: &mut impl Extend<C::TypeVariable>,
) where
C: TypingContext<'a>;
/// Get the number of parameters this operator has.
fn parameters_arity(&self) -> u8;
/// Get the types of this operator's parameters.
fn parameter_types<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
types: &mut impl Extend<C::TypeVariable>,
) where
C: TypingContext<'a>;
/// Is this a bit width reducing instruction?
///
/// E.g. Cranelift's `ireduce` instruction.
fn is_reduce(&self) -> bool;
/// Is this a bit width extending instruction?
///
/// E.g. Cranelift's `uextend` and `sextend` instructions.
fn is_extend(&self) -> bool;
}

View File

@@ -22,8 +22,8 @@
use peepmatic_macro::Ast; use peepmatic_macro::Ast;
use peepmatic_runtime::{ use peepmatic_runtime::{
operator::{Operator, UnquoteOperator},
r#type::{BitWidth, Type}, r#type::{BitWidth, Type},
unquote::UnquoteOperator,
}; };
use std::cell::Cell; use std::cell::Cell;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@@ -32,58 +32,58 @@ use wast::Id;
/// A reference to any AST node. /// A reference to any AST node.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum DynAstRef<'a> { pub enum DynAstRef<'a, TOperator> {
/// A reference to an `Optimizations`. /// A reference to an `Optimizations`.
Optimizations(&'a Optimizations<'a>), Optimizations(&'a Optimizations<'a, TOperator>),
/// A reference to an `Optimization`. /// A reference to an `Optimization`.
Optimization(&'a Optimization<'a>), Optimization(&'a Optimization<'a, TOperator>),
/// A reference to an `Lhs`. /// A reference to an `Lhs`.
Lhs(&'a Lhs<'a>), Lhs(&'a Lhs<'a, TOperator>),
/// A reference to an `Rhs`. /// A reference to an `Rhs`.
Rhs(&'a Rhs<'a>), Rhs(&'a Rhs<'a, TOperator>),
/// A reference to a `Pattern`. /// A reference to a `Pattern`.
Pattern(&'a Pattern<'a>), Pattern(&'a Pattern<'a, TOperator>),
/// A reference to a `Precondition`. /// A reference to a `Precondition`.
Precondition(&'a Precondition<'a>), Precondition(&'a Precondition<'a, TOperator>),
/// A reference to a `ConstraintOperand`. /// A reference to a `ConstraintOperand`.
ConstraintOperand(&'a ConstraintOperand<'a>), ConstraintOperand(&'a ConstraintOperand<'a, TOperator>),
/// A reference to a `ValueLiteral`. /// A reference to a `ValueLiteral`.
ValueLiteral(&'a ValueLiteral<'a>), ValueLiteral(&'a ValueLiteral<'a, TOperator>),
/// A reference to a `Constant`. /// A reference to a `Constant`.
Constant(&'a Constant<'a>), Constant(&'a Constant<'a, TOperator>),
/// A reference to a `PatternOperation`. /// A reference to a `PatternOperation`.
PatternOperation(&'a Operation<'a, Pattern<'a>>), PatternOperation(&'a Operation<'a, TOperator, Pattern<'a, TOperator>>),
/// A reference to a `Variable`. /// A reference to a `Variable`.
Variable(&'a Variable<'a>), Variable(&'a Variable<'a, TOperator>),
/// A reference to an `Integer`. /// A reference to an `Integer`.
Integer(&'a Integer<'a>), Integer(&'a Integer<'a, TOperator>),
/// A reference to a `Boolean`. /// A reference to a `Boolean`.
Boolean(&'a Boolean<'a>), Boolean(&'a Boolean<'a, TOperator>),
/// A reference to a `ConditionCode`. /// A reference to a `ConditionCode`.
ConditionCode(&'a ConditionCode<'a>), ConditionCode(&'a ConditionCode<'a, TOperator>),
/// A reference to an `Unquote`. /// A reference to an `Unquote`.
Unquote(&'a Unquote<'a>), Unquote(&'a Unquote<'a, TOperator>),
/// A reference to an `RhsOperation`. /// A reference to an `RhsOperation`.
RhsOperation(&'a Operation<'a, Rhs<'a>>), RhsOperation(&'a Operation<'a, TOperator, Rhs<'a, TOperator>>),
} }
impl<'a, 'b> ChildNodes<'a, 'b> for DynAstRef<'a> { impl<'a, 'b, TOperator> ChildNodes<'a, 'b, TOperator> for DynAstRef<'a, TOperator> {
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a>>) { fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a, TOperator>>) {
match self { match self {
Self::Optimizations(x) => x.child_nodes(sink), Self::Optimizations(x) => x.child_nodes(sink),
Self::Optimization(x) => x.child_nodes(sink), Self::Optimization(x) => x.child_nodes(sink),
@@ -118,23 +118,28 @@ impl<'a, 'b> ChildNodes<'a, 'b> for DynAstRef<'a> {
/// This trait is blanked implemented for everything that does those three /// This trait is blanked implemented for everything that does those three
/// things, and in practice those three thrings are all implemented by the /// things, and in practice those three thrings are all implemented by the
/// `derive(Ast)` macro. /// `derive(Ast)` macro.
pub trait Ast<'a>: 'a + ChildNodes<'a, 'a> + Span pub trait Ast<'a, TOperator>: 'a + ChildNodes<'a, 'a, TOperator> + Span
where where
DynAstRef<'a>: From<&'a Self>, DynAstRef<'a, TOperator>: From<&'a Self>,
TOperator: 'a,
{ {
} }
impl<'a, T> Ast<'a> for T impl<'a, T, TOperator> Ast<'a, TOperator> for T
where where
T: 'a + ?Sized + ChildNodes<'a, 'a> + Span, T: 'a + ?Sized + ChildNodes<'a, 'a, TOperator> + Span,
DynAstRef<'a>: From<&'a Self>, DynAstRef<'a, TOperator>: From<&'a Self>,
TOperator: 'a,
{ {
} }
/// Enumerate the child AST nodes of a given node. /// Enumerate the child AST nodes of a given node.
pub trait ChildNodes<'a, 'b> { pub trait ChildNodes<'a, 'b, TOperator>
where
TOperator: 'a,
{
/// Get each of this AST node's children, in order. /// Get each of this AST node's children, in order.
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a>>); fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a, TOperator>>);
} }
/// A trait for getting the span where an AST node was defined. /// A trait for getting the span where an AST node was defined.
@@ -147,30 +152,30 @@ pub trait Span {
/// ///
/// This is the root AST node. /// This is the root AST node.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Optimizations<'a> { pub struct Optimizations<'a, TOperator> {
/// Where these `Optimizations` were defined. /// Where these `Optimizations` were defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
/// The optimizations. /// The optimizations.
#[peepmatic(flatten)] #[peepmatic(flatten)]
pub optimizations: Vec<Optimization<'a>>, pub optimizations: Vec<Optimization<'a, TOperator>>,
} }
/// A complete optimization: a left-hand side to match against and a right-hand /// A complete optimization: a left-hand side to match against and a right-hand
/// side replacement. /// side replacement.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Optimization<'a> { pub struct Optimization<'a, TOperator> {
/// Where this `Optimization` was defined. /// Where this `Optimization` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
/// The left-hand side that matches when this optimization applies. /// The left-hand side that matches when this optimization applies.
pub lhs: Lhs<'a>, pub lhs: Lhs<'a, TOperator>,
/// The new sequence of instructions to replace an old sequence that matches /// The new sequence of instructions to replace an old sequence that matches
/// the left-hand side with. /// the left-hand side with.
pub rhs: Rhs<'a>, pub rhs: Rhs<'a, TOperator>,
} }
/// A left-hand side describes what is required for a particular optimization to /// A left-hand side describes what is required for a particular optimization to
@@ -180,58 +185,58 @@ pub struct Optimization<'a> {
/// candidate instruction sequences, and zero or more preconditions that add /// candidate instruction sequences, and zero or more preconditions that add
/// additional constraints upon instruction sequences matched by the pattern. /// additional constraints upon instruction sequences matched by the pattern.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Lhs<'a> { pub struct Lhs<'a, TOperator> {
/// Where this `Lhs` was defined. /// Where this `Lhs` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
/// A pattern that describes sequences of instructions to match. /// A pattern that describes sequences of instructions to match.
pub pattern: Pattern<'a>, pub pattern: Pattern<'a, TOperator>,
/// Additional constraints that a match must satisfy in addition to /// Additional constraints that a match must satisfy in addition to
/// structually matching the pattern, e.g. some constant must be a power of /// structually matching the pattern, e.g. some constant must be a power of
/// two. /// two.
#[peepmatic(flatten)] #[peepmatic(flatten)]
pub preconditions: Vec<Precondition<'a>>, pub preconditions: Vec<Precondition<'a, TOperator>>,
} }
/// A structural pattern, potentially with wildcard variables for matching whole /// A structural pattern, potentially with wildcard variables for matching whole
/// subtrees. /// subtrees.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub enum Pattern<'a> { pub enum Pattern<'a, TOperator> {
/// A specific value. These are written as `1234` or `0x1234` or `true` or /// A specific value. These are written as `1234` or `0x1234` or `true` or
/// `false`. /// `false`.
ValueLiteral(ValueLiteral<'a>), ValueLiteral(ValueLiteral<'a, TOperator>),
/// A constant that matches any constant value. This subsumes value /// A constant that matches any constant value. This subsumes value
/// patterns. These are upper-case identifiers like `$C`. /// patterns. These are upper-case identifiers like `$C`.
Constant(Constant<'a>), Constant(Constant<'a, TOperator>),
/// An operation pattern with zero or more operand patterns. These are /// An operation pattern with zero or more operand patterns. These are
/// s-expressions like `(iadd $x $y)`. /// s-expressions like `(iadd $x $y)`.
Operation(Operation<'a, Pattern<'a>>), Operation(Operation<'a, TOperator, Pattern<'a, TOperator>>),
/// A variable that matches any kind of subexpression. This subsumes all /// A variable that matches any kind of subexpression. This subsumes all
/// other patterns. These are lower-case identifiers like `$x`. /// other patterns. These are lower-case identifiers like `$x`.
Variable(Variable<'a>), Variable(Variable<'a, TOperator>),
} }
/// An integer or boolean value literal. /// An integer or boolean value literal.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub enum ValueLiteral<'a> { pub enum ValueLiteral<'a, TOperator> {
/// An integer value. /// An integer value.
Integer(Integer<'a>), Integer(Integer<'a, TOperator>),
/// A boolean value: `true` or `false`. /// A boolean value: `true` or `false`.
Boolean(Boolean<'a>), Boolean(Boolean<'a, TOperator>),
/// A condition code: `eq`, `ne`, etc... /// A condition code: `eq`, `ne`, etc...
ConditionCode(ConditionCode<'a>), ConditionCode(ConditionCode<'a, TOperator>),
} }
/// An integer literal. /// An integer literal.
#[derive(Debug, PartialEq, Eq, Ast)] #[derive(Debug, PartialEq, Eq, Ast)]
pub struct Integer<'a> { pub struct Integer<'a, TOperator> {
/// Where this `Integer` was defined. /// Where this `Integer` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -255,10 +260,10 @@ pub struct Integer<'a> {
#[allow(missing_docs)] #[allow(missing_docs)]
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub marker: PhantomData<&'a ()>, pub marker: PhantomData<&'a TOperator>,
} }
impl Hash for Integer<'_> { impl<TOperator> Hash for Integer<'_, TOperator> {
fn hash<H>(&self, state: &mut H) fn hash<H>(&self, state: &mut H)
where where
H: Hasher, H: Hasher,
@@ -278,7 +283,7 @@ impl Hash for Integer<'_> {
/// A boolean literal. /// A boolean literal.
#[derive(Debug, PartialEq, Eq, Ast)] #[derive(Debug, PartialEq, Eq, Ast)]
pub struct Boolean<'a> { pub struct Boolean<'a, TOperator> {
/// Where this `Boolean` was defined. /// Where this `Boolean` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -299,10 +304,10 @@ pub struct Boolean<'a> {
#[allow(missing_docs)] #[allow(missing_docs)]
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub marker: PhantomData<&'a ()>, pub marker: PhantomData<&'a TOperator>,
} }
impl Hash for Boolean<'_> { impl<TOperator> Hash for Boolean<'_, TOperator> {
fn hash<H>(&self, state: &mut H) fn hash<H>(&self, state: &mut H)
where where
H: Hasher, H: Hasher,
@@ -322,7 +327,7 @@ impl Hash for Boolean<'_> {
/// A condition code. /// A condition code.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct ConditionCode<'a> { pub struct ConditionCode<'a, TOperator> {
/// Where this `ConditionCode` was defined. /// Where this `ConditionCode` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -333,7 +338,7 @@ pub struct ConditionCode<'a> {
#[allow(missing_docs)] #[allow(missing_docs)]
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub marker: PhantomData<&'a ()>, pub marker: PhantomData<&'a TOperator>,
} }
/// A symbolic constant. /// A symbolic constant.
@@ -341,7 +346,7 @@ pub struct ConditionCode<'a> {
/// These are identifiers containing uppercase letters: `$C`, `$MY-CONST`, /// These are identifiers containing uppercase letters: `$C`, `$MY-CONST`,
/// `$CONSTANT1`. /// `$CONSTANT1`.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Constant<'a> { pub struct Constant<'a, TOperator> {
/// Where this `Constant` was defined. /// Where this `Constant` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -349,6 +354,10 @@ pub struct Constant<'a> {
/// This constant's identifier. /// This constant's identifier.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub id: Id<'a>, pub id: Id<'a>,
#[allow(missing_docs)]
#[peepmatic(skip_child)]
pub marker: PhantomData<&'a TOperator>,
} }
/// A variable that matches any subtree. /// A variable that matches any subtree.
@@ -357,7 +366,7 @@ pub struct Constant<'a> {
/// being the same as each other occurrence as well, e.g. `(iadd $x $x)` matches /// being the same as each other occurrence as well, e.g. `(iadd $x $x)` matches
/// `(iadd 5 5)` but not `(iadd 1 2)`. /// `(iadd 5 5)` but not `(iadd 1 2)`.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Variable<'a> { pub struct Variable<'a, TOperator> {
/// Where this `Variable` was defined. /// Where this `Variable` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -365,15 +374,20 @@ pub struct Variable<'a> {
/// This variable's identifier. /// This variable's identifier.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub id: Id<'a>, pub id: Id<'a>,
#[allow(missing_docs)]
#[peepmatic(skip_child)]
pub marker: PhantomData<&'a TOperator>,
} }
/// An operation with an operator, and operands of type `T`. /// An operation with an operator, and operands of type `T`.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
#[peepmatic(no_into_dyn_node)] #[peepmatic(no_into_dyn_node)]
pub struct Operation<'a, T> pub struct Operation<'a, TOperator, TOperand>
where where
T: 'a + Ast<'a>, TOperator: 'a,
DynAstRef<'a>: From<&'a T>, TOperand: 'a + Ast<'a, TOperator>,
DynAstRef<'a, TOperator>: From<&'a TOperand>,
{ {
/// The span where this operation was written. /// The span where this operation was written.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
@@ -381,7 +395,7 @@ where
/// The operator for this operation, e.g. `imul` or `iadd`. /// The operator for this operation, e.g. `imul` or `iadd`.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub operator: Operator, pub operator: TOperator,
/// An optional ascribed or inferred type for the operator. /// An optional ascribed or inferred type for the operator.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
@@ -393,23 +407,27 @@ where
/// the operands. When `Operation is used in a right-hand side replacement, /// the operands. When `Operation is used in a right-hand side replacement,
/// these are the sub-replacements for the operands. /// these are the sub-replacements for the operands.
#[peepmatic(flatten)] #[peepmatic(flatten)]
pub operands: Vec<T>, pub operands: Vec<TOperand>,
#[allow(missing_docs)] #[allow(missing_docs)]
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub marker: PhantomData<&'a ()>, pub marker: PhantomData<&'a ()>,
} }
impl<'a> From<&'a Operation<'a, Pattern<'a>>> for DynAstRef<'a> { impl<'a, TOperator> From<&'a Operation<'a, TOperator, Pattern<'a, TOperator>>>
for DynAstRef<'a, TOperator>
{
#[inline] #[inline]
fn from(o: &'a Operation<'a, Pattern<'a>>) -> DynAstRef<'a> { fn from(o: &'a Operation<'a, TOperator, Pattern<'a, TOperator>>) -> DynAstRef<'a, TOperator> {
DynAstRef::PatternOperation(o) DynAstRef::PatternOperation(o)
} }
} }
impl<'a> From<&'a Operation<'a, Rhs<'a>>> for DynAstRef<'a> { impl<'a, TOperator> From<&'a Operation<'a, TOperator, Rhs<'a, TOperator>>>
for DynAstRef<'a, TOperator>
{
#[inline] #[inline]
fn from(o: &'a Operation<'a, Rhs<'a>>) -> DynAstRef<'a> { fn from(o: &'a Operation<'a, TOperator, Rhs<'a, TOperator>>) -> DynAstRef<'a, TOperator> {
DynAstRef::RhsOperation(o) DynAstRef::RhsOperation(o)
} }
} }
@@ -417,7 +435,7 @@ impl<'a> From<&'a Operation<'a, Rhs<'a>>> for DynAstRef<'a> {
/// A precondition adds additional constraints to a pattern, such as "$C must be /// A precondition adds additional constraints to a pattern, such as "$C must be
/// a power of two". /// a power of two".
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Precondition<'a> { pub struct Precondition<'a, TOperator> {
/// Where this `Precondition` was defined. /// Where this `Precondition` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -428,7 +446,11 @@ pub struct Precondition<'a> {
/// The operands of the constraint. /// The operands of the constraint.
#[peepmatic(flatten)] #[peepmatic(flatten)]
pub operands: Vec<ConstraintOperand<'a>>, pub operands: Vec<ConstraintOperand<'a, TOperator>>,
#[allow(missing_docs)]
#[peepmatic(skip_child)]
pub marker: PhantomData<&'a TOperator>,
} }
/// Contraint operators. /// Contraint operators.
@@ -446,40 +468,40 @@ pub enum Constraint {
/// An operand of a precondition's constraint. /// An operand of a precondition's constraint.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub enum ConstraintOperand<'a> { pub enum ConstraintOperand<'a, TOperator> {
/// A value literal operand. /// A value literal operand.
ValueLiteral(ValueLiteral<'a>), ValueLiteral(ValueLiteral<'a, TOperator>),
/// A constant operand. /// A constant operand.
Constant(Constant<'a>), Constant(Constant<'a, TOperator>),
/// A variable operand. /// A variable operand.
Variable(Variable<'a>), Variable(Variable<'a, TOperator>),
} }
/// The right-hand side of an optimization that contains the instructions to /// The right-hand side of an optimization that contains the instructions to
/// replace any matched left-hand side with. /// replace any matched left-hand side with.
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub enum Rhs<'a> { pub enum Rhs<'a, TOperator> {
/// A value literal right-hand side. /// A value literal right-hand side.
ValueLiteral(ValueLiteral<'a>), ValueLiteral(ValueLiteral<'a, TOperator>),
/// A constant right-hand side (the constant must have been matched and /// A constant right-hand side (the constant must have been matched and
/// bound in the left-hand side's pattern). /// bound in the left-hand side's pattern).
Constant(Constant<'a>), Constant(Constant<'a, TOperator>),
/// A variable right-hand side (the variable must have been matched and /// A variable right-hand side (the variable must have been matched and
/// bound in the left-hand side's pattern). /// bound in the left-hand side's pattern).
Variable(Variable<'a>), Variable(Variable<'a, TOperator>),
/// An unquote expression that is evaluated while replacing the left-hand /// An unquote expression that is evaluated while replacing the left-hand
/// side with the right-hand side. The result of the evaluation is used in /// side with the right-hand side. The result of the evaluation is used in
/// the replacement. /// the replacement.
Unquote(Unquote<'a>), Unquote(Unquote<'a, TOperator>),
/// A compound right-hand side consisting of an operation and subsequent /// A compound right-hand side consisting of an operation and subsequent
/// right-hand side operands. /// right-hand side operands.
Operation(Operation<'a, Rhs<'a>>), Operation(Operation<'a, TOperator, Rhs<'a, TOperator>>),
} }
/// An unquote operation. /// An unquote operation.
@@ -493,7 +515,7 @@ pub enum Rhs<'a> {
/// instructions that match its left-hand side with the compile-time result of /// instructions that match its left-hand side with the compile-time result of
/// `log2($C)` (the left-hand side must match and bind the constant `$C`). /// `log2($C)` (the left-hand side must match and bind the constant `$C`).
#[derive(Debug, Ast)] #[derive(Debug, Ast)]
pub struct Unquote<'a> { pub struct Unquote<'a, TOperator> {
/// Where this `Unquote` was defined. /// Where this `Unquote` was defined.
#[peepmatic(skip_child)] #[peepmatic(skip_child)]
pub span: wast::Span, pub span: wast::Span,
@@ -504,5 +526,9 @@ pub struct Unquote<'a> {
/// The operands for this unquote operation. /// The operands for this unquote operation.
#[peepmatic(flatten)] #[peepmatic(flatten)]
pub operands: Vec<Rhs<'a>>, pub operands: Vec<Rhs<'a, TOperator>>,
#[allow(missing_docs)]
#[peepmatic(skip_child)]
pub marker: PhantomData<&'a TOperator>,
} }

View File

@@ -2,14 +2,20 @@
use peepmatic_automata::{Automaton, Builder}; use peepmatic_automata::{Automaton, Builder};
use peepmatic_runtime::linear; use peepmatic_runtime::linear;
use std::fmt::Debug;
use std::hash::Hash;
/// Construct an automaton from a set of linear optimizations. /// Construct an automaton from a set of linear optimizations.
pub fn automatize( pub fn automatize<TOperator>(
opts: &linear::Optimizations, opts: &linear::Optimizations<TOperator>,
) -> Automaton<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> { ) -> Automaton<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
where
TOperator: Copy + Debug + Eq + Hash,
{
debug_assert!(crate::linear_passes::is_sorted_lexicographically(opts)); debug_assert!(crate::linear_passes::is_sorted_lexicographically(opts));
let mut builder = Builder::<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>>::new(); let mut builder =
Builder::<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>::new();
for opt in &opts.optimizations { for opt in &opts.optimizations {
let mut insertion = builder.insert(); let mut insertion = builder.insert();

View File

@@ -7,17 +7,21 @@ use peepmatic_runtime::{
cc::ConditionCode, cc::ConditionCode,
integer_interner::{IntegerId, IntegerInterner}, integer_interner::{IntegerId, IntegerInterner},
linear, linear,
operator::Operator,
paths::{PathId, PathInterner}, paths::{PathId, PathInterner},
}; };
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt::Debug;
use std::io::{self, Write}; use std::io::{self, Write};
use std::num::NonZeroU16; use std::num::{NonZeroU16, NonZeroU32};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct PeepholeDotFmt<'a>(pub(crate) &'a PathInterner, pub(crate) &'a IntegerInterner); pub(crate) struct PeepholeDotFmt<'a>(pub(crate) &'a PathInterner, pub(crate) &'a IntegerInterner);
impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for PeepholeDotFmt<'_> { impl<TOperator> DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
for PeepholeDotFmt<'_>
where
TOperator: Debug + TryFrom<NonZeroU32>,
{
fn fmt_transition( fn fmt_transition(
&self, &self,
w: &mut impl Write, w: &mut impl Write,
@@ -26,22 +30,23 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
_to: Option<&linear::MatchOp>, _to: Option<&linear::MatchOp>,
) -> io::Result<()> { ) -> io::Result<()> {
let from = from.expect("we should have match op for every state"); let from = from.expect("we should have match op for every state");
if let Some(x) = input.ok().map(|x| x.get()) { if let Some(x) = input.ok() {
match from { match from {
linear::MatchOp::Opcode { .. } => { linear::MatchOp::Opcode { .. } => {
let opcode = let opcode = TOperator::try_from(x)
Operator::try_from(x).expect("we shouldn't generate non-opcode edges"); .map_err(|_| ())
write!(w, "{}", opcode) .expect("we shouldn't generate non-opcode edges");
write!(w, "{:?}", opcode)
} }
linear::MatchOp::ConditionCode { .. } => { linear::MatchOp::ConditionCode { .. } => {
let cc = let cc = ConditionCode::try_from(x.get())
ConditionCode::try_from(x).expect("we shouldn't generate non-CC edges"); .expect("we shouldn't generate non-CC edges");
write!(w, "{}", cc) write!(w, "{}", cc)
} }
linear::MatchOp::IntegerValue { .. } => { linear::MatchOp::IntegerValue { .. } => {
let x = self let x = self.1.lookup(IntegerId(
.1 NonZeroU16::new(x.get().try_into().unwrap()).unwrap(),
.lookup(IntegerId(NonZeroU16::new(x.try_into().unwrap()).unwrap())); ));
write!(w, "{}", x) write!(w, "{}", x)
} }
_ => write!(w, "Ok({})", x), _ => write!(w, "Ok({})", x),
@@ -73,7 +78,11 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
writeln!(w, "</font>") writeln!(w, "</font>")
} }
fn fmt_output(&self, w: &mut impl Write, actions: &Box<[linear::Action]>) -> io::Result<()> { fn fmt_output(
&self,
w: &mut impl Write,
actions: &Box<[linear::Action<TOperator>]>,
) -> io::Result<()> {
use linear::Action::*; use linear::Action::*;
if actions.is_empty() { if actions.is_empty() {
@@ -88,11 +97,11 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
match a { match a {
GetLhs { path } => write!(w, "get-lhs @ {}<br/>", p(path))?, GetLhs { path } => write!(w, "get-lhs @ {}<br/>", p(path))?,
UnaryUnquote { operator, operand } => { UnaryUnquote { operator, operand } => {
write!(w, "eval {} $rhs{}<br/>", operator, operand.0)? write!(w, "eval {:?} $rhs{}<br/>", operator, operand.0)?
} }
BinaryUnquote { operator, operands } => write!( BinaryUnquote { operator, operands } => write!(
w, w,
"eval {} $rhs{}, $rhs{}<br/>", "eval {:?} $rhs{}, $rhs{}<br/>",
operator, operands[0].0, operands[1].0, operator, operands[0].0, operands[1].0,
)?, )?,
MakeIntegerConst { MakeIntegerConst {
@@ -108,14 +117,14 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
operand, operand,
operator, operator,
r#type: _, r#type: _,
} => write!(w, "make {} $rhs{}<br/>", operator, operand.0,)?, } => write!(w, "make {:?} $rhs{}<br/>", operator, operand.0,)?,
MakeBinaryInst { MakeBinaryInst {
operator, operator,
operands, operands,
r#type: _, r#type: _,
} => write!( } => write!(
w, w,
"make {} $rhs{}, $rhs{}<br/>", "make {:?} $rhs{}, $rhs{}<br/>",
operator, operands[0].0, operands[1].0, operator, operands[0].0, operands[1].0,
)?, )?,
MakeTernaryInst { MakeTernaryInst {
@@ -124,7 +133,7 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
r#type: _, r#type: _,
} => write!( } => write!(
w, w,
"make {} $rhs{}, $rhs{}, $rhs{}<br/>", "make {:?} $rhs{}, $rhs{}, $rhs{}<br/>",
operator, operands[0].0, operands[1].0, operands[2].0, operator, operands[0].0, operands[1].0, operands[2].0,
)?, )?,
} }

View File

@@ -23,20 +23,25 @@ pub use self::{
}; };
use peepmatic_runtime::PeepholeOptimizations; use peepmatic_runtime::PeepholeOptimizations;
use peepmatic_traits::TypingRules;
use std::convert::TryFrom;
use std::fmt::Debug;
use std::fs; use std::fs;
use std::hash::Hash;
use std::num::NonZeroU32;
use std::path::Path; use std::path::Path;
/// Compile the given DSL file into a compact peephole optimizations automaton! /// Compile the given DSL file into a compact peephole optimizations automaton!
/// ///
/// ## Example /// ## Example
/// ///
/// ```no_run /// ```ignore
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// use std::path::Path; /// use std::path::Path;
/// ///
/// let peep_opts = peepmatic::compile_file(Path::new( /// let peep_opts = peepmatic::compile_file::<cranelift_codegen::ir::Opcode>(
/// "path/to/optimizations.peepmatic" /// Path::new("path/to/optimizations.peepmatic")
/// ))?; /// )?;
/// ///
/// // Use the peephole optimizations or serialize them into bytes here... /// // Use the peephole optimizations or serialize them into bytes here...
/// # Ok(()) /// # Ok(())
@@ -49,9 +54,19 @@ use std::path::Path;
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz /// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the /// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
/// peephole optimizer's automaton will be written to that file path. /// peephole optimizer's automaton will be written to that file path.
pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> { pub fn compile_file<TOperator>(filename: &Path) -> anyhow::Result<PeepholeOptimizations<TOperator>>
where
TOperator: Copy
+ Debug
+ Eq
+ Hash
+ for<'a> wast::parser::Parse<'a>
+ TypingRules
+ Into<NonZeroU32>
+ TryFrom<NonZeroU32>,
{
let source = fs::read_to_string(filename)?; let source = fs::read_to_string(filename)?;
compile_str(&source, filename) compile_str::<TOperator>(&source, filename)
} }
/// Compile the given DSL source text down into a compact peephole optimizations /// Compile the given DSL source text down into a compact peephole optimizations
@@ -64,11 +79,11 @@ pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
/// ///
/// ## Example /// ## Example
/// ///
/// ```no_run /// ```ignore
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// use std::path::Path; /// use std::path::Path;
/// ///
/// let peep_opts = peepmatic::compile_str( /// let peep_opts = peepmatic::compile_str::<cranelift_codegen::ir::Opcode>(
/// " /// "
/// (=> (iadd $x 0) $x) /// (=> (iadd $x 0) $x)
/// (=> (imul $x 0) 0) /// (=> (imul $x 0) 0)
@@ -88,14 +103,27 @@ pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz /// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the /// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
/// peephole optimizer's automaton will be written to that file path. /// peephole optimizer's automaton will be written to that file path.
pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result<PeepholeOptimizations> { pub fn compile_str<TOperator>(
source: &str,
filename: &Path,
) -> anyhow::Result<PeepholeOptimizations<TOperator>>
where
TOperator: Copy
+ Debug
+ Eq
+ Hash
+ for<'a> wast::parser::Parse<'a>
+ TypingRules
+ Into<NonZeroU32>
+ TryFrom<NonZeroU32>,
{
let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| { let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| {
e.set_path(filename); e.set_path(filename);
e.set_text(source); e.set_text(source);
e e
})?; })?;
let opts = wast::parser::parse::<Optimizations>(&buf).map_err(|mut e| { let opts = wast::parser::parse::<Optimizations<'_, TOperator>>(&buf).map_err(|mut e| {
e.set_path(filename); e.set_path(filename);
e.set_text(source); e.set_text(source);
e e
@@ -137,9 +165,10 @@ pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result<PeepholeOpti
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use peepmatic_test_operator::TestOperator;
fn assert_compiles(path: &str) { fn assert_compiles(path: &str) {
match compile_file(Path::new(path)) { match compile_file::<TestOperator>(Path::new(path)) {
Ok(_) => return, Ok(_) => return,
Err(e) => { Err(e) => {
eprintln!("error: {}", e); eprintln!("error: {}", e);

View File

@@ -5,6 +5,8 @@ use peepmatic_runtime::{
paths::{PathId, PathInterner}, paths::{PathId, PathInterner},
}; };
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::Debug;
use std::hash::Hash;
/// Sort a set of optimizations from least to most general. /// Sort a set of optimizations from least to most general.
/// ///
@@ -25,7 +27,10 @@ use std::cmp::Ordering;
/// ///
/// and we are matching `(imul 4 (..))`, then we want to apply the second /// and we are matching `(imul 4 (..))`, then we want to apply the second
/// optimization, because it is more specific than the first. /// optimization, because it is more specific than the first.
pub fn sort_least_to_most_general(opts: &mut linear::Optimizations) { pub fn sort_least_to_most_general<TOperator>(opts: &mut linear::Optimizations<TOperator>)
where
TOperator: Copy + Debug + Eq + Hash,
{
let linear::Optimizations { let linear::Optimizations {
ref mut optimizations, ref mut optimizations,
ref paths, ref paths,
@@ -41,7 +46,10 @@ pub fn sort_least_to_most_general(opts: &mut linear::Optimizations) {
/// Sort the linear optimizations lexicographically. /// Sort the linear optimizations lexicographically.
/// ///
/// This sort order is required for automata construction. /// This sort order is required for automata construction.
pub fn sort_lexicographically(opts: &mut linear::Optimizations) { pub fn sort_lexicographically<TOperator>(opts: &mut linear::Optimizations<TOperator>)
where
TOperator: Copy + Debug + Eq + Hash,
{
let linear::Optimizations { let linear::Optimizations {
ref mut optimizations, ref mut optimizations,
ref paths, ref paths,
@@ -53,12 +61,15 @@ pub fn sort_lexicographically(opts: &mut linear::Optimizations) {
.sort_by(|a, b| compare_optimizations(paths, a, b, |a_len, b_len| a_len.cmp(&b_len))); .sort_by(|a, b| compare_optimizations(paths, a, b, |a_len, b_len| a_len.cmp(&b_len)));
} }
fn compare_optimizations( fn compare_optimizations<TOperator>(
paths: &PathInterner, paths: &PathInterner,
a: &linear::Optimization, a: &linear::Optimization<TOperator>,
b: &linear::Optimization, b: &linear::Optimization<TOperator>,
compare_lengths: impl Fn(usize, usize) -> Ordering, compare_lengths: impl Fn(usize, usize) -> Ordering,
) -> Ordering { ) -> Ordering
where
TOperator: Copy + Debug + Eq + Hash,
{
for (a, b) in a.increments.iter().zip(b.increments.iter()) { for (a, b) in a.increments.iter().zip(b.increments.iter()) {
let c = compare_match_op_generality(paths, a.operation, b.operation); let c = compare_match_op_generality(paths, a.operation, b.operation);
if c != Ordering::Equal { if c != Ordering::Equal {
@@ -79,11 +90,14 @@ fn compare_optimizations(
compare_lengths(a.increments.len(), b.increments.len()) compare_lengths(a.increments.len(), b.increments.len())
} }
fn compare_optimization_generality( fn compare_optimization_generality<TOperator>(
paths: &PathInterner, paths: &PathInterner,
a: &linear::Optimization, a: &linear::Optimization<TOperator>,
b: &linear::Optimization, b: &linear::Optimization<TOperator>,
) -> Ordering { ) -> Ordering
where
TOperator: Copy + Debug + Eq + Hash,
{
compare_optimizations(paths, a, b, |a_len, b_len| { compare_optimizations(paths, a, b, |a_len, b_len| {
// If they shared equivalent prefixes, then compare lengths and invert the // If they shared equivalent prefixes, then compare lengths and invert the
// result because longer patterns are less general than shorter patterns. // result because longer patterns are less general than shorter patterns.
@@ -158,14 +172,22 @@ fn compare_paths(paths: &PathInterner, a: PathId, b: PathId) -> Ordering {
} }
/// Are the given optimizations sorted from least to most general? /// Are the given optimizations sorted from least to most general?
pub(crate) fn is_sorted_by_generality(opts: &linear::Optimizations) -> bool { pub(crate) fn is_sorted_by_generality<TOperator>(opts: &linear::Optimizations<TOperator>) -> bool
where
TOperator: Copy + Debug + Eq + Hash,
{
opts.optimizations opts.optimizations
.windows(2) .windows(2)
.all(|w| compare_optimization_generality(&opts.paths, &w[0], &w[1]) <= Ordering::Equal) .all(|w| compare_optimization_generality(&opts.paths, &w[0], &w[1]) <= Ordering::Equal)
} }
/// Are the given optimizations sorted lexicographically? /// Are the given optimizations sorted lexicographically?
pub(crate) fn is_sorted_lexicographically(opts: &linear::Optimizations) -> bool { pub(crate) fn is_sorted_lexicographically<TOperator>(
opts: &linear::Optimizations<TOperator>,
) -> bool
where
TOperator: Copy + Debug + Eq + Hash,
{
opts.optimizations.windows(2).all(|w| { opts.optimizations.windows(2).all(|w| {
compare_optimizations(&opts.paths, &w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len)) compare_optimizations(&opts.paths, &w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len))
<= Ordering::Equal <= Ordering::Equal
@@ -207,7 +229,10 @@ pub(crate) fn is_sorted_lexicographically(opts: &linear::Optimizations) -> bool
/// opcode @ 0 --iadd--> /// opcode @ 0 --iadd-->
/// opcode @ 0 --(else)--> is-const? @ 0 --true--> /// opcode @ 0 --(else)--> is-const? @ 0 --true-->
/// ``` /// ```
pub fn match_in_same_order(opts: &mut linear::Optimizations) { pub fn match_in_same_order<TOperator>(opts: &mut linear::Optimizations<TOperator>)
where
TOperator: Copy + Debug + Eq + Hash,
{
assert!(!opts.optimizations.is_empty()); assert!(!opts.optimizations.is_empty());
let mut prefix = vec![]; let mut prefix = vec![];
@@ -267,7 +292,10 @@ pub fn match_in_same_order(opts: &mut linear::Optimizations) {
/// existence completely. So we just emit nop match operations for all variable /// existence completely. So we just emit nop match operations for all variable
/// patterns, and then in this post-processing pass, we fuse them and their /// patterns, and then in this post-processing pass, we fuse them and their
/// actions with their preceding increment. /// actions with their preceding increment.
pub fn remove_unnecessary_nops(opts: &mut linear::Optimizations) { pub fn remove_unnecessary_nops<TOperator>(opts: &mut linear::Optimizations<TOperator>)
where
TOperator: Copy + Debug + Eq + Hash,
{
for opt in &mut opts.optimizations { for opt in &mut opts.optimizations {
if opt.increments.len() < 2 { if opt.increments.len() < 2 {
debug_assert!(!opt.increments.is_empty()); debug_assert!(!opt.increments.is_empty());
@@ -289,9 +317,9 @@ mod tests {
use crate::ast::*; use crate::ast::*;
use peepmatic_runtime::{ use peepmatic_runtime::{
linear::{bool_to_match_result, Else, MatchOp::*, MatchResult}, linear::{bool_to_match_result, Else, MatchOp::*, MatchResult},
operator::Operator,
paths::*, paths::*,
}; };
use peepmatic_test_operator::TestOperator;
use std::num::NonZeroU32; use std::num::NonZeroU32;
#[test] #[test]
@@ -306,7 +334,7 @@ mod tests {
fn $test_name() { fn $test_name() {
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK"); let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
let opts = match wast::parser::parse::<Optimizations>(&buf) { let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
Ok(opts) => opts, Ok(opts) => opts,
Err(mut e) => { Err(mut e) => {
e.set_path(std::path::Path::new(stringify!($test_name))); e.set_path(std::path::Path::new(stringify!($test_name)));
@@ -383,7 +411,7 @@ mod tests {
fn $test_name() { fn $test_name() {
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK"); let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
let opts = match wast::parser::parse::<Optimizations>(&buf) { let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
Ok(opts) => opts, Ok(opts) => opts,
Err(mut e) => { Err(mut e) => {
e.set_path(std::path::Path::new(stringify!($test_name))); e.set_path(std::path::Path::new(stringify!($test_name)));
@@ -468,31 +496,19 @@ mod tests {
", ",
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![ |p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
( (Opcode { path: p(&[0, 1]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0, 1]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(Nop, Err(Else)), (Nop, Err(Else)),
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IntegerValue { path: p(&[0, 1]) }, i(42)) (IntegerValue { path: p(&[0, 1]) }, i(42))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)), (IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
( (
@@ -501,10 +517,7 @@ mod tests {
) )
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)), (IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
( (
@@ -513,18 +526,12 @@ mod tests {
) )
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)) (IsConst { path: p(&[0, 1]) }, bool_to_match_result(true))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
( (
Eq { Eq {
@@ -535,10 +542,7 @@ mod tests {
) )
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(Nop, Err(Else)), (Nop, Err(Else)),
], ],
@@ -558,50 +562,32 @@ mod tests {
", ",
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![ |p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
),
(IntegerValue { path: p(&[0, 0]) }, i(2)), (IntegerValue { path: p(&[0, 0]) }, i(2)),
(Nop, Err(Else)) (Nop, Err(Else))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
),
(IntegerValue { path: p(&[0, 0]) }, i(1)), (IntegerValue { path: p(&[0, 0]) }, i(1)),
(Nop, Err(Else)) (Nop, Err(Else))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IntegerValue { path: p(&[0, 1]) }, i(2)) (IntegerValue { path: p(&[0, 1]) }, i(2))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IntegerValue { path: p(&[0, 1]) }, i(1)) (IntegerValue { path: p(&[0, 1]) }, i(1))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(IntegerValue { path: p(&[0, 0]) }, i(0)), (IntegerValue { path: p(&[0, 0]) }, i(0)),
(Nop, Err(Else)) (Nop, Err(Else))
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
Opcode { path: p(&[0]) },
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(IntegerValue { path: p(&[0, 1]) }, i(0)) (IntegerValue { path: p(&[0, 1]) }, i(0))
] ]
@@ -619,14 +605,8 @@ mod tests {
", ",
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![ |p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
Opcode { path: p(&[0]) }, (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(
Opcode { path: p(&[0, 0]) },
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(Nop, Err(Else)), (Nop, Err(Else)),
( (
@@ -638,14 +618,8 @@ mod tests {
), ),
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
Opcode { path: p(&[0]) }, (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(
Opcode { path: p(&[0, 0]) },
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(Nop, Err(Else)), (Nop, Err(Else)),
( (
@@ -670,14 +644,8 @@ mod tests {
", ",
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![ |p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
Opcode { path: p(&[0]) }, (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(
Opcode { path: p(&[0, 0]) },
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(Nop, Err(Else)), (Nop, Err(Else)),
( (
@@ -689,14 +657,8 @@ mod tests {
), ),
], ],
vec![ vec![
( (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
Opcode { path: p(&[0]) }, (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(
Opcode { path: p(&[0, 0]) },
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
),
(Nop, Err(Else)), (Nop, Err(Else)),
(Nop, Err(Else)), (Nop, Err(Else)),
( (

View File

@@ -93,11 +93,17 @@ use peepmatic_runtime::{
}; };
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use wast::Id; use wast::Id;
/// Translate the given AST optimizations into linear optimizations. /// Translate the given AST optimizations into linear optimizations.
pub fn linearize(opts: &Optimizations) -> linear::Optimizations { pub fn linearize<TOperator>(opts: &Optimizations<TOperator>) -> linear::Optimizations<TOperator>
where
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
{
let mut optimizations = vec![]; let mut optimizations = vec![];
let mut paths = PathInterner::new(); let mut paths = PathInterner::new();
let mut integers = IntegerInterner::new(); let mut integers = IntegerInterner::new();
@@ -113,12 +119,15 @@ pub fn linearize(opts: &Optimizations) -> linear::Optimizations {
} }
/// Translate an AST optimization into a linear optimization! /// Translate an AST optimization into a linear optimization!
fn linearize_optimization( fn linearize_optimization<TOperator>(
paths: &mut PathInterner, paths: &mut PathInterner,
integers: &mut IntegerInterner, integers: &mut IntegerInterner,
opt: &Optimization, opt: &Optimization<TOperator>,
) -> linear::Optimization { ) -> linear::Optimization<TOperator>
let mut increments: Vec<linear::Increment> = vec![]; where
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
{
let mut increments: Vec<linear::Increment<_>> = vec![];
let mut lhs_id_to_path = LhsIdToPath::new(); let mut lhs_id_to_path = LhsIdToPath::new();
@@ -175,20 +184,26 @@ fn linearize_optimization(
/// ///
/// Does not maintain any extra state about the traversal, such as where in the /// Does not maintain any extra state about the traversal, such as where in the
/// tree each yielded node comes from. /// tree each yielded node comes from.
struct RhsPostOrder<'a> { struct RhsPostOrder<'a, TOperator> {
dfs: Dfs<'a>, dfs: Dfs<'a, TOperator>,
} }
impl<'a> RhsPostOrder<'a> { impl<'a, TOperator> RhsPostOrder<'a, TOperator>
fn new(rhs: &'a Rhs<'a>) -> Self { where
TOperator: Copy + Debug + Eq + Hash,
{
fn new(rhs: &'a Rhs<'a, TOperator>) -> Self {
Self { dfs: Dfs::new(rhs) } Self { dfs: Dfs::new(rhs) }
} }
} }
impl<'a> Iterator for RhsPostOrder<'a> { impl<'a, TOperator> Iterator for RhsPostOrder<'a, TOperator>
type Item = &'a Rhs<'a>; where
TOperator: Copy,
{
type Item = &'a Rhs<'a, TOperator>;
fn next(&mut self) -> Option<&'a Rhs<'a>> { fn next(&mut self) -> Option<&'a Rhs<'a, TOperator>> {
use crate::traversals::TraversalEvent as TE; use crate::traversals::TraversalEvent as TE;
loop { loop {
match self.dfs.next()? { match self.dfs.next()? {
@@ -203,14 +218,17 @@ impl<'a> Iterator for RhsPostOrder<'a> {
/// ///
/// Keeps track of the path to each pattern, and yields it along side the /// Keeps track of the path to each pattern, and yields it along side the
/// pattern AST node. /// pattern AST node.
struct PatternPreOrder<'a> { struct PatternPreOrder<'a, TOperator> {
last_child: Option<u8>, last_child: Option<u8>,
path: Vec<u8>, path: Vec<u8>,
dfs: Dfs<'a>, dfs: Dfs<'a, TOperator>,
} }
impl<'a> PatternPreOrder<'a> { impl<'a, TOperator> PatternPreOrder<'a, TOperator>
fn new(pattern: &'a Pattern<'a>) -> Self { where
TOperator: Copy + Debug + Eq + Hash,
{
fn new(pattern: &'a Pattern<'a, TOperator>) -> Self {
Self { Self {
last_child: None, last_child: None,
path: vec![], path: vec![],
@@ -218,7 +236,7 @@ impl<'a> PatternPreOrder<'a> {
} }
} }
fn next(&mut self, paths: &mut PathInterner) -> Option<(PathId, &'a Pattern<'a>)> { fn next(&mut self, paths: &mut PathInterner) -> Option<(PathId, &'a Pattern<'a, TOperator>)> {
use crate::traversals::TraversalEvent as TE; use crate::traversals::TraversalEvent as TE;
loop { loop {
match self.dfs.next()? { match self.dfs.next()? {
@@ -252,15 +270,17 @@ impl<'a> PatternPreOrder<'a> {
/// A map from left-hand side identifiers to the path in the left-hand side /// A map from left-hand side identifiers to the path in the left-hand side
/// where they first occurred. /// where they first occurred.
struct LhsIdToPath<'a> { struct LhsIdToPath<'a, TOperator> {
id_to_path: BTreeMap<&'a str, PathId>, id_to_path: BTreeMap<&'a str, PathId>,
_marker: PhantomData<&'a TOperator>,
} }
impl<'a> LhsIdToPath<'a> { impl<'a, TOperator> LhsIdToPath<'a, TOperator> {
/// Construct a new, empty `LhsIdToPath`. /// Construct a new, empty `LhsIdToPath`.
fn new() -> Self { fn new() -> Self {
Self { Self {
id_to_path: Default::default(), id_to_path: Default::default(),
_marker: PhantomData,
} }
} }
@@ -280,7 +300,7 @@ impl<'a> LhsIdToPath<'a> {
} }
/// Remember the path to any LHS ids used in the given pattern. /// Remember the path to any LHS ids used in the given pattern.
fn remember_path_to_pattern_ids(&mut self, pattern: &'a Pattern<'a>, path: PathId) { fn remember_path_to_pattern_ids(&mut self, pattern: &'a Pattern<'a, TOperator>, path: PathId) {
match pattern { match pattern {
// If this is the first time we've seen an identifier defined on the // If this is the first time we've seen an identifier defined on the
// left-hand side, remember it. // left-hand side, remember it.
@@ -294,10 +314,10 @@ impl<'a> LhsIdToPath<'a> {
/// An `RhsBuilder` emits the actions for building the right-hand side /// An `RhsBuilder` emits the actions for building the right-hand side
/// instructions. /// instructions.
struct RhsBuilder<'a> { struct RhsBuilder<'a, TOperator> {
// We do a post order traversal of the RHS because an RHS instruction cannot // We do a post order traversal of the RHS because an RHS instruction cannot
// be created until after all of its operands are created. // be created until after all of its operands are created.
rhs_post_order: RhsPostOrder<'a>, rhs_post_order: RhsPostOrder<'a, TOperator>,
// A map from a right-hand side's span to its `linear::RhsId`. This is used // A map from a right-hand side's span to its `linear::RhsId`. This is used
// by RHS-construction actions to reference operands. In practice the // by RHS-construction actions to reference operands. In practice the
@@ -306,9 +326,12 @@ struct RhsBuilder<'a> {
rhs_span_to_id: BTreeMap<wast::Span, linear::RhsId>, rhs_span_to_id: BTreeMap<wast::Span, linear::RhsId>,
} }
impl<'a> RhsBuilder<'a> { impl<'a, TOperator> RhsBuilder<'a, TOperator>
where
TOperator: Copy + Debug + Eq + Hash,
{
/// Create a new builder for the given right-hand side. /// Create a new builder for the given right-hand side.
fn new(rhs: &'a Rhs<'a>) -> Self { fn new(rhs: &'a Rhs<'a, TOperator>) -> Self {
let rhs_post_order = RhsPostOrder::new(rhs); let rhs_post_order = RhsPostOrder::new(rhs);
let rhs_span_to_id = Default::default(); let rhs_span_to_id = Default::default();
Self { Self {
@@ -323,7 +346,7 @@ impl<'a> RhsBuilder<'a> {
/// ///
/// Panics if we haven't already emitted the action for building this RHS's /// Panics if we haven't already emitted the action for building this RHS's
/// instruction. /// instruction.
fn get_rhs_id(&self, rhs: &Rhs) -> linear::RhsId { fn get_rhs_id(&self, rhs: &Rhs<TOperator>) -> linear::RhsId {
self.rhs_span_to_id[&rhs.span()] self.rhs_span_to_id[&rhs.span()]
} }
@@ -335,8 +358,8 @@ impl<'a> RhsBuilder<'a> {
fn add_rhs_build_actions( fn add_rhs_build_actions(
&mut self, &mut self,
integers: &mut IntegerInterner, integers: &mut IntegerInterner,
lhs_id_to_path: &LhsIdToPath, lhs_id_to_path: &LhsIdToPath<TOperator>,
actions: &mut Vec<linear::Action>, actions: &mut Vec<linear::Action<TOperator>>,
) { ) {
while let Some(rhs) = self.rhs_post_order.next() { while let Some(rhs) = self.rhs_post_order.next() {
actions.push(self.rhs_to_linear_action(integers, lhs_id_to_path, rhs)); actions.push(self.rhs_to_linear_action(integers, lhs_id_to_path, rhs));
@@ -348,9 +371,9 @@ impl<'a> RhsBuilder<'a> {
fn rhs_to_linear_action( fn rhs_to_linear_action(
&self, &self,
integers: &mut IntegerInterner, integers: &mut IntegerInterner,
lhs_id_to_path: &LhsIdToPath, lhs_id_to_path: &LhsIdToPath<TOperator>,
rhs: &Rhs, rhs: &Rhs<TOperator>,
) -> linear::Action { ) -> linear::Action<TOperator> {
match rhs { match rhs {
Rhs::ValueLiteral(ValueLiteral::Integer(i)) => linear::Action::MakeIntegerConst { Rhs::ValueLiteral(ValueLiteral::Integer(i)) => linear::Action::MakeIntegerConst {
value: integers.intern(i.value as u64), value: integers.intern(i.value as u64),
@@ -425,9 +448,15 @@ impl<'a> RhsBuilder<'a> {
} }
} }
impl Precondition<'_> { impl<TOperator> Precondition<'_, TOperator>
where
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
{
/// Convert this precondition into a `linear::Increment`. /// Convert this precondition into a `linear::Increment`.
fn to_linear_increment(&self, lhs_id_to_path: &LhsIdToPath) -> linear::Increment { fn to_linear_increment(
&self,
lhs_id_to_path: &LhsIdToPath<TOperator>,
) -> linear::Increment<TOperator> {
match self.constraint { match self.constraint {
Constraint::IsPowerOfTwo => { Constraint::IsPowerOfTwo => {
let id = match &self.operands[0] { let id = match &self.operands[0] {
@@ -484,7 +513,10 @@ impl Precondition<'_> {
} }
} }
impl Pattern<'_> { impl<TOperator> Pattern<'_, TOperator>
where
TOperator: Copy,
{
/// Convert this pattern into its linear match operation and the expected /// Convert this pattern into its linear match operation and the expected
/// result of that operation. /// result of that operation.
/// ///
@@ -493,9 +525,12 @@ impl Pattern<'_> {
fn to_linear_match_op( fn to_linear_match_op(
&self, &self,
integers: &mut IntegerInterner, integers: &mut IntegerInterner,
lhs_id_to_path: &LhsIdToPath, lhs_id_to_path: &LhsIdToPath<TOperator>,
path: PathId, path: PathId,
) -> (linear::MatchOp, linear::MatchResult) { ) -> (linear::MatchOp, linear::MatchResult)
where
TOperator: Into<NonZeroU32>,
{
match self { match self {
Pattern::ValueLiteral(ValueLiteral::Integer(Integer { value, .. })) => ( Pattern::ValueLiteral(ValueLiteral::Integer(Integer { value, .. })) => (
linear::MatchOp::IntegerValue { path }, linear::MatchOp::IntegerValue { path },
@@ -543,9 +578,7 @@ impl Pattern<'_> {
} }
} }
Pattern::Operation(op) => { Pattern::Operation(op) => {
let op = op.operator as u32; let expected = Ok(op.operator.into());
debug_assert!(op != 0, "no `Operator` variants are zero");
let expected = Ok(unsafe { NonZeroU32::new_unchecked(op) });
(linear::MatchOp::Opcode { path }, expected) (linear::MatchOp::Opcode { path }, expected)
} }
} }
@@ -558,9 +591,9 @@ mod tests {
use peepmatic_runtime::{ use peepmatic_runtime::{
integer_interner::IntegerId, integer_interner::IntegerId,
linear::{bool_to_match_result, Action::*, Else, MatchOp::*}, linear::{bool_to_match_result, Action::*, Else, MatchOp::*},
operator::Operator,
r#type::{BitWidth, Kind, Type}, r#type::{BitWidth, Kind, Type},
}; };
use peepmatic_test_operator::TestOperator;
macro_rules! linearizes_to { macro_rules! linearizes_to {
($name:ident, $source:expr, $make_expected:expr $(,)* ) => { ($name:ident, $source:expr, $make_expected:expr $(,)* ) => {
@@ -568,7 +601,7 @@ mod tests {
fn $name() { fn $name() {
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK"); let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
let opts = match wast::parser::parse::<Optimizations>(&buf) { let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
Ok(opts) => opts, Ok(opts) => opts,
Err(mut e) => { Err(mut e) => {
e.set_path(std::path::Path::new(stringify!($name))); e.set_path(std::path::Path::new(stringify!($name)));
@@ -602,7 +635,7 @@ mod tests {
let make_expected: fn( let make_expected: fn(
&mut dyn FnMut(&[u8]) -> PathId, &mut dyn FnMut(&[u8]) -> PathId,
&mut dyn FnMut(u64) -> IntegerId, &mut dyn FnMut(u64) -> IntegerId,
) -> Vec<linear::Increment> = $make_expected; ) -> Vec<linear::Increment<TestOperator>> = $make_expected;
let expected = make_expected(&mut p, &mut i); let expected = make_expected(&mut p, &mut i);
dbg!(&expected); dbg!(&expected);
@@ -624,12 +657,12 @@ mod tests {
|p, i| vec![ |p, i| vec![
linear::Increment { linear::Increment {
operation: Opcode { path: p(&[0]) }, operation: Opcode { path: p(&[0]) },
expected: Ok(NonZeroU32::new(Operator::Imul as _).unwrap()), expected: Ok(TestOperator::Imul.into()),
actions: vec![ actions: vec![
GetLhs { path: p(&[0, 0]) }, GetLhs { path: p(&[0, 0]) },
GetLhs { path: p(&[0, 1]) }, GetLhs { path: p(&[0, 1]) },
MakeBinaryInst { MakeBinaryInst {
operator: Operator::Ishl, operator: TestOperator::Ishl,
r#type: Type { r#type: Type {
kind: Kind::Int, kind: Kind::Int,
bit_width: BitWidth::Polymorphic, bit_width: BitWidth::Polymorphic,
@@ -702,11 +735,11 @@ mod tests {
|p, i| vec![ |p, i| vec![
linear::Increment { linear::Increment {
operation: Opcode { path: p(&[0]) }, operation: Opcode { path: p(&[0]) },
expected: Ok(NonZeroU32::new(Operator::Iconst as _).unwrap()), expected: Ok(TestOperator::Iconst.into()),
actions: vec![ actions: vec![
GetLhs { path: p(&[0, 0]) }, GetLhs { path: p(&[0, 0]) },
MakeUnaryInst { MakeUnaryInst {
operator: Operator::Iconst, operator: TestOperator::Iconst,
r#type: Type { r#type: Type {
kind: Kind::Int, kind: Kind::Int,
bit_width: BitWidth::Polymorphic, bit_width: BitWidth::Polymorphic,
@@ -729,14 +762,14 @@ mod tests {
|p, i| vec![ |p, i| vec![
linear::Increment { linear::Increment {
operation: Opcode { path: p(&[0]) }, operation: Opcode { path: p(&[0]) },
expected: Ok(NonZeroU32::new(Operator::Bor as _).unwrap()), expected: Ok(TestOperator::Bor.into()),
actions: vec![ actions: vec![
GetLhs { path: p(&[0, 0]) }, GetLhs { path: p(&[0, 0]) },
GetLhs { GetLhs {
path: p(&[0, 1, 1]), path: p(&[0, 1, 1]),
}, },
MakeBinaryInst { MakeBinaryInst {
operator: Operator::Bor, operator: TestOperator::Bor,
r#type: Type { r#type: Type {
kind: Kind::Int, kind: Kind::Int,
bit_width: BitWidth::Polymorphic, bit_width: BitWidth::Polymorphic,
@@ -752,7 +785,7 @@ mod tests {
}, },
linear::Increment { linear::Increment {
operation: Opcode { path: p(&[0, 1]) }, operation: Opcode { path: p(&[0, 1]) },
expected: Ok(NonZeroU32::new(Operator::Bor as _).unwrap()), expected: Ok(TestOperator::Bor.into()),
actions: vec![], actions: vec![],
}, },
linear::Increment { linear::Increment {
@@ -791,7 +824,7 @@ mod tests {
|p, i| vec![ |p, i| vec![
linear::Increment { linear::Increment {
operation: Opcode { path: p(&[0]) }, operation: Opcode { path: p(&[0]) },
expected: Ok(NonZeroU32::new(Operator::Ireduce as _).unwrap()), expected: Ok(TestOperator::Ireduce.into()),
actions: vec![MakeIntegerConst { actions: vec![MakeIntegerConst {
value: i(0), value: i(0),
bit_width: BitWidth::ThirtyTwo, bit_width: BitWidth::ThirtyTwo,

View File

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

View File

@@ -1,6 +1,8 @@
//! Traversals over the AST. //! Traversals over the AST.
use crate::ast::*; use crate::ast::*;
use std::fmt::Debug;
use std::hash::Hash;
/// A low-level DFS traversal event: either entering or exiting the traversal of /// A low-level DFS traversal event: either entering or exiting the traversal of
/// an AST node. /// an AST node.
@@ -32,13 +34,16 @@ pub enum TraversalEvent {
/// the AST, because the `new` constructor takes anything that can convert into /// the AST, because the `new` constructor takes anything that can convert into
/// a `DynAstRef`. /// a `DynAstRef`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Dfs<'a> { pub struct Dfs<'a, TOperator> {
stack: Vec<(TraversalEvent, DynAstRef<'a>)>, stack: Vec<(TraversalEvent, DynAstRef<'a, TOperator>)>,
} }
impl<'a> Dfs<'a> { impl<'a, TOperator> Dfs<'a, TOperator>
where
TOperator: Copy + Debug + Eq + Hash,
{
/// Construct a new `Dfs` traversal starting at the given `start` AST node. /// Construct a new `Dfs` traversal starting at the given `start` AST node.
pub fn new(start: impl Into<DynAstRef<'a>>) -> Self { pub fn new(start: impl Into<DynAstRef<'a, TOperator>>) -> Self {
let start = start.into(); let start = start.into();
Dfs { Dfs {
stack: vec![ stack: vec![
@@ -49,15 +54,18 @@ impl<'a> Dfs<'a> {
} }
/// Peek at the next traversal event and AST node pair, if any. /// Peek at the next traversal event and AST node pair, if any.
pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a>)> { pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> {
self.stack.last().cloned() self.stack.last().copied()
} }
} }
impl<'a> Iterator for Dfs<'a> { impl<'a, TOperator> Iterator for Dfs<'a, TOperator>
type Item = (TraversalEvent, DynAstRef<'a>); where
TOperator: Copy,
{
type Item = (TraversalEvent, DynAstRef<'a, TOperator>);
fn next(&mut self) -> Option<(TraversalEvent, DynAstRef<'a>)> { fn next(&mut self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> {
let (event, node) = self.stack.pop()?; let (event, node) = self.stack.pop()?;
if let TraversalEvent::Enter = event { if let TraversalEvent::Enter = event {
let mut enqueue_children = EnqueueChildren(self); let mut enqueue_children = EnqueueChildren(self);
@@ -65,15 +73,16 @@ impl<'a> Iterator for Dfs<'a> {
} }
return Some((event, node)); return Some((event, node));
struct EnqueueChildren<'a, 'b>(&'b mut Dfs<'a>) struct EnqueueChildren<'a, 'b, TOperator>(&'b mut Dfs<'a, TOperator>)
where where
'a: 'b; 'a: 'b;
impl<'a, 'b> Extend<DynAstRef<'a>> for EnqueueChildren<'a, 'b> impl<'a, 'b, TOperator> Extend<DynAstRef<'a, TOperator>> for EnqueueChildren<'a, 'b, TOperator>
where where
'a: 'b, 'a: 'b,
TOperator: Copy,
{ {
fn extend<T: IntoIterator<Item = DynAstRef<'a>>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = DynAstRef<'a, TOperator>>>(&mut self, iter: T) {
let iter = iter.into_iter(); let iter = iter.into_iter();
let (min, max) = iter.size_hint(); let (min, max) = iter.size_hint();
@@ -97,6 +106,7 @@ impl<'a> Iterator for Dfs<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use peepmatic_test_operator::TestOperator;
use DynAstRef::*; use DynAstRef::*;
#[test] #[test]
@@ -107,7 +117,7 @@ mod tests {
(ishl $x $(log2 $C))) (ishl $x $(log2 $C)))
"; ";
let buf = wast::parser::ParseBuffer::new(input).expect("input should lex OK"); let buf = wast::parser::ParseBuffer::new(input).expect("input should lex OK");
let ast = match wast::parser::parse::<crate::ast::Optimizations>(&buf) { let ast = match wast::parser::parse::<crate::ast::Optimizations<TestOperator>>(&buf) {
Ok(ast) => ast, Ok(ast) => ast,
Err(e) => panic!("expected to parse OK, got error:\n\n{}", e), Err(e) => panic!("expected to parse OK, got error:\n\n{}", e),
}; };

View File

@@ -14,14 +14,13 @@
use crate::ast::{Span as _, *}; use crate::ast::{Span as _, *};
use crate::traversals::{Dfs, TraversalEvent}; use crate::traversals::{Dfs, TraversalEvent};
use peepmatic_runtime::{ use peepmatic_runtime::r#type::{BitWidth, Kind, Type};
operator::{Operator, TypingContext as TypingContextTrait}, use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules};
r#type::{BitWidth, Kind, Type},
};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
use std::iter; use std::iter;
use std::mem; use std::mem;
@@ -94,7 +93,10 @@ impl VerifyError {
pub type VerifyResult<T> = Result<T, VerifyError>; pub type VerifyResult<T> = Result<T, VerifyError>;
/// Verify and type check a set of optimizations. /// Verify and type check a set of optimizations.
pub fn verify(opts: &Optimizations) -> VerifyResult<()> { pub fn verify<TOperator>(opts: &Optimizations<TOperator>) -> VerifyResult<()>
where
TOperator: Copy + Debug + Eq + Hash + TypingRules,
{
if opts.optimizations.is_empty() { if opts.optimizations.is_empty() {
return Err(anyhow::anyhow!("no optimizations").into()); return Err(anyhow::anyhow!("no optimizations").into());
} }
@@ -113,7 +115,10 @@ pub fn verify(opts: &Optimizations) -> VerifyResult<()> {
/// If there were duplicates, then it would be nondeterministic which one we /// If there were duplicates, then it would be nondeterministic which one we
/// applied and would make automata construction more difficult. It is better to /// applied and would make automata construction more difficult. It is better to
/// check for duplicates and reject them if found. /// check for duplicates and reject them if found.
fn verify_unique_left_hand_sides(opts: &Optimizations) -> VerifyResult<()> { fn verify_unique_left_hand_sides<TOperator>(opts: &Optimizations<TOperator>) -> VerifyResult<()>
where
TOperator: Copy + Eq + Debug + Hash,
{
let mut lefts = HashMap::new(); let mut lefts = HashMap::new();
for opt in &opts.optimizations { for opt in &opts.optimizations {
let canon_lhs = canonicalized_lhs_key(&opt.lhs); let canon_lhs = canonicalized_lhs_key(&opt.lhs);
@@ -146,7 +151,10 @@ fn verify_unique_left_hand_sides(opts: &Optimizations) -> VerifyResult<()> {
/// ///
/// This function creates an opaque, canonicalized hash key for left-hand sides /// This function creates an opaque, canonicalized hash key for left-hand sides
/// that sees through identifier renaming. /// that sees through identifier renaming.
fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq { fn canonicalized_lhs_key<TOperator>(lhs: &Lhs<TOperator>) -> impl Hash + Eq
where
TOperator: Copy + Debug + Eq + Hash,
{
let mut var_to_canon = HashMap::new(); let mut var_to_canon = HashMap::new();
let mut const_to_canon = HashMap::new(); let mut const_to_canon = HashMap::new();
let mut canonicalized = vec![]; let mut canonicalized = vec![];
@@ -183,19 +191,20 @@ fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq {
return canonicalized; return canonicalized;
#[derive(Hash, PartialEq, Eq)] #[derive(Hash, PartialEq, Eq)]
enum CanonicalBit { enum CanonicalBit<TOperator> {
Var(u32), Var(u32),
Const(u32), Const(u32),
Integer(i64), Integer(i64),
Boolean(bool), Boolean(bool),
ConditionCode(peepmatic_runtime::cc::ConditionCode), ConditionCode(peepmatic_runtime::cc::ConditionCode),
Operation(Operator, Option<Type>), Operation(TOperator, Option<Type>),
Precondition(Constraint), Precondition(Constraint),
Other(&'static str), Other(&'static str),
} }
} }
pub(crate) struct TypingContext<'a> { #[derive(Debug)]
struct TypingContext<'a, TOperator> {
z3: &'a z3::Context, z3: &'a z3::Context,
type_kind_sort: z3::DatatypeSort<'a>, type_kind_sort: z3::DatatypeSort<'a>,
solver: z3::Solver<'a>, solver: z3::Solver<'a>,
@@ -218,12 +227,15 @@ pub(crate) struct TypingContext<'a> {
// Keep track of AST nodes that need to have their types assigned to // Keep track of AST nodes that need to have their types assigned to
// them. For these AST nodes, we know what bit width to use when // them. For these AST nodes, we know what bit width to use when
// interpreting peephole optimization actions. // interpreting peephole optimization actions.
boolean_literals: Vec<(&'a Boolean<'a>, TypeVar<'a>)>, boolean_literals: Vec<(&'a Boolean<'a, TOperator>, TypeVar<'a>)>,
integer_literals: Vec<(&'a Integer<'a>, TypeVar<'a>)>, integer_literals: Vec<(&'a Integer<'a, TOperator>, TypeVar<'a>)>,
rhs_operations: Vec<(&'a Operation<'a, Rhs<'a>>, TypeVar<'a>)>, rhs_operations: Vec<(
&'a Operation<'a, TOperator, Rhs<'a, TOperator>>,
TypeVar<'a>,
)>,
} }
impl<'a> TypingContext<'a> { impl<'a, TOperator> TypingContext<'a, TOperator> {
fn new(z3: &'a z3::Context) -> Self { fn new(z3: &'a z3::Context) -> Self {
let type_kind_sort = z3::DatatypeBuilder::new(z3) let type_kind_sort = z3::DatatypeBuilder::new(z3)
.variant("int", &[]) .variant("int", &[])
@@ -301,51 +313,55 @@ impl<'a> TypingContext<'a> {
// and similar refer to the same type variables. // and similar refer to the same type variables.
fn enter_operation_scope<'b>( fn enter_operation_scope<'b>(
&'b mut self, &'b mut self,
) -> impl DerefMut<Target = TypingContext<'a>> + Drop + 'b { ) -> impl DerefMut<Target = TypingContext<'a, TOperator>> + Drop + 'b {
assert!(self.operation_scope.is_empty()); assert!(self.operation_scope.is_empty());
return Scope(self); return Scope(self);
struct Scope<'a, 'b>(&'b mut TypingContext<'a>) struct Scope<'a, 'b, TOperator>(&'b mut TypingContext<'a, TOperator>)
where where
'a: 'b; 'a: 'b;
impl<'a, 'b> Deref for Scope<'a, 'b> impl<'a, 'b, TOperator> Deref for Scope<'a, 'b, TOperator>
where where
'a: 'b, 'a: 'b,
{ {
type Target = TypingContext<'a>; type Target = TypingContext<'a, TOperator>;
fn deref(&self) -> &TypingContext<'a> { fn deref(&self) -> &TypingContext<'a, TOperator> {
self.0 self.0
} }
} }
impl<'a, 'b> DerefMut for Scope<'a, 'b> impl<'a, 'b, TOperator> DerefMut for Scope<'a, 'b, TOperator>
where where
'a: 'b, 'a: 'b,
{ {
fn deref_mut(&mut self) -> &mut TypingContext<'a> { fn deref_mut(&mut self) -> &mut TypingContext<'a, TOperator> {
self.0 self.0
} }
} }
impl Drop for Scope<'_, '_> { impl<TOperator> Drop for Scope<'_, '_, TOperator> {
fn drop(&mut self) { fn drop(&mut self) {
self.0.operation_scope.clear(); self.0.operation_scope.clear();
} }
} }
} }
fn remember_boolean_literal(&mut self, b: &'a Boolean<'a>, ty: TypeVar<'a>) { fn remember_boolean_literal(&mut self, b: &'a Boolean<'a, TOperator>, ty: TypeVar<'a>) {
self.assert_is_bool(b.span, &ty); self.assert_is_bool(b.span, &ty);
self.boolean_literals.push((b, ty)); self.boolean_literals.push((b, ty));
} }
fn remember_integer_literal(&mut self, i: &'a Integer<'a>, ty: TypeVar<'a>) { fn remember_integer_literal(&mut self, i: &'a Integer<'a, TOperator>, ty: TypeVar<'a>) {
self.assert_is_integer(i.span, &ty); self.assert_is_integer(i.span, &ty);
self.integer_literals.push((i, ty)); self.integer_literals.push((i, ty));
} }
fn remember_rhs_operation(&mut self, op: &'a Operation<'a, Rhs<'a>>, ty: TypeVar<'a>) { fn remember_rhs_operation(
&mut self,
op: &'a Operation<'a, TOperator, Rhs<'a, TOperator>>,
ty: TypeVar<'a>,
) {
self.rhs_operations.push((op, ty)); self.rhs_operations.push((op, ty));
} }
@@ -638,7 +654,8 @@ impl<'a> TypingContext<'a> {
} }
} }
impl<'a> TypingContextTrait<'a> for TypingContext<'a> { impl<'a, TOperator> TypingContextTrait<'a> for TypingContext<'a, TOperator> {
type Span = Span;
type TypeVariable = TypeVar<'a>; type TypeVariable = TypeVar<'a>;
fn cc(&mut self, span: Span) -> TypeVar<'a> { fn cc(&mut self, span: Span) -> TypeVar<'a> {
@@ -737,13 +754,19 @@ impl<'a> TypingContextTrait<'a> for TypingContext<'a> {
} }
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub(crate) struct TypeVar<'a> { struct TypeVar<'a> {
kind: z3::ast::Datatype<'a>, kind: z3::ast::Datatype<'a>,
width: z3::ast::BV<'a>, width: z3::ast::BV<'a>,
} }
fn verify_optimization(z3: &z3::Context, opt: &Optimization) -> VerifyResult<()> { fn verify_optimization<TOperator>(
z3: &z3::Context,
opt: &Optimization<TOperator>,
) -> VerifyResult<()>
where
TOperator: Copy + Debug + Eq + Hash + TypingRules,
{
let mut context = TypingContext::new(z3); let mut context = TypingContext::new(z3);
collect_type_constraints(&mut context, opt)?; collect_type_constraints(&mut context, opt)?;
context.type_check(opt.span)?; context.type_check(opt.span)?;
@@ -755,10 +778,13 @@ fn verify_optimization(z3: &z3::Context, opt: &Optimization) -> VerifyResult<()>
Ok(()) Ok(())
} }
fn collect_type_constraints<'a>( fn collect_type_constraints<'a, TOperator>(
context: &mut TypingContext<'a>, context: &mut TypingContext<'a, TOperator>,
opt: &'a Optimization<'a>, opt: &'a Optimization<'a, TOperator>,
) -> VerifyResult<()> { ) -> VerifyResult<()>
where
TOperator: Copy + Debug + Eq + Hash + TypingRules,
{
use crate::traversals::TraversalEvent as TE; use crate::traversals::TraversalEvent as TE;
let lhs_ty = context.new_type_var(); let lhs_ty = context.new_type_var();
@@ -780,8 +806,22 @@ fn collect_type_constraints<'a>(
// Build up the type constraints for the left-hand side. // Build up the type constraints for the left-hand side.
for (event, node) in Dfs::new(&opt.lhs) { for (event, node) in Dfs::new(&opt.lhs) {
match (event, node) { match (event, node) {
(TE::Enter, DynAstRef::Pattern(Pattern::Constant(Constant { id, span }))) (
| (TE::Enter, DynAstRef::Pattern(Pattern::Variable(Variable { id, span }))) => { TE::Enter,
DynAstRef::Pattern(Pattern::Constant(Constant {
id,
span,
marker: _,
})),
)
| (
TE::Enter,
DynAstRef::Pattern(Pattern::Variable(Variable {
id,
span,
marker: _,
})),
) => {
let id = context.get_or_create_type_var_for_id(*id); let id = context.get_or_create_type_var_for_id(*id);
context.assert_type_eq(*span, expected_types.last().unwrap(), &id, None); context.assert_type_eq(*span, expected_types.last().unwrap(), &id, None);
} }
@@ -805,11 +845,11 @@ fn collect_type_constraints<'a>(
let mut operand_types = vec![]; let mut operand_types = vec![];
{ {
let mut scope = context.enter_operation_scope(); let mut scope = context.enter_operation_scope();
result_ty = op.operator.result_type(&mut *scope, op.span); result_ty = op.operator.result_type(op.span, &mut *scope);
op.operator op.operator
.immediate_types(&mut *scope, op.span, &mut operand_types); .immediate_types(op.span, &mut *scope, &mut operand_types);
op.operator op.operator
.param_types(&mut *scope, op.span, &mut operand_types); .parameter_types(op.span, &mut *scope, &mut operand_types);
} }
if op.operands.len() != operand_types.len() { if op.operands.len() != operand_types.len() {
@@ -841,9 +881,8 @@ fn collect_type_constraints<'a>(
} }
} }
match op.operator { if (op.operator.is_reduce() || op.operator.is_extend()) && op.r#type.get().is_none()
Operator::Ireduce | Operator::Uextend | Operator::Sextend => { {
if op.r#type.get().is_none() {
return Err(WastError::new( return Err(WastError::new(
op.span, op.span,
"`ireduce`, `sextend`, and `uextend` require an ascribed type, \ "`ireduce`, `sextend`, and `uextend` require an ascribed type, \
@@ -852,19 +891,13 @@ fn collect_type_constraints<'a>(
) )
.into()); .into());
} }
}
_ => {}
}
match op.operator { if op.operator.is_extend() {
Operator::Uextend | Operator::Sextend => {
context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]); context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]);
} }
Operator::Ireduce => { if op.operator.is_reduce() {
context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]); context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]);
} }
_ => {}
}
if let Some(ty) = op.r#type.get() { if let Some(ty) = op.r#type.get() {
match ty.kind { match ty.kind {
@@ -916,8 +949,22 @@ fn collect_type_constraints<'a>(
let ty = expected_types.last().unwrap(); let ty = expected_types.last().unwrap();
context.assert_is_cc(cc.span, ty); context.assert_is_cc(cc.span, ty);
} }
(TE::Enter, DynAstRef::Rhs(Rhs::Constant(Constant { span, id }))) (
| (TE::Enter, DynAstRef::Rhs(Rhs::Variable(Variable { span, id }))) => { TE::Enter,
DynAstRef::Rhs(Rhs::Constant(Constant {
span,
id,
marker: _,
})),
)
| (
TE::Enter,
DynAstRef::Rhs(Rhs::Variable(Variable {
span,
id,
marker: _,
})),
) => {
let id_ty = context.get_type_var_for_id(*id)?; let id_ty = context.get_type_var_for_id(*id)?;
context.assert_type_eq(*span, expected_types.last().unwrap(), &id_ty, None); context.assert_type_eq(*span, expected_types.last().unwrap(), &id_ty, None);
} }
@@ -926,11 +973,11 @@ fn collect_type_constraints<'a>(
let mut operand_types = vec![]; let mut operand_types = vec![];
{ {
let mut scope = context.enter_operation_scope(); let mut scope = context.enter_operation_scope();
result_ty = op.operator.result_type(&mut *scope, op.span); result_ty = op.operator.result_type(op.span, &mut *scope);
op.operator op.operator
.immediate_types(&mut *scope, op.span, &mut operand_types); .immediate_types(op.span, &mut *scope, &mut operand_types);
op.operator op.operator
.param_types(&mut *scope, op.span, &mut operand_types); .parameter_types(op.span, &mut *scope, &mut operand_types);
} }
if op.operands.len() != operand_types.len() { if op.operands.len() != operand_types.len() {
@@ -965,9 +1012,8 @@ fn collect_type_constraints<'a>(
} }
} }
match op.operator { if (op.operator.is_reduce() || op.operator.is_extend()) && op.r#type.get().is_none()
Operator::Ireduce | Operator::Uextend | Operator::Sextend => { {
if op.r#type.get().is_none() {
return Err(WastError::new( return Err(WastError::new(
op.span, op.span,
"`ireduce`, `sextend`, and `uextend` require an ascribed type, \ "`ireduce`, `sextend`, and `uextend` require an ascribed type, \
@@ -976,19 +1022,13 @@ fn collect_type_constraints<'a>(
) )
.into()); .into());
} }
}
_ => {}
}
match op.operator { if op.operator.is_extend() {
Operator::Uextend | Operator::Sextend => {
context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]); context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]);
} }
Operator::Ireduce => { if op.operator.is_reduce() {
context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]); context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]);
} }
_ => {}
}
if let Some(ty) = op.r#type.get() { if let Some(ty) = op.r#type.get() {
match ty.kind { match ty.kind {
@@ -1017,11 +1057,11 @@ fn collect_type_constraints<'a>(
let mut operand_types = vec![]; let mut operand_types = vec![];
{ {
let mut scope = context.enter_operation_scope(); let mut scope = context.enter_operation_scope();
result_ty = unq.operator.result_type(&mut *scope, unq.span); result_ty = unq.operator.result_type(unq.span, &mut *scope);
unq.operator unq.operator
.immediate_types(&mut *scope, unq.span, &mut operand_types); .immediate_types(unq.span, &mut *scope, &mut operand_types);
unq.operator unq.operator
.param_types(&mut *scope, unq.span, &mut operand_types); .parameter_types(unq.span, &mut *scope, &mut operand_types);
} }
if unq.operands.len() != operand_types.len() { if unq.operands.len() != operand_types.len() {
@@ -1068,9 +1108,9 @@ fn collect_type_constraints<'a>(
Ok(()) Ok(())
} }
fn type_constrain_precondition<'a>( fn type_constrain_precondition<'a, TOperator>(
context: &mut TypingContext<'a>, context: &mut TypingContext<'a, TOperator>,
pre: &Precondition<'a>, pre: &Precondition<'a, TOperator>,
) -> VerifyResult<()> { ) -> VerifyResult<()> {
match pre.constraint { match pre.constraint {
Constraint::BitWidth => { Constraint::BitWidth => {
@@ -1182,13 +1222,14 @@ fn type_constrain_precondition<'a>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use peepmatic_test_operator::TestOperator;
macro_rules! verify_ok { macro_rules! verify_ok {
($name:ident, $src:expr) => { ($name:ident, $src:expr) => {
#[test] #[test]
fn $name() { fn $name() {
let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK"); let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK");
let opts = match wast::parser::parse::<Optimizations>(&buf) { let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
Ok(opts) => opts, Ok(opts) => opts,
Err(mut e) => { Err(mut e) => {
e.set_path(Path::new(stringify!($name))); e.set_path(Path::new(stringify!($name)));
@@ -1215,7 +1256,7 @@ mod tests {
#[test] #[test]
fn $name() { fn $name() {
let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK"); let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK");
let opts = match wast::parser::parse::<Optimizations>(&buf) { let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
Ok(opts) => opts, Ok(opts) => opts,
Err(mut e) => { Err(mut e) => {
e.set_path(Path::new(stringify!($name))); e.set_path(Path::new(stringify!($name)));

View File

@@ -16,8 +16,10 @@ use std::process::{Command, Stdio};
// note that this list must be topologically sorted by dependencies // note that this list must be topologically sorted by dependencies
const CRATES_TO_PUBLISH: &[&str] = &[ const CRATES_TO_PUBLISH: &[&str] = &[
// peepmatic // peepmatic
"peepmatic-traits",
"peepmatic-macro", "peepmatic-macro",
"peepmatic-automata", "peepmatic-automata",
"peepmatic-test-operator",
"peepmatic-runtime", "peepmatic-runtime",
"peepmatic", "peepmatic",
// cranelift // cranelift