From ee5982fd162a134e294f14b89fee898f8d41d21b Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 30 Jun 2020 11:50:10 -0700 Subject: [PATCH] peepmatic: Be generic over the operator type This lets us avoid the cost of `cranelift_codegen::ir::Opcode` to `peepmatic_runtime::Operator` conversion overhead, and paves the way for allowing Peepmatic to support non-clif optimizations (e.g. vcode optimizations). Rather than defining our own `peepmatic::Operator` type like we used to, now the whole `peepmatic` crate is effectively generic over a `TOperator` type parameter. For the Cranelift integration, we use `cranelift_codegen::ir::Opcode` as the concrete type for our `TOperator` type parameter. For testing, we also define a `TestOperator` type, so that we can test Peepmatic code without building all of Cranelift, and we can keep them somewhat isolated from each other. The methods that `peepmatic::Operator` had are now translated into trait bounds on the `TOperator` type. These traits need to be shared between all of `peepmatic`, `peepmatic-runtime`, and `cranelift-codegen`'s Peepmatic integration. Therefore, these new traits live in a new crate: `peepmatic-traits`. This crate acts as a header file of sorts for shared trait/type/macro definitions. Additionally, the `peepmatic-runtime` crate no longer depends on the `peepmatic-macro` procedural macro crate, which should lead to faster build times for Cranelift when it is using pre-built peephole optimizers. --- .github/workflows/main.yml | 10 +- Cargo.lock | 33 +- cranelift/codegen/Cargo.toml | 10 +- cranelift/codegen/meta/src/gen_inst.rs | 25 + cranelift/codegen/src/ir/instructions.rs | 20 + cranelift/codegen/src/peepmatic.rs | 456 ++++++++++++------ cranelift/codegen/src/preopt.serialized | Bin 5613 -> 5613 bytes cranelift/peepmatic/Cargo.toml | 11 +- .../peepmatic/crates/automata/Cargo.toml | 2 +- cranelift/peepmatic/crates/fuzzing/Cargo.toml | 4 +- .../peepmatic/crates/fuzzing/src/compile.rs | 7 +- .../peepmatic/crates/fuzzing/src/interp.rs | 30 +- .../peepmatic/crates/fuzzing/src/parser.rs | 3 +- cranelift/peepmatic/crates/macro/Cargo.toml | 2 +- .../peepmatic/crates/macro/src/child_nodes.rs | 11 +- .../crates/macro/src/into_dyn_ast_ref.rs | 2 +- cranelift/peepmatic/crates/macro/src/lib.rs | 6 - .../peepmatic/crates/macro/src/operator.rs | 325 ------------- cranelift/peepmatic/crates/macro/src/span.rs | 3 + cranelift/peepmatic/crates/runtime/Cargo.toml | 7 +- .../crates/runtime/src/instruction_set.rs | 22 +- cranelift/peepmatic/crates/runtime/src/lib.rs | 2 +- .../peepmatic/crates/runtime/src/linear.rs | 36 +- .../peepmatic/crates/runtime/src/operator.rs | 306 ------------ .../crates/runtime/src/optimizations.rs | 37 +- .../peepmatic/crates/runtime/src/optimizer.rs | 52 +- .../peepmatic/crates/runtime/src/unquote.rs | 44 ++ .../peepmatic/crates/test-operator/Cargo.toml | 12 + .../peepmatic/crates/test-operator/src/lib.rs | 219 +++++++++ cranelift/peepmatic/crates/test/Cargo.toml | 2 + cranelift/peepmatic/crates/test/src/lib.rs | 47 +- .../peepmatic/crates/test/tests/tests.rs | 84 ++-- cranelift/peepmatic/crates/traits/Cargo.toml | 9 + cranelift/peepmatic/crates/traits/src/lib.rs | 26 + .../peepmatic/crates/traits/src/operator.rs | 317 ++++++++++++ .../peepmatic/crates/traits/src/typing.rs | 97 ++++ cranelift/peepmatic/src/ast.rs | 180 ++++--- cranelift/peepmatic/src/automatize.rs | 14 +- cranelift/peepmatic/src/dot_fmt.rs | 45 +- cranelift/peepmatic/src/lib.rs | 51 +- cranelift/peepmatic/src/linear_passes.rs | 172 +++---- cranelift/peepmatic/src/linearize.rs | 131 +++-- cranelift/peepmatic/src/parser.rs | 195 +++++--- cranelift/peepmatic/src/traversals.rs | 36 +- cranelift/peepmatic/src/verify.rs | 227 +++++---- scripts/publish.rs | 2 + 46 files changed, 1945 insertions(+), 1387 deletions(-) delete mode 100644 cranelift/peepmatic/crates/macro/src/operator.rs delete mode 100644 cranelift/peepmatic/crates/runtime/src/operator.rs create mode 100644 cranelift/peepmatic/crates/runtime/src/unquote.rs create mode 100644 cranelift/peepmatic/crates/test-operator/Cargo.toml create mode 100644 cranelift/peepmatic/crates/test-operator/src/lib.rs create mode 100644 cranelift/peepmatic/crates/traits/Cargo.toml create mode 100644 cranelift/peepmatic/crates/traits/src/lib.rs create mode 100644 cranelift/peepmatic/crates/traits/src/operator.rs create mode 100644 cranelift/peepmatic/crates/traits/src/typing.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 074cd2531c..e6a54b2498 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -163,10 +163,16 @@ jobs: --package peepmatic-runtime \ --package peepmatic-test - name: Rebuild Peepmatic-based peephole optimizers and test them - run: cargo test --features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers' + run: | + cargo test \ + --features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers' \ + peepmatic working-directory: ./cranelift - - name: Check that peephole optimizers are up to date + - name: Check that built peephole optimizers are up to date run: git diff --exit-code + - name: Test with Peepmatic-based peephole optimizers + run: cargo test --features 'enable-peepmatic' + working-directory: ./cranelift # Perform all tests (debug mode) for `wasmtime`. This runs stable/beta/nightly # channels of Rust as well as macOS/Linux/Windows. diff --git a/Cargo.lock b/Cargo.lock index a5b6723cc0..fa36376f0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,11 +386,13 @@ dependencies = [ "log", "peepmatic", "peepmatic-runtime", + "peepmatic-traits", "regalloc", "serde", "smallvec", "target-lexicon", "thiserror", + "wast 15.0.0", ] [[package]] @@ -1298,20 +1300,23 @@ dependencies = [ "peepmatic-automata", "peepmatic-macro", "peepmatic-runtime", + "peepmatic-test-operator", + "peepmatic-traits", + "serde", "wast 15.0.0", "z3", ] [[package]] name = "peepmatic-automata" -version = "0.2.0" +version = "0.66.0" dependencies = [ "serde", ] [[package]] name = "peepmatic-fuzzing" -version = "0.2.0" +version = "0.66.0" dependencies = [ "arbitrary", "bincode", @@ -1322,6 +1327,8 @@ dependencies = [ "peepmatic-automata", "peepmatic-runtime", "peepmatic-test", + "peepmatic-test-operator", + "peepmatic-traits", "rand 0.7.3", "serde", "wast 15.0.0", @@ -1329,7 +1336,7 @@ dependencies = [ [[package]] name = "peepmatic-macro" -version = "0.2.0" +version = "0.66.0" dependencies = [ "proc-macro2", "quote", @@ -1338,13 +1345,14 @@ dependencies = [ [[package]] name = "peepmatic-runtime" -version = "0.2.0" +version = "0.66.0" dependencies = [ "bincode", "bumpalo", "log", "peepmatic-automata", - "peepmatic-macro", + "peepmatic-test-operator", + "peepmatic-traits", "serde", "serde_test", "thiserror", @@ -1359,8 +1367,23 @@ dependencies = [ "log", "peepmatic", "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]] name = "plain" version = "0.2.3" diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index be40645534..26980ed56b 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -26,8 +26,10 @@ smallvec = { version = "1.0.0" } thiserror = "1.0.4" byteorder = { version = "1.3.2", default-features = false } peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" } -peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.2.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. # 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 @@ -80,10 +82,10 @@ regalloc-snapshot = ["bincode", "regalloc/enable-serde"] # Recompile our optimizations that are written in the `peepmatic` DSL into a # 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-peepmatic = ["peepmatic-runtime"] +enable-peepmatic = ["peepmatic-runtime", "peepmatic-traits", "serde"] [badges] maintenance = { status = "experimental" } diff --git a/cranelift/codegen/meta/src/gen_inst.rs b/cranelift/codegen/meta/src/gen_inst.rs index bda3e03569..a2760b34d7 100644 --- a/cranelift/codegen/meta/src/gen_inst.rs +++ b/cranelift/codegen/meta/src/gen_inst.rs @@ -407,7 +407,11 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) { All instructions from all supported ISAs are present. "#, ); + fmt.line("#[repr(u16)]"); 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 // 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(); } +fn gen_try_from(all_inst: &AllInstructions, fmt: &mut Formatter) { + fmt.line("impl core::convert::TryFrom for Opcode {"); + fmt.indent(|fmt| { + fmt.line("type Error = ();"); + fmt.line("#[inline]"); + fmt.line("fn try_from(x: u16) -> Result {"); + 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 /// `ctrl_typevar` is the controlling type variable. /// @@ -1147,7 +1169,10 @@ pub(crate) fn generate( gen_instruction_data_impl(&formats, &mut fmt); fmt.empty_line(); gen_opcodes(all_inst, &mut fmt); + fmt.empty_line(); gen_type_constraints(all_inst, &mut fmt); + fmt.empty_line(); + gen_try_from(all_inst, &mut fmt); fmt.update_file(opcode_filename, out_dir)?; // Instruction builder. diff --git a/cranelift/codegen/src/ir/instructions.rs b/cranelift/codegen/src/ir/instructions.rs index 2ba730b687..6c71212020 100644 --- a/cranelift/codegen/src/ir/instructions.rs +++ b/cranelift/codegen/src/ir/instructions.rs @@ -7,7 +7,9 @@ //! directory. use alloc::vec::Vec; +use core::convert::{TryFrom, TryInto}; use core::fmt::{self, Display, Formatter}; +use core::num::NonZeroU32; use core::ops::{Deref, DerefMut}; use core::str::FromStr; @@ -69,6 +71,24 @@ impl Opcode { } } +impl TryFrom for Opcode { + type Error = (); + + #[inline] + fn try_from(x: NonZeroU32) -> Result { + let x: u16 = x.get().try_into().map_err(|_| ())?; + Self::try_from(x) + } +} + +impl From 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 // 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 diff --git a/cranelift/codegen/src/peepmatic.rs b/cranelift/codegen/src/peepmatic.rs index 8c098e6bcf..2fbd58d49c 100644 --- a/cranelift/codegen/src/peepmatic.rs +++ b/cranelift/codegen/src/peepmatic.rs @@ -13,55 +13,287 @@ use cranelift_codegen_shared::condcodes::IntCC; use peepmatic_runtime::{ cc::ConditionCode, instruction_set::InstructionSet, - operator::Operator, part::{Constant, Part}, paths::Path, r#type::{BitWidth, Kind, Type}, PeepholeOptimizations, PeepholeOptimizer, }; +use peepmatic_traits::TypingRules; use std::borrow::Cow; use std::boxed::Box; use std::convert::{TryFrom, TryInto}; use std::ptr; 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 { + 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::(&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. pub(crate) fn preopt<'a, 'b>( isa: &'b dyn TargetIsa, ) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> { #[cfg(feature = "rebuild-peephole-optimizers")] fn get_serialized() -> Cow<'static, [u8]> { - use std::fs; - use std::path::Path; - - let codegen_path = Path::new(include_str!(concat!( - env!("OUT_DIR"), - "/CRANELIFT_CODEGEN_PATH" - ))); - let source_path = codegen_path.join("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`"); - - 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`") - .into() + rebuild::rebuild_preopt().into() } #[cfg(not(feature = "rebuild-peephole-optimizers"))] fn get_serialized() -> Cow<'static, [u8]> { static SERIALIZED: &[u8] = include_bytes!("preopt.serialized"); + SERIALIZED.into() } // Once initialized, this must never be re-assigned. The initialized value // is semantically "static data" and is intentionally leaked for the whole // program's lifetime. - static DESERIALIZED: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + static DESERIALIZED: AtomicPtr> = AtomicPtr::new(ptr::null_mut()); // If `DESERIALIZED` has already been initialized, then just use it. let ptr = DESERIALIZED.load(Ordering::SeqCst); @@ -247,70 +479,6 @@ fn part_to_value(pos: &mut FuncCursor, root: Inst, part: Part) -> O } } -impl Opcode { - fn to_peepmatic_operator(&self) -> Option { - 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 for Imm64 { type Error = &'static str; @@ -457,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 { type Context = FuncCursor<'b>; + type Operator = Opcode; + type Instruction = ValueOrInst; fn replace_instruction( @@ -524,7 +694,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { let mut part = Part::Instruction(root); for p in path.0[1..].iter().copied() { 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() { part = get_immediate(&pos.func.dfg, inst, p as usize); @@ -541,16 +711,16 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { Some(part) } - fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option { + fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option { 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( &self, pos: &mut FuncCursor<'b>, root: ValueOrInst, - operator: Operator, + operator: Opcode, r#type: Type, a: Part, ) -> ValueOrInst { @@ -558,32 +728,32 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { let root = root.resolve_inst(&pos.func.dfg).unwrap(); match operator { - Operator::AdjustSpDown => { + Opcode::AdjustSpDown => { let a = part_to_value(pos, root, a).unwrap(); pos.ins().adjust_sp_down(a).into() } - Operator::AdjustSpDownImm => { + Opcode::AdjustSpDownImm => { let c = a.unwrap_constant(); let imm = Imm64::try_from(c).unwrap(); pos.ins().adjust_sp_down_imm(imm).into() } - Operator::Bconst => { + Opcode::Bconst => { let c = a.unwrap_constant(); let val = const_to_value(pos.ins(), c, root); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Bint => { + Opcode::Bint => { let a = part_to_value(pos, root, a).unwrap(); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let val = pos.ins().bint(ty, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Bnot => { + Opcode::Bnot => { let a = part_to_value(pos, root, a).unwrap(); let val = pos.ins().bnot(a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Brnz => { + Opcode::Brnz => { let a = part_to_value(pos, root, a).unwrap(); // NB: branching instructions must be the root of an @@ -595,37 +765,37 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { pos.ins().brnz(a, block, &args).into() } - Operator::Brz => { + Opcode::Brz => { 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 args = pos.func.dfg.inst_args(root)[1..].to_vec(); pos.ins().brz(a, block, &args).into() } - Operator::Iconst => { + Opcode::Iconst => { let a = a.unwrap_constant(); let val = const_to_value(pos.ins(), a, root); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Ireduce => { + Opcode::Ireduce => { let a = part_to_value(pos, root, a).unwrap(); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let val = pos.ins().ireduce(ty, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Sextend => { + Opcode::Sextend => { let a = part_to_value(pos, root, a).unwrap(); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let val = pos.ins().sextend(ty, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Trapnz => { + Opcode::Trapnz => { let a = part_to_value(pos, root, a).unwrap(); // 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 // trap code from the root of the left-hand side. Peepmatic // doesn't currently represent trap codes. @@ -633,13 +803,13 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { pos.ins().trapnz(a, code).into() } - Operator::Trapz => { + Opcode::Trapz => { 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(); pos.ins().trapz(a, code).into() } - Operator::Uextend => { + Opcode::Uextend => { let a = part_to_value(pos, root, a).unwrap(); let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root); let val = pos.ins().uextend(ty, a); @@ -653,7 +823,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { &self, pos: &mut FuncCursor<'b>, root: ValueOrInst, - operator: Operator, + operator: Opcode, _: Type, a: Part, b: Part, @@ -662,193 +832,193 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { let root = root.resolve_inst(&pos.func.dfg).unwrap(); match operator { - Operator::Band => { + Opcode::Band => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().band(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::BandImm => { + Opcode::BandImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().band_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Bor => { + Opcode::Bor => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().bor(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::BorImm => { + Opcode::BorImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().bor_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Bxor => { + Opcode::Bxor => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().bxor(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::BxorImm => { + Opcode::BxorImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().bxor_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Iadd => { + Opcode::Iadd => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().iadd(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::IaddImm => { + Opcode::IaddImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().iadd_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Ifcmp => { + Opcode::Ifcmp => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().ifcmp(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::IfcmpImm => { + Opcode::IfcmpImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().ifcmp_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Imul => { + Opcode::Imul => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().imul(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::ImulImm => { + Opcode::ImulImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().imul_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::IrsubImm => { + Opcode::IrsubImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().irsub_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Ishl => { + Opcode::Ishl => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().ishl(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::IshlImm => { + Opcode::IshlImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().ishl_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Isub => { + Opcode::Isub => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().isub(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Rotl => { + Opcode::Rotl => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().rotl(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::RotlImm => { + Opcode::RotlImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().rotl_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Rotr => { + Opcode::Rotr => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().rotr(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::RotrImm => { + Opcode::RotrImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().rotr_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Sdiv => { + Opcode::Sdiv => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().sdiv(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::SdivImm => { + Opcode::SdivImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().sdiv_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Srem => { + Opcode::Srem => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().srem(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::SremImm => { + Opcode::SremImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().srem_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Sshr => { + Opcode::Sshr => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().sshr(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::SshrImm => { + Opcode::SshrImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().sshr_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Udiv => { + Opcode::Udiv => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().udiv(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::UdivImm => { + Opcode::UdivImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().udiv_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Urem => { + Opcode::Urem => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().urem(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::UremImm => { + Opcode::UremImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().urem_imm(b, a); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Ushr => { + Opcode::Ushr => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().ushr(a, b); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::UshrImm => { + Opcode::UshrImm => { let a = part_to_imm64(pos, a); let b = part_to_value(pos, root, b).unwrap(); let val = pos.ins().ushr_imm(b, a); @@ -862,7 +1032,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { &self, pos: &mut FuncCursor<'b>, root: ValueOrInst, - operator: Operator, + operator: Opcode, _: Type, a: Part, b: Part, @@ -872,7 +1042,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { let root = root.resolve_inst(&pos.func.dfg).unwrap(); match operator { - Operator::Icmp => { + Opcode::Icmp => { let cond = a.unwrap_condition_code(); let cond = peepmatic_to_intcc(cond); let b = part_to_value(pos, root, b).unwrap(); @@ -880,7 +1050,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { let val = pos.ins().icmp(cond, b, c); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::IcmpImm => { + Opcode::IcmpImm => { let cond = a.unwrap_condition_code(); let cond = peepmatic_to_intcc(cond); let imm = part_to_imm64(pos, b); @@ -888,7 +1058,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa { let val = pos.ins().icmp_imm(cond, c, imm); pos.func.dfg.value_def(val).unwrap_inst().into() } - Operator::Select => { + Opcode::Select => { let a = part_to_value(pos, root, a).unwrap(); let b = part_to_value(pos, root, b).unwrap(); let c = part_to_value(pos, root, c).unwrap(); diff --git a/cranelift/codegen/src/preopt.serialized b/cranelift/codegen/src/preopt.serialized index 1319882a51b346e3caf54241aeed5740b81d38b3..8765e4bb8422713c9c5bcfe5f4c0988675d932d7 100644 GIT binary patch literal 5613 zcmcIoS8f$S5S)EZn4EJk!Q`By#3|qc@DB)yA0)sw;RL`3IT?-ue{dnF+MasbJ8ySh zkZ5VPXS%Dpd(!%?r-Mlmb?B&5^@^^oGbV2ls&3KkhRPb8wKFYR)XMfZ%93c`9QEp` zFGhVb>Y1p?SBz*X)m1#N$xs$B5sFC|#tq)dk2sG$gjPslp({0xV93WZqpFIq`pe2d z078(8-e_b@Kq9@aI8FGtR}~~@Y|PMTtS5>i)-qM@xB=uqxL>>bnxl+ zKF0Ky#``Av@Rl)H6wlM8YVvX3E98LoJl3+(`M4Gm;iPG|wP(atXztyByH%|dQNPe= zF*cEZ&?ubaa6XpGRCm3`GC)&rOFIU?Z$MttXF{BBz%tFai z-6C4HqP10S40~GTZy7d9S~-2W%D0OMt(s_3UDEWaUm+`t)_ezWZIv^wqFCi`%v3(p z%KaV_;K%ImLhG%#)3cY%$U4_2c#Bsw;)luQSAURPDSnt-e!T|C)r7|~l;ZQ!P~S^i zUqkl#_Q?V5xGIe zb(PDX8=F7Riiy2r{0w<+hRN!=gUqlAuWEc=+Uh$O!WjV@e9l$l{q&B6=i?LR^V4_1 z;q9*RbkHLtbvue^A<+$Y(fA?m^YZsp2&&SfC*yB?AQ}rFRu-x>-sn~bwvIy;#~X2|+97FIXNDE-+1?d8E&9iWe_Z%qh5uIgx^VE0 zQB)Iq8^8ZrF(q&`jK`rkl>4ebVjZ^t7G1_+JPdfuKw`ldg_7C@Q^r#+OUiJ>lXN#& zfy3%j+L&d;v_{S_}rFE z8@5l9agO^m*>d+;wm&PA%rL$zFYSgeYfrb zop)?is%VyNS;n4yk?D}!yYkn`&8LqUu|13@pD&%AdPc_S%;&f7{CwpMB3QE$-W1=U zER?ecx3(epX4W-U?O6bxv5m&)BA&LJ@jsjpTBR^Y)2irw6P@h1=#YE0%-CGZyb8rr z1-JRvxh;ve>l%iuWX?8jw_bIx6E3gKG0bt(zCRCXseW3SFsffZ75mmNV|S_FHh(XV z?}lC-ez0Tk-Ri(yx>N`6DnV5T-gKsY>yWX#)M1;y=g#|yZobxELT4Y{3hpa^>48}G z;)^E7^v%-h+Q?uaZhUSj&yVi3iH4;=H<*5gtfS2a%v{%I=(UsKi+I`$^9ax0Ir}~A zru-go40C*xrM+xnEFGFZ;#oR{Lg-hgFn`JKdkQmlms7aSKa!n@_iV39uZthd>qmAC z%&}Rno>fB)8Sy9jQkB9KB#f$*XWX|+8M{lBw)ra~@4rXG@T`hczHefjPx4s4`CJv( zf$={Hi^vZAJ=3vcf6jEA=T|cw{rYmIJCCvd0b&#Hp`Se{ocHigGac{k?`Jyht?yMIlh~A6V&aP;5>-K2tTcTmFq-YnR zAZLwa)#|A&waI&|cd_gT(;kDb"] edition = "2018" license = "Apache-2.0 WITH LLVM-exception" diff --git a/cranelift/peepmatic/crates/fuzzing/Cargo.toml b/cranelift/peepmatic/crates/fuzzing/Cargo.toml index 6c25319ec5..a09911719e 100644 --- a/cranelift/peepmatic/crates/fuzzing/Cargo.toml +++ b/cranelift/peepmatic/crates/fuzzing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peepmatic-fuzzing" -version = "0.2.0" +version = "0.66.0" authors = ["Nick Fitzgerald "] edition = "2018" publish = false @@ -17,6 +17,8 @@ peepmatic = { path = "../.." } peepmatic-automata = { path = "../automata", features = ["serde"] } peepmatic-runtime = { path = "../runtime", features = ["construct"] } peepmatic-test = { path = "../test" } +peepmatic-test-operator = { path = "../test-operator" } +peepmatic-traits = { path = "../traits" } rand = { version = "0.7.3", features = ["small_rng"] } serde = "1.0.106" wast = "15.0.0" diff --git a/cranelift/peepmatic/crates/fuzzing/src/compile.rs b/cranelift/peepmatic/crates/fuzzing/src/compile.rs index ef635a6ba2..c0e4dad59d 100644 --- a/cranelift/peepmatic/crates/fuzzing/src/compile.rs +++ b/cranelift/peepmatic/crates/fuzzing/src/compile.rs @@ -1,6 +1,7 @@ //! Fuzz testing utilities related to AST pattern matching. use peepmatic_runtime::PeepholeOptimizations; +use peepmatic_test_operator::TestOperator; use std::path::Path; use std::str; @@ -19,18 +20,18 @@ pub fn compile(data: &[u8]) { Ok(s) => s, }; - let opt = match peepmatic::compile_str(source, Path::new("fuzz")) { + let opt = match peepmatic::compile_str::(source, Path::new("fuzz")) { Err(_) => return, Ok(o) => o, }; // Should be able to serialize and deserialize the peephole optimizer. let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK"); - let _: PeepholeOptimizations = + let _: PeepholeOptimizations = bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK"); // Compiling the same source text again should be deterministic. - let opt2 = peepmatic::compile_str(source, Path::new("fuzz")) + let opt2 = peepmatic::compile_str::(source, Path::new("fuzz")) .expect("should be able to compile source text again, if it compiled OK the first time"); let opt2_bytes = bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK"); diff --git a/cranelift/peepmatic/crates/fuzzing/src/interp.rs b/cranelift/peepmatic/crates/fuzzing/src/interp.rs index 0ebdeebe28..3cd7aa9280 100644 --- a/cranelift/peepmatic/crates/fuzzing/src/interp.rs +++ b/cranelift/peepmatic/crates/fuzzing/src/interp.rs @@ -6,12 +6,13 @@ use peepmatic::{ }; use peepmatic_runtime::{ cc::ConditionCode, - operator::TypingContext as TypingContextTrait, part::Constant, r#type::BitWidth, r#type::{Kind, Type}, }; use peepmatic_test::{Program, TestIsa}; +use peepmatic_test_operator::TestOperator; +use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules}; use std::collections::{BTreeMap, HashMap}; use std::path::Path; 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. let buf = wast::parser::ParseBuffer::new(&source).unwrap(); - let ast = wast::parser::parse::(&buf).unwrap(); + let ast = wast::parser::parse::>(&buf).unwrap(); // And we need access to the assigned types, so re-verify it as well. peepmatic::verify(&ast).unwrap(); @@ -87,7 +88,7 @@ pub fn interp(data: &[u8]) { // Generate this operation's immediates. let mut imm_tys = vec![]; op.operator - .immediate_types(&mut TypingContext, op.span(), &mut imm_tys); + .immediate_types((), &mut TypingContext, &mut imm_tys); let imms: Vec<_> = op .operands .iter() @@ -121,7 +122,7 @@ pub fn interp(data: &[u8]) { // this operation's arguments. let mut arg_tys = vec![]; op.operator - .param_types(&mut TypingContext, op.span(), &mut arg_tys); + .parameter_types((), &mut TypingContext, &mut arg_tys); let args: Vec<_> = op .operands .iter() @@ -165,7 +166,7 @@ pub fn interp(data: &[u8]) { }) .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::ConditionCode => { unreachable!("condition codes cannot be operation results") @@ -206,41 +207,42 @@ enum TypeOrConditionCode { struct TypingContext; impl<'a> TypingContextTrait<'a> for TypingContext { + type Span = (); type TypeVariable = TypeOrConditionCode; - fn cc(&mut self, _: wast::Span) -> Self::TypeVariable { + fn cc(&mut self, _: ()) -> Self::TypeVariable { TypeOrConditionCode::ConditionCode } - fn bNN(&mut self, _: wast::Span) -> Self::TypeVariable { + fn bNN(&mut self, _: ()) -> Self::TypeVariable { TypeOrConditionCode::Type(Type::b1()) } - fn iNN(&mut self, _: wast::Span) -> Self::TypeVariable { + fn iNN(&mut self, _: ()) -> Self::TypeVariable { TypeOrConditionCode::Type(Type::i32()) } - fn iMM(&mut self, _: wast::Span) -> Self::TypeVariable { + fn iMM(&mut self, _: ()) -> Self::TypeVariable { 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()) } - fn b1(&mut self, _: wast::Span) -> Self::TypeVariable { + fn b1(&mut self, _: ()) -> Self::TypeVariable { TypeOrConditionCode::Type(Type::b1()) } - fn void(&mut self, _: wast::Span) -> Self::TypeVariable { + fn void(&mut self, _: ()) -> Self::TypeVariable { 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()) } - fn any_t(&mut self, _: wast::Span) -> Self::TypeVariable { + fn any_t(&mut self, _: ()) -> Self::TypeVariable { TypeOrConditionCode::Type(Type::i32()) } } diff --git a/cranelift/peepmatic/crates/fuzzing/src/parser.rs b/cranelift/peepmatic/crates/fuzzing/src/parser.rs index a9972ff7ff..be59fa658b 100644 --- a/cranelift/peepmatic/crates/fuzzing/src/parser.rs +++ b/cranelift/peepmatic/crates/fuzzing/src/parser.rs @@ -1,6 +1,7 @@ //! Utilities for fuzzing our DSL's parser. use peepmatic::Optimizations; +use peepmatic_test_operator::TestOperator; use std::str; /// 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, }; - let _ = wast::parser::parse::(&buf); + let _ = wast::parser::parse::>(&buf); } #[cfg(test)] diff --git a/cranelift/peepmatic/crates/macro/Cargo.toml b/cranelift/peepmatic/crates/macro/Cargo.toml index 868ec60380..9cd1dacd5c 100644 --- a/cranelift/peepmatic/crates/macro/Cargo.toml +++ b/cranelift/peepmatic/crates/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peepmatic-macro" -version = "0.2.0" +version = "0.66.0" authors = ["Nick Fitzgerald "] edition = "2018" license = "Apache-2.0 WITH LLVM-exception" diff --git a/cranelift/peepmatic/crates/macro/src/child_nodes.rs b/cranelift/peepmatic/crates/macro/src/child_nodes.rs index c71ac409a0..85a5e10382 100644 --- a/cranelift/peepmatic/crates/macro/src/child_nodes.rs +++ b/cranelift/peepmatic/crates/macro/src/child_nodes.rs @@ -9,8 +9,8 @@ pub fn derive_child_nodes(input: &DeriveInput) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); Ok(quote! { - impl #impl_generics ChildNodes<'a, 'a> for #name #ty_generics #where_clause { - fn child_nodes(&'a self, children: &mut impl Extend>) { + impl #impl_generics ChildNodes<'a, 'a, TOperator> for #name #ty_generics #where_clause { + fn child_nodes(&'a self, children: &mut impl Extend>) { #children } } @@ -103,7 +103,12 @@ fn get_child_nodes(data: &syn::Data) -> Result { fn add_trait_bounds(mut generics: Generics) -> Generics { for param in &mut generics.params { 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 diff --git a/cranelift/peepmatic/crates/macro/src/into_dyn_ast_ref.rs b/cranelift/peepmatic/crates/macro/src/into_dyn_ast_ref.rs index 4cd993a72c..04944ee311 100644 --- a/cranelift/peepmatic/crates/macro/src/into_dyn_ast_ref.rs +++ b/cranelift/peepmatic/crates/macro/src/into_dyn_ast_ref.rs @@ -13,7 +13,7 @@ pub fn derive_into_dyn_ast_ref(input: &DeriveInput) -> Result for DynAstRef<'a> #where_clause { + impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a, TOperator> #where_clause { #[inline] fn from(x: &'a #ty #ty_generics) -> Self { Self::#ty(x) diff --git a/cranelift/peepmatic/crates/macro/src/lib.rs b/cranelift/peepmatic/crates/macro/src/lib.rs index 5375947c36..22908bb947 100644 --- a/cranelift/peepmatic/crates/macro/src/lib.rs +++ b/cranelift/peepmatic/crates/macro/src/lib.rs @@ -11,14 +11,8 @@ use syn::{parse_macro_input, Ident, Result}; mod child_nodes; mod into_dyn_ast_ref; -mod operator; mod span; -#[proc_macro_derive(PeepmaticOperator, attributes(peepmatic))] -pub fn operator(input: TokenStream) -> TokenStream { - operator::derive_operator(input) -} - #[proc_macro_derive(Ast, attributes(peepmatic))] pub fn derive_ast(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/cranelift/peepmatic/crates/macro/src/operator.rs b/cranelift/peepmatic/crates/macro/src/operator.rs deleted file mode 100644 index 0d53a1219d..0000000000 --- a/cranelift/peepmatic/crates/macro/src/operator.rs +++ /dev/null @@ -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> { - 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 { - 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, - ) - 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, - ) - 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 { - #( #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 for #ident { - type Error = &'static str; - - fn try_from(value: u32) -> Result { - match value { - #( #matches )* - _ => Err(#error_msg) - } - } - } - } -} diff --git a/cranelift/peepmatic/crates/macro/src/span.rs b/cranelift/peepmatic/crates/macro/src/span.rs index bdc0a63655..a6cea5a262 100644 --- a/cranelift/peepmatic/crates/macro/src/span.rs +++ b/cranelift/peepmatic/crates/macro/src/span.rs @@ -45,6 +45,9 @@ pub fn derive_span(input: &DeriveInput) -> Result { fn add_span_trait_bounds(mut generics: Generics) -> Generics { for param in &mut generics.params { if let GenericParam::Type(ref mut type_param) = *param { + if type_param.ident == "TOperator" { + continue; + } type_param.bounds.push(parse_quote!(Span)); } } diff --git a/cranelift/peepmatic/crates/runtime/Cargo.toml b/cranelift/peepmatic/crates/runtime/Cargo.toml index fe21c3defd..7432a34834 100644 --- a/cranelift/peepmatic/crates/runtime/Cargo.toml +++ b/cranelift/peepmatic/crates/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peepmatic-runtime" -version = "0.2.0" +version = "0.66.0" authors = ["Nick Fitzgerald "] edition = "2018" license = "Apache-2.0 WITH LLVM-exception" @@ -12,13 +12,14 @@ description = "Runtime support for peepmatic peephole optimizers" bincode = "1.2.1" bumpalo = "3.2.0" log = "0.4.8" -peepmatic-automata = { version = "0.2.0", path = "../automata", features = ["serde"] } -peepmatic-macro = { version = "0.2.0", path = "../macro" } +peepmatic-automata = { version = "0.66.0", path = "../automata", features = ["serde"] } +peepmatic-traits = { version = "0.66.0", path = "../traits" } serde = { version = "1.0.105", features = ["derive"] } thiserror = "1.0.15" wast = { version = "15.0.0", optional = true } [dev-dependencies] +peepmatic-test-operator = { version = "0.66.0", path = "../test-operator" } serde_test = "1.0.114" [features] diff --git a/cranelift/peepmatic/crates/runtime/src/instruction_set.rs b/cranelift/peepmatic/crates/runtime/src/instruction_set.rs index 4d9a0752e5..d8b9129d6d 100644 --- a/cranelift/peepmatic/crates/runtime/src/instruction_set.rs +++ b/cranelift/peepmatic/crates/runtime/src/instruction_set.rs @@ -1,10 +1,11 @@ //! Interfacing with actual instructions. -use crate::operator::Operator; use crate::part::{Constant, Part}; use crate::paths::Path; use crate::r#type::Type; use std::fmt::Debug; +use std::hash::Hash; +use std::num::NonZeroU32; /// A trait for interfacing with actual instruction sequences. /// @@ -32,6 +33,9 @@ pub unsafe trait InstructionSet<'a> { /// implementation. type Context; + /// An operator. + type Operator: 'static + Copy + Debug + Eq + Hash + Into; + /// An instruction (or identifier for an instruction). type Instruction: Copy + Debug + Eq; @@ -64,10 +68,12 @@ pub unsafe trait InstructionSet<'a> { /// Get the given instruction's operator. /// - /// If the instruction's opcode does not have an associated - /// `peepmatic_runtime::operator::Operator` variant (i.e. that instruction - /// isn't supported by `peepmatic` yet) then `None` should be returned. - fn operator(&self, context: &mut Self::Context, instr: Self::Instruction) -> Option; + /// If the instruction isn't supported, then `None` should be returned. + fn operator( + &self, + context: &mut Self::Context, + instr: Self::Instruction, + ) -> Option; /// Make a unary instruction. /// @@ -76,7 +82,7 @@ pub unsafe trait InstructionSet<'a> { &self, context: &mut Self::Context, root: Self::Instruction, - operator: Operator, + operator: Self::Operator, r#type: Type, a: Part, ) -> Self::Instruction; @@ -92,7 +98,7 @@ pub unsafe trait InstructionSet<'a> { &self, context: &mut Self::Context, root: Self::Instruction, - operator: Operator, + operator: Self::Operator, r#type: Type, a: Part, b: Part, @@ -108,7 +114,7 @@ pub unsafe trait InstructionSet<'a> { &self, context: &mut Self::Context, root: Self::Instruction, - operator: Operator, + operator: Self::Operator, r#type: Type, a: Part, b: Part, diff --git a/cranelift/peepmatic/crates/runtime/src/lib.rs b/cranelift/peepmatic/crates/runtime/src/lib.rs index ae45c42f2b..0a0cf1012d 100644 --- a/cranelift/peepmatic/crates/runtime/src/lib.rs +++ b/cranelift/peepmatic/crates/runtime/src/lib.rs @@ -22,12 +22,12 @@ pub mod error; pub mod instruction_set; pub mod integer_interner; pub mod linear; -pub mod operator; pub mod optimizations; pub mod optimizer; pub mod part; pub mod paths; pub mod r#type; +pub mod unquote; pub use error::{Error, Result}; pub use optimizations::PeepholeOptimizations; diff --git a/cranelift/peepmatic/crates/runtime/src/linear.rs b/cranelift/peepmatic/crates/runtime/src/linear.rs index f156311740..21bbdd316a 100644 --- a/cranelift/peepmatic/crates/runtime/src/linear.rs +++ b/cranelift/peepmatic/crates/runtime/src/linear.rs @@ -7,17 +7,22 @@ use crate::cc::ConditionCode; use crate::integer_interner::{IntegerId, IntegerInterner}; -use crate::operator::{Operator, UnquoteOperator}; use crate::paths::{PathId, PathInterner}; use crate::r#type::{BitWidth, Type}; +use crate::unquote::UnquoteOperator; use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use std::hash::Hash; use std::num::NonZeroU32; /// A set of linear optimizations. #[derive(Debug)] -pub struct Optimizations { +pub struct Optimizations +where + TOperator: 'static + Copy + Debug + Eq + Hash, +{ /// The linear optimizations. - pub optimizations: Vec, + pub optimizations: Vec>, /// The de-duplicated paths referenced by these optimizations. pub paths: PathInterner, @@ -28,9 +33,12 @@ pub struct Optimizations { /// A linearized optimization. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Optimization { +pub struct Optimization +where + TOperator: 'static + Copy + Debug + Eq + Hash, +{ /// The chain of increments for this optimization. - pub increments: Vec, + pub increments: Vec>, } /// Match any value. @@ -63,7 +71,10 @@ pub fn bool_to_match_result(b: bool) -> MatchResult { /// basically become a state and a transition edge out of that state in the /// final automata. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Increment { +pub struct Increment +where + TOperator: 'static + Copy + Debug + Eq + Hash, +{ /// The matching operation to perform. pub operation: MatchOp, @@ -74,7 +85,7 @@ pub struct Increment { /// Actions to perform, given that the operation resulted in the expected /// value. - pub actions: Vec, + pub actions: Vec>, } /// A matching operation to be performed on some Cranelift instruction as part @@ -163,7 +174,7 @@ pub struct RhsId(pub u16); /// When evaluating actions, the `i^th` action implicitly defines the /// `RhsId(i)`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Action { +pub enum Action { /// Reuse something from the left-hand side. GetLhs { /// The path to the instruction or value. @@ -215,13 +226,13 @@ pub enum Action { /// The type of this instruction's result. r#type: Type, /// The operator for this instruction. - operator: Operator, + operator: TOperator, }, /// Make a binary instruction. MakeBinaryInst { /// The opcode for this instruction. - operator: Operator, + operator: TOperator, /// The type of this instruction's result. r#type: Type, /// The operands for this instruction. @@ -231,7 +242,7 @@ pub enum Action { /// Make a ternary instruction. MakeTernaryInst { /// The opcode for this instruction. - operator: Operator, + operator: TOperator, /// The type of this instruction's result. r#type: Type, /// The operands for this instruction. @@ -242,6 +253,7 @@ pub enum Action { #[cfg(test)] mod tests { use super::*; + use peepmatic_test_operator::TestOperator; // These types all end up in the automaton, so we should take care that they // are small and don't fill up the data cache (or take up too much @@ -259,6 +271,6 @@ mod tests { #[test] fn action_size() { - assert_eq!(std::mem::size_of::(), 16); + assert_eq!(std::mem::size_of::>(), 16); } } diff --git a/cranelift/peepmatic/crates/runtime/src/operator.rs b/cranelift/peepmatic/crates/runtime/src/operator.rs deleted file mode 100644 index 8e03e8b8ec..0000000000 --- a/cranelift/peepmatic/crates/runtime/src/operator.rs +++ /dev/null @@ -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; -} diff --git a/cranelift/peepmatic/crates/runtime/src/optimizations.rs b/cranelift/peepmatic/crates/runtime/src/optimizations.rs index 50cf93184c..3636b0e7b3 100644 --- a/cranelift/peepmatic/crates/runtime/src/optimizations.rs +++ b/cranelift/peepmatic/crates/runtime/src/optimizations.rs @@ -8,6 +8,8 @@ use crate::optimizer::PeepholeOptimizer; use crate::paths::PathInterner; use peepmatic_automata::Automaton; use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use std::hash::Hash; #[cfg(feature = "construct")] use std::fs; @@ -19,7 +21,10 @@ use std::path::Path; /// This is the compilation result of the `peepmatic` crate, after its taken a /// bunch of optimizations written in the DSL and lowered and combined them. #[derive(Debug, Serialize, Deserialize)] -pub struct PeepholeOptimizations { +pub struct PeepholeOptimizations +where + TOperator: 'static + Copy + Debug + Eq + Hash, +{ /// The instruction paths referenced by the peephole optimizations. pub paths: PathInterner, @@ -29,12 +34,18 @@ pub struct PeepholeOptimizations { /// The underlying automata for matching optimizations' left-hand sides, and /// building up the corresponding right-hand side. - pub automata: Automaton>, + pub automata: Automaton]>>, } -impl PeepholeOptimizations { +impl PeepholeOptimizations +where + TOperator: 'static + Copy + Debug + Eq + Hash, +{ /// Deserialize a `PeepholeOptimizations` from bytes. - pub fn deserialize(serialized: &[u8]) -> Result { + pub fn deserialize<'a>(serialized: &'a [u8]) -> Result + where + TOperator: serde::Deserialize<'a>, + { let peep_opt: Self = bincode::deserialize(serialized)?; Ok(peep_opt) } @@ -43,12 +54,20 @@ impl PeepholeOptimizations { /// /// Requires that the `"construct"` cargo feature is enabled. #[cfg(feature = "construct")] - pub fn serialize_to_file(&self, path: &Path) -> Result<()> { + pub fn serialize_to_file(&self, path: &Path) -> Result<()> + where + TOperator: serde::Serialize, + { let file = fs::File::create(path)?; bincode::serialize_into(file, self)?; Ok(()) } +} +impl PeepholeOptimizations +where + TOperator: 'static + Copy + Debug + Eq + Hash, +{ /// Create a new peephole optimizer instance from this set of peephole /// optimizations. /// @@ -58,9 +77,13 @@ impl PeepholeOptimizations { /// instance, rather than create a new one for each instruction. Reusing the /// peephole optimizer instance allows the reuse of a few internal /// allocations. - pub fn optimizer<'peep, 'ctx, I>(&'peep self, instr_set: I) -> PeepholeOptimizer<'peep, 'ctx, I> + pub fn optimizer<'peep, 'ctx, TInstructionSet>( + &'peep self, + instr_set: TInstructionSet, + ) -> PeepholeOptimizer<'peep, 'ctx, TInstructionSet> where - I: InstructionSet<'ctx>, + TInstructionSet: InstructionSet<'ctx, Operator = TOperator>, + TOperator: Into, { PeepholeOptimizer { peep_opt: self, diff --git a/cranelift/peepmatic/crates/runtime/src/optimizer.rs b/cranelift/peepmatic/crates/runtime/src/optimizer.rs index f477db85ac..48014adc4f 100644 --- a/cranelift/peepmatic/crates/runtime/src/optimizer.rs +++ b/cranelift/peepmatic/crates/runtime/src/optimizer.rs @@ -2,10 +2,10 @@ use crate::instruction_set::InstructionSet; use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult}; -use crate::operator::UnquoteOperator; use crate::optimizations::PeepholeOptimizations; use crate::part::{Constant, Part}; use crate::r#type::{BitWidth, Type}; +use crate::unquote::UnquoteOperator; use peepmatic_automata::State; use std::convert::TryFrom; use std::fmt::{self, Debug}; @@ -21,20 +21,20 @@ use std::num::NonZeroU32; /// Reusing an instance when applying peephole optimizations to different /// instruction sequences means that you reuse internal allocations that are /// used to match left-hand sides and build up right-hand sides. -pub struct PeepholeOptimizer<'peep, 'ctx, I> +pub struct PeepholeOptimizer<'peep, 'ctx, TInstructionSet> where - I: InstructionSet<'ctx>, + TInstructionSet: InstructionSet<'ctx>, { - pub(crate) peep_opt: &'peep PeepholeOptimizations, - pub(crate) instr_set: I, - pub(crate) right_hand_sides: Vec>, - pub(crate) actions: Vec, + pub(crate) peep_opt: &'peep PeepholeOptimizations, + pub(crate) instr_set: TInstructionSet, + pub(crate) right_hand_sides: Vec>, + pub(crate) actions: Vec>, pub(crate) backtracking_states: Vec<(State, usize)>, } -impl<'peep, 'ctx, I> Debug for PeepholeOptimizer<'peep, 'ctx, I> +impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet> where - I: InstructionSet<'ctx>, + TInstructionSet: InstructionSet<'ctx>, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let PeepholeOptimizer { @@ -54,9 +54,9 @@ where } } -impl<'peep, 'ctx, I> PeepholeOptimizer<'peep, 'ctx, I> +impl<'peep, 'ctx, TInstructionSet> PeepholeOptimizer<'peep, 'ctx, TInstructionSet> where - I: InstructionSet<'ctx>, + TInstructionSet: InstructionSet<'ctx>, { fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant { use Constant::*; @@ -107,7 +107,11 @@ where } } - fn eval_actions(&mut self, context: &mut I::Context, root: I::Instruction) { + fn eval_actions( + &mut self, + context: &mut TInstructionSet::Context, + root: TInstructionSet::Instruction, + ) { let mut actions = mem::replace(&mut self.actions, vec![]); for action in actions.drain(..) { @@ -272,8 +276,8 @@ where fn eval_match_op( &mut self, - context: &mut I::Context, - root: I::Instruction, + context: &mut TInstructionSet::Context, + root: TInstructionSet::Instruction, match_op: MatchOp, ) -> MatchResult { use crate::linear::MatchOp::*; @@ -288,13 +292,7 @@ where .ok_or(Else)?; let inst = part.as_instruction().ok_or(Else)?; let op = self.instr_set.operator(context, inst).ok_or(Else)?; - let op = op as u32; - debug_assert!( - op != 0, - "`Operator` doesn't have any variant represented - with zero" - ); - Ok(unsafe { NonZeroU32::new_unchecked(op as u32) }) + Ok(op.into()) } IsConst { path } => { let path = self.peep_opt.paths.lookup(path); @@ -477,9 +475,9 @@ where /// untouched and `None` is returned. pub fn apply_one( &mut self, - context: &mut I::Context, - root: I::Instruction, - ) -> Option { + context: &mut TInstructionSet::Context, + root: TInstructionSet::Instruction, + ) -> Option { log::trace!("PeepholeOptimizer::apply_one"); self.backtracking_states.clear(); @@ -566,7 +564,11 @@ where /// Keep applying peephole optimizations to the given instruction until none /// can be applied anymore. - pub fn apply_all(&mut self, context: &mut I::Context, mut inst: I::Instruction) { + pub fn apply_all( + &mut self, + context: &mut TInstructionSet::Context, + mut inst: TInstructionSet::Instruction, + ) { loop { if let Some(new_inst) = self.apply_one(context, inst) { inst = new_inst; diff --git a/cranelift/peepmatic/crates/runtime/src/unquote.rs b/cranelift/peepmatic/crates/runtime/src/unquote.rs new file mode 100644 index 0000000000..216eb903b5 --- /dev/null +++ b/cranelift/peepmatic/crates/runtime/src/unquote.rs @@ -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"); +} diff --git a/cranelift/peepmatic/crates/test-operator/Cargo.toml b/cranelift/peepmatic/crates/test-operator/Cargo.toml new file mode 100644 index 0000000000..27df2e6c1e --- /dev/null +++ b/cranelift/peepmatic/crates/test-operator/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "peepmatic-test-operator" +version = "0.66.0" +authors = ["Nick Fitzgerald "] +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" diff --git a/cranelift/peepmatic/crates/test-operator/src/lib.rs b/cranelift/peepmatic/crates/test-operator/src/lib.rs new file mode 100644 index 0000000000..cff0ad5876 --- /dev/null +++ b/cranelift/peepmatic/crates/test-operator/src/lib.rs @@ -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); + } + } +} diff --git a/cranelift/peepmatic/crates/test/Cargo.toml b/cranelift/peepmatic/crates/test/Cargo.toml index 08d357ef95..3f4799ffdd 100644 --- a/cranelift/peepmatic/crates/test/Cargo.toml +++ b/cranelift/peepmatic/crates/test/Cargo.toml @@ -12,3 +12,5 @@ env_logger = "0.7.1" log = "0.4.8" peepmatic = { path = "../.." } peepmatic-runtime = { path = "../runtime" } +peepmatic-test-operator = { path = "../test-operator" } +peepmatic-traits = { path = "../traits" } diff --git a/cranelift/peepmatic/crates/test/src/lib.rs b/cranelift/peepmatic/crates/test/src/lib.rs index f74e9b7c53..15055e2b1a 100644 --- a/cranelift/peepmatic/crates/test/src/lib.rs +++ b/cranelift/peepmatic/crates/test/src/lib.rs @@ -5,11 +5,12 @@ use peepmatic_runtime::{ cc::ConditionCode, instruction_set::InstructionSet, - operator::Operator, part::{Constant, Part}, paths::Path, r#type::{BitWidth, Kind, Type}, }; +use peepmatic_test_operator::TestOperator; +use peepmatic_traits::TypingRules; use std::cell::RefCell; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -19,7 +20,7 @@ pub struct Instruction(pub usize); #[derive(Debug)] pub struct InstructionData { - pub operator: Operator, + pub operator: TestOperator, pub r#type: Type, pub immediates: Vec, pub arguments: Vec, @@ -174,7 +175,7 @@ impl Program { pub fn new_instruction( &mut self, - operator: Operator, + operator: TestOperator, r#type: Type, immediates: Vec, arguments: Vec, @@ -188,11 +189,11 @@ impl Program { immediates.len(), ); assert_eq!( - operator.params_arity() as usize, + operator.parameters_arity() as usize, arguments.len(), "wrong number of arguments for {:?}: expected {}, found {}", operator, - operator.params_arity(), + operator.parameters_arity(), arguments.len(), ); @@ -222,7 +223,7 @@ impl Program { assert!(!root_bit_width.is_polymorphic()); match c { Constant::Bool(_, bit_width) => self.new_instruction( - Operator::Bconst, + TestOperator::Bconst, if bit_width.is_polymorphic() { Type { kind: Kind::Bool, @@ -238,7 +239,7 @@ impl Program { vec![], ), Constant::Int(_, bit_width) => self.new_instruction( - Operator::Iconst, + TestOperator::Iconst, if bit_width.is_polymorphic() { Type { kind: Kind::Int, @@ -259,12 +260,12 @@ impl Program { fn instruction_to_constant(&mut self, inst: Instruction) -> Option { match self.data(inst) { InstructionData { - operator: Operator::Iconst, + operator: TestOperator::Iconst, immediates, .. } => Some(immediates[0].unwrap_constant()), InstructionData { - operator: Operator::Bconst, + operator: TestOperator::Bconst, immediates, .. } => Some(immediates[0].unwrap_constant()), @@ -310,6 +311,8 @@ pub struct TestIsa { // Unsafe because we must ensure that `instruction_result_bit_width` never // returns zero. unsafe impl<'a> InstructionSet<'a> for TestIsa { + type Operator = TestOperator; + type Context = Program; type Instruction = Instruction; @@ -360,7 +363,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { Some(part) } - fn operator(&self, program: &mut Program, instr: Instruction) -> Option { + fn operator(&self, program: &mut Program, instr: Instruction) -> Option { log::debug!("operator({:?})", instr); let data = program.data(instr); Some(data.operator) @@ -370,7 +373,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { &self, program: &mut Program, root: Instruction, - operator: Operator, + operator: TestOperator, r#type: Type, a: Part, ) -> Instruction { @@ -383,11 +386,11 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { let (imms, args) = match operator.immediates_arity() { 0 => { - assert_eq!(operator.params_arity(), 1); + assert_eq!(operator.parameters_arity(), 1); (vec![], vec![program.part_to_instruction(root, a).unwrap()]) } 1 => { - assert_eq!(operator.params_arity(), 0); + assert_eq!(operator.parameters_arity(), 0); (vec![program.part_to_immediate(a).unwrap()], vec![]) } _ => unreachable!(), @@ -399,7 +402,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { &self, program: &mut Program, root: Instruction, - operator: Operator, + operator: TestOperator, r#type: Type, a: Part, b: Part, @@ -414,7 +417,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { let (imms, args) = match operator.immediates_arity() { 0 => { - assert_eq!(operator.params_arity(), 2); + assert_eq!(operator.parameters_arity(), 2); ( vec![], vec![ @@ -424,14 +427,14 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { ) } 1 => { - assert_eq!(operator.params_arity(), 1); + assert_eq!(operator.parameters_arity(), 1); ( vec![program.part_to_immediate(a).unwrap()], vec![program.part_to_instruction(root, b).unwrap()], ) } 2 => { - assert_eq!(operator.params_arity(), 0); + assert_eq!(operator.parameters_arity(), 0); ( vec![ program.part_to_immediate(a).unwrap(), @@ -449,7 +452,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { &self, program: &mut Program, root: Instruction, - operator: Operator, + operator: TestOperator, r#type: Type, a: Part, b: Part, @@ -465,7 +468,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { ); let (imms, args) = match operator.immediates_arity() { 0 => { - assert_eq!(operator.params_arity(), 3); + assert_eq!(operator.parameters_arity(), 3); ( vec![], vec![ @@ -476,7 +479,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { ) } 1 => { - assert_eq!(operator.params_arity(), 2); + assert_eq!(operator.parameters_arity(), 2); ( vec![program.part_to_immediate(a).unwrap()], vec![ @@ -486,7 +489,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { ) } 2 => { - assert_eq!(operator.params_arity(), 1); + assert_eq!(operator.parameters_arity(), 1); ( vec![ program.part_to_immediate(a).unwrap(), @@ -496,7 +499,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa { ) } 3 => { - assert_eq!(operator.params_arity(), 0); + assert_eq!(operator.parameters_arity(), 0); ( vec![ program.part_to_immediate(a).unwrap(), diff --git a/cranelift/peepmatic/crates/test/tests/tests.rs b/cranelift/peepmatic/crates/test/tests/tests.rs index 989424adb8..ced151c529 100644 --- a/cranelift/peepmatic/crates/test/tests/tests.rs +++ b/cranelift/peepmatic/crates/test/tests/tests.rs @@ -1,10 +1,10 @@ use peepmatic_runtime::{ cc::ConditionCode, - operator::Operator, part::Constant, r#type::{BitWidth, Type}, }; use peepmatic_test::*; +use peepmatic_test_operator::TestOperator; const TEST_ISA: TestIsa = TestIsa { native_word_size_in_bits: 32, @@ -26,13 +26,13 @@ fn opcode() { let mut program = Program::default(); 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 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 = new.expect("optimization should have applied"); 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); assert!(replacement.is_none()); } @@ -45,10 +45,10 @@ fn constant() { let mut program = Program::default(); 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 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( - Operator::IaddImm, + TestOperator::IaddImm, Type::i32(), vec![Constant::Int(5, BitWidth::ThirtyTwo).into()], vec![zero], @@ -58,8 +58,8 @@ fn constant() { let new = new.expect("optimization should have applied"); assert!(program.structurally_eq(new, expected)); - let mul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, zero]); - let add = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![mul, five]); + let mul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, zero]); + let add = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![mul, five]); let replacement = optimizer.apply_one(&mut program, add); assert!(replacement.is_none()); } @@ -71,7 +71,7 @@ fn boolean() { let mut program = Program::default(); 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 new = optimizer.apply_one(&mut program, bint); @@ -79,7 +79,7 @@ fn boolean() { assert!(program.structurally_eq(new, 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); assert!(replacement.is_none()); } @@ -92,7 +92,7 @@ fn condition_codes() { let mut program = Program::default(); let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::One); let icmp_eq = program.new_instruction( - Operator::Icmp, + TestOperator::Icmp, Type::b1(), vec![ConditionCode::Eq.into()], vec![five, five], @@ -104,7 +104,7 @@ fn condition_codes() { assert!(program.structurally_eq(new, t)); let icmp_ne = program.new_instruction( - Operator::Icmp, + TestOperator::Icmp, Type::b1(), vec![ConditionCode::Ne.into()], vec![five, five], @@ -128,17 +128,17 @@ fn is_power_of_two() { let mut program = Program::default(); 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 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 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 = new.expect("optimization should have applied"); assert!(program.structurally_eq(new, ishl)); 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); assert!(replacement.is_none()); @@ -159,10 +159,10 @@ fn bit_width() { let mut program = Program::default(); 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 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( - Operator::ImulImm, + TestOperator::ImulImm, Type::i32(), vec![Constant::Int(5, BitWidth::ThirtyTwo).into()], vec![two], @@ -174,7 +174,7 @@ fn bit_width() { 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 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); assert!(replacement.is_none()); @@ -195,10 +195,10 @@ fn fits_in_native_word() { let mut program = Program::default(); 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 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( - Operator::ImulImm, + TestOperator::ImulImm, Type::i32(), vec![Constant::Int(5, BitWidth::ThirtyTwo).into()], vec![two], @@ -210,7 +210,7 @@ fn fits_in_native_word() { 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 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); assert!(replacement.is_none()); @@ -230,10 +230,10 @@ fn unquote_neg() { let mut program = Program::default(); 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 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( - Operator::IaddImm, + TestOperator::IaddImm, Type::i64(), vec![Constant::Int(-2 as _, BitWidth::SixtyFour).into()], vec![five], @@ -276,13 +276,13 @@ fn subsumption() { 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(Operator::Iadd, Type::i64(), vec![], vec![iadd, y]); - let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, z]); - let expected_lhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]); - let expected_rhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]); + let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]); + let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, y]); + let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, z]); + let expected_lhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]); + let expected_rhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]); let expected = program.new_instruction( - Operator::Iadd, + TestOperator::Iadd, Type::i64(), vec![], vec![expected_lhs, expected_rhs], @@ -294,17 +294,17 @@ fn subsumption() { 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 = new.expect("optimization should have applied"); assert!(program.structurally_eq(new, y)); 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 = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]); + let iadd_y_z = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]); + let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]); let iadd_imm = program.new_instruction( - Operator::IaddImm, + TestOperator::IaddImm, Type::i64(), vec![Constant::Int(22, BitWidth::SixtyFour).into()], 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))"); let imul_imm = program.new_instruction( - Operator::ImulImm, + TestOperator::ImulImm, Type::i64(), vec![Constant::Int(1, BitWidth::SixtyFour).into()], vec![x], ); let iadd = program.new_instruction( - Operator::Iadd, + TestOperator::Iadd, Type::i64(), vec![], vec![imul_imm, imul_imm], ); let ishl_imm = program.new_instruction( - Operator::IshlImm, + TestOperator::IshlImm, Type::i64(), vec![Constant::Int(1, BitWidth::SixtyFour).into()], vec![imul_imm], @@ -339,10 +339,10 @@ fn subsumption() { 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_y_z = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![y, z]); + let imul_w_x = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![w, x]); + let imul_y_z = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![y, z]); let iadd = program.new_instruction( - Operator::Iadd, + TestOperator::Iadd, Type::i64(), vec![], 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 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( - Operator::IaddImm, + TestOperator::IaddImm, Type::i32(), vec![Constant::Int(42, BitWidth::ThirtyTwo).into()], vec![y], @@ -379,9 +379,9 @@ fn polymorphic_bit_widths() { 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 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( - Operator::IaddImm, + TestOperator::IaddImm, Type::i16(), vec![Constant::Int(42, BitWidth::Sixteen).into()], vec![y], diff --git a/cranelift/peepmatic/crates/traits/Cargo.toml b/cranelift/peepmatic/crates/traits/Cargo.toml new file mode 100644 index 0000000000..eb04dbd7d0 --- /dev/null +++ b/cranelift/peepmatic/crates/traits/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "peepmatic-traits" +version = "0.66.0" +authors = ["Nick Fitzgerald "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/cranelift/peepmatic/crates/traits/src/lib.rs b/cranelift/peepmatic/crates/traits/src/lib.rs new file mode 100644 index 0000000000..2da48bbee9 --- /dev/null +++ b/cranelift/peepmatic/crates/traits/src/lib.rs @@ -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) +} diff --git a/cranelift/peepmatic/crates/traits/src/operator.rs b/cranelift/peepmatic/crates/traits/src/operator.rs new file mode 100644 index 0000000000..ac1c2650a3 --- /dev/null +++ b/cranelift/peepmatic/crates/traits/src/operator.rs @@ -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::() { + p.parse::()?; + 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, + ) + 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, + ) + 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 for $operator { + type Error = (); + + #[inline] + fn try_from(x: u32) -> Result { + match x { + $( + x if x == Self::$variant.into() => Ok(Self::$variant), + )* + _ => Err(()) + } + } + } + + impl core::convert::TryFrom for $operator { + type Error = (); + + #[inline] + fn try_from(x: core::num::NonZeroU32) -> Result { + 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); )? + } + } +} diff --git a/cranelift/peepmatic/crates/traits/src/typing.rs b/cranelift/peepmatic/crates/traits/src/typing.rs new file mode 100644 index 0000000000..19babdf097 --- /dev/null +++ b/cranelift/peepmatic/crates/traits/src/typing.rs @@ -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, + ) 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, + ) 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; +} diff --git a/cranelift/peepmatic/src/ast.rs b/cranelift/peepmatic/src/ast.rs index daac871553..2cd0643bc4 100644 --- a/cranelift/peepmatic/src/ast.rs +++ b/cranelift/peepmatic/src/ast.rs @@ -22,8 +22,8 @@ use peepmatic_macro::Ast; use peepmatic_runtime::{ - operator::{Operator, UnquoteOperator}, r#type::{BitWidth, Type}, + unquote::UnquoteOperator, }; use std::cell::Cell; use std::hash::{Hash, Hasher}; @@ -32,58 +32,58 @@ use wast::Id; /// A reference to any AST node. #[derive(Debug, Clone, Copy)] -pub enum DynAstRef<'a> { +pub enum DynAstRef<'a, TOperator> { /// A reference to an `Optimizations`. - Optimizations(&'a Optimizations<'a>), + Optimizations(&'a Optimizations<'a, TOperator>), /// A reference to an `Optimization`. - Optimization(&'a Optimization<'a>), + Optimization(&'a Optimization<'a, TOperator>), /// A reference to an `Lhs`. - Lhs(&'a Lhs<'a>), + Lhs(&'a Lhs<'a, TOperator>), /// A reference to an `Rhs`. - Rhs(&'a Rhs<'a>), + Rhs(&'a Rhs<'a, TOperator>), /// A reference to a `Pattern`. - Pattern(&'a Pattern<'a>), + Pattern(&'a Pattern<'a, TOperator>), /// A reference to a `Precondition`. - Precondition(&'a Precondition<'a>), + Precondition(&'a Precondition<'a, TOperator>), /// A reference to a `ConstraintOperand`. - ConstraintOperand(&'a ConstraintOperand<'a>), + ConstraintOperand(&'a ConstraintOperand<'a, TOperator>), /// A reference to a `ValueLiteral`. - ValueLiteral(&'a ValueLiteral<'a>), + ValueLiteral(&'a ValueLiteral<'a, TOperator>), /// A reference to a `Constant`. - Constant(&'a Constant<'a>), + Constant(&'a Constant<'a, TOperator>), /// A reference to a `PatternOperation`. - PatternOperation(&'a Operation<'a, Pattern<'a>>), + PatternOperation(&'a Operation<'a, TOperator, Pattern<'a, TOperator>>), /// A reference to a `Variable`. - Variable(&'a Variable<'a>), + Variable(&'a Variable<'a, TOperator>), /// A reference to an `Integer`. - Integer(&'a Integer<'a>), + Integer(&'a Integer<'a, TOperator>), /// A reference to a `Boolean`. - Boolean(&'a Boolean<'a>), + Boolean(&'a Boolean<'a, TOperator>), /// A reference to a `ConditionCode`. - ConditionCode(&'a ConditionCode<'a>), + ConditionCode(&'a ConditionCode<'a, TOperator>), /// A reference to an `Unquote`. - Unquote(&'a Unquote<'a>), + Unquote(&'a Unquote<'a, TOperator>), /// 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> { - fn child_nodes(&'b self, sink: &mut impl Extend>) { +impl<'a, 'b, TOperator> ChildNodes<'a, 'b, TOperator> for DynAstRef<'a, TOperator> { + fn child_nodes(&'b self, sink: &mut impl Extend>) { match self { Self::Optimizations(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 /// things, and in practice those three thrings are all implemented by the /// `derive(Ast)` macro. -pub trait Ast<'a>: 'a + ChildNodes<'a, 'a> + Span +pub trait Ast<'a, TOperator>: 'a + ChildNodes<'a, 'a, TOperator> + Span 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 - T: 'a + ?Sized + ChildNodes<'a, 'a> + Span, - DynAstRef<'a>: From<&'a Self>, + T: 'a + ?Sized + ChildNodes<'a, 'a, TOperator> + Span, + DynAstRef<'a, TOperator>: From<&'a Self>, + TOperator: 'a, { } /// 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. - fn child_nodes(&'b self, sink: &mut impl Extend>); + fn child_nodes(&'b self, sink: &mut impl Extend>); } /// 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. #[derive(Debug, Ast)] -pub struct Optimizations<'a> { +pub struct Optimizations<'a, TOperator> { /// Where these `Optimizations` were defined. #[peepmatic(skip_child)] pub span: wast::Span, /// The optimizations. #[peepmatic(flatten)] - pub optimizations: Vec>, + pub optimizations: Vec>, } /// A complete optimization: a left-hand side to match against and a right-hand /// side replacement. #[derive(Debug, Ast)] -pub struct Optimization<'a> { +pub struct Optimization<'a, TOperator> { /// Where this `Optimization` was defined. #[peepmatic(skip_child)] pub span: wast::Span, /// 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 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 @@ -180,58 +185,58 @@ pub struct Optimization<'a> { /// candidate instruction sequences, and zero or more preconditions that add /// additional constraints upon instruction sequences matched by the pattern. #[derive(Debug, Ast)] -pub struct Lhs<'a> { +pub struct Lhs<'a, TOperator> { /// Where this `Lhs` was defined. #[peepmatic(skip_child)] pub span: wast::Span, /// 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 /// structually matching the pattern, e.g. some constant must be a power of /// two. #[peepmatic(flatten)] - pub preconditions: Vec>, + pub preconditions: Vec>, } /// A structural pattern, potentially with wildcard variables for matching whole /// subtrees. #[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 /// `false`. - ValueLiteral(ValueLiteral<'a>), + ValueLiteral(ValueLiteral<'a, TOperator>), /// A constant that matches any constant value. This subsumes value /// 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 /// 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 /// other patterns. These are lower-case identifiers like `$x`. - Variable(Variable<'a>), + Variable(Variable<'a, TOperator>), } /// An integer or boolean value literal. #[derive(Debug, Ast)] -pub enum ValueLiteral<'a> { +pub enum ValueLiteral<'a, TOperator> { /// An integer value. - Integer(Integer<'a>), + Integer(Integer<'a, TOperator>), /// A boolean value: `true` or `false`. - Boolean(Boolean<'a>), + Boolean(Boolean<'a, TOperator>), /// A condition code: `eq`, `ne`, etc... - ConditionCode(ConditionCode<'a>), + ConditionCode(ConditionCode<'a, TOperator>), } /// An integer literal. #[derive(Debug, PartialEq, Eq, Ast)] -pub struct Integer<'a> { +pub struct Integer<'a, TOperator> { /// Where this `Integer` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -255,10 +260,10 @@ pub struct Integer<'a> { #[allow(missing_docs)] #[peepmatic(skip_child)] - pub marker: PhantomData<&'a ()>, + pub marker: PhantomData<&'a TOperator>, } -impl Hash for Integer<'_> { +impl Hash for Integer<'_, TOperator> { fn hash(&self, state: &mut H) where H: Hasher, @@ -278,7 +283,7 @@ impl Hash for Integer<'_> { /// A boolean literal. #[derive(Debug, PartialEq, Eq, Ast)] -pub struct Boolean<'a> { +pub struct Boolean<'a, TOperator> { /// Where this `Boolean` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -299,10 +304,10 @@ pub struct Boolean<'a> { #[allow(missing_docs)] #[peepmatic(skip_child)] - pub marker: PhantomData<&'a ()>, + pub marker: PhantomData<&'a TOperator>, } -impl Hash for Boolean<'_> { +impl Hash for Boolean<'_, TOperator> { fn hash(&self, state: &mut H) where H: Hasher, @@ -322,7 +327,7 @@ impl Hash for Boolean<'_> { /// A condition code. #[derive(Debug, Ast)] -pub struct ConditionCode<'a> { +pub struct ConditionCode<'a, TOperator> { /// Where this `ConditionCode` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -333,7 +338,7 @@ pub struct ConditionCode<'a> { #[allow(missing_docs)] #[peepmatic(skip_child)] - pub marker: PhantomData<&'a ()>, + pub marker: PhantomData<&'a TOperator>, } /// A symbolic constant. @@ -341,7 +346,7 @@ pub struct ConditionCode<'a> { /// These are identifiers containing uppercase letters: `$C`, `$MY-CONST`, /// `$CONSTANT1`. #[derive(Debug, Ast)] -pub struct Constant<'a> { +pub struct Constant<'a, TOperator> { /// Where this `Constant` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -349,6 +354,10 @@ pub struct Constant<'a> { /// This constant's identifier. #[peepmatic(skip_child)] pub id: Id<'a>, + + #[allow(missing_docs)] + #[peepmatic(skip_child)] + pub marker: PhantomData<&'a TOperator>, } /// 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 /// `(iadd 5 5)` but not `(iadd 1 2)`. #[derive(Debug, Ast)] -pub struct Variable<'a> { +pub struct Variable<'a, TOperator> { /// Where this `Variable` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -365,15 +374,20 @@ pub struct Variable<'a> { /// This variable's identifier. #[peepmatic(skip_child)] 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`. #[derive(Debug, Ast)] #[peepmatic(no_into_dyn_node)] -pub struct Operation<'a, T> +pub struct Operation<'a, TOperator, TOperand> where - T: 'a + Ast<'a>, - DynAstRef<'a>: From<&'a T>, + TOperator: 'a, + TOperand: 'a + Ast<'a, TOperator>, + DynAstRef<'a, TOperator>: From<&'a TOperand>, { /// The span where this operation was written. #[peepmatic(skip_child)] @@ -381,7 +395,7 @@ where /// The operator for this operation, e.g. `imul` or `iadd`. #[peepmatic(skip_child)] - pub operator: Operator, + pub operator: TOperator, /// An optional ascribed or inferred type for the operator. #[peepmatic(skip_child)] @@ -393,23 +407,27 @@ where /// the operands. When `Operation is used in a right-hand side replacement, /// these are the sub-replacements for the operands. #[peepmatic(flatten)] - pub operands: Vec, + pub operands: Vec, #[allow(missing_docs)] #[peepmatic(skip_child)] 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] - 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) } } -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] - 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) } } @@ -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 power of two". #[derive(Debug, Ast)] -pub struct Precondition<'a> { +pub struct Precondition<'a, TOperator> { /// Where this `Precondition` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -428,7 +446,11 @@ pub struct Precondition<'a> { /// The operands of the constraint. #[peepmatic(flatten)] - pub operands: Vec>, + pub operands: Vec>, + + #[allow(missing_docs)] + #[peepmatic(skip_child)] + pub marker: PhantomData<&'a TOperator>, } /// Contraint operators. @@ -446,40 +468,40 @@ pub enum Constraint { /// An operand of a precondition's constraint. #[derive(Debug, Ast)] -pub enum ConstraintOperand<'a> { +pub enum ConstraintOperand<'a, TOperator> { /// A value literal operand. - ValueLiteral(ValueLiteral<'a>), + ValueLiteral(ValueLiteral<'a, TOperator>), /// A constant operand. - Constant(Constant<'a>), + Constant(Constant<'a, TOperator>), /// A variable operand. - Variable(Variable<'a>), + Variable(Variable<'a, TOperator>), } /// The right-hand side of an optimization that contains the instructions to /// replace any matched left-hand side with. #[derive(Debug, Ast)] -pub enum Rhs<'a> { +pub enum Rhs<'a, TOperator> { /// 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 /// 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 /// 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 /// side with the right-hand side. The result of the evaluation is used in /// the replacement. - Unquote(Unquote<'a>), + Unquote(Unquote<'a, TOperator>), /// A compound right-hand side consisting of an operation and subsequent /// right-hand side operands. - Operation(Operation<'a, Rhs<'a>>), + Operation(Operation<'a, TOperator, Rhs<'a, TOperator>>), } /// An unquote operation. @@ -493,7 +515,7 @@ pub enum Rhs<'a> { /// 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`). #[derive(Debug, Ast)] -pub struct Unquote<'a> { +pub struct Unquote<'a, TOperator> { /// Where this `Unquote` was defined. #[peepmatic(skip_child)] pub span: wast::Span, @@ -504,5 +526,9 @@ pub struct Unquote<'a> { /// The operands for this unquote operation. #[peepmatic(flatten)] - pub operands: Vec>, + pub operands: Vec>, + + #[allow(missing_docs)] + #[peepmatic(skip_child)] + pub marker: PhantomData<&'a TOperator>, } diff --git a/cranelift/peepmatic/src/automatize.rs b/cranelift/peepmatic/src/automatize.rs index de053c5574..5d4e8a2849 100644 --- a/cranelift/peepmatic/src/automatize.rs +++ b/cranelift/peepmatic/src/automatize.rs @@ -2,14 +2,20 @@ use peepmatic_automata::{Automaton, Builder}; use peepmatic_runtime::linear; +use std::fmt::Debug; +use std::hash::Hash; /// Construct an automaton from a set of linear optimizations. -pub fn automatize( - opts: &linear::Optimizations, -) -> Automaton> { +pub fn automatize( + opts: &linear::Optimizations, +) -> Automaton]>> +where + TOperator: Copy + Debug + Eq + Hash, +{ debug_assert!(crate::linear_passes::is_sorted_lexicographically(opts)); - let mut builder = Builder::>::new(); + let mut builder = + Builder::]>>::new(); for opt in &opts.optimizations { let mut insertion = builder.insert(); diff --git a/cranelift/peepmatic/src/dot_fmt.rs b/cranelift/peepmatic/src/dot_fmt.rs index 6939577147..e92c3468ba 100644 --- a/cranelift/peepmatic/src/dot_fmt.rs +++ b/cranelift/peepmatic/src/dot_fmt.rs @@ -7,17 +7,21 @@ use peepmatic_runtime::{ cc::ConditionCode, integer_interner::{IntegerId, IntegerInterner}, linear, - operator::Operator, paths::{PathId, PathInterner}, }; use std::convert::{TryFrom, TryInto}; +use std::fmt::Debug; use std::io::{self, Write}; -use std::num::NonZeroU16; +use std::num::{NonZeroU16, NonZeroU32}; #[derive(Debug)] pub(crate) struct PeepholeDotFmt<'a>(pub(crate) &'a PathInterner, pub(crate) &'a IntegerInterner); -impl DotFmt> for PeepholeDotFmt<'_> { +impl DotFmt]>> + for PeepholeDotFmt<'_> +where + TOperator: Debug + TryFrom, +{ fn fmt_transition( &self, w: &mut impl Write, @@ -26,22 +30,23 @@ impl DotFmt> for Pee _to: Option<&linear::MatchOp>, ) -> io::Result<()> { 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 { linear::MatchOp::Opcode { .. } => { - let opcode = - Operator::try_from(x).expect("we shouldn't generate non-opcode edges"); - write!(w, "{}", opcode) + let opcode = TOperator::try_from(x) + .map_err(|_| ()) + .expect("we shouldn't generate non-opcode edges"); + write!(w, "{:?}", opcode) } linear::MatchOp::ConditionCode { .. } => { - let cc = - ConditionCode::try_from(x).expect("we shouldn't generate non-CC edges"); + let cc = ConditionCode::try_from(x.get()) + .expect("we shouldn't generate non-CC edges"); write!(w, "{}", cc) } linear::MatchOp::IntegerValue { .. } => { - let x = self - .1 - .lookup(IntegerId(NonZeroU16::new(x.try_into().unwrap()).unwrap())); + let x = self.1.lookup(IntegerId( + NonZeroU16::new(x.get().try_into().unwrap()).unwrap(), + )); write!(w, "{}", x) } _ => write!(w, "Ok({})", x), @@ -73,7 +78,11 @@ impl DotFmt> for Pee writeln!(w, "") } - 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]>, + ) -> io::Result<()> { use linear::Action::*; if actions.is_empty() { @@ -88,11 +97,11 @@ impl DotFmt> for Pee match a { GetLhs { path } => write!(w, "get-lhs @ {}
", p(path))?, UnaryUnquote { operator, operand } => { - write!(w, "eval {} $rhs{}
", operator, operand.0)? + write!(w, "eval {:?} $rhs{}
", operator, operand.0)? } BinaryUnquote { operator, operands } => write!( w, - "eval {} $rhs{}, $rhs{}
", + "eval {:?} $rhs{}, $rhs{}
", operator, operands[0].0, operands[1].0, )?, MakeIntegerConst { @@ -108,14 +117,14 @@ impl DotFmt> for Pee operand, operator, r#type: _, - } => write!(w, "make {} $rhs{}
", operator, operand.0,)?, + } => write!(w, "make {:?} $rhs{}
", operator, operand.0,)?, MakeBinaryInst { operator, operands, r#type: _, } => write!( w, - "make {} $rhs{}, $rhs{}
", + "make {:?} $rhs{}, $rhs{}
", operator, operands[0].0, operands[1].0, )?, MakeTernaryInst { @@ -124,7 +133,7 @@ impl DotFmt> for Pee r#type: _, } => write!( w, - "make {} $rhs{}, $rhs{}, $rhs{}
", + "make {:?} $rhs{}, $rhs{}, $rhs{}
", operator, operands[0].0, operands[1].0, operands[2].0, )?, } diff --git a/cranelift/peepmatic/src/lib.rs b/cranelift/peepmatic/src/lib.rs index 407ba3670d..86f28f41d8 100644 --- a/cranelift/peepmatic/src/lib.rs +++ b/cranelift/peepmatic/src/lib.rs @@ -23,20 +23,25 @@ pub use self::{ }; use peepmatic_runtime::PeepholeOptimizations; +use peepmatic_traits::TypingRules; +use std::convert::TryFrom; +use std::fmt::Debug; use std::fs; +use std::hash::Hash; +use std::num::NonZeroU32; use std::path::Path; /// Compile the given DSL file into a compact peephole optimizations automaton! /// /// ## Example /// -/// ```no_run +/// ```ignore /// # fn main() -> anyhow::Result<()> { /// use std::path::Path; /// -/// let peep_opts = peepmatic::compile_file(Path::new( -/// "path/to/optimizations.peepmatic" -/// ))?; +/// let peep_opts = peepmatic::compile_file::( +/// Path::new("path/to/optimizations.peepmatic") +/// )?; /// /// // Use the peephole optimizations or serialize them into bytes here... /// # Ok(()) @@ -49,9 +54,19 @@ use std::path::Path; /// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz /// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the /// peephole optimizer's automaton will be written to that file path. -pub fn compile_file(filename: &Path) -> anyhow::Result { +pub fn compile_file(filename: &Path) -> anyhow::Result> +where + TOperator: Copy + + Debug + + Eq + + Hash + + for<'a> wast::parser::Parse<'a> + + TypingRules + + Into + + TryFrom, +{ let source = fs::read_to_string(filename)?; - compile_str(&source, filename) + compile_str::(&source, filename) } /// Compile the given DSL source text down into a compact peephole optimizations @@ -64,11 +79,11 @@ pub fn compile_file(filename: &Path) -> anyhow::Result { /// /// ## Example /// -/// ```no_run +/// ```ignore /// # fn main() -> anyhow::Result<()> { /// use std::path::Path; /// -/// let peep_opts = peepmatic::compile_str( +/// let peep_opts = peepmatic::compile_str::( /// " /// (=> (iadd $x 0) $x) /// (=> (imul $x 0) 0) @@ -88,14 +103,27 @@ pub fn compile_file(filename: &Path) -> anyhow::Result { /// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz /// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the /// peephole optimizer's automaton will be written to that file path. -pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result { +pub fn compile_str( + source: &str, + filename: &Path, +) -> anyhow::Result> +where + TOperator: Copy + + Debug + + Eq + + Hash + + for<'a> wast::parser::Parse<'a> + + TypingRules + + Into + + TryFrom, +{ let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| { e.set_path(filename); e.set_text(source); e })?; - let opts = wast::parser::parse::(&buf).map_err(|mut e| { + let opts = wast::parser::parse::>(&buf).map_err(|mut e| { e.set_path(filename); e.set_text(source); e @@ -137,9 +165,10 @@ pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result(Path::new(path)) { Ok(_) => return, Err(e) => { eprintln!("error: {}", e); diff --git a/cranelift/peepmatic/src/linear_passes.rs b/cranelift/peepmatic/src/linear_passes.rs index 9e87ae0036..82c2095139 100644 --- a/cranelift/peepmatic/src/linear_passes.rs +++ b/cranelift/peepmatic/src/linear_passes.rs @@ -5,6 +5,8 @@ use peepmatic_runtime::{ paths::{PathId, PathInterner}, }; use std::cmp::Ordering; +use std::fmt::Debug; +use std::hash::Hash; /// 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 /// 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(opts: &mut linear::Optimizations) +where + TOperator: Copy + Debug + Eq + Hash, +{ let linear::Optimizations { ref mut optimizations, ref paths, @@ -41,7 +46,10 @@ pub fn sort_least_to_most_general(opts: &mut linear::Optimizations) { /// Sort the linear optimizations lexicographically. /// /// This sort order is required for automata construction. -pub fn sort_lexicographically(opts: &mut linear::Optimizations) { +pub fn sort_lexicographically(opts: &mut linear::Optimizations) +where + TOperator: Copy + Debug + Eq + Hash, +{ let linear::Optimizations { ref mut optimizations, 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))); } -fn compare_optimizations( +fn compare_optimizations( paths: &PathInterner, - a: &linear::Optimization, - b: &linear::Optimization, + a: &linear::Optimization, + b: &linear::Optimization, 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()) { let c = compare_match_op_generality(paths, a.operation, b.operation); if c != Ordering::Equal { @@ -79,11 +90,14 @@ fn compare_optimizations( compare_lengths(a.increments.len(), b.increments.len()) } -fn compare_optimization_generality( +fn compare_optimization_generality( paths: &PathInterner, - a: &linear::Optimization, - b: &linear::Optimization, -) -> Ordering { + a: &linear::Optimization, + b: &linear::Optimization, +) -> Ordering +where + TOperator: Copy + Debug + Eq + Hash, +{ compare_optimizations(paths, a, b, |a_len, b_len| { // If they shared equivalent prefixes, then compare lengths and invert the // 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? -pub(crate) fn is_sorted_by_generality(opts: &linear::Optimizations) -> bool { +pub(crate) fn is_sorted_by_generality(opts: &linear::Optimizations) -> bool +where + TOperator: Copy + Debug + Eq + Hash, +{ opts.optimizations .windows(2) .all(|w| compare_optimization_generality(&opts.paths, &w[0], &w[1]) <= Ordering::Equal) } /// Are the given optimizations sorted lexicographically? -pub(crate) fn is_sorted_lexicographically(opts: &linear::Optimizations) -> bool { +pub(crate) fn is_sorted_lexicographically( + opts: &linear::Optimizations, +) -> bool +where + TOperator: Copy + Debug + Eq + Hash, +{ opts.optimizations.windows(2).all(|w| { compare_optimizations(&opts.paths, &w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len)) <= Ordering::Equal @@ -207,7 +229,10 @@ pub(crate) fn is_sorted_lexicographically(opts: &linear::Optimizations) -> bool /// opcode @ 0 --iadd--> /// opcode @ 0 --(else)--> is-const? @ 0 --true--> /// ``` -pub fn match_in_same_order(opts: &mut linear::Optimizations) { +pub fn match_in_same_order(opts: &mut linear::Optimizations) +where + TOperator: Copy + Debug + Eq + Hash, +{ assert!(!opts.optimizations.is_empty()); 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 /// patterns, and then in this post-processing pass, we fuse them and their /// actions with their preceding increment. -pub fn remove_unnecessary_nops(opts: &mut linear::Optimizations) { +pub fn remove_unnecessary_nops(opts: &mut linear::Optimizations) +where + TOperator: Copy + Debug + Eq + Hash, +{ for opt in &mut opts.optimizations { if opt.increments.len() < 2 { debug_assert!(!opt.increments.is_empty()); @@ -289,9 +317,9 @@ mod tests { use crate::ast::*; use peepmatic_runtime::{ linear::{bool_to_match_result, Else, MatchOp::*, MatchResult}, - operator::Operator, paths::*, }; + use peepmatic_test_operator::TestOperator; use std::num::NonZeroU32; #[test] @@ -306,7 +334,7 @@ mod tests { fn $test_name() { let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK"); - let opts = match wast::parser::parse::(&buf) { + let opts = match wast::parser::parse::>(&buf) { Ok(opts) => opts, Err(mut e) => { e.set_path(std::path::Path::new(stringify!($test_name))); @@ -383,7 +411,7 @@ mod tests { fn $test_name() { let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK"); - let opts = match wast::parser::parse::(&buf) { + let opts = match wast::parser::parse::>(&buf) { Ok(opts) => opts, Err(mut e) => { 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![ vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), - ( - Opcode { path: p(&[0, 1]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0, 1]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), (Nop, Err(Else)), ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), (IntegerValue { path: p(&[0, 1]) }, i(42)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), (IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)), ( @@ -501,10 +517,7 @@ mod tests { ) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), (IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)), ( @@ -513,18 +526,12 @@ mod tests { ) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), (IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), ( Eq { @@ -535,10 +542,7 @@ mod tests { ) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (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![ vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Imul as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())), (IntegerValue { path: p(&[0, 0]) }, i(2)), (Nop, Err(Else)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Imul as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())), (IntegerValue { path: p(&[0, 0]) }, i(1)), (Nop, Err(Else)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Imul as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())), (Nop, Err(Else)), (IntegerValue { path: p(&[0, 1]) }, i(2)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Imul as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())), (Nop, Err(Else)), (IntegerValue { path: p(&[0, 1]) }, i(1)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (IntegerValue { path: p(&[0, 0]) }, i(0)), (Nop, Err(Else)) ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Iadd as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())), (Nop, Err(Else)), (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![ vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), - ( - Opcode { path: p(&[0, 0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())), + (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())), (Nop, Err(Else)), (Nop, Err(Else)), ( @@ -638,14 +618,8 @@ mod tests { ), ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), - ( - Opcode { path: p(&[0, 0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())), + (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())), (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![ vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), - ( - Opcode { path: p(&[0, 0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())), + (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())), (Nop, Err(Else)), (Nop, Err(Else)), ( @@ -689,14 +657,8 @@ mod tests { ), ], vec![ - ( - Opcode { path: p(&[0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), - ( - Opcode { path: p(&[0, 0]) }, - Ok(NonZeroU32::new(Operator::Bor as _).unwrap()) - ), + (Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())), + (Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())), (Nop, Err(Else)), (Nop, Err(Else)), ( diff --git a/cranelift/peepmatic/src/linearize.rs b/cranelift/peepmatic/src/linearize.rs index 9f7c1fe602..fb991686e7 100644 --- a/cranelift/peepmatic/src/linearize.rs +++ b/cranelift/peepmatic/src/linearize.rs @@ -93,11 +93,17 @@ use peepmatic_runtime::{ }; use std::collections::BTreeMap; use std::convert::TryInto; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::PhantomData; use std::num::NonZeroU32; use wast::Id; /// Translate the given AST optimizations into linear optimizations. -pub fn linearize(opts: &Optimizations) -> linear::Optimizations { +pub fn linearize(opts: &Optimizations) -> linear::Optimizations +where + TOperator: Copy + Debug + Eq + Hash + Into, +{ let mut optimizations = vec![]; let mut paths = PathInterner::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! -fn linearize_optimization( +fn linearize_optimization( paths: &mut PathInterner, integers: &mut IntegerInterner, - opt: &Optimization, -) -> linear::Optimization { - let mut increments: Vec = vec![]; + opt: &Optimization, +) -> linear::Optimization +where + TOperator: Copy + Debug + Eq + Hash + Into, +{ + let mut increments: Vec> = vec![]; 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 /// tree each yielded node comes from. -struct RhsPostOrder<'a> { - dfs: Dfs<'a>, +struct RhsPostOrder<'a, TOperator> { + dfs: Dfs<'a, TOperator>, } -impl<'a> RhsPostOrder<'a> { - fn new(rhs: &'a Rhs<'a>) -> Self { +impl<'a, TOperator> RhsPostOrder<'a, TOperator> +where + TOperator: Copy + Debug + Eq + Hash, +{ + fn new(rhs: &'a Rhs<'a, TOperator>) -> Self { Self { dfs: Dfs::new(rhs) } } } -impl<'a> Iterator for RhsPostOrder<'a> { - type Item = &'a Rhs<'a>; +impl<'a, TOperator> Iterator for RhsPostOrder<'a, TOperator> +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; loop { 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 /// pattern AST node. -struct PatternPreOrder<'a> { +struct PatternPreOrder<'a, TOperator> { last_child: Option, path: Vec, - dfs: Dfs<'a>, + dfs: Dfs<'a, TOperator>, } -impl<'a> PatternPreOrder<'a> { - fn new(pattern: &'a Pattern<'a>) -> Self { +impl<'a, TOperator> PatternPreOrder<'a, TOperator> +where + TOperator: Copy + Debug + Eq + Hash, +{ + fn new(pattern: &'a Pattern<'a, TOperator>) -> Self { Self { last_child: None, 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; loop { 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 /// where they first occurred. -struct LhsIdToPath<'a> { +struct LhsIdToPath<'a, TOperator> { 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`. fn new() -> Self { Self { 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. - 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 { // If this is the first time we've seen an identifier defined on the // left-hand side, remember it. @@ -294,10 +314,10 @@ impl<'a> LhsIdToPath<'a> { /// An `RhsBuilder` emits the actions for building the right-hand side /// instructions. -struct RhsBuilder<'a> { +struct RhsBuilder<'a, TOperator> { // We do a post order traversal of the RHS because an RHS instruction cannot // 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 // by RHS-construction actions to reference operands. In practice the @@ -306,9 +326,12 @@ struct RhsBuilder<'a> { rhs_span_to_id: BTreeMap, } -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. - fn new(rhs: &'a Rhs<'a>) -> Self { + fn new(rhs: &'a Rhs<'a, TOperator>) -> Self { let rhs_post_order = RhsPostOrder::new(rhs); let rhs_span_to_id = Default::default(); Self { @@ -323,7 +346,7 @@ impl<'a> RhsBuilder<'a> { /// /// Panics if we haven't already emitted the action for building this RHS's /// instruction. - fn get_rhs_id(&self, rhs: &Rhs) -> linear::RhsId { + fn get_rhs_id(&self, rhs: &Rhs) -> linear::RhsId { self.rhs_span_to_id[&rhs.span()] } @@ -335,8 +358,8 @@ impl<'a> RhsBuilder<'a> { fn add_rhs_build_actions( &mut self, integers: &mut IntegerInterner, - lhs_id_to_path: &LhsIdToPath, - actions: &mut Vec, + lhs_id_to_path: &LhsIdToPath, + actions: &mut Vec>, ) { while let Some(rhs) = self.rhs_post_order.next() { 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( &self, integers: &mut IntegerInterner, - lhs_id_to_path: &LhsIdToPath, - rhs: &Rhs, - ) -> linear::Action { + lhs_id_to_path: &LhsIdToPath, + rhs: &Rhs, + ) -> linear::Action { match rhs { Rhs::ValueLiteral(ValueLiteral::Integer(i)) => linear::Action::MakeIntegerConst { value: integers.intern(i.value as u64), @@ -425,9 +448,15 @@ impl<'a> RhsBuilder<'a> { } } -impl Precondition<'_> { +impl Precondition<'_, TOperator> +where + TOperator: Copy + Debug + Eq + Hash + Into, +{ /// 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, + ) -> linear::Increment { match self.constraint { Constraint::IsPowerOfTwo => { let id = match &self.operands[0] { @@ -484,7 +513,10 @@ impl Precondition<'_> { } } -impl Pattern<'_> { +impl Pattern<'_, TOperator> +where + TOperator: Copy, +{ /// Convert this pattern into its linear match operation and the expected /// result of that operation. /// @@ -493,9 +525,12 @@ impl Pattern<'_> { fn to_linear_match_op( &self, integers: &mut IntegerInterner, - lhs_id_to_path: &LhsIdToPath, + lhs_id_to_path: &LhsIdToPath, path: PathId, - ) -> (linear::MatchOp, linear::MatchResult) { + ) -> (linear::MatchOp, linear::MatchResult) + where + TOperator: Into, + { match self { Pattern::ValueLiteral(ValueLiteral::Integer(Integer { value, .. })) => ( linear::MatchOp::IntegerValue { path }, @@ -543,9 +578,7 @@ impl Pattern<'_> { } } Pattern::Operation(op) => { - let op = op.operator as u32; - debug_assert!(op != 0, "no `Operator` variants are zero"); - let expected = Ok(unsafe { NonZeroU32::new_unchecked(op) }); + let expected = Ok(op.operator.into()); (linear::MatchOp::Opcode { path }, expected) } } @@ -558,9 +591,9 @@ mod tests { use peepmatic_runtime::{ integer_interner::IntegerId, linear::{bool_to_match_result, Action::*, Else, MatchOp::*}, - operator::Operator, r#type::{BitWidth, Kind, Type}, }; + use peepmatic_test_operator::TestOperator; macro_rules! linearizes_to { ($name:ident, $source:expr, $make_expected:expr $(,)* ) => { @@ -568,7 +601,7 @@ mod tests { fn $name() { let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK"); - let opts = match wast::parser::parse::(&buf) { + let opts = match wast::parser::parse::>(&buf) { Ok(opts) => opts, Err(mut e) => { e.set_path(std::path::Path::new(stringify!($name))); @@ -602,7 +635,7 @@ mod tests { let make_expected: fn( &mut dyn FnMut(&[u8]) -> PathId, &mut dyn FnMut(u64) -> IntegerId, - ) -> Vec = $make_expected; + ) -> Vec> = $make_expected; let expected = make_expected(&mut p, &mut i); dbg!(&expected); @@ -624,12 +657,12 @@ mod tests { |p, i| vec![ linear::Increment { operation: Opcode { path: p(&[0]) }, - expected: Ok(NonZeroU32::new(Operator::Imul as _).unwrap()), + expected: Ok(TestOperator::Imul.into()), actions: vec![ GetLhs { path: p(&[0, 0]) }, GetLhs { path: p(&[0, 1]) }, MakeBinaryInst { - operator: Operator::Ishl, + operator: TestOperator::Ishl, r#type: Type { kind: Kind::Int, bit_width: BitWidth::Polymorphic, @@ -702,11 +735,11 @@ mod tests { |p, i| vec![ linear::Increment { operation: Opcode { path: p(&[0]) }, - expected: Ok(NonZeroU32::new(Operator::Iconst as _).unwrap()), + expected: Ok(TestOperator::Iconst.into()), actions: vec![ GetLhs { path: p(&[0, 0]) }, MakeUnaryInst { - operator: Operator::Iconst, + operator: TestOperator::Iconst, r#type: Type { kind: Kind::Int, bit_width: BitWidth::Polymorphic, @@ -729,14 +762,14 @@ mod tests { |p, i| vec![ linear::Increment { operation: Opcode { path: p(&[0]) }, - expected: Ok(NonZeroU32::new(Operator::Bor as _).unwrap()), + expected: Ok(TestOperator::Bor.into()), actions: vec![ GetLhs { path: p(&[0, 0]) }, GetLhs { path: p(&[0, 1, 1]), }, MakeBinaryInst { - operator: Operator::Bor, + operator: TestOperator::Bor, r#type: Type { kind: Kind::Int, bit_width: BitWidth::Polymorphic, @@ -752,7 +785,7 @@ mod tests { }, linear::Increment { operation: Opcode { path: p(&[0, 1]) }, - expected: Ok(NonZeroU32::new(Operator::Bor as _).unwrap()), + expected: Ok(TestOperator::Bor.into()), actions: vec![], }, linear::Increment { @@ -791,7 +824,7 @@ mod tests { |p, i| vec![ linear::Increment { operation: Opcode { path: p(&[0]) }, - expected: Ok(NonZeroU32::new(Operator::Ireduce as _).unwrap()), + expected: Ok(TestOperator::Ireduce.into()), actions: vec![MakeIntegerConst { value: i(0), bit_width: BitWidth::ThirtyTwo, diff --git a/cranelift/peepmatic/src/parser.rs b/cranelift/peepmatic/src/parser.rs index 19ad49017c..95a84c9433 100644 --- a/cranelift/peepmatic/src/parser.rs +++ b/cranelift/peepmatic/src/parser.rs @@ -84,7 +84,10 @@ mod tok { 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 { let span = p.cur_span(); 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 { let span = p.cur_span(); 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 { let span = p.cur_span(); 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 { - if p.peek::() { + if p.peek::>() { return Ok(Pattern::ValueLiteral(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(Pattern::Constant(p.parse()?)); } - if p.peek::>() { + if p.peek::>() { return Ok(Pattern::Operation(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(Pattern::Variable(p.parse()?)); } 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 { - ValueLiteral::peek(c) - || Constant::peek(c) - || Variable::peek(c) - || Operation::::peek(c) + ValueLiteral::::peek(c) + || Constant::::peek(c) + || Variable::::peek(c) + || Operation::::peek(c) } 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 { - if let Ok(b) = p.parse::() { + if let Ok(b) = p.parse::>() { return Ok(ValueLiteral::Boolean(b)); } - if let Ok(i) = p.parse::() { + if let Ok(i) = p.parse::>() { return Ok(ValueLiteral::Integer(i)); } - if let Ok(cc) = p.parse::() { + if let Ok(cc) = p.parse::>() { return Ok(ValueLiteral::ConditionCode(cc)); } 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 { - c.integer().is_some() || Boolean::peek(c) || ConditionCode::peek(c) + c.integer().is_some() + || Boolean::::peek(c) + || ConditionCode::::peek(c) } 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 { let span = p.cur_span(); 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 { let span = p.cur_span(); if p.parse::().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 { ::peek(c) || ::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 { 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 { macro_rules! peek_cc { ( $( $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 { let span = p.cur_span(); let id = Id::parse(p)?; @@ -330,7 +347,11 @@ impl<'a> Parse<'a> for Constant<'a> { .chars() .all(|c| !c.is_alphabetic() || c.is_uppercase()) { - Ok(Constant { span, id }) + Ok(Constant { + span, + id, + marker: PhantomData, + }) } else { let upper = id .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 { if let Some((id, _rest)) = c.id() { 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 { let span = p.cur_span(); let id = Id::parse(p)?; @@ -368,7 +389,11 @@ impl<'a> Parse<'a> for Variable<'a> { .chars() .all(|c| !c.is_alphabetic() || c.is_lowercase()) { - Ok(Variable { span, id }) + Ok(Variable { + span, + id, + marker: PhantomData, + }) } else { let lower = id .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 { if let Some((id, _rest)) = c.id() { 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 - T: 'a + Ast<'a> + Peek + Parse<'a>, - DynAstRef<'a>: From<&'a T>, + TOperator: Parse<'a>, + TOperand: 'a + Ast<'a, TOperator> + Peek + Parse<'a>, + DynAstRef<'a, TOperator>: From<&'a TOperand>, { fn parse(p: Parser<'a>) -> ParseResult { let span = p.cur_span(); @@ -417,7 +443,7 @@ where }); let mut operands = vec![]; - while p.peek::() { + while p.peek::() { operands.push(p.parse()?); } 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 - T: 'a + Ast<'a>, - DynAstRef<'a>: From<&'a T>, + TOperand: 'a + Ast<'a, TOperator>, + DynAstRef<'a, TOperator>: From<&'a TOperand>, { fn peek(c: Cursor) -> bool { 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 { let span = p.cur_span(); p.parens(|p| { let constraint = p.parse()?; let mut operands = vec![]; - while p.peek::() { + while p.peek::>() { operands.push(p.parse()?); } Ok(Precondition { span, constraint, 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 { - if p.peek::() { + if p.peek::>() { return Ok(ConstraintOperand::ValueLiteral(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(ConstraintOperand::Constant(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(ConstraintOperand::Variable(p.parse()?)); } 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 { - ValueLiteral::peek(c) || Constant::peek(c) || Variable::peek(c) + ValueLiteral::::peek(c) + || Constant::::peek(c) + || Variable::::peek(c) } 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 { - if p.peek::() { + if p.peek::>() { return Ok(Rhs::ValueLiteral(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(Rhs::Constant(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(Rhs::Variable(p.parse()?)); } - if p.peek::() { + if p.peek::>() { return Ok(Rhs::Unquote(p.parse()?)); } - if p.peek::>() { + if p.peek::>() { return Ok(Rhs::Operation(p.parse()?)); } 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 { - ValueLiteral::peek(c) - || Constant::peek(c) - || Variable::peek(c) - || Unquote::peek(c) - || Operation::::peek(c) + ValueLiteral::::peek(c) + || Constant::::peek(c) + || Variable::::peek(c) + || Unquote::::peek(c) + || Operation::::peek(c) } 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 { let span = p.cur_span(); p.parse::()?; p.parens(|p| { let operator = p.parse()?; let mut operands = vec![]; - while p.peek::() { + while p.peek::>() { operands.push(p.parse()?); } Ok(Unquote { span, operator, operands, + marker: PhantomData, }) }) } } -impl<'a> Peek for Unquote<'a> { +impl<'a, TOperator> Peek for Unquote<'a, TOperator> { fn peek(c: Cursor) -> bool { tok::dollar::peek(c) } @@ -573,7 +612,7 @@ impl<'a> Peek for Unquote<'a> { #[cfg(test)] mod test { use super::*; - use peepmatic_runtime::operator::Operator; + use peepmatic_test_operator::TestOperator; macro_rules! test_parse { ( @@ -623,7 +662,7 @@ mod test { } test_parse! { - parse_boolean { + parse_boolean> { ok { "true", "false", @@ -641,7 +680,7 @@ mod test { "falsezzz", } } - parse_cc { + parse_cc> { ok { "eq", "ne", @@ -661,7 +700,7 @@ mod test { "neq", } } - parse_constant { + parse_constant> { ok { "$C", "$C1", @@ -693,7 +732,7 @@ mod test { "imul", } } - parse_constraint_operand { + parse_constraint_operand> { ok { "1234", "true", @@ -707,7 +746,7 @@ mod test { "(iadd 1 2)", } } - parse_integer { + parse_integer> { ok { "0", "1", @@ -746,7 +785,7 @@ mod test { "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", } } - parse_lhs { + parse_lhs> { ok { "(when (imul $C1 $C2) (is-power-of-two $C1) (is-power-of-two $C2))", "(when (imul $x $C) (is-power-of-two $C))", @@ -762,7 +801,7 @@ mod test { "abc", } } - parse_operation_pattern> { + parse_operation_pattern>> { ok { "(iadd)", "(iadd 1)", @@ -779,7 +818,7 @@ mod test { "(ishl $x $(log2 $C))", } } - parse_operation_rhs> { + parse_operation_rhs>> { ok { "(iadd)", "(iadd 1)", @@ -793,7 +832,7 @@ mod test { "$CONST", } } - parse_operator { + parse_operator { ok { "bor", "iadd", @@ -812,7 +851,7 @@ mod test { "iadd{i32}", } } - parse_optimization { + parse_optimization> { ok { "(=> (when (iadd $x $C) (is-power-of-two $C) (is-power-of-two $C)) (iadd $C $x))", "(=> (when (iadd $x $C)) (iadd $C $x))", @@ -825,7 +864,7 @@ mod test { "(=> () ())", } } - parse_optimizations { + parse_optimizations> { ok { "", r#" @@ -844,7 +883,7 @@ mod test { "#, } } - parse_pattern { + parse_pattern> { ok { "1234", "$C", @@ -857,7 +896,7 @@ mod test { "abc", } } - parse_precondition { + parse_precondition> { ok { "(is-power-of-two)", "(is-power-of-two $C)", @@ -871,7 +910,7 @@ mod test { "$CONST", } } - parse_rhs { + parse_rhs> { ok { "5", "$C", @@ -884,7 +923,7 @@ mod test { "()", } } - parse_unquote { + parse_unquote> { ok { "$(log2)", "$(log2 $C)", @@ -899,7 +938,7 @@ mod test { "$()", } } - parse_value_literal { + parse_value_literal> { ok { "12345", "true", @@ -911,7 +950,7 @@ mod test { "12.34", } } - parse_variable { + parse_variable> { ok { "$v", "$v1", diff --git a/cranelift/peepmatic/src/traversals.rs b/cranelift/peepmatic/src/traversals.rs index 5eb4101c37..f0e147d1f1 100644 --- a/cranelift/peepmatic/src/traversals.rs +++ b/cranelift/peepmatic/src/traversals.rs @@ -1,6 +1,8 @@ //! Traversals over the 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 /// an AST node. @@ -32,13 +34,16 @@ pub enum TraversalEvent { /// the AST, because the `new` constructor takes anything that can convert into /// a `DynAstRef`. #[derive(Debug, Clone)] -pub struct Dfs<'a> { - stack: Vec<(TraversalEvent, DynAstRef<'a>)>, +pub struct Dfs<'a, TOperator> { + 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. - pub fn new(start: impl Into>) -> Self { + pub fn new(start: impl Into>) -> Self { let start = start.into(); Dfs { stack: vec![ @@ -49,15 +54,18 @@ impl<'a> Dfs<'a> { } /// Peek at the next traversal event and AST node pair, if any. - pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a>)> { - self.stack.last().cloned() + pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> { + self.stack.last().copied() } } -impl<'a> Iterator for Dfs<'a> { - type Item = (TraversalEvent, DynAstRef<'a>); +impl<'a, TOperator> Iterator for Dfs<'a, TOperator> +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()?; if let TraversalEvent::Enter = event { let mut enqueue_children = EnqueueChildren(self); @@ -65,15 +73,16 @@ impl<'a> Iterator for Dfs<'a> { } return Some((event, node)); - struct EnqueueChildren<'a, 'b>(&'b mut Dfs<'a>) + struct EnqueueChildren<'a, 'b, TOperator>(&'b mut Dfs<'a, TOperator>) where 'a: 'b; - impl<'a, 'b> Extend> for EnqueueChildren<'a, 'b> + impl<'a, 'b, TOperator> Extend> for EnqueueChildren<'a, 'b, TOperator> where 'a: 'b, + TOperator: Copy, { - fn extend>>(&mut self, iter: T) { + fn extend>>(&mut self, iter: T) { let iter = iter.into_iter(); let (min, max) = iter.size_hint(); @@ -97,6 +106,7 @@ impl<'a> Iterator for Dfs<'a> { #[cfg(test)] mod tests { use super::*; + use peepmatic_test_operator::TestOperator; use DynAstRef::*; #[test] @@ -107,7 +117,7 @@ mod tests { (ishl $x $(log2 $C))) "; let buf = wast::parser::ParseBuffer::new(input).expect("input should lex OK"); - let ast = match wast::parser::parse::(&buf) { + let ast = match wast::parser::parse::>(&buf) { Ok(ast) => ast, Err(e) => panic!("expected to parse OK, got error:\n\n{}", e), }; diff --git a/cranelift/peepmatic/src/verify.rs b/cranelift/peepmatic/src/verify.rs index 53970e9070..978a0cf1ee 100644 --- a/cranelift/peepmatic/src/verify.rs +++ b/cranelift/peepmatic/src/verify.rs @@ -14,14 +14,13 @@ use crate::ast::{Span as _, *}; use crate::traversals::{Dfs, TraversalEvent}; -use peepmatic_runtime::{ - operator::{Operator, TypingContext as TypingContextTrait}, - r#type::{BitWidth, Kind, Type}, -}; +use peepmatic_runtime::r#type::{BitWidth, Kind, Type}; +use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules}; use std::borrow::Cow; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::fmt; +use std::fmt::Debug; use std::hash::Hash; use std::iter; use std::mem; @@ -94,7 +93,10 @@ impl VerifyError { pub type VerifyResult = Result; /// Verify and type check a set of optimizations. -pub fn verify(opts: &Optimizations) -> VerifyResult<()> { +pub fn verify(opts: &Optimizations) -> VerifyResult<()> +where + TOperator: Copy + Debug + Eq + Hash + TypingRules, +{ if opts.optimizations.is_empty() { 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 /// applied and would make automata construction more difficult. It is better to /// check for duplicates and reject them if found. -fn verify_unique_left_hand_sides(opts: &Optimizations) -> VerifyResult<()> { +fn verify_unique_left_hand_sides(opts: &Optimizations) -> VerifyResult<()> +where + TOperator: Copy + Eq + Debug + Hash, +{ let mut lefts = HashMap::new(); for opt in &opts.optimizations { 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 /// that sees through identifier renaming. -fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq { +fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq +where + TOperator: Copy + Debug + Eq + Hash, +{ let mut var_to_canon = HashMap::new(); let mut const_to_canon = HashMap::new(); let mut canonicalized = vec![]; @@ -183,19 +191,20 @@ fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq { return canonicalized; #[derive(Hash, PartialEq, Eq)] - enum CanonicalBit { + enum CanonicalBit { Var(u32), Const(u32), Integer(i64), Boolean(bool), ConditionCode(peepmatic_runtime::cc::ConditionCode), - Operation(Operator, Option), + Operation(TOperator, Option), Precondition(Constraint), Other(&'static str), } } -pub(crate) struct TypingContext<'a> { +#[derive(Debug)] +struct TypingContext<'a, TOperator> { z3: &'a z3::Context, type_kind_sort: z3::DatatypeSort<'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 // them. For these AST nodes, we know what bit width to use when // interpreting peephole optimization actions. - boolean_literals: Vec<(&'a Boolean<'a>, TypeVar<'a>)>, - integer_literals: Vec<(&'a Integer<'a>, TypeVar<'a>)>, - rhs_operations: Vec<(&'a Operation<'a, Rhs<'a>>, TypeVar<'a>)>, + boolean_literals: Vec<(&'a Boolean<'a, TOperator>, TypeVar<'a>)>, + integer_literals: Vec<(&'a Integer<'a, TOperator>, 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 { let type_kind_sort = z3::DatatypeBuilder::new(z3) .variant("int", &[]) @@ -301,51 +313,55 @@ impl<'a> TypingContext<'a> { // and similar refer to the same type variables. fn enter_operation_scope<'b>( &'b mut self, - ) -> impl DerefMut> + Drop + 'b { + ) -> impl DerefMut> + Drop + 'b { assert!(self.operation_scope.is_empty()); return Scope(self); - struct Scope<'a, 'b>(&'b mut TypingContext<'a>) + struct Scope<'a, 'b, TOperator>(&'b mut TypingContext<'a, TOperator>) where 'a: 'b; - impl<'a, 'b> Deref for Scope<'a, 'b> + impl<'a, 'b, TOperator> Deref for Scope<'a, 'b, TOperator> where 'a: 'b, { - type Target = TypingContext<'a>; - fn deref(&self) -> &TypingContext<'a> { + type Target = TypingContext<'a, TOperator>; + fn deref(&self) -> &TypingContext<'a, TOperator> { self.0 } } - impl<'a, 'b> DerefMut for Scope<'a, 'b> + impl<'a, 'b, TOperator> DerefMut for Scope<'a, 'b, TOperator> where 'a: 'b, { - fn deref_mut(&mut self) -> &mut TypingContext<'a> { + fn deref_mut(&mut self) -> &mut TypingContext<'a, TOperator> { self.0 } } - impl Drop for Scope<'_, '_> { + impl Drop for Scope<'_, '_, TOperator> { fn drop(&mut self) { 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.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.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)); } @@ -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>; fn cc(&mut self, span: Span) -> TypeVar<'a> { @@ -737,13 +754,19 @@ impl<'a> TypingContextTrait<'a> for TypingContext<'a> { } } -#[derive(Clone)] -pub(crate) struct TypeVar<'a> { +#[derive(Clone, Debug)] +struct TypeVar<'a> { kind: z3::ast::Datatype<'a>, width: z3::ast::BV<'a>, } -fn verify_optimization(z3: &z3::Context, opt: &Optimization) -> VerifyResult<()> { +fn verify_optimization( + z3: &z3::Context, + opt: &Optimization, +) -> VerifyResult<()> +where + TOperator: Copy + Debug + Eq + Hash + TypingRules, +{ let mut context = TypingContext::new(z3); collect_type_constraints(&mut context, opt)?; context.type_check(opt.span)?; @@ -755,10 +778,13 @@ fn verify_optimization(z3: &z3::Context, opt: &Optimization) -> VerifyResult<()> Ok(()) } -fn collect_type_constraints<'a>( - context: &mut TypingContext<'a>, - opt: &'a Optimization<'a>, -) -> VerifyResult<()> { +fn collect_type_constraints<'a, TOperator>( + context: &mut TypingContext<'a, TOperator>, + opt: &'a Optimization<'a, TOperator>, +) -> VerifyResult<()> +where + TOperator: Copy + Debug + Eq + Hash + TypingRules, +{ use crate::traversals::TraversalEvent as TE; 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. for (event, node) in Dfs::new(&opt.lhs) { 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); 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 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 - .immediate_types(&mut *scope, op.span, &mut operand_types); + .immediate_types(op.span, &mut *scope, &mut operand_types); 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() { @@ -841,29 +881,22 @@ fn collect_type_constraints<'a>( } } - match op.operator { - Operator::Ireduce | Operator::Uextend | Operator::Sextend => { - if op.r#type.get().is_none() { - return Err(WastError::new( - op.span, - "`ireduce`, `sextend`, and `uextend` require an ascribed type, \ - like `(sextend{i64} ...)`" - .into(), - ) - .into()); - } - } - _ => {} + if (op.operator.is_reduce() || op.operator.is_extend()) && op.r#type.get().is_none() + { + return Err(WastError::new( + op.span, + "`ireduce`, `sextend`, and `uextend` require an ascribed type, \ + like `(sextend{i64} ...)`" + .into(), + ) + .into()); } - match op.operator { - Operator::Uextend | Operator::Sextend => { - context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]); - } - Operator::Ireduce => { - context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]); - } - _ => {} + if op.operator.is_extend() { + context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]); + } + if op.operator.is_reduce() { + context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]); } if let Some(ty) = op.r#type.get() { @@ -916,8 +949,22 @@ fn collect_type_constraints<'a>( let ty = expected_types.last().unwrap(); 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)?; 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 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 - .immediate_types(&mut *scope, op.span, &mut operand_types); + .immediate_types(op.span, &mut *scope, &mut operand_types); 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() { @@ -965,29 +1012,22 @@ fn collect_type_constraints<'a>( } } - match op.operator { - Operator::Ireduce | Operator::Uextend | Operator::Sextend => { - if op.r#type.get().is_none() { - return Err(WastError::new( - op.span, - "`ireduce`, `sextend`, and `uextend` require an ascribed type, \ - like `(sextend{i64} ...)`" - .into(), - ) - .into()); - } - } - _ => {} + if (op.operator.is_reduce() || op.operator.is_extend()) && op.r#type.get().is_none() + { + return Err(WastError::new( + op.span, + "`ireduce`, `sextend`, and `uextend` require an ascribed type, \ + like `(sextend{i64} ...)`" + .into(), + ) + .into()); } - match op.operator { - Operator::Uextend | Operator::Sextend => { - context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]); - } - Operator::Ireduce => { - context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]); - } - _ => {} + if op.operator.is_extend() { + context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]); + } + if op.operator.is_reduce() { + context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]); } if let Some(ty) = op.r#type.get() { @@ -1017,11 +1057,11 @@ fn collect_type_constraints<'a>( let mut operand_types = vec![]; { 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 - .immediate_types(&mut *scope, unq.span, &mut operand_types); + .immediate_types(unq.span, &mut *scope, &mut operand_types); 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() { @@ -1068,9 +1108,9 @@ fn collect_type_constraints<'a>( Ok(()) } -fn type_constrain_precondition<'a>( - context: &mut TypingContext<'a>, - pre: &Precondition<'a>, +fn type_constrain_precondition<'a, TOperator>( + context: &mut TypingContext<'a, TOperator>, + pre: &Precondition<'a, TOperator>, ) -> VerifyResult<()> { match pre.constraint { Constraint::BitWidth => { @@ -1182,13 +1222,14 @@ fn type_constrain_precondition<'a>( #[cfg(test)] mod tests { use super::*; + use peepmatic_test_operator::TestOperator; macro_rules! verify_ok { ($name:ident, $src:expr) => { #[test] fn $name() { let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK"); - let opts = match wast::parser::parse::(&buf) { + let opts = match wast::parser::parse::>(&buf) { Ok(opts) => opts, Err(mut e) => { e.set_path(Path::new(stringify!($name))); @@ -1215,7 +1256,7 @@ mod tests { #[test] fn $name() { let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK"); - let opts = match wast::parser::parse::(&buf) { + let opts = match wast::parser::parse::>(&buf) { Ok(opts) => opts, Err(mut e) => { e.set_path(Path::new(stringify!($name))); diff --git a/scripts/publish.rs b/scripts/publish.rs index 57749a9d2b..af2f694b32 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -16,8 +16,10 @@ use std::process::{Command, Stdio}; // note that this list must be topologically sorted by dependencies const CRATES_TO_PUBLISH: &[&str] = &[ // peepmatic + "peepmatic-traits", "peepmatic-macro", "peepmatic-automata", + "peepmatic-test-operator", "peepmatic-runtime", "peepmatic", // cranelift