Merge pull request #1960 from fitzgen/peepmatic-generic-over-ir
peepmatic: Be generic over the IR we are optimizing
This commit is contained in:
17
.github/workflows/main.yml
vendored
17
.github/workflows/main.yml
vendored
@@ -162,16 +162,17 @@ jobs:
|
||||
--package peepmatic-macro \
|
||||
--package peepmatic-runtime \
|
||||
--package peepmatic-test
|
||||
- name: Rebuild Cranelift's peepmatic-based peephole optimizers
|
||||
- name: Rebuild Peepmatic-based peephole optimizers and test them
|
||||
run: |
|
||||
cd cranelift/
|
||||
cargo build --features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers'
|
||||
- name: Check that peephole optimizers are up to date
|
||||
cargo test \
|
||||
--features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers' \
|
||||
peepmatic
|
||||
working-directory: ./cranelift
|
||||
- name: Check that built peephole optimizers are up to date
|
||||
run: git diff --exit-code
|
||||
- name: Test `cranelift-codegen` with `peepmatic` enabled
|
||||
run: |
|
||||
cd cranelift/
|
||||
cargo test --features 'enable-peepmatic'
|
||||
- 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.
|
||||
|
||||
33
Cargo.lock
generated
33
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -25,8 +25,11 @@ gimli = { version = "0.21.0", default-features = false, features = ["write"], op
|
||||
smallvec = { version = "1.0.0" }
|
||||
thiserror = "1.0.4"
|
||||
byteorder = { version = "1.3.2", default-features = false }
|
||||
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.2.0" }
|
||||
regalloc = { version = "0.0.28" }
|
||||
peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
|
||||
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
|
||||
@@ -34,7 +37,6 @@ regalloc = { version = "0.0.28" }
|
||||
|
||||
[build-dependencies]
|
||||
cranelift-codegen-meta = { path = "meta", version = "0.66.0" }
|
||||
peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
|
||||
|
||||
[features]
|
||||
default = ["std", "unwind"]
|
||||
@@ -80,10 +82,10 @@ regalloc-snapshot = ["bincode", "regalloc/enable-serde"]
|
||||
|
||||
# Recompile our optimizations that are written in the `peepmatic` DSL into a
|
||||
# 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" }
|
||||
|
||||
@@ -90,20 +90,11 @@ fn main() {
|
||||
}
|
||||
|
||||
#[cfg(feature = "rebuild-peephole-optimizers")]
|
||||
rebuild_peephole_optimizers();
|
||||
}
|
||||
|
||||
#[cfg(feature = "rebuild-peephole-optimizers")]
|
||||
fn rebuild_peephole_optimizers() {
|
||||
use std::path::Path;
|
||||
|
||||
let source_path = Path::new("src").join("preopt.peepmatic");
|
||||
println!("cargo:rerun-if-changed={}", source_path.display());
|
||||
|
||||
let preopt =
|
||||
peepmatic::compile_file(&source_path).expect("failed to compile `src/preopt.peepmatic`");
|
||||
|
||||
preopt
|
||||
.serialize_to_file(&Path::new("src").join("preopt.serialized"))
|
||||
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
|
||||
{
|
||||
std::fs::write(
|
||||
std::path::Path::new(&out_dir).join("CRANELIFT_CODEGEN_PATH"),
|
||||
cur_dir.to_str().unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u16> for Opcode {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("type Error = ();");
|
||||
fmt.line("#[inline]");
|
||||
fmt.line("fn try_from(x: u16) -> Result<Self, ()> {");
|
||||
fmt.indent(|fmt| {
|
||||
fmtln!(fmt, "if 0 < x && x <= {} {{", all_inst.len());
|
||||
fmt.indent(|fmt| fmt.line("Ok(unsafe { core::mem::transmute(x) })"));
|
||||
fmt.line("} else {");
|
||||
fmt.indent(|fmt| fmt.line("Err(())"));
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
/// Get the value type constraint for an SSA value operand, where
|
||||
/// `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.
|
||||
|
||||
@@ -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<NonZeroU32> for Opcode {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(x: NonZeroU32) -> Result<Self, ()> {
|
||||
let x: u16 = x.get().try_into().map_err(|_| ())?;
|
||||
Self::try_from(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Opcode> for NonZeroU32 {
|
||||
#[inline]
|
||||
fn from(op: Opcode) -> NonZeroU32 {
|
||||
let x = op as u8;
|
||||
NonZeroU32::new(x as u32).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since
|
||||
// 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
|
||||
|
||||
@@ -13,27 +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<u8> {
|
||||
let codegen_path = Path::new(include_str!(concat!(
|
||||
env!("OUT_DIR"),
|
||||
"/CRANELIFT_CODEGEN_PATH"
|
||||
)));
|
||||
let source_path = codegen_path.join("src").join("preopt.peepmatic");
|
||||
|
||||
let preopt = peepmatic::compile_file::<Opcode>(&source_path)
|
||||
.expect("failed to compile `src/preopt.peepmatic`");
|
||||
|
||||
let serialized_path = codegen_path.join("src").join("preopt.serialized");
|
||||
preopt
|
||||
.serialize_to_file(&serialized_path)
|
||||
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
|
||||
fs::read(&serialized_path).expect("failed to read `src/preopt.serialized`")
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `preopt.peepmatic` peephole optimizer.
|
||||
pub(crate) fn preopt<'a, 'b>(
|
||||
isa: &'b dyn TargetIsa,
|
||||
) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> {
|
||||
static SERIALIZED: &[u8] = include_bytes!("preopt.serialized");
|
||||
#[cfg(feature = "rebuild-peephole-optimizers")]
|
||||
fn get_serialized() -> Cow<'static, [u8]> {
|
||||
rebuild::rebuild_preopt().into()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rebuild-peephole-optimizers"))]
|
||||
fn get_serialized() -> Cow<'static, [u8]> {
|
||||
static SERIALIZED: &[u8] = include_bytes!("preopt.serialized");
|
||||
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<PeepholeOptimizations> = AtomicPtr::new(ptr::null_mut());
|
||||
static DESERIALIZED: AtomicPtr<PeepholeOptimizations<Opcode>> = AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
// If `DESERIALIZED` has already been initialized, then just use it.
|
||||
let ptr = DESERIALIZED.load(Ordering::SeqCst);
|
||||
@@ -46,7 +306,7 @@ pub(crate) fn preopt<'a, 'b>(
|
||||
// another thread could be doing the same thing concurrently, so there is a
|
||||
// race to see who initializes `DESERIALIZED` first, and we need to be
|
||||
// prepared to both win or lose that race.
|
||||
let peep_opts = PeepholeOptimizations::deserialize(SERIALIZED)
|
||||
let peep_opts = PeepholeOptimizations::deserialize(&get_serialized())
|
||||
.expect("should always be able to deserialize `preopt.serialized`");
|
||||
let peep_opts = Box::into_raw(Box::new(peep_opts));
|
||||
|
||||
@@ -219,70 +479,6 @@ fn part_to_value(pos: &mut FuncCursor, root: Inst, part: Part<ValueOrInst>) -> O
|
||||
}
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
fn to_peepmatic_operator(&self) -> Option<Operator> {
|
||||
macro_rules! convert {
|
||||
( $( $op:ident $(,)* )* ) => {
|
||||
match self {
|
||||
$( Self::$op => Some(Operator::$op), )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convert!(
|
||||
AdjustSpDown,
|
||||
AdjustSpDownImm,
|
||||
Band,
|
||||
BandImm,
|
||||
Bconst,
|
||||
Bint,
|
||||
Bnot,
|
||||
Bor,
|
||||
BorImm,
|
||||
Brnz,
|
||||
Brz,
|
||||
Bxor,
|
||||
BxorImm,
|
||||
Iadd,
|
||||
IaddImm,
|
||||
Icmp,
|
||||
IcmpImm,
|
||||
Iconst,
|
||||
Ifcmp,
|
||||
IfcmpImm,
|
||||
Imul,
|
||||
ImulImm,
|
||||
Ireduce,
|
||||
IrsubImm,
|
||||
Ishl,
|
||||
IshlImm,
|
||||
Isub,
|
||||
Rotl,
|
||||
RotlImm,
|
||||
Rotr,
|
||||
RotrImm,
|
||||
Sdiv,
|
||||
SdivImm,
|
||||
Select,
|
||||
Sextend,
|
||||
Srem,
|
||||
SremImm,
|
||||
Sshr,
|
||||
SshrImm,
|
||||
Trapnz,
|
||||
Trapz,
|
||||
Udiv,
|
||||
UdivImm,
|
||||
Uextend,
|
||||
Urem,
|
||||
UremImm,
|
||||
Ushr,
|
||||
UshrImm,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Constant> for Imm64 {
|
||||
type Error = &'static str;
|
||||
|
||||
@@ -429,6 +625,8 @@ fn peepmatic_ty_to_ir_ty(ty: Type, dfg: &DataFlowGraph, root: Inst) -> types::Ty
|
||||
unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
type Context = FuncCursor<'b>;
|
||||
|
||||
type Operator = Opcode;
|
||||
|
||||
type Instruction = ValueOrInst;
|
||||
|
||||
fn replace_instruction(
|
||||
@@ -496,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);
|
||||
@@ -513,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<Operator> {
|
||||
fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Opcode> {
|
||||
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>,
|
||||
) -> ValueOrInst {
|
||||
@@ -530,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
|
||||
@@ -567,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.
|
||||
@@ -605,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);
|
||||
@@ -625,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<ValueOrInst>,
|
||||
b: Part<ValueOrInst>,
|
||||
@@ -634,193 +832,193 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
|
||||
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
||||
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);
|
||||
@@ -834,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<ValueOrInst>,
|
||||
b: Part<ValueOrInst>,
|
||||
@@ -844,7 +1042,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
|
||||
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
||||
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();
|
||||
@@ -852,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);
|
||||
@@ -860,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();
|
||||
@@ -891,3 +1089,21 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
self.pointer_bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "x86")]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::isa::lookup;
|
||||
use crate::settings::{builder, Flags};
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
#[test]
|
||||
fn get_peepmatic_preopt() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
let _ = preopt(&*isa);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -10,8 +10,13 @@ description = "DSL and compiler for generating peephole optimizers"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.27"
|
||||
peepmatic-automata = { version = "0.2.0", path = "crates/automata", features = ["dot"] }
|
||||
peepmatic-macro = { version = "0.2.0", path = "crates/macro" }
|
||||
peepmatic-runtime = { version = "0.2.0", path = "crates/runtime", features = ["construct"] }
|
||||
peepmatic-automata = { version = "0.66.0", path = "crates/automata", features = ["dot"] }
|
||||
peepmatic-macro = { version = "0.66.0", path = "crates/macro" }
|
||||
peepmatic-runtime = { version = "0.66.0", path = "crates/runtime", features = ["construct"] }
|
||||
peepmatic-traits = { version = "0.66.0", path = "crates/traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "15.0.0"
|
||||
z3 = { version = "0.6.0", features = ["static-link-z3"] }
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic-test-operator = { version = "0.66.0", path = "crates/test-operator" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-automata"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-fuzzing"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
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"
|
||||
|
||||
@@ -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::<TestOperator>(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<TestOperator> =
|
||||
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::<TestOperator>(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");
|
||||
|
||||
@@ -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::<Optimizations>(&buf).unwrap();
|
||||
let ast = wast::parser::parse::<Optimizations<TestOperator>>(&buf).unwrap();
|
||||
|
||||
// And we need access to the assigned types, so re-verify it as well.
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<Optimizations>(&buf);
|
||||
let _ = wast::parser::parse::<Optimizations<TestOperator>>(&buf);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-macro"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -9,8 +9,8 @@ pub fn derive_child_nodes(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
Ok(quote! {
|
||||
impl #impl_generics ChildNodes<'a, 'a> for #name #ty_generics #where_clause {
|
||||
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a>>) {
|
||||
impl #impl_generics ChildNodes<'a, 'a, TOperator> for #name #ty_generics #where_clause {
|
||||
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a, TOperator>>) {
|
||||
#children
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,12 @@ fn get_child_nodes(data: &syn::Data) -> Result<impl quote::ToTokens> {
|
||||
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
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn derive_into_dyn_ast_ref(input: &DeriveInput) -> Result<impl quote::ToToke
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
Ok(quote! {
|
||||
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a> #where_clause {
|
||||
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a, TOperator> #where_clause {
|
||||
#[inline]
|
||||
fn from(x: &'a #ty #ty_generics) -> Self {
|
||||
Self::#ty(x)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
//! Implementation of the `#[peepmatic]` macro for the `Operator` AST node.
|
||||
|
||||
use crate::proc_macro::TokenStream;
|
||||
use crate::PeepmaticOpts;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::DeriveInput;
|
||||
use syn::Error;
|
||||
use syn::{parse_macro_input, Result};
|
||||
|
||||
pub fn derive_operator(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let variants = match get_enum_variants(&input) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let arity = match create_arity(&variants) {
|
||||
Ok(a) => a,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let num_operators = variants.len();
|
||||
let type_methods = create_type_methods(&variants);
|
||||
let parse_impl = create_parse_impl(&input.ident, &variants);
|
||||
let display_impl = create_display_impl(&input.ident, &variants);
|
||||
let try_from_u32_impl = create_try_from_u32_impl(&input.ident, &variants);
|
||||
let ident = &input.ident;
|
||||
|
||||
let expanded = quote! {
|
||||
impl #ident {
|
||||
#arity
|
||||
#type_methods
|
||||
|
||||
/// Get the total number of different operators.
|
||||
pub const fn num_operators() -> usize {
|
||||
#num_operators
|
||||
}
|
||||
}
|
||||
|
||||
#display_impl
|
||||
#try_from_u32_impl
|
||||
#parse_impl
|
||||
};
|
||||
|
||||
// eprintln!("{}", expanded);
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn get_enum_variants(input: &DeriveInput) -> Result<Vec<OperatorVariant>> {
|
||||
let en = match &input.data {
|
||||
syn::Data::Enum(en) => en,
|
||||
syn::Data::Struct(_) => {
|
||||
panic!("can only put #[peepmatic] on an enum; found it on a struct")
|
||||
}
|
||||
syn::Data::Union(_) => panic!("can only put #[peepmatic] on an enum; found it on a union"),
|
||||
};
|
||||
en.variants
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|mut variant| {
|
||||
Ok(OperatorVariant {
|
||||
opts: PeepmaticOpts::from_attrs(&mut variant.attrs)?,
|
||||
syn: variant,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct OperatorVariant {
|
||||
syn: syn::Variant,
|
||||
opts: PeepmaticOpts,
|
||||
}
|
||||
|
||||
fn create_arity(variants: &[OperatorVariant]) -> Result<impl quote::ToTokens> {
|
||||
let mut imm_arities = vec![];
|
||||
let mut params_arities = vec![];
|
||||
|
||||
for v in variants {
|
||||
let variant = &v.syn.ident;
|
||||
|
||||
let imm_arity = v.opts.immediates.len();
|
||||
if imm_arity > std::u8::MAX as usize {
|
||||
return Err(Error::new(
|
||||
v.opts.immediates_paren.span,
|
||||
"cannot have more than u8::MAX immediates",
|
||||
));
|
||||
}
|
||||
let imm_arity = imm_arity as u8;
|
||||
|
||||
imm_arities.push(quote! {
|
||||
Self::#variant => #imm_arity,
|
||||
});
|
||||
|
||||
let params_arity = v.opts.params.len();
|
||||
if params_arity > std::u8::MAX as usize {
|
||||
return Err(Error::new(
|
||||
v.opts.params_paren.span,
|
||||
"cannot have more than u8::MAX params",
|
||||
));
|
||||
}
|
||||
let params_arity = params_arity as u8;
|
||||
|
||||
params_arities.push(quote! {
|
||||
Self::#variant => #params_arity,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
/// Get the number of immediates that this operator has.
|
||||
pub fn immediates_arity(&self) -> u8 {
|
||||
match *self {
|
||||
#( #imm_arities )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of parameters that this operator takes.
|
||||
pub fn params_arity(&self) -> u8 {
|
||||
match *self {
|
||||
#( #params_arities )*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_type_methods(variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||
let mut result_types = vec![];
|
||||
let mut imm_types = vec![];
|
||||
let mut param_types = vec![];
|
||||
|
||||
for v in variants {
|
||||
let variant = &v.syn.ident;
|
||||
|
||||
let result_ty = v.opts.result.as_ref().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"must define #[peepmatic(result(..))] on operator `{}`",
|
||||
variant
|
||||
)
|
||||
});
|
||||
result_types.push(quote! {
|
||||
Self::#variant => {
|
||||
context.#result_ty(span)
|
||||
}
|
||||
});
|
||||
|
||||
let imm_tys = match &v.opts.immediates[..] {
|
||||
[] => quote! {},
|
||||
[ty, rest @ ..] => {
|
||||
let rest = rest.iter().map(|ty| {
|
||||
quote! { .chain(::std::iter::once(context.#ty(span))) }
|
||||
});
|
||||
quote! {
|
||||
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
|
||||
}
|
||||
}
|
||||
};
|
||||
imm_types.push(quote! {
|
||||
Self::#variant => {
|
||||
#imm_tys
|
||||
}
|
||||
});
|
||||
|
||||
let param_tys = match &v.opts.params[..] {
|
||||
[] => quote! {},
|
||||
[ty, rest @ ..] => {
|
||||
let rest = rest.iter().map(|ty| {
|
||||
quote! { .chain(::std::iter::once(context.#ty(span))) }
|
||||
});
|
||||
quote! {
|
||||
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
|
||||
}
|
||||
}
|
||||
};
|
||||
param_types.push(quote! {
|
||||
Self::#variant => {
|
||||
#param_tys
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
/// Get the result type of this operator.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn result_type<'a, C>(
|
||||
&self,
|
||||
context: &mut C,
|
||||
span: wast::Span,
|
||||
) -> C::TypeVariable
|
||||
where
|
||||
C: 'a + TypingContext<'a>,
|
||||
{
|
||||
match *self {
|
||||
#( #result_types )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the immediate types of this operator.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn immediate_types<'a, C>(
|
||||
&self,
|
||||
context: &mut C,
|
||||
span: wast::Span,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
)
|
||||
where
|
||||
C: 'a + TypingContext<'a>,
|
||||
{
|
||||
match *self {
|
||||
#( #imm_types )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the parameter types of this operator.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn param_types<'a, C>(
|
||||
&self,
|
||||
context: &mut C,
|
||||
span: wast::Span,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
)
|
||||
where
|
||||
C: 'a + TypingContext<'a>,
|
||||
{
|
||||
match *self {
|
||||
#( #param_types )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn snake_case(s: &str) -> String {
|
||||
let mut t = String::with_capacity(s.len() + 1);
|
||||
for (i, ch) in s.chars().enumerate() {
|
||||
if i != 0 && ch.is_uppercase() {
|
||||
t.push('_');
|
||||
}
|
||||
t.extend(ch.to_lowercase());
|
||||
}
|
||||
t
|
||||
}
|
||||
|
||||
fn create_parse_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||
let token_defs = variants.iter().map(|v| {
|
||||
let tok = snake_case(&v.syn.ident.to_string());
|
||||
let tok = Ident::new(&tok, Span::call_site());
|
||||
quote! {
|
||||
wast::custom_keyword!(#tok);
|
||||
}
|
||||
});
|
||||
|
||||
let parses = variants.iter().map(|v| {
|
||||
let tok = snake_case(&v.syn.ident.to_string());
|
||||
let tok = Ident::new(&tok, Span::call_site());
|
||||
let ident = &v.syn.ident;
|
||||
quote! {
|
||||
if p.peek::<#tok>() {
|
||||
p.parse::<#tok>()?;
|
||||
return Ok(Self::#ident);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let expected = format!("expected {}", ident);
|
||||
|
||||
quote! {
|
||||
#[cfg(feature = "construct")]
|
||||
impl<'a> wast::parser::Parse<'a> for #ident {
|
||||
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<Self> {
|
||||
#( #token_defs )*
|
||||
|
||||
#( #parses )*
|
||||
|
||||
Err(p.error(#expected))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_display_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||
let displays = variants.iter().map(|v| {
|
||||
let variant = &v.syn.ident;
|
||||
let snake = snake_case(&v.syn.ident.to_string());
|
||||
quote! {
|
||||
Self::#variant => write!(f, #snake),
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
impl std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
#( #displays )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_try_from_u32_impl(
|
||||
ident: &syn::Ident,
|
||||
variants: &[OperatorVariant],
|
||||
) -> impl quote::ToTokens {
|
||||
let matches = variants.iter().map(|v| {
|
||||
let variant = &v.syn.ident;
|
||||
quote! {
|
||||
x if Self::#variant as u32 == x => Ok(Self::#variant),
|
||||
}
|
||||
});
|
||||
|
||||
let error_msg = format!("value is not an `{}`", ident);
|
||||
|
||||
quote! {
|
||||
impl std::convert::TryFrom<u32> for #ident {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
#( #matches )*
|
||||
_ => Err(#error_msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,9 @@ pub fn derive_span(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-runtime"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -12,13 +12,14 @@ description = "Runtime support for peepmatic peephole optimizers"
|
||||
bincode = "1.2.1"
|
||||
bumpalo = "3.2.0"
|
||||
log = "0.4.8"
|
||||
peepmatic-automata = { version = "0.2.0", path = "../automata", features = ["serde"] }
|
||||
peepmatic-macro = { version = "0.2.0", path = "../macro" }
|
||||
peepmatic-automata = { version = "0.66.0", path = "../automata", features = ["serde"] }
|
||||
peepmatic-traits = { version = "0.66.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
thiserror = "1.0.15"
|
||||
wast = { version = "15.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic-test-operator = { version = "0.66.0", path = "../test-operator" }
|
||||
serde_test = "1.0.114"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! Interfacing with actual instructions.
|
||||
|
||||
use crate::operator::Operator;
|
||||
use crate::part::{Constant, Part};
|
||||
use crate::paths::Path;
|
||||
use crate::r#type::Type;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// A trait for interfacing with actual instruction sequences.
|
||||
///
|
||||
@@ -32,6 +33,9 @@ pub unsafe trait InstructionSet<'a> {
|
||||
/// implementation.
|
||||
type Context;
|
||||
|
||||
/// An operator.
|
||||
type Operator: 'static + Copy + Debug + Eq + Hash + Into<NonZeroU32>;
|
||||
|
||||
/// An instruction (or identifier for an instruction).
|
||||
type Instruction: Copy + Debug + Eq;
|
||||
|
||||
@@ -64,10 +68,12 @@ pub unsafe trait InstructionSet<'a> {
|
||||
|
||||
/// Get the given instruction's operator.
|
||||
///
|
||||
/// If the instruction's opcode does not have an associated
|
||||
/// `peepmatic_runtime::operator::Operator` variant (i.e. that instruction
|
||||
/// isn't supported by `peepmatic` yet) then `None` should be returned.
|
||||
fn operator(&self, context: &mut Self::Context, instr: Self::Instruction) -> Option<Operator>;
|
||||
/// If the instruction isn't supported, then `None` should be returned.
|
||||
fn operator(
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
instr: Self::Instruction,
|
||||
) -> Option<Self::Operator>;
|
||||
|
||||
/// Make a unary instruction.
|
||||
///
|
||||
@@ -76,7 +82,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
) -> Self::Instruction;
|
||||
@@ -92,7 +98,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
b: Part<Self::Instruction>,
|
||||
@@ -108,7 +114,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
b: Part<Self::Instruction>,
|
||||
|
||||
@@ -22,12 +22,12 @@ pub mod error;
|
||||
pub mod instruction_set;
|
||||
pub mod integer_interner;
|
||||
pub mod linear;
|
||||
pub mod operator;
|
||||
pub mod optimizations;
|
||||
pub mod optimizer;
|
||||
pub mod part;
|
||||
pub mod paths;
|
||||
pub mod r#type;
|
||||
pub mod unquote;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use optimizations::PeepholeOptimizations;
|
||||
|
||||
@@ -7,17 +7,22 @@
|
||||
|
||||
use crate::cc::ConditionCode;
|
||||
use crate::integer_interner::{IntegerId, IntegerInterner};
|
||||
use crate::operator::{Operator, UnquoteOperator};
|
||||
use crate::paths::{PathId, PathInterner};
|
||||
use crate::r#type::{BitWidth, Type};
|
||||
use crate::unquote::UnquoteOperator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// A set of linear optimizations.
|
||||
#[derive(Debug)]
|
||||
pub struct Optimizations {
|
||||
pub struct Optimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The linear optimizations.
|
||||
pub optimizations: Vec<Optimization>,
|
||||
pub optimizations: Vec<Optimization<TOperator>>,
|
||||
|
||||
/// The de-duplicated paths referenced by these optimizations.
|
||||
pub paths: PathInterner,
|
||||
@@ -28,9 +33,12 @@ pub struct Optimizations {
|
||||
|
||||
/// A linearized optimization.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Optimization {
|
||||
pub struct Optimization<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The chain of increments for this optimization.
|
||||
pub increments: Vec<Increment>,
|
||||
pub increments: Vec<Increment<TOperator>>,
|
||||
}
|
||||
|
||||
/// Match any value.
|
||||
@@ -63,7 +71,10 @@ pub fn bool_to_match_result(b: bool) -> MatchResult {
|
||||
/// basically become a state and a transition edge out of that state in the
|
||||
/// final automata.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Increment {
|
||||
pub struct Increment<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The matching operation to perform.
|
||||
pub operation: MatchOp,
|
||||
|
||||
@@ -74,7 +85,7 @@ pub struct Increment {
|
||||
|
||||
/// Actions to perform, given that the operation resulted in the expected
|
||||
/// value.
|
||||
pub actions: Vec<Action>,
|
||||
pub actions: Vec<Action<TOperator>>,
|
||||
}
|
||||
|
||||
/// A matching operation to be performed on some Cranelift instruction as part
|
||||
@@ -163,7 +174,7 @@ pub struct RhsId(pub u16);
|
||||
/// When evaluating actions, the `i^th` action implicitly defines the
|
||||
/// `RhsId(i)`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
pub enum Action<TOperator> {
|
||||
/// Reuse something from the left-hand side.
|
||||
GetLhs {
|
||||
/// The path to the instruction or value.
|
||||
@@ -215,13 +226,13 @@ pub enum Action {
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operator for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
},
|
||||
|
||||
/// Make a binary instruction.
|
||||
MakeBinaryInst {
|
||||
/// The opcode for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operands for this instruction.
|
||||
@@ -231,7 +242,7 @@ pub enum Action {
|
||||
/// Make a ternary instruction.
|
||||
MakeTernaryInst {
|
||||
/// The opcode for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operands for this instruction.
|
||||
@@ -242,6 +253,7 @@ pub enum Action {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
|
||||
// These types all end up in the automaton, so we should take care that they
|
||||
// are small and don't fill up the data cache (or take up too much
|
||||
@@ -259,6 +271,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn action_size() {
|
||||
assert_eq!(std::mem::size_of::<Action>(), 16);
|
||||
assert_eq!(std::mem::size_of::<Action<TestOperator>>(), 16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
//! Operator definitions.
|
||||
|
||||
use peepmatic_macro::PeepmaticOperator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An operator.
|
||||
///
|
||||
/// These are a subset of Cranelift IR's operators.
|
||||
///
|
||||
/// ## Caveats for Branching and Trapping Operators
|
||||
///
|
||||
/// Branching operators are not fully modeled: we do not represent their label
|
||||
/// and jump arguments. It is up to the interpreter doing the instruction
|
||||
/// replacement to recognize when we are replacing one branch with another, and
|
||||
/// copy over the extra information.
|
||||
///
|
||||
/// Affected operations: `brz`, `brnz`, `trapz`, `trapnz`.
|
||||
#[derive(PeepmaticOperator, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum Operator {
|
||||
/// `adjust_sp_down`
|
||||
#[peepmatic(params(iNN), result(void))]
|
||||
// NB: We convert `Operator`s into `NonZeroU32`s with unchecked casts;
|
||||
// memory safety relies on `Operator` starting at `1` and no variant ever
|
||||
// being zero.
|
||||
AdjustSpDown = 1,
|
||||
|
||||
/// `adjust_sp_down_imm`
|
||||
#[peepmatic(immediates(iNN), result(void))]
|
||||
AdjustSpDownImm,
|
||||
|
||||
/// `band`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Band,
|
||||
|
||||
/// `band_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BandImm,
|
||||
|
||||
/// `bconst`
|
||||
#[peepmatic(immediates(b1), result(bNN))]
|
||||
Bconst,
|
||||
|
||||
/// `bint`
|
||||
#[peepmatic(params(bNN), result(iNN))]
|
||||
Bint,
|
||||
|
||||
/// `bnot`
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Bnot,
|
||||
|
||||
/// `bor`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bor,
|
||||
|
||||
/// `bor_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BorImm,
|
||||
|
||||
/// `brnz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Brnz,
|
||||
|
||||
/// `brz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Brz,
|
||||
|
||||
/// `bxor`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bxor,
|
||||
|
||||
/// `bxor_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BxorImm,
|
||||
|
||||
/// `iadd`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Iadd,
|
||||
|
||||
/// `iadd_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IaddImm,
|
||||
|
||||
/// `icmp`
|
||||
#[peepmatic(immediates(cc), params(iNN, iNN), result(b1))]
|
||||
Icmp,
|
||||
|
||||
/// `icmp_imm`
|
||||
#[peepmatic(immediates(cc, iNN), params(iNN), result(b1))]
|
||||
IcmpImm,
|
||||
|
||||
/// `iconst`
|
||||
#[peepmatic(immediates(iNN), result(iNN))]
|
||||
Iconst,
|
||||
|
||||
/// `ifcmp`
|
||||
#[peepmatic(params(iNN, iNN), result(cpu_flags))]
|
||||
Ifcmp,
|
||||
|
||||
/// `ifcmp_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(cpu_flags))]
|
||||
IfcmpImm,
|
||||
|
||||
/// `imul`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Imul,
|
||||
|
||||
/// `imul_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
ImulImm,
|
||||
|
||||
/// `ireduce`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Ireduce,
|
||||
|
||||
/// `irsub_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IrsubImm,
|
||||
|
||||
/// `ishl`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Ishl,
|
||||
|
||||
/// `ishl_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IshlImm,
|
||||
|
||||
/// `isub`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Isub,
|
||||
|
||||
/// `rotl`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Rotl,
|
||||
|
||||
/// `rotl_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
RotlImm,
|
||||
|
||||
/// `rotr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Rotr,
|
||||
|
||||
/// `rotr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
RotrImm,
|
||||
|
||||
/// `sdiv`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Sdiv,
|
||||
|
||||
/// `sdiv_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SdivImm,
|
||||
|
||||
/// `select`
|
||||
#[peepmatic(params(bool_or_int, any_t, any_t), result(any_t))]
|
||||
Select,
|
||||
|
||||
/// `sextend`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Sextend,
|
||||
|
||||
/// `srem`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Srem,
|
||||
|
||||
/// `srem_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SremImm,
|
||||
|
||||
/// `sshr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Sshr,
|
||||
|
||||
/// `sshr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SshrImm,
|
||||
|
||||
/// `trapnz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Trapnz,
|
||||
|
||||
/// `trapz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Trapz,
|
||||
|
||||
/// `udiv`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Udiv,
|
||||
|
||||
/// `udiv_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UdivImm,
|
||||
|
||||
/// `uextend`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Uextend,
|
||||
|
||||
/// `urem`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Urem,
|
||||
|
||||
/// `urem_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UremImm,
|
||||
|
||||
/// `ushr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Ushr,
|
||||
|
||||
/// `ushr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UshrImm,
|
||||
}
|
||||
|
||||
/// Compile-time unquote operators.
|
||||
///
|
||||
/// These are used in the right-hand side to perform compile-time evaluation of
|
||||
/// constants matched on the left-hand side.
|
||||
#[derive(PeepmaticOperator, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum UnquoteOperator {
|
||||
/// Compile-time `band` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Band,
|
||||
|
||||
/// Compile-time `bor` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bor,
|
||||
|
||||
/// Compile-time `bxor` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bxor,
|
||||
|
||||
/// Compile-time `iadd` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Iadd,
|
||||
|
||||
/// Compile-time `imul` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Imul,
|
||||
|
||||
/// Compile-time `isub` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Isub,
|
||||
|
||||
/// Take the base-2 log of a power of two integer.
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Log2,
|
||||
|
||||
/// Wrapping negation of an integer.
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Neg,
|
||||
}
|
||||
|
||||
/// A trait to represent a typing context.
|
||||
///
|
||||
/// This is used by the macro-generated operator methods that create the type
|
||||
/// variables for their immediates, parameters, and results. This trait is
|
||||
/// implemented by the concrete typing context in `peepmatic/src/verify.rs`.
|
||||
#[cfg(feature = "construct")]
|
||||
pub trait TypingContext<'a> {
|
||||
/// A type variable.
|
||||
type TypeVariable;
|
||||
|
||||
/// Create a condition code type.
|
||||
fn cc(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `bNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn bNN(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iNN(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iMM` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iMM(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the CPU flags type variable.
|
||||
fn cpu_flags(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type of size one bit.
|
||||
fn b1(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the void type, used as the result of operators that branch away,
|
||||
/// or do not return anything.
|
||||
fn void(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that may be either a boolean or an integer.
|
||||
fn bool_or_int(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that can be any type T.
|
||||
///
|
||||
/// Each use of `any_t` by the same operator refers to the same type
|
||||
/// variable.
|
||||
fn any_t(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ use crate::optimizer::PeepholeOptimizer;
|
||||
use crate::paths::PathInterner;
|
||||
use peepmatic_automata::Automaton;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[cfg(feature = "construct")]
|
||||
use std::fs;
|
||||
@@ -19,7 +21,10 @@ use std::path::Path;
|
||||
/// This is the compilation result of the `peepmatic` crate, after its taken a
|
||||
/// bunch of optimizations written in the DSL and lowered and combined them.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PeepholeOptimizations {
|
||||
pub struct PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The instruction paths referenced by the peephole optimizations.
|
||||
pub paths: PathInterner,
|
||||
|
||||
@@ -29,12 +34,18 @@ pub struct PeepholeOptimizations {
|
||||
|
||||
/// The underlying automata for matching optimizations' left-hand sides, and
|
||||
/// building up the corresponding right-hand side.
|
||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action]>>,
|
||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action<TOperator>]>>,
|
||||
}
|
||||
|
||||
impl PeepholeOptimizations {
|
||||
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Deserialize a `PeepholeOptimizations` from bytes.
|
||||
pub fn deserialize(serialized: &[u8]) -> Result<Self> {
|
||||
pub fn deserialize<'a>(serialized: &'a [u8]) -> Result<Self>
|
||||
where
|
||||
TOperator: serde::Deserialize<'a>,
|
||||
{
|
||||
let peep_opt: Self = bincode::deserialize(serialized)?;
|
||||
Ok(peep_opt)
|
||||
}
|
||||
@@ -43,12 +54,20 @@ impl PeepholeOptimizations {
|
||||
///
|
||||
/// Requires that the `"construct"` cargo feature is enabled.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()> {
|
||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()>
|
||||
where
|
||||
TOperator: serde::Serialize,
|
||||
{
|
||||
let file = fs::File::create(path)?;
|
||||
bincode::serialize_into(file, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Create a new peephole optimizer instance from this set of peephole
|
||||
/// optimizations.
|
||||
///
|
||||
@@ -58,9 +77,13 @@ impl PeepholeOptimizations {
|
||||
/// instance, rather than create a new one for each instruction. Reusing the
|
||||
/// peephole optimizer instance allows the reuse of a few internal
|
||||
/// allocations.
|
||||
pub fn optimizer<'peep, 'ctx, I>(&'peep self, instr_set: I) -> PeepholeOptimizer<'peep, 'ctx, I>
|
||||
pub fn optimizer<'peep, 'ctx, TInstructionSet>(
|
||||
&'peep self,
|
||||
instr_set: TInstructionSet,
|
||||
) -> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx, Operator = TOperator>,
|
||||
TOperator: Into<std::num::NonZeroU32>,
|
||||
{
|
||||
PeepholeOptimizer {
|
||||
peep_opt: self,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
use crate::instruction_set::InstructionSet;
|
||||
use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult};
|
||||
use crate::operator::UnquoteOperator;
|
||||
use crate::optimizations::PeepholeOptimizations;
|
||||
use crate::part::{Constant, Part};
|
||||
use crate::r#type::{BitWidth, Type};
|
||||
use crate::unquote::UnquoteOperator;
|
||||
use peepmatic_automata::State;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Debug};
|
||||
@@ -21,20 +21,20 @@ use std::num::NonZeroU32;
|
||||
/// Reusing an instance when applying peephole optimizations to different
|
||||
/// instruction sequences means that you reuse internal allocations that are
|
||||
/// used to match left-hand sides and build up right-hand sides.
|
||||
pub struct PeepholeOptimizer<'peep, 'ctx, I>
|
||||
pub struct PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx>,
|
||||
{
|
||||
pub(crate) peep_opt: &'peep PeepholeOptimizations,
|
||||
pub(crate) instr_set: I,
|
||||
pub(crate) right_hand_sides: Vec<Part<I::Instruction>>,
|
||||
pub(crate) actions: Vec<Action>,
|
||||
pub(crate) peep_opt: &'peep PeepholeOptimizations<TInstructionSet::Operator>,
|
||||
pub(crate) instr_set: TInstructionSet,
|
||||
pub(crate) right_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
|
||||
pub(crate) actions: Vec<Action<TInstructionSet::Operator>>,
|
||||
pub(crate) backtracking_states: Vec<(State, usize)>,
|
||||
}
|
||||
|
||||
impl<'peep, 'ctx, I> Debug for PeepholeOptimizer<'peep, 'ctx, I>
|
||||
impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let PeepholeOptimizer {
|
||||
@@ -54,9 +54,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'peep, 'ctx, I> PeepholeOptimizer<'peep, 'ctx, I>
|
||||
impl<'peep, 'ctx, TInstructionSet> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx>,
|
||||
{
|
||||
fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant {
|
||||
use Constant::*;
|
||||
@@ -107,7 +107,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_actions(&mut self, context: &mut I::Context, root: I::Instruction) {
|
||||
fn eval_actions(
|
||||
&mut self,
|
||||
context: &mut TInstructionSet::Context,
|
||||
root: TInstructionSet::Instruction,
|
||||
) {
|
||||
let mut actions = mem::replace(&mut self.actions, vec![]);
|
||||
|
||||
for action in actions.drain(..) {
|
||||
@@ -272,8 +276,8 @@ where
|
||||
|
||||
fn eval_match_op(
|
||||
&mut self,
|
||||
context: &mut I::Context,
|
||||
root: I::Instruction,
|
||||
context: &mut TInstructionSet::Context,
|
||||
root: TInstructionSet::Instruction,
|
||||
match_op: MatchOp,
|
||||
) -> MatchResult {
|
||||
use crate::linear::MatchOp::*;
|
||||
@@ -288,13 +292,7 @@ where
|
||||
.ok_or(Else)?;
|
||||
let inst = part.as_instruction().ok_or(Else)?;
|
||||
let op = self.instr_set.operator(context, inst).ok_or(Else)?;
|
||||
let op = op as u32;
|
||||
debug_assert!(
|
||||
op != 0,
|
||||
"`Operator` doesn't have any variant represented
|
||||
with zero"
|
||||
);
|
||||
Ok(unsafe { NonZeroU32::new_unchecked(op as u32) })
|
||||
Ok(op.into())
|
||||
}
|
||||
IsConst { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
@@ -477,9 +475,9 @@ where
|
||||
/// untouched and `None` is returned.
|
||||
pub fn apply_one(
|
||||
&mut self,
|
||||
context: &mut I::Context,
|
||||
root: I::Instruction,
|
||||
) -> Option<I::Instruction> {
|
||||
context: &mut TInstructionSet::Context,
|
||||
root: TInstructionSet::Instruction,
|
||||
) -> Option<TInstructionSet::Instruction> {
|
||||
log::trace!("PeepholeOptimizer::apply_one");
|
||||
|
||||
self.backtracking_states.clear();
|
||||
@@ -566,7 +564,11 @@ where
|
||||
|
||||
/// Keep applying peephole optimizations to the given instruction until none
|
||||
/// can be applied anymore.
|
||||
pub fn apply_all(&mut self, context: &mut I::Context, mut inst: I::Instruction) {
|
||||
pub fn apply_all(
|
||||
&mut self,
|
||||
context: &mut TInstructionSet::Context,
|
||||
mut inst: TInstructionSet::Instruction,
|
||||
) {
|
||||
loop {
|
||||
if let Some(new_inst) = self.apply_one(context, inst) {
|
||||
inst = new_inst;
|
||||
|
||||
44
cranelift/peepmatic/crates/runtime/src/unquote.rs
Normal file
44
cranelift/peepmatic/crates/runtime/src/unquote.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
//! Unquote operator definition.
|
||||
|
||||
peepmatic_traits::define_operator! {
|
||||
/// Compile-time unquote operators.
|
||||
///
|
||||
/// These are used in the right-hand side to perform compile-time evaluation of
|
||||
/// constants matched on the left-hand side.
|
||||
#[allow(missing_docs)]
|
||||
UnquoteOperator {
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor => Bor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bxor => Bxor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd => Iadd {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
imul => Imul {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
isub => Isub {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
log2 => Log2 {
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
neg => Neg {
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
}
|
||||
parse_cfg(feature = "construct");
|
||||
}
|
||||
12
cranelift/peepmatic/crates/test-operator/Cargo.toml
Normal file
12
cranelift/peepmatic/crates/test-operator/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "peepmatic-test-operator"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
peepmatic-traits = { version = "0.66.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "15.0.0"
|
||||
219
cranelift/peepmatic/crates/test-operator/src/lib.rs
Normal file
219
cranelift/peepmatic/crates/test-operator/src/lib.rs
Normal file
@@ -0,0 +1,219 @@
|
||||
//! This crate defines `TestOperator`: a `TOperator` type for usage in tests.
|
||||
//!
|
||||
//! This allows us to write Peepmatic-specific tests that do not depend on
|
||||
//! building all of Cranelift.
|
||||
|
||||
peepmatic_traits::define_operator! {
|
||||
/// A `TOperator` type for use inside tests.
|
||||
TestOperator {
|
||||
adjust_sp_down => AdjustSpDown {
|
||||
parameters(iNN);
|
||||
result(void);
|
||||
}
|
||||
adjust_sp_down_imm => AdjustSpDownImm {
|
||||
immediates(iNN);
|
||||
result(void);
|
||||
}
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
band_imm => BandImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bconst => Bconst {
|
||||
immediates(b1);
|
||||
result(bNN);
|
||||
}
|
||||
bint => Bint {
|
||||
parameters(bNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor => Bor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor_imm => BorImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
brnz => Brnz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
brz => Brz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
bxor => Bxor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bxor_imm => BxorImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd => Iadd {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd_imm => IaddImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
icmp => Icmp {
|
||||
immediates(cc);
|
||||
parameters(iNN, iNN);
|
||||
result(b1);
|
||||
}
|
||||
icmp_imm => IcmpImm {
|
||||
immediates(cc, iNN);
|
||||
parameters(iNN);
|
||||
result(b1);
|
||||
}
|
||||
iconst => Iconst {
|
||||
immediates(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ifcmp => Ifcmp {
|
||||
parameters(iNN, iNN);
|
||||
result(cpu_flags);
|
||||
}
|
||||
ifcmp_imm => IfcmpImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(cpu_flags);
|
||||
}
|
||||
imul => Imul {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
imul_imm => ImulImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ireduce => Ireduce {
|
||||
parameters(iNN);
|
||||
result(iMM);
|
||||
is_reduce(true);
|
||||
}
|
||||
irsub_imm => IrsubImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ishl => Ishl {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ishl_imm => IshlImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
isub => Isub {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotl => Rotl {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotl_imm => RotlImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotr => Rotr {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotr_imm => RotrImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sdiv => Sdiv {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sdiv_imm => SdivImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
select => Select {
|
||||
parameters(bool_or_int, any_t, any_t);
|
||||
result(any_t);
|
||||
}
|
||||
sextend => Sextend {
|
||||
parameters(iNN);
|
||||
result(iMM);
|
||||
is_extend(true);
|
||||
}
|
||||
srem => Srem {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
srem_imm => SremImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sshr => Sshr {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sshr_imm => SshrImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
trapnz => Trapnz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
trapz => Trapz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
udiv => Udiv {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
udiv_imm => UdivImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
uextend => Uextend {
|
||||
parameters(iNN);
|
||||
result(iMM);
|
||||
is_extend(true);
|
||||
}
|
||||
urem => Urem {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
urem_imm => UremImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ushr => Ushr {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ushr_imm => UshrImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" }
|
||||
|
||||
@@ -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<Immediate>,
|
||||
pub arguments: Vec<Instruction>,
|
||||
@@ -174,7 +175,7 @@ impl Program {
|
||||
|
||||
pub fn new_instruction(
|
||||
&mut self,
|
||||
operator: Operator,
|
||||
operator: TestOperator,
|
||||
r#type: Type,
|
||||
immediates: Vec<Immediate>,
|
||||
arguments: Vec<Instruction>,
|
||||
@@ -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<Constant> {
|
||||
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<Operator> {
|
||||
fn operator(&self, program: &mut Program, instr: Instruction) -> Option<TestOperator> {
|
||||
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>,
|
||||
) -> 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<Instruction>,
|
||||
b: Part<Instruction>,
|
||||
@@ -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<Instruction>,
|
||||
b: Part<Instruction>,
|
||||
@@ -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(),
|
||||
|
||||
@@ -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],
|
||||
|
||||
9
cranelift/peepmatic/crates/traits/Cargo.toml
Normal file
9
cranelift/peepmatic/crates/traits/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "peepmatic-traits"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
26
cranelift/peepmatic/crates/traits/src/lib.rs
Normal file
26
cranelift/peepmatic/crates/traits/src/lib.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
//! Shared traits, types, and macros for Peepmatic.
|
||||
//!
|
||||
//! This crate is used both at build time when constructing peephole optimizers
|
||||
//! (i.e. in the `peepmatic` crate), and at run time when using pre-built
|
||||
//! peephole optimizers (i.e. in the `peepmatic-runtime` crate and in
|
||||
//! Cranelift's Peepmatic integration at `cranelift/codegen/src/peepmatic.rs`).
|
||||
//!
|
||||
//! This crate is similar to a header file: it should generally only contain
|
||||
//! trait/type/macro definitions, not any code.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
|
||||
#[macro_use]
|
||||
mod operator;
|
||||
pub use operator::*;
|
||||
|
||||
mod typing;
|
||||
pub use typing::*;
|
||||
|
||||
/// Raise a panic about an unsupported operation.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn unsupported(msg: &str) -> ! {
|
||||
panic!("unsupported: {}", msg)
|
||||
}
|
||||
317
cranelift/peepmatic/crates/traits/src/operator.rs
Normal file
317
cranelift/peepmatic/crates/traits/src/operator.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
/// Define a `wast::parser::Parse` implementation for an operator type.
|
||||
#[macro_export]
|
||||
macro_rules! define_parse_impl_for_operator {
|
||||
(
|
||||
$operator:ident {
|
||||
$(
|
||||
$keyword:ident => $variant:ident;
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
impl<'a> wast::parser::Parse<'a> for $operator {
|
||||
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<$operator> {
|
||||
/// Token definitions for our `Opcode` keywords.
|
||||
mod tok {
|
||||
$(
|
||||
wast::custom_keyword!($keyword);
|
||||
)*
|
||||
}
|
||||
|
||||
// Peek at the next token, and if it is the variant's
|
||||
// keyword, then consume it with `parse`, and finally return
|
||||
// the `Opcode` variant.
|
||||
$(
|
||||
if p.peek::<tok::$keyword>() {
|
||||
p.parse::<tok::$keyword>()?;
|
||||
return Ok(Self::$variant);
|
||||
}
|
||||
)*
|
||||
|
||||
// If none of the keywords matched, then we get a parse error.
|
||||
Err(p.error(concat!("expected `", stringify!($operator), "`")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a `peepmatic_traits::TypingRules` implementation for the given
|
||||
/// operator type.
|
||||
#[macro_export]
|
||||
macro_rules! define_typing_rules_impl_for_operator {
|
||||
(
|
||||
$operator:ident {
|
||||
$(
|
||||
$variant:ident {
|
||||
$( immediates( $($immediate:ident),* ); )?
|
||||
$( parameters( $($parameter:ident),* ); )?
|
||||
result( $result:ident );
|
||||
$( is_reduce($is_reduce:expr); )?
|
||||
$( is_extend($is_extend:expr); )?
|
||||
}
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
impl $crate::TypingRules for $operator {
|
||||
fn result_type<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
) -> C::TypeVariable
|
||||
where
|
||||
C: $crate::TypingContext<'a> {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant => typing_context.$result(span),
|
||||
)*
|
||||
|
||||
#[allow(dead_code)]
|
||||
_ => $crate::unsupported("no typing rules defined for variant"),
|
||||
}
|
||||
}
|
||||
|
||||
fn immediates_arity(&self) -> u8 {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant => $crate::define_typing_rules_impl_for_operator!(
|
||||
@arity;
|
||||
$( $( $immediate, )* )?
|
||||
),
|
||||
)*
|
||||
|
||||
#[allow(dead_code)]
|
||||
_ => $crate::unsupported("no typing rules defined for variant"),
|
||||
}
|
||||
}
|
||||
|
||||
fn immediate_types<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
)
|
||||
where
|
||||
C: $crate::TypingContext<'a>
|
||||
{
|
||||
match self {
|
||||
$(
|
||||
Self::$variant => types.extend(
|
||||
None.into_iter()
|
||||
$(
|
||||
$(
|
||||
.chain(Some(typing_context.$immediate(span)))
|
||||
)*
|
||||
)?
|
||||
),
|
||||
)*
|
||||
|
||||
#[allow(dead_code)]
|
||||
_ => $crate::unsupported("no typing rules defined for variant"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parameters_arity(&self) -> u8 {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant => $crate::define_typing_rules_impl_for_operator!(
|
||||
@arity;
|
||||
$( $( $parameter, )* )?
|
||||
),
|
||||
)*
|
||||
|
||||
#[allow(dead_code)]
|
||||
_ => $crate::unsupported("no typing rules defined for variant"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parameter_types<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
)
|
||||
where
|
||||
C: $crate::TypingContext<'a>
|
||||
{
|
||||
match self {
|
||||
$(
|
||||
Self::$variant => types.extend(
|
||||
None.into_iter()
|
||||
$(
|
||||
$(
|
||||
.chain(Some(typing_context.$parameter(span)))
|
||||
)*
|
||||
)?
|
||||
),
|
||||
)*
|
||||
|
||||
#[allow(dead_code)]
|
||||
_ => $crate::unsupported("no typing rules defined for variant"),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_reduce(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant if false $( || $is_reduce )? => false $( || $is_reduce )?,
|
||||
)*
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_extend(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant if false $( || $is_extend )? => false $( || $is_extend )?,
|
||||
)*
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Base case: zero arity.
|
||||
(
|
||||
@arity;
|
||||
) => {
|
||||
0
|
||||
};
|
||||
|
||||
// Recursive case: count one for the head and add that to the arity of the
|
||||
// rest.
|
||||
(
|
||||
@arity;
|
||||
$head:ident,
|
||||
$( $rest:ident, )*
|
||||
) => {
|
||||
1 + $crate::define_typing_rules_impl_for_operator!(
|
||||
@arity;
|
||||
$( $rest, )*
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Define both a `wast::parser::Parse` implementation and a
|
||||
/// `peepmatic_traits::TypingRules` implementation for the given operator type.
|
||||
#[macro_export]
|
||||
macro_rules! define_parse_and_typing_rules_for_operator {
|
||||
(
|
||||
$operator:ident {
|
||||
$(
|
||||
$keyword:ident => $variant:ident {
|
||||
$( immediates( $($immediate:ident),* ); )?
|
||||
$( parameters( $($parameter:ident),* ); )?
|
||||
result( $result:ident );
|
||||
$( is_reduce($is_reduce:expr); )?
|
||||
$( is_extend($is_extend:expr); )?
|
||||
}
|
||||
)*
|
||||
}
|
||||
$( parse_cfg($parse_cfg:meta); )?
|
||||
) => {
|
||||
$( #[cfg($parse_cfg)] )?
|
||||
$crate::define_parse_impl_for_operator! {
|
||||
$operator {
|
||||
$(
|
||||
$keyword => $variant;
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
$crate::define_typing_rules_impl_for_operator! {
|
||||
$operator {
|
||||
$(
|
||||
$variant {
|
||||
$( immediates( $($immediate),* ); )?
|
||||
$( parameters( $($parameter),* ); )?
|
||||
result( $result );
|
||||
$( is_reduce($is_reduce); )?
|
||||
$( is_extend($is_extend); )?
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Define an operator type, as well as its parsing and typing rules.
|
||||
#[macro_export]
|
||||
macro_rules! define_operator {
|
||||
(
|
||||
$( #[$attr:meta] )*
|
||||
$operator:ident {
|
||||
$(
|
||||
$keywrord:ident => $variant:ident {
|
||||
$( immediates( $($immediate:ident),* ); )?
|
||||
$( parameters( $($parameter:ident),* ); )?
|
||||
result( $result:ident );
|
||||
$( is_reduce($is_reduce:expr); )?
|
||||
$( is_extend($is_extend:expr); )?
|
||||
}
|
||||
)*
|
||||
}
|
||||
$( parse_cfg($parse_cfg:meta); )?
|
||||
) => {
|
||||
$( #[$attr] )*
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum $operator {
|
||||
$(
|
||||
$variant,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<$operator> for u32 {
|
||||
#[inline]
|
||||
fn from(x: $operator) -> u32 {
|
||||
x as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$operator> for core::num::NonZeroU32 {
|
||||
#[inline]
|
||||
fn from(x: $operator) -> core::num::NonZeroU32 {
|
||||
let x: u32 = x.into();
|
||||
core::num::NonZeroU32::new(x.checked_add(1).unwrap()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::convert::TryFrom<u32> for $operator {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(x: u32) -> Result<Self, ()> {
|
||||
match x {
|
||||
$(
|
||||
x if x == Self::$variant.into() => Ok(Self::$variant),
|
||||
)*
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::convert::TryFrom<core::num::NonZeroU32> for $operator {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(x: core::num::NonZeroU32) -> Result<Self, ()> {
|
||||
let x = x.get().checked_sub(1).ok_or(())?;
|
||||
Self::try_from(x)
|
||||
}
|
||||
}
|
||||
|
||||
$crate::define_parse_and_typing_rules_for_operator! {
|
||||
$operator {
|
||||
$(
|
||||
$keywrord => $variant {
|
||||
$( immediates( $($immediate),* ); )?
|
||||
$( parameters( $($parameter),* ); )?
|
||||
result( $result );
|
||||
$( is_reduce($is_reduce); )?
|
||||
$( is_extend($is_extend); )?
|
||||
}
|
||||
)*
|
||||
}
|
||||
$( parse_cfg($parse_cfg); )?
|
||||
}
|
||||
}
|
||||
}
|
||||
97
cranelift/peepmatic/crates/traits/src/typing.rs
Normal file
97
cranelift/peepmatic/crates/traits/src/typing.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
/// A trait to represent a typing context.
|
||||
///
|
||||
/// This is used by the macro-generated operator methods that create the type
|
||||
/// variables for their immediates, parameters, and results. This trait is
|
||||
/// implemented by the concrete typing context in `peepmatic/src/verify.rs`.
|
||||
pub trait TypingContext<'a> {
|
||||
/// A source span.
|
||||
type Span: Copy;
|
||||
|
||||
/// A type variable.
|
||||
type TypeVariable;
|
||||
|
||||
/// Create a condition code type.
|
||||
fn cc(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `bNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn bNN(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iNN(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iMM` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iMM(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the CPU flags type variable.
|
||||
fn cpu_flags(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type of size one bit.
|
||||
fn b1(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the void type, used as the result of operators that branch away,
|
||||
/// or do not return anything.
|
||||
fn void(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that may be either a boolean or an integer.
|
||||
fn bool_or_int(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that can be any type T.
|
||||
///
|
||||
/// Each use of `any_t` by the same operator refers to the same type
|
||||
/// variable.
|
||||
fn any_t(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
}
|
||||
|
||||
/// The typing rules for a `TOperator` type.
|
||||
///
|
||||
/// This trait describes the types of immediates, parameters, and results of an
|
||||
/// operator type, as well as their arity.
|
||||
pub trait TypingRules {
|
||||
/// Get the result type of this operator.
|
||||
fn result_type<'a, C>(&self, span: C::Span, typing_context: &mut C) -> C::TypeVariable
|
||||
where
|
||||
C: TypingContext<'a>;
|
||||
|
||||
/// Get the number of immediates this operator has.
|
||||
fn immediates_arity(&self) -> u8;
|
||||
|
||||
/// Get the types of this operator's immediates.
|
||||
fn immediate_types<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
) where
|
||||
C: TypingContext<'a>;
|
||||
|
||||
/// Get the number of parameters this operator has.
|
||||
fn parameters_arity(&self) -> u8;
|
||||
|
||||
/// Get the types of this operator's parameters.
|
||||
fn parameter_types<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
) where
|
||||
C: TypingContext<'a>;
|
||||
|
||||
/// Is this a bit width reducing instruction?
|
||||
///
|
||||
/// E.g. Cranelift's `ireduce` instruction.
|
||||
fn is_reduce(&self) -> bool;
|
||||
|
||||
/// Is this a bit width extending instruction?
|
||||
///
|
||||
/// E.g. Cranelift's `uextend` and `sextend` instructions.
|
||||
fn is_extend(&self) -> bool;
|
||||
}
|
||||
@@ -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<DynAstRef<'a>>) {
|
||||
impl<'a, 'b, TOperator> ChildNodes<'a, 'b, TOperator> for DynAstRef<'a, TOperator> {
|
||||
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a, TOperator>>) {
|
||||
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<DynAstRef<'a>>);
|
||||
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a, TOperator>>);
|
||||
}
|
||||
|
||||
/// A trait for getting the span where an AST node was defined.
|
||||
@@ -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<Optimization<'a>>,
|
||||
pub optimizations: Vec<Optimization<'a, TOperator>>,
|
||||
}
|
||||
|
||||
/// 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<Precondition<'a>>,
|
||||
pub preconditions: Vec<Precondition<'a, TOperator>>,
|
||||
}
|
||||
|
||||
/// 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<TOperator> Hash for Integer<'_, TOperator> {
|
||||
fn hash<H>(&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<TOperator> Hash for Boolean<'_, TOperator> {
|
||||
fn hash<H>(&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<T>,
|
||||
pub operands: Vec<TOperand>,
|
||||
|
||||
#[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<ConstraintOperand<'a>>,
|
||||
pub operands: Vec<ConstraintOperand<'a, TOperator>>,
|
||||
|
||||
#[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<Rhs<'a>>,
|
||||
pub operands: Vec<Rhs<'a, TOperator>>,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[peepmatic(skip_child)]
|
||||
pub marker: PhantomData<&'a TOperator>,
|
||||
}
|
||||
|
||||
@@ -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<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> {
|
||||
pub fn automatize<TOperator>(
|
||||
opts: &linear::Optimizations<TOperator>,
|
||||
) -> Automaton<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash,
|
||||
{
|
||||
debug_assert!(crate::linear_passes::is_sorted_lexicographically(opts));
|
||||
|
||||
let mut builder = Builder::<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>>::new();
|
||||
let mut builder =
|
||||
Builder::<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>::new();
|
||||
|
||||
for opt in &opts.optimizations {
|
||||
let mut insertion = builder.insert();
|
||||
|
||||
@@ -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<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for PeepholeDotFmt<'_> {
|
||||
impl<TOperator> DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
|
||||
for PeepholeDotFmt<'_>
|
||||
where
|
||||
TOperator: Debug + TryFrom<NonZeroU32>,
|
||||
{
|
||||
fn fmt_transition(
|
||||
&self,
|
||||
w: &mut impl Write,
|
||||
@@ -26,22 +30,23 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> 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<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
||||
writeln!(w, "</font>")
|
||||
}
|
||||
|
||||
fn fmt_output(&self, w: &mut impl Write, actions: &Box<[linear::Action]>) -> io::Result<()> {
|
||||
fn fmt_output(
|
||||
&self,
|
||||
w: &mut impl Write,
|
||||
actions: &Box<[linear::Action<TOperator>]>,
|
||||
) -> io::Result<()> {
|
||||
use linear::Action::*;
|
||||
|
||||
if actions.is_empty() {
|
||||
@@ -88,11 +97,11 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
||||
match a {
|
||||
GetLhs { path } => write!(w, "get-lhs @ {}<br/>", p(path))?,
|
||||
UnaryUnquote { operator, operand } => {
|
||||
write!(w, "eval {} $rhs{}<br/>", operator, operand.0)?
|
||||
write!(w, "eval {:?} $rhs{}<br/>", operator, operand.0)?
|
||||
}
|
||||
BinaryUnquote { operator, operands } => write!(
|
||||
w,
|
||||
"eval {} $rhs{}, $rhs{}<br/>",
|
||||
"eval {:?} $rhs{}, $rhs{}<br/>",
|
||||
operator, operands[0].0, operands[1].0,
|
||||
)?,
|
||||
MakeIntegerConst {
|
||||
@@ -108,14 +117,14 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
||||
operand,
|
||||
operator,
|
||||
r#type: _,
|
||||
} => write!(w, "make {} $rhs{}<br/>", operator, operand.0,)?,
|
||||
} => write!(w, "make {:?} $rhs{}<br/>", operator, operand.0,)?,
|
||||
MakeBinaryInst {
|
||||
operator,
|
||||
operands,
|
||||
r#type: _,
|
||||
} => write!(
|
||||
w,
|
||||
"make {} $rhs{}, $rhs{}<br/>",
|
||||
"make {:?} $rhs{}, $rhs{}<br/>",
|
||||
operator, operands[0].0, operands[1].0,
|
||||
)?,
|
||||
MakeTernaryInst {
|
||||
@@ -124,7 +133,7 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
||||
r#type: _,
|
||||
} => write!(
|
||||
w,
|
||||
"make {} $rhs{}, $rhs{}, $rhs{}<br/>",
|
||||
"make {:?} $rhs{}, $rhs{}, $rhs{}<br/>",
|
||||
operator, operands[0].0, operands[1].0, operands[2].0,
|
||||
)?,
|
||||
}
|
||||
|
||||
@@ -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::<cranelift_codegen::ir::Opcode>(
|
||||
/// 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<PeepholeOptimizations> {
|
||||
pub fn compile_file<TOperator>(filename: &Path) -> anyhow::Result<PeepholeOptimizations<TOperator>>
|
||||
where
|
||||
TOperator: Copy
|
||||
+ Debug
|
||||
+ Eq
|
||||
+ Hash
|
||||
+ for<'a> wast::parser::Parse<'a>
|
||||
+ TypingRules
|
||||
+ Into<NonZeroU32>
|
||||
+ TryFrom<NonZeroU32>,
|
||||
{
|
||||
let source = fs::read_to_string(filename)?;
|
||||
compile_str(&source, filename)
|
||||
compile_str::<TOperator>(&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<PeepholeOptimizations> {
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let peep_opts = peepmatic::compile_str(
|
||||
/// let peep_opts = peepmatic::compile_str::<cranelift_codegen::ir::Opcode>(
|
||||
/// "
|
||||
/// (=> (iadd $x 0) $x)
|
||||
/// (=> (imul $x 0) 0)
|
||||
@@ -88,14 +103,27 @@ pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
|
||||
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
|
||||
/// 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<PeepholeOptimizations> {
|
||||
pub fn compile_str<TOperator>(
|
||||
source: &str,
|
||||
filename: &Path,
|
||||
) -> anyhow::Result<PeepholeOptimizations<TOperator>>
|
||||
where
|
||||
TOperator: Copy
|
||||
+ Debug
|
||||
+ Eq
|
||||
+ Hash
|
||||
+ for<'a> wast::parser::Parse<'a>
|
||||
+ TypingRules
|
||||
+ Into<NonZeroU32>
|
||||
+ TryFrom<NonZeroU32>,
|
||||
{
|
||||
let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| {
|
||||
e.set_path(filename);
|
||||
e.set_text(source);
|
||||
e
|
||||
})?;
|
||||
|
||||
let opts = wast::parser::parse::<Optimizations>(&buf).map_err(|mut e| {
|
||||
let opts = wast::parser::parse::<Optimizations<'_, TOperator>>(&buf).map_err(|mut e| {
|
||||
e.set_path(filename);
|
||||
e.set_text(source);
|
||||
e
|
||||
@@ -137,9 +165,10 @@ pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result<PeepholeOpti
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
|
||||
fn assert_compiles(path: &str) {
|
||||
match compile_file(Path::new(path)) {
|
||||
match compile_file::<TestOperator>(Path::new(path)) {
|
||||
Ok(_) => return,
|
||||
Err(e) => {
|
||||
eprintln!("error: {}", e);
|
||||
|
||||
@@ -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<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||
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<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||
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<TOperator>(
|
||||
paths: &PathInterner,
|
||||
a: &linear::Optimization,
|
||||
b: &linear::Optimization,
|
||||
a: &linear::Optimization<TOperator>,
|
||||
b: &linear::Optimization<TOperator>,
|
||||
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<TOperator>(
|
||||
paths: &PathInterner,
|
||||
a: &linear::Optimization,
|
||||
b: &linear::Optimization,
|
||||
) -> Ordering {
|
||||
a: &linear::Optimization<TOperator>,
|
||||
b: &linear::Optimization<TOperator>,
|
||||
) -> 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<TOperator>(opts: &linear::Optimizations<TOperator>) -> 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<TOperator>(
|
||||
opts: &linear::Optimizations<TOperator>,
|
||||
) -> 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<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||
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<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||
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::<Optimizations>(&buf) {
|
||||
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&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::<Optimizations>(&buf) {
|
||||
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&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)),
|
||||
(
|
||||
|
||||
@@ -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<TOperator>(opts: &Optimizations<TOperator>) -> linear::Optimizations<TOperator>
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
|
||||
{
|
||||
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<TOperator>(
|
||||
paths: &mut PathInterner,
|
||||
integers: &mut IntegerInterner,
|
||||
opt: &Optimization,
|
||||
) -> linear::Optimization {
|
||||
let mut increments: Vec<linear::Increment> = vec![];
|
||||
opt: &Optimization<TOperator>,
|
||||
) -> linear::Optimization<TOperator>
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
|
||||
{
|
||||
let mut increments: Vec<linear::Increment<_>> = 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<u8>,
|
||||
path: Vec<u8>,
|
||||
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<wast::Span, linear::RhsId>,
|
||||
}
|
||||
|
||||
impl<'a> RhsBuilder<'a> {
|
||||
impl<'a, TOperator> RhsBuilder<'a, TOperator>
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Create a new builder for the given right-hand side.
|
||||
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<TOperator>) -> 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<linear::Action>,
|
||||
lhs_id_to_path: &LhsIdToPath<TOperator>,
|
||||
actions: &mut Vec<linear::Action<TOperator>>,
|
||||
) {
|
||||
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<TOperator>,
|
||||
rhs: &Rhs<TOperator>,
|
||||
) -> linear::Action<TOperator> {
|
||||
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<TOperator> Precondition<'_, TOperator>
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
|
||||
{
|
||||
/// Convert this precondition into a `linear::Increment`.
|
||||
fn to_linear_increment(&self, lhs_id_to_path: &LhsIdToPath) -> linear::Increment {
|
||||
fn to_linear_increment(
|
||||
&self,
|
||||
lhs_id_to_path: &LhsIdToPath<TOperator>,
|
||||
) -> linear::Increment<TOperator> {
|
||||
match self.constraint {
|
||||
Constraint::IsPowerOfTwo => {
|
||||
let id = match &self.operands[0] {
|
||||
@@ -484,7 +513,10 @@ impl Precondition<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pattern<'_> {
|
||||
impl<TOperator> Pattern<'_, TOperator>
|
||||
where
|
||||
TOperator: Copy,
|
||||
{
|
||||
/// Convert this pattern into its linear match operation and the expected
|
||||
/// 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<TOperator>,
|
||||
path: PathId,
|
||||
) -> (linear::MatchOp, linear::MatchResult) {
|
||||
) -> (linear::MatchOp, linear::MatchResult)
|
||||
where
|
||||
TOperator: Into<NonZeroU32>,
|
||||
{
|
||||
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::<Optimizations>(&buf) {
|
||||
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&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<linear::Increment> = $make_expected;
|
||||
) -> Vec<linear::Increment<TestOperator>> = $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,
|
||||
|
||||
@@ -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<Self> {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
if p.peek::<ValueLiteral>() {
|
||||
if p.peek::<ValueLiteral<TOperator>>() {
|
||||
return Ok(Pattern::ValueLiteral(p.parse()?));
|
||||
}
|
||||
if p.peek::<Constant>() {
|
||||
if p.peek::<Constant<TOperator>>() {
|
||||
return Ok(Pattern::Constant(p.parse()?));
|
||||
}
|
||||
if p.peek::<Operation<Self>>() {
|
||||
if p.peek::<Operation<TOperator, Self>>() {
|
||||
return Ok(Pattern::Operation(p.parse()?));
|
||||
}
|
||||
if p.peek::<Variable>() {
|
||||
if p.peek::<Variable<TOperator>>() {
|
||||
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::<Self>::peek(c)
|
||||
ValueLiteral::<TOperator>::peek(c)
|
||||
|| Constant::<TOperator>::peek(c)
|
||||
|| Variable::<TOperator>::peek(c)
|
||||
|| Operation::<TOperator, Self>::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<Self> {
|
||||
if let Ok(b) = p.parse::<Boolean>() {
|
||||
if let Ok(b) = p.parse::<Boolean<TOperator>>() {
|
||||
return Ok(ValueLiteral::Boolean(b));
|
||||
}
|
||||
if let Ok(i) = p.parse::<Integer>() {
|
||||
if let Ok(i) = p.parse::<Integer<TOperator>>() {
|
||||
return Ok(ValueLiteral::Integer(i));
|
||||
}
|
||||
if let Ok(cc) = p.parse::<ConditionCode>() {
|
||||
if let Ok(cc) = p.parse::<ConditionCode<TOperator>>() {
|
||||
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::<TOperator>::peek(c)
|
||||
|| ConditionCode::<TOperator>::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<Self> {
|
||||
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<Self> {
|
||||
let span = p.cur_span();
|
||||
if p.parse::<tok::r#true>().is_ok() {
|
||||
@@ -244,7 +261,7 @@ impl<'a> Parse<'a> for Boolean<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Peek for Boolean<'a> {
|
||||
impl<'a, TOperator> Peek for Boolean<'a, TOperator> {
|
||||
fn peek(c: Cursor) -> bool {
|
||||
<tok::r#true as Peek>::peek(c) || <tok::r#false as Peek>::peek(c)
|
||||
}
|
||||
@@ -254,7 +271,7 @@ impl<'a> Peek for Boolean<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ConditionCode<'a> {
|
||||
impl<'a, TOperator> Parse<'a> for ConditionCode<'a, TOperator> {
|
||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
let span = p.cur_span();
|
||||
@@ -417,7 +443,7 @@ where
|
||||
});
|
||||
|
||||
let mut operands = vec![];
|
||||
while p.peek::<T>() {
|
||||
while p.peek::<TOperand>() {
|
||||
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<Self> {
|
||||
let span = p.cur_span();
|
||||
p.parens(|p| {
|
||||
let constraint = p.parse()?;
|
||||
let mut operands = vec![];
|
||||
while p.peek::<ConstraintOperand>() {
|
||||
while p.peek::<ConstraintOperand<TOperator>>() {
|
||||
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<Self> {
|
||||
if p.peek::<ValueLiteral>() {
|
||||
if p.peek::<ValueLiteral<TOperator>>() {
|
||||
return Ok(ConstraintOperand::ValueLiteral(p.parse()?));
|
||||
}
|
||||
if p.peek::<Constant>() {
|
||||
if p.peek::<Constant<TOperator>>() {
|
||||
return Ok(ConstraintOperand::Constant(p.parse()?));
|
||||
}
|
||||
if p.peek::<Variable>() {
|
||||
if p.peek::<Variable<TOperator>>() {
|
||||
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::<TOperator>::peek(c)
|
||||
|| Constant::<TOperator>::peek(c)
|
||||
|| Variable::<TOperator>::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<Self> {
|
||||
if p.peek::<ValueLiteral>() {
|
||||
if p.peek::<ValueLiteral<TOperator>>() {
|
||||
return Ok(Rhs::ValueLiteral(p.parse()?));
|
||||
}
|
||||
if p.peek::<Constant>() {
|
||||
if p.peek::<Constant<TOperator>>() {
|
||||
return Ok(Rhs::Constant(p.parse()?));
|
||||
}
|
||||
if p.peek::<Variable>() {
|
||||
if p.peek::<Variable<TOperator>>() {
|
||||
return Ok(Rhs::Variable(p.parse()?));
|
||||
}
|
||||
if p.peek::<Unquote>() {
|
||||
if p.peek::<Unquote<TOperator>>() {
|
||||
return Ok(Rhs::Unquote(p.parse()?));
|
||||
}
|
||||
if p.peek::<Operation<Self>>() {
|
||||
if p.peek::<Operation<TOperator, Self>>() {
|
||||
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::<Self>::peek(c)
|
||||
ValueLiteral::<TOperator>::peek(c)
|
||||
|| Constant::<TOperator>::peek(c)
|
||||
|| Variable::<TOperator>::peek(c)
|
||||
|| Unquote::<TOperator>::peek(c)
|
||||
|| Operation::<TOperator, Self>::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<Self> {
|
||||
let span = p.cur_span();
|
||||
p.parse::<tok::dollar>()?;
|
||||
p.parens(|p| {
|
||||
let operator = p.parse()?;
|
||||
let mut operands = vec![];
|
||||
while p.peek::<Rhs>() {
|
||||
while p.peek::<Rhs<TOperator>>() {
|
||||
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<Boolean> {
|
||||
parse_boolean<Boolean<TestOperator>> {
|
||||
ok {
|
||||
"true",
|
||||
"false",
|
||||
@@ -641,7 +680,7 @@ mod test {
|
||||
"falsezzz",
|
||||
}
|
||||
}
|
||||
parse_cc<ConditionCode> {
|
||||
parse_cc<ConditionCode<TestOperator>> {
|
||||
ok {
|
||||
"eq",
|
||||
"ne",
|
||||
@@ -661,7 +700,7 @@ mod test {
|
||||
"neq",
|
||||
}
|
||||
}
|
||||
parse_constant<Constant> {
|
||||
parse_constant<Constant<TestOperator>> {
|
||||
ok {
|
||||
"$C",
|
||||
"$C1",
|
||||
@@ -693,7 +732,7 @@ mod test {
|
||||
"imul",
|
||||
}
|
||||
}
|
||||
parse_constraint_operand<ConstraintOperand> {
|
||||
parse_constraint_operand<ConstraintOperand<TestOperator>> {
|
||||
ok {
|
||||
"1234",
|
||||
"true",
|
||||
@@ -707,7 +746,7 @@ mod test {
|
||||
"(iadd 1 2)",
|
||||
}
|
||||
}
|
||||
parse_integer<Integer> {
|
||||
parse_integer<Integer<TestOperator>> {
|
||||
ok {
|
||||
"0",
|
||||
"1",
|
||||
@@ -746,7 +785,7 @@ mod test {
|
||||
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
}
|
||||
}
|
||||
parse_lhs<Lhs> {
|
||||
parse_lhs<Lhs<TestOperator>> {
|
||||
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<Operation<Pattern>> {
|
||||
parse_operation_pattern<Operation<TestOperator, Pattern<TestOperator>>> {
|
||||
ok {
|
||||
"(iadd)",
|
||||
"(iadd 1)",
|
||||
@@ -779,7 +818,7 @@ mod test {
|
||||
"(ishl $x $(log2 $C))",
|
||||
}
|
||||
}
|
||||
parse_operation_rhs<Operation<Rhs>> {
|
||||
parse_operation_rhs<Operation<TestOperator, Rhs<TestOperator>>> {
|
||||
ok {
|
||||
"(iadd)",
|
||||
"(iadd 1)",
|
||||
@@ -793,7 +832,7 @@ mod test {
|
||||
"$CONST",
|
||||
}
|
||||
}
|
||||
parse_operator<Operator> {
|
||||
parse_operator<TestOperator> {
|
||||
ok {
|
||||
"bor",
|
||||
"iadd",
|
||||
@@ -812,7 +851,7 @@ mod test {
|
||||
"iadd{i32}",
|
||||
}
|
||||
}
|
||||
parse_optimization<Optimization> {
|
||||
parse_optimization<Optimization<TestOperator>> {
|
||||
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<Optimizations> {
|
||||
parse_optimizations<Optimizations<TestOperator>> {
|
||||
ok {
|
||||
"",
|
||||
r#"
|
||||
@@ -844,7 +883,7 @@ mod test {
|
||||
"#,
|
||||
}
|
||||
}
|
||||
parse_pattern<Pattern> {
|
||||
parse_pattern<Pattern<TestOperator>> {
|
||||
ok {
|
||||
"1234",
|
||||
"$C",
|
||||
@@ -857,7 +896,7 @@ mod test {
|
||||
"abc",
|
||||
}
|
||||
}
|
||||
parse_precondition<Precondition> {
|
||||
parse_precondition<Precondition<TestOperator>> {
|
||||
ok {
|
||||
"(is-power-of-two)",
|
||||
"(is-power-of-two $C)",
|
||||
@@ -871,7 +910,7 @@ mod test {
|
||||
"$CONST",
|
||||
}
|
||||
}
|
||||
parse_rhs<Rhs> {
|
||||
parse_rhs<Rhs<TestOperator>> {
|
||||
ok {
|
||||
"5",
|
||||
"$C",
|
||||
@@ -884,7 +923,7 @@ mod test {
|
||||
"()",
|
||||
}
|
||||
}
|
||||
parse_unquote<Unquote> {
|
||||
parse_unquote<Unquote<TestOperator>> {
|
||||
ok {
|
||||
"$(log2)",
|
||||
"$(log2 $C)",
|
||||
@@ -899,7 +938,7 @@ mod test {
|
||||
"$()",
|
||||
}
|
||||
}
|
||||
parse_value_literal<ValueLiteral> {
|
||||
parse_value_literal<ValueLiteral<TestOperator>> {
|
||||
ok {
|
||||
"12345",
|
||||
"true",
|
||||
@@ -911,7 +950,7 @@ mod test {
|
||||
"12.34",
|
||||
}
|
||||
}
|
||||
parse_variable<Variable> {
|
||||
parse_variable<Variable<TestOperator>> {
|
||||
ok {
|
||||
"$v",
|
||||
"$v1",
|
||||
|
||||
@@ -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<DynAstRef<'a>>) -> Self {
|
||||
pub fn new(start: impl Into<DynAstRef<'a, TOperator>>) -> 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<DynAstRef<'a>> for EnqueueChildren<'a, 'b>
|
||||
impl<'a, 'b, TOperator> Extend<DynAstRef<'a, TOperator>> for EnqueueChildren<'a, 'b, TOperator>
|
||||
where
|
||||
'a: 'b,
|
||||
TOperator: Copy,
|
||||
{
|
||||
fn extend<T: IntoIterator<Item = DynAstRef<'a>>>(&mut self, iter: T) {
|
||||
fn extend<T: IntoIterator<Item = DynAstRef<'a, TOperator>>>(&mut self, iter: T) {
|
||||
let iter = iter.into_iter();
|
||||
|
||||
let (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::<crate::ast::Optimizations>(&buf) {
|
||||
let ast = match wast::parser::parse::<crate::ast::Optimizations<TestOperator>>(&buf) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => panic!("expected to parse OK, got error:\n\n{}", e),
|
||||
};
|
||||
|
||||
@@ -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<T> = Result<T, VerifyError>;
|
||||
|
||||
/// Verify and type check a set of optimizations.
|
||||
pub fn verify(opts: &Optimizations) -> VerifyResult<()> {
|
||||
pub fn verify<TOperator>(opts: &Optimizations<TOperator>) -> VerifyResult<()>
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash + TypingRules,
|
||||
{
|
||||
if opts.optimizations.is_empty() {
|
||||
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<TOperator>(opts: &Optimizations<TOperator>) -> 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<TOperator>(lhs: &Lhs<TOperator>) -> 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<TOperator> {
|
||||
Var(u32),
|
||||
Const(u32),
|
||||
Integer(i64),
|
||||
Boolean(bool),
|
||||
ConditionCode(peepmatic_runtime::cc::ConditionCode),
|
||||
Operation(Operator, Option<Type>),
|
||||
Operation(TOperator, Option<Type>),
|
||||
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<Target = TypingContext<'a>> + Drop + 'b {
|
||||
) -> impl DerefMut<Target = TypingContext<'a, TOperator>> + 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<TOperator> 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<TOperator>(
|
||||
z3: &z3::Context,
|
||||
opt: &Optimization<TOperator>,
|
||||
) -> 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::<Optimizations>(&buf) {
|
||||
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&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::<Optimizations>(&buf) {
|
||||
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
|
||||
Ok(opts) => opts,
|
||||
Err(mut e) => {
|
||||
e.set_path(Path::new(stringify!($name)));
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user