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-macro \
|
||||||
--package peepmatic-runtime \
|
--package peepmatic-runtime \
|
||||||
--package peepmatic-test
|
--package peepmatic-test
|
||||||
- name: Rebuild Cranelift's peepmatic-based peephole optimizers
|
- name: Rebuild Peepmatic-based peephole optimizers and test them
|
||||||
run: |
|
run: |
|
||||||
cd cranelift/
|
cargo test \
|
||||||
cargo build --features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers'
|
--features 'enable-peepmatic cranelift-codegen/rebuild-peephole-optimizers' \
|
||||||
- name: Check that peephole optimizers are up to date
|
peepmatic
|
||||||
|
working-directory: ./cranelift
|
||||||
|
- name: Check that built peephole optimizers are up to date
|
||||||
run: git diff --exit-code
|
run: git diff --exit-code
|
||||||
- name: Test `cranelift-codegen` with `peepmatic` enabled
|
- name: Test with Peepmatic-based peephole optimizers
|
||||||
run: |
|
run: cargo test --features 'enable-peepmatic'
|
||||||
cd cranelift/
|
working-directory: ./cranelift
|
||||||
cargo test --features 'enable-peepmatic'
|
|
||||||
|
|
||||||
# Perform all tests (debug mode) for `wasmtime`. This runs stable/beta/nightly
|
# Perform all tests (debug mode) for `wasmtime`. This runs stable/beta/nightly
|
||||||
# channels of Rust as well as macOS/Linux/Windows.
|
# channels of Rust as well as macOS/Linux/Windows.
|
||||||
|
|||||||
33
Cargo.lock
generated
33
Cargo.lock
generated
@@ -386,11 +386,13 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"peepmatic",
|
"peepmatic",
|
||||||
"peepmatic-runtime",
|
"peepmatic-runtime",
|
||||||
|
"peepmatic-traits",
|
||||||
"regalloc",
|
"regalloc",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"wast 15.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1298,20 +1300,23 @@ dependencies = [
|
|||||||
"peepmatic-automata",
|
"peepmatic-automata",
|
||||||
"peepmatic-macro",
|
"peepmatic-macro",
|
||||||
"peepmatic-runtime",
|
"peepmatic-runtime",
|
||||||
|
"peepmatic-test-operator",
|
||||||
|
"peepmatic-traits",
|
||||||
|
"serde",
|
||||||
"wast 15.0.0",
|
"wast 15.0.0",
|
||||||
"z3",
|
"z3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peepmatic-automata"
|
name = "peepmatic-automata"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peepmatic-fuzzing"
|
name = "peepmatic-fuzzing"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"bincode",
|
"bincode",
|
||||||
@@ -1322,6 +1327,8 @@ dependencies = [
|
|||||||
"peepmatic-automata",
|
"peepmatic-automata",
|
||||||
"peepmatic-runtime",
|
"peepmatic-runtime",
|
||||||
"peepmatic-test",
|
"peepmatic-test",
|
||||||
|
"peepmatic-test-operator",
|
||||||
|
"peepmatic-traits",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"serde",
|
"serde",
|
||||||
"wast 15.0.0",
|
"wast 15.0.0",
|
||||||
@@ -1329,7 +1336,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peepmatic-macro"
|
name = "peepmatic-macro"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1338,13 +1345,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peepmatic-runtime"
|
name = "peepmatic-runtime"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"peepmatic-automata",
|
"peepmatic-automata",
|
||||||
"peepmatic-macro",
|
"peepmatic-test-operator",
|
||||||
|
"peepmatic-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_test",
|
"serde_test",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -1359,8 +1367,23 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"peepmatic",
|
"peepmatic",
|
||||||
"peepmatic-runtime",
|
"peepmatic-runtime",
|
||||||
|
"peepmatic-test-operator",
|
||||||
|
"peepmatic-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peepmatic-test-operator"
|
||||||
|
version = "0.66.0"
|
||||||
|
dependencies = [
|
||||||
|
"peepmatic-traits",
|
||||||
|
"serde",
|
||||||
|
"wast 15.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peepmatic-traits"
|
||||||
|
version = "0.66.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plain"
|
name = "plain"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|||||||
@@ -25,8 +25,11 @@ gimli = { version = "0.21.0", default-features = false, features = ["write"], op
|
|||||||
smallvec = { version = "1.0.0" }
|
smallvec = { version = "1.0.0" }
|
||||||
thiserror = "1.0.4"
|
thiserror = "1.0.4"
|
||||||
byteorder = { version = "1.3.2", default-features = false }
|
byteorder = { version = "1.3.2", default-features = false }
|
||||||
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.2.0" }
|
peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
|
||||||
regalloc = { version = "0.0.28" }
|
peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, version = "0.66.0" }
|
||||||
|
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.66.0" }
|
||||||
|
regalloc = "0.0.28"
|
||||||
|
wast = { version = "15.0.0", optional = true }
|
||||||
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
|
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
|
||||||
# Please don't add any unless they are essential to the task of creating binary
|
# Please don't add any unless they are essential to the task of creating binary
|
||||||
# machine code. Integration tests that need external dependencies can be
|
# machine code. Integration tests that need external dependencies can be
|
||||||
@@ -34,7 +37,6 @@ regalloc = { version = "0.0.28" }
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cranelift-codegen-meta = { path = "meta", version = "0.66.0" }
|
cranelift-codegen-meta = { path = "meta", version = "0.66.0" }
|
||||||
peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "unwind"]
|
default = ["std", "unwind"]
|
||||||
@@ -80,10 +82,10 @@ regalloc-snapshot = ["bincode", "regalloc/enable-serde"]
|
|||||||
|
|
||||||
# Recompile our optimizations that are written in the `peepmatic` DSL into a
|
# Recompile our optimizations that are written in the `peepmatic` DSL into a
|
||||||
# compact finite-state transducer automaton.
|
# compact finite-state transducer automaton.
|
||||||
rebuild-peephole-optimizers = ["peepmatic"]
|
rebuild-peephole-optimizers = ["peepmatic", "peepmatic-traits", "wast"]
|
||||||
|
|
||||||
# Enable the use of `peepmatic`-generated peephole optimizers.
|
# Enable the use of `peepmatic`-generated peephole optimizers.
|
||||||
enable-peepmatic = ["peepmatic-runtime"]
|
enable-peepmatic = ["peepmatic-runtime", "peepmatic-traits", "serde"]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
|
|||||||
@@ -90,20 +90,11 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rebuild-peephole-optimizers")]
|
#[cfg(feature = "rebuild-peephole-optimizers")]
|
||||||
rebuild_peephole_optimizers();
|
{
|
||||||
|
std::fs::write(
|
||||||
|
std::path::Path::new(&out_dir).join("CRANELIFT_CODEGEN_PATH"),
|
||||||
|
cur_dir.to_str().unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rebuild-peephole-optimizers")]
|
|
||||||
fn rebuild_peephole_optimizers() {
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
let source_path = Path::new("src").join("preopt.peepmatic");
|
|
||||||
println!("cargo:rerun-if-changed={}", source_path.display());
|
|
||||||
|
|
||||||
let preopt =
|
|
||||||
peepmatic::compile_file(&source_path).expect("failed to compile `src/preopt.peepmatic`");
|
|
||||||
|
|
||||||
preopt
|
|
||||||
.serialize_to_file(&Path::new("src").join("preopt.serialized"))
|
|
||||||
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,7 +407,11 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
|
|||||||
All instructions from all supported ISAs are present.
|
All instructions from all supported ISAs are present.
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
fmt.line("#[repr(u16)]");
|
||||||
fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
|
fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");
|
||||||
|
fmt.line(
|
||||||
|
r#"#[cfg_attr(feature = "enable-peepmatic", derive(serde::Serialize, serde::Deserialize))]"#
|
||||||
|
);
|
||||||
|
|
||||||
// We explicitly set the discriminant of the first variant to 1, which allows us to take
|
// We explicitly set the discriminant of the first variant to 1, which allows us to take
|
||||||
// advantage of the NonZero optimization, meaning that wrapping enums can use the 0
|
// advantage of the NonZero optimization, meaning that wrapping enums can use the 0
|
||||||
@@ -589,6 +593,24 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
|
|||||||
fmt.empty_line();
|
fmt.empty_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_try_from(all_inst: &AllInstructions, fmt: &mut Formatter) {
|
||||||
|
fmt.line("impl core::convert::TryFrom<u16> for Opcode {");
|
||||||
|
fmt.indent(|fmt| {
|
||||||
|
fmt.line("type Error = ();");
|
||||||
|
fmt.line("#[inline]");
|
||||||
|
fmt.line("fn try_from(x: u16) -> Result<Self, ()> {");
|
||||||
|
fmt.indent(|fmt| {
|
||||||
|
fmtln!(fmt, "if 0 < x && x <= {} {{", all_inst.len());
|
||||||
|
fmt.indent(|fmt| fmt.line("Ok(unsafe { core::mem::transmute(x) })"));
|
||||||
|
fmt.line("} else {");
|
||||||
|
fmt.indent(|fmt| fmt.line("Err(())"));
|
||||||
|
fmt.line("}");
|
||||||
|
});
|
||||||
|
fmt.line("}");
|
||||||
|
});
|
||||||
|
fmt.line("}");
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the value type constraint for an SSA value operand, where
|
/// Get the value type constraint for an SSA value operand, where
|
||||||
/// `ctrl_typevar` is the controlling type variable.
|
/// `ctrl_typevar` is the controlling type variable.
|
||||||
///
|
///
|
||||||
@@ -1147,7 +1169,10 @@ pub(crate) fn generate(
|
|||||||
gen_instruction_data_impl(&formats, &mut fmt);
|
gen_instruction_data_impl(&formats, &mut fmt);
|
||||||
fmt.empty_line();
|
fmt.empty_line();
|
||||||
gen_opcodes(all_inst, &mut fmt);
|
gen_opcodes(all_inst, &mut fmt);
|
||||||
|
fmt.empty_line();
|
||||||
gen_type_constraints(all_inst, &mut fmt);
|
gen_type_constraints(all_inst, &mut fmt);
|
||||||
|
fmt.empty_line();
|
||||||
|
gen_try_from(all_inst, &mut fmt);
|
||||||
fmt.update_file(opcode_filename, out_dir)?;
|
fmt.update_file(opcode_filename, out_dir)?;
|
||||||
|
|
||||||
// Instruction builder.
|
// Instruction builder.
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
//! directory.
|
//! directory.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use core::convert::{TryFrom, TryInto};
|
||||||
use core::fmt::{self, Display, Formatter};
|
use core::fmt::{self, Display, Formatter};
|
||||||
|
use core::num::NonZeroU32;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
@@ -69,6 +71,24 @@ impl Opcode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<NonZeroU32> for Opcode {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(x: NonZeroU32) -> Result<Self, ()> {
|
||||||
|
let x: u16 = x.get().try_into().map_err(|_| ())?;
|
||||||
|
Self::try_from(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Opcode> for NonZeroU32 {
|
||||||
|
#[inline]
|
||||||
|
fn from(op: Opcode) -> NonZeroU32 {
|
||||||
|
let x = op as u8;
|
||||||
|
NonZeroU32::new(x as u32).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since
|
// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since
|
||||||
// it critically depends on the `opcode_name()` function which is needed here anyway, it lives in
|
// it critically depends on the `opcode_name()` function which is needed here anyway, it lives in
|
||||||
// this module. This also saves us from running the build script twice to generate code for the two
|
// this module. This also saves us from running the build script twice to generate code for the two
|
||||||
|
|||||||
@@ -13,27 +13,287 @@ use cranelift_codegen_shared::condcodes::IntCC;
|
|||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
cc::ConditionCode,
|
cc::ConditionCode,
|
||||||
instruction_set::InstructionSet,
|
instruction_set::InstructionSet,
|
||||||
operator::Operator,
|
|
||||||
part::{Constant, Part},
|
part::{Constant, Part},
|
||||||
paths::Path,
|
paths::Path,
|
||||||
r#type::{BitWidth, Kind, Type},
|
r#type::{BitWidth, Kind, Type},
|
||||||
PeepholeOptimizations, PeepholeOptimizer,
|
PeepholeOptimizations, PeepholeOptimizer,
|
||||||
};
|
};
|
||||||
|
use peepmatic_traits::TypingRules;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||||
|
|
||||||
|
peepmatic_traits::define_parse_and_typing_rules_for_operator! {
|
||||||
|
Opcode {
|
||||||
|
adjust_sp_down => AdjustSpDown {
|
||||||
|
parameters(iNN);
|
||||||
|
result(void);
|
||||||
|
}
|
||||||
|
adjust_sp_down_imm => AdjustSpDownImm {
|
||||||
|
immediates(iNN);
|
||||||
|
result(void);
|
||||||
|
}
|
||||||
|
band => Band {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
band_imm => BandImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
bconst => Bconst {
|
||||||
|
immediates(b1);
|
||||||
|
result(bNN);
|
||||||
|
}
|
||||||
|
bint => Bint {
|
||||||
|
parameters(bNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
bor => Bor {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
bor_imm => BorImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
brnz => Brnz {
|
||||||
|
parameters(bool_or_int);
|
||||||
|
result(void);
|
||||||
|
}
|
||||||
|
brz => Brz {
|
||||||
|
parameters(bool_or_int);
|
||||||
|
result(void);
|
||||||
|
}
|
||||||
|
bxor => Bxor {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
bxor_imm => BxorImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
iadd => Iadd {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
iadd_imm => IaddImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
icmp => Icmp {
|
||||||
|
immediates(cc);
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(b1);
|
||||||
|
}
|
||||||
|
icmp_imm => IcmpImm {
|
||||||
|
immediates(cc, iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(b1);
|
||||||
|
}
|
||||||
|
iconst => Iconst {
|
||||||
|
immediates(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
ifcmp => Ifcmp {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(cpu_flags);
|
||||||
|
}
|
||||||
|
ifcmp_imm => IfcmpImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(cpu_flags);
|
||||||
|
}
|
||||||
|
imul => Imul {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
imul_imm => ImulImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
ireduce => Ireduce {
|
||||||
|
parameters(iNN);
|
||||||
|
result(iMM);
|
||||||
|
is_reduce(true);
|
||||||
|
}
|
||||||
|
irsub_imm => IrsubImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
ishl => Ishl {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
ishl_imm => IshlImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
isub => Isub {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
rotl => Rotl {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
rotl_imm => RotlImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
rotr => Rotr {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
rotr_imm => RotrImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
sdiv => Sdiv {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
sdiv_imm => SdivImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
select => Select {
|
||||||
|
parameters(bool_or_int, any_t, any_t);
|
||||||
|
result(any_t);
|
||||||
|
}
|
||||||
|
sextend => Sextend {
|
||||||
|
parameters(iNN);
|
||||||
|
result(iMM);
|
||||||
|
is_extend(true);
|
||||||
|
}
|
||||||
|
srem => Srem {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
srem_imm => SremImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
sshr => Sshr {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
sshr_imm => SshrImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
trapnz => Trapnz {
|
||||||
|
parameters(bool_or_int);
|
||||||
|
result(void);
|
||||||
|
}
|
||||||
|
trapz => Trapz {
|
||||||
|
parameters(bool_or_int);
|
||||||
|
result(void);
|
||||||
|
}
|
||||||
|
udiv => Udiv {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
udiv_imm => UdivImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
uextend => Uextend {
|
||||||
|
parameters(iNN);
|
||||||
|
result(iMM);
|
||||||
|
is_extend(true);
|
||||||
|
}
|
||||||
|
urem => Urem {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
urem_imm => UremImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
ushr => Ushr {
|
||||||
|
parameters(iNN, iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
ushr_imm => UshrImm {
|
||||||
|
immediates(iNN);
|
||||||
|
parameters(iNN);
|
||||||
|
result(iNN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_cfg(feature = "rebuild-peephole-optimizers");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Code required to rebuild Peepmatic-based peephole optimizers.
|
||||||
|
///
|
||||||
|
/// This module is used to scope imports and dependencies that are only required
|
||||||
|
/// for building peephole optimizers (as opposed to just using pre-built
|
||||||
|
/// peephole optimizers). This helps ensure that our regular builds using
|
||||||
|
/// pre-built peephole optimizers stay lean.
|
||||||
|
#[cfg(feature = "rebuild-peephole-optimizers")]
|
||||||
|
mod rebuild {
|
||||||
|
use super::*;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// Rebuild the `preopt.peepmatic` peephole optimizer.
|
||||||
|
///
|
||||||
|
/// Saves and overwrites the old `preopt.serialized` build and returns a
|
||||||
|
/// copy of the result.
|
||||||
|
pub fn rebuild_preopt() -> Vec<u8> {
|
||||||
|
let codegen_path = Path::new(include_str!(concat!(
|
||||||
|
env!("OUT_DIR"),
|
||||||
|
"/CRANELIFT_CODEGEN_PATH"
|
||||||
|
)));
|
||||||
|
let source_path = codegen_path.join("src").join("preopt.peepmatic");
|
||||||
|
|
||||||
|
let preopt = peepmatic::compile_file::<Opcode>(&source_path)
|
||||||
|
.expect("failed to compile `src/preopt.peepmatic`");
|
||||||
|
|
||||||
|
let serialized_path = codegen_path.join("src").join("preopt.serialized");
|
||||||
|
preopt
|
||||||
|
.serialize_to_file(&serialized_path)
|
||||||
|
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
|
||||||
|
fs::read(&serialized_path).expect("failed to read `src/preopt.serialized`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the `preopt.peepmatic` peephole optimizer.
|
/// Get the `preopt.peepmatic` peephole optimizer.
|
||||||
pub(crate) fn preopt<'a, 'b>(
|
pub(crate) fn preopt<'a, 'b>(
|
||||||
isa: &'b dyn TargetIsa,
|
isa: &'b dyn TargetIsa,
|
||||||
) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> {
|
) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> {
|
||||||
|
#[cfg(feature = "rebuild-peephole-optimizers")]
|
||||||
|
fn get_serialized() -> Cow<'static, [u8]> {
|
||||||
|
rebuild::rebuild_preopt().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rebuild-peephole-optimizers"))]
|
||||||
|
fn get_serialized() -> Cow<'static, [u8]> {
|
||||||
static SERIALIZED: &[u8] = include_bytes!("preopt.serialized");
|
static SERIALIZED: &[u8] = include_bytes!("preopt.serialized");
|
||||||
|
SERIALIZED.into()
|
||||||
|
}
|
||||||
|
|
||||||
// Once initialized, this must never be re-assigned. The initialized value
|
// Once initialized, this must never be re-assigned. The initialized value
|
||||||
// is semantically "static data" and is intentionally leaked for the whole
|
// is semantically "static data" and is intentionally leaked for the whole
|
||||||
// program's lifetime.
|
// program's lifetime.
|
||||||
static DESERIALIZED: AtomicPtr<PeepholeOptimizations> = AtomicPtr::new(ptr::null_mut());
|
static DESERIALIZED: AtomicPtr<PeepholeOptimizations<Opcode>> = AtomicPtr::new(ptr::null_mut());
|
||||||
|
|
||||||
// If `DESERIALIZED` has already been initialized, then just use it.
|
// If `DESERIALIZED` has already been initialized, then just use it.
|
||||||
let ptr = DESERIALIZED.load(Ordering::SeqCst);
|
let ptr = DESERIALIZED.load(Ordering::SeqCst);
|
||||||
@@ -46,7 +306,7 @@ pub(crate) fn preopt<'a, 'b>(
|
|||||||
// another thread could be doing the same thing concurrently, so there is a
|
// another thread could be doing the same thing concurrently, so there is a
|
||||||
// race to see who initializes `DESERIALIZED` first, and we need to be
|
// race to see who initializes `DESERIALIZED` first, and we need to be
|
||||||
// prepared to both win or lose that race.
|
// prepared to both win or lose that race.
|
||||||
let peep_opts = PeepholeOptimizations::deserialize(SERIALIZED)
|
let peep_opts = PeepholeOptimizations::deserialize(&get_serialized())
|
||||||
.expect("should always be able to deserialize `preopt.serialized`");
|
.expect("should always be able to deserialize `preopt.serialized`");
|
||||||
let peep_opts = Box::into_raw(Box::new(peep_opts));
|
let peep_opts = Box::into_raw(Box::new(peep_opts));
|
||||||
|
|
||||||
@@ -219,70 +479,6 @@ fn part_to_value(pos: &mut FuncCursor, root: Inst, part: Part<ValueOrInst>) -> O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opcode {
|
|
||||||
fn to_peepmatic_operator(&self) -> Option<Operator> {
|
|
||||||
macro_rules! convert {
|
|
||||||
( $( $op:ident $(,)* )* ) => {
|
|
||||||
match self {
|
|
||||||
$( Self::$op => Some(Operator::$op), )*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convert!(
|
|
||||||
AdjustSpDown,
|
|
||||||
AdjustSpDownImm,
|
|
||||||
Band,
|
|
||||||
BandImm,
|
|
||||||
Bconst,
|
|
||||||
Bint,
|
|
||||||
Bnot,
|
|
||||||
Bor,
|
|
||||||
BorImm,
|
|
||||||
Brnz,
|
|
||||||
Brz,
|
|
||||||
Bxor,
|
|
||||||
BxorImm,
|
|
||||||
Iadd,
|
|
||||||
IaddImm,
|
|
||||||
Icmp,
|
|
||||||
IcmpImm,
|
|
||||||
Iconst,
|
|
||||||
Ifcmp,
|
|
||||||
IfcmpImm,
|
|
||||||
Imul,
|
|
||||||
ImulImm,
|
|
||||||
Ireduce,
|
|
||||||
IrsubImm,
|
|
||||||
Ishl,
|
|
||||||
IshlImm,
|
|
||||||
Isub,
|
|
||||||
Rotl,
|
|
||||||
RotlImm,
|
|
||||||
Rotr,
|
|
||||||
RotrImm,
|
|
||||||
Sdiv,
|
|
||||||
SdivImm,
|
|
||||||
Select,
|
|
||||||
Sextend,
|
|
||||||
Srem,
|
|
||||||
SremImm,
|
|
||||||
Sshr,
|
|
||||||
SshrImm,
|
|
||||||
Trapnz,
|
|
||||||
Trapz,
|
|
||||||
Udiv,
|
|
||||||
UdivImm,
|
|
||||||
Uextend,
|
|
||||||
Urem,
|
|
||||||
UremImm,
|
|
||||||
Ushr,
|
|
||||||
UshrImm,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Constant> for Imm64 {
|
impl TryFrom<Constant> for Imm64 {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
@@ -429,6 +625,8 @@ fn peepmatic_ty_to_ir_ty(ty: Type, dfg: &DataFlowGraph, root: Inst) -> types::Ty
|
|||||||
unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||||
type Context = FuncCursor<'b>;
|
type Context = FuncCursor<'b>;
|
||||||
|
|
||||||
|
type Operator = Opcode;
|
||||||
|
|
||||||
type Instruction = ValueOrInst;
|
type Instruction = ValueOrInst;
|
||||||
|
|
||||||
fn replace_instruction(
|
fn replace_instruction(
|
||||||
@@ -496,7 +694,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
let mut part = Part::Instruction(root);
|
let mut part = Part::Instruction(root);
|
||||||
for p in path.0[1..].iter().copied() {
|
for p in path.0[1..].iter().copied() {
|
||||||
let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?;
|
let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?;
|
||||||
let operator = pos.func.dfg[inst].opcode().to_peepmatic_operator()?;
|
let operator = pos.func.dfg[inst].opcode();
|
||||||
|
|
||||||
if p < operator.immediates_arity() {
|
if p < operator.immediates_arity() {
|
||||||
part = get_immediate(&pos.func.dfg, inst, p as usize);
|
part = get_immediate(&pos.func.dfg, inst, p as usize);
|
||||||
@@ -513,16 +711,16 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
Some(part)
|
Some(part)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Operator> {
|
fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Opcode> {
|
||||||
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
|
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
|
||||||
pos.func.dfg[inst].opcode().to_peepmatic_operator()
|
Some(pos.func.dfg[inst].opcode())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_inst_1(
|
fn make_inst_1(
|
||||||
&self,
|
&self,
|
||||||
pos: &mut FuncCursor<'b>,
|
pos: &mut FuncCursor<'b>,
|
||||||
root: ValueOrInst,
|
root: ValueOrInst,
|
||||||
operator: Operator,
|
operator: Opcode,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<ValueOrInst>,
|
a: Part<ValueOrInst>,
|
||||||
) -> ValueOrInst {
|
) -> ValueOrInst {
|
||||||
@@ -530,32 +728,32 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
|
|
||||||
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
||||||
match operator {
|
match operator {
|
||||||
Operator::AdjustSpDown => {
|
Opcode::AdjustSpDown => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
pos.ins().adjust_sp_down(a).into()
|
pos.ins().adjust_sp_down(a).into()
|
||||||
}
|
}
|
||||||
Operator::AdjustSpDownImm => {
|
Opcode::AdjustSpDownImm => {
|
||||||
let c = a.unwrap_constant();
|
let c = a.unwrap_constant();
|
||||||
let imm = Imm64::try_from(c).unwrap();
|
let imm = Imm64::try_from(c).unwrap();
|
||||||
pos.ins().adjust_sp_down_imm(imm).into()
|
pos.ins().adjust_sp_down_imm(imm).into()
|
||||||
}
|
}
|
||||||
Operator::Bconst => {
|
Opcode::Bconst => {
|
||||||
let c = a.unwrap_constant();
|
let c = a.unwrap_constant();
|
||||||
let val = const_to_value(pos.ins(), c, root);
|
let val = const_to_value(pos.ins(), c, root);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Bint => {
|
Opcode::Bint => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
||||||
let val = pos.ins().bint(ty, a);
|
let val = pos.ins().bint(ty, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Bnot => {
|
Opcode::Bnot => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let val = pos.ins().bnot(a);
|
let val = pos.ins().bnot(a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Brnz => {
|
Opcode::Brnz => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
|
|
||||||
// NB: branching instructions must be the root of an
|
// NB: branching instructions must be the root of an
|
||||||
@@ -567,37 +765,37 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
|
|
||||||
pos.ins().brnz(a, block, &args).into()
|
pos.ins().brnz(a, block, &args).into()
|
||||||
}
|
}
|
||||||
Operator::Brz => {
|
Opcode::Brz => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
|
|
||||||
// See the comment in the `Operator::Brnz` match argm.
|
// See the comment in the `Opcode::Brnz` match argm.
|
||||||
let block = pos.func.dfg[root].branch_destination().unwrap();
|
let block = pos.func.dfg[root].branch_destination().unwrap();
|
||||||
let args = pos.func.dfg.inst_args(root)[1..].to_vec();
|
let args = pos.func.dfg.inst_args(root)[1..].to_vec();
|
||||||
|
|
||||||
pos.ins().brz(a, block, &args).into()
|
pos.ins().brz(a, block, &args).into()
|
||||||
}
|
}
|
||||||
Operator::Iconst => {
|
Opcode::Iconst => {
|
||||||
let a = a.unwrap_constant();
|
let a = a.unwrap_constant();
|
||||||
let val = const_to_value(pos.ins(), a, root);
|
let val = const_to_value(pos.ins(), a, root);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Ireduce => {
|
Opcode::Ireduce => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
||||||
let val = pos.ins().ireduce(ty, a);
|
let val = pos.ins().ireduce(ty, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Sextend => {
|
Opcode::Sextend => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
||||||
let val = pos.ins().sextend(ty, a);
|
let val = pos.ins().sextend(ty, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Trapnz => {
|
Opcode::Trapnz => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
|
|
||||||
// NB: similar to branching instructions (see comment in the
|
// NB: similar to branching instructions (see comment in the
|
||||||
// `Operator::Brnz` match arm) trapping instructions must be the
|
// `Opcode::Brnz` match arm) trapping instructions must be the
|
||||||
// root of an optimization's right-hand side, and we get the
|
// root of an optimization's right-hand side, and we get the
|
||||||
// trap code from the root of the left-hand side. Peepmatic
|
// trap code from the root of the left-hand side. Peepmatic
|
||||||
// doesn't currently represent trap codes.
|
// doesn't currently represent trap codes.
|
||||||
@@ -605,13 +803,13 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
|
|
||||||
pos.ins().trapnz(a, code).into()
|
pos.ins().trapnz(a, code).into()
|
||||||
}
|
}
|
||||||
Operator::Trapz => {
|
Opcode::Trapz => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
// See comment in the `Operator::Trapnz` match arm.
|
// See comment in the `Opcode::Trapnz` match arm.
|
||||||
let code = pos.func.dfg[root].trap_code().unwrap();
|
let code = pos.func.dfg[root].trap_code().unwrap();
|
||||||
pos.ins().trapz(a, code).into()
|
pos.ins().trapz(a, code).into()
|
||||||
}
|
}
|
||||||
Operator::Uextend => {
|
Opcode::Uextend => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
||||||
let val = pos.ins().uextend(ty, a);
|
let val = pos.ins().uextend(ty, a);
|
||||||
@@ -625,7 +823,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
&self,
|
&self,
|
||||||
pos: &mut FuncCursor<'b>,
|
pos: &mut FuncCursor<'b>,
|
||||||
root: ValueOrInst,
|
root: ValueOrInst,
|
||||||
operator: Operator,
|
operator: Opcode,
|
||||||
_: Type,
|
_: Type,
|
||||||
a: Part<ValueOrInst>,
|
a: Part<ValueOrInst>,
|
||||||
b: Part<ValueOrInst>,
|
b: Part<ValueOrInst>,
|
||||||
@@ -634,193 +832,193 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
|
|
||||||
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
||||||
match operator {
|
match operator {
|
||||||
Operator::Band => {
|
Opcode::Band => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().band(a, b);
|
let val = pos.ins().band(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::BandImm => {
|
Opcode::BandImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().band_imm(b, a);
|
let val = pos.ins().band_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Bor => {
|
Opcode::Bor => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().bor(a, b);
|
let val = pos.ins().bor(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::BorImm => {
|
Opcode::BorImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().bor_imm(b, a);
|
let val = pos.ins().bor_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Bxor => {
|
Opcode::Bxor => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().bxor(a, b);
|
let val = pos.ins().bxor(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::BxorImm => {
|
Opcode::BxorImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().bxor_imm(b, a);
|
let val = pos.ins().bxor_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Iadd => {
|
Opcode::Iadd => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().iadd(a, b);
|
let val = pos.ins().iadd(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::IaddImm => {
|
Opcode::IaddImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().iadd_imm(b, a);
|
let val = pos.ins().iadd_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Ifcmp => {
|
Opcode::Ifcmp => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().ifcmp(a, b);
|
let val = pos.ins().ifcmp(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::IfcmpImm => {
|
Opcode::IfcmpImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().ifcmp_imm(b, a);
|
let val = pos.ins().ifcmp_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Imul => {
|
Opcode::Imul => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().imul(a, b);
|
let val = pos.ins().imul(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::ImulImm => {
|
Opcode::ImulImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().imul_imm(b, a);
|
let val = pos.ins().imul_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::IrsubImm => {
|
Opcode::IrsubImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().irsub_imm(b, a);
|
let val = pos.ins().irsub_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Ishl => {
|
Opcode::Ishl => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().ishl(a, b);
|
let val = pos.ins().ishl(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::IshlImm => {
|
Opcode::IshlImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().ishl_imm(b, a);
|
let val = pos.ins().ishl_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Isub => {
|
Opcode::Isub => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().isub(a, b);
|
let val = pos.ins().isub(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Rotl => {
|
Opcode::Rotl => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().rotl(a, b);
|
let val = pos.ins().rotl(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::RotlImm => {
|
Opcode::RotlImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().rotl_imm(b, a);
|
let val = pos.ins().rotl_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Rotr => {
|
Opcode::Rotr => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().rotr(a, b);
|
let val = pos.ins().rotr(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::RotrImm => {
|
Opcode::RotrImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().rotr_imm(b, a);
|
let val = pos.ins().rotr_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Sdiv => {
|
Opcode::Sdiv => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().sdiv(a, b);
|
let val = pos.ins().sdiv(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::SdivImm => {
|
Opcode::SdivImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().sdiv_imm(b, a);
|
let val = pos.ins().sdiv_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Srem => {
|
Opcode::Srem => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().srem(a, b);
|
let val = pos.ins().srem(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::SremImm => {
|
Opcode::SremImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().srem_imm(b, a);
|
let val = pos.ins().srem_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Sshr => {
|
Opcode::Sshr => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().sshr(a, b);
|
let val = pos.ins().sshr(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::SshrImm => {
|
Opcode::SshrImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().sshr_imm(b, a);
|
let val = pos.ins().sshr_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Udiv => {
|
Opcode::Udiv => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().udiv(a, b);
|
let val = pos.ins().udiv(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::UdivImm => {
|
Opcode::UdivImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().udiv_imm(b, a);
|
let val = pos.ins().udiv_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Urem => {
|
Opcode::Urem => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().urem(a, b);
|
let val = pos.ins().urem(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::UremImm => {
|
Opcode::UremImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().urem_imm(b, a);
|
let val = pos.ins().urem_imm(b, a);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Ushr => {
|
Opcode::Ushr => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().ushr(a, b);
|
let val = pos.ins().ushr(a, b);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::UshrImm => {
|
Opcode::UshrImm => {
|
||||||
let a = part_to_imm64(pos, a);
|
let a = part_to_imm64(pos, a);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let val = pos.ins().ushr_imm(b, a);
|
let val = pos.ins().ushr_imm(b, a);
|
||||||
@@ -834,7 +1032,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
&self,
|
&self,
|
||||||
pos: &mut FuncCursor<'b>,
|
pos: &mut FuncCursor<'b>,
|
||||||
root: ValueOrInst,
|
root: ValueOrInst,
|
||||||
operator: Operator,
|
operator: Opcode,
|
||||||
_: Type,
|
_: Type,
|
||||||
a: Part<ValueOrInst>,
|
a: Part<ValueOrInst>,
|
||||||
b: Part<ValueOrInst>,
|
b: Part<ValueOrInst>,
|
||||||
@@ -844,7 +1042,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
|
|
||||||
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
||||||
match operator {
|
match operator {
|
||||||
Operator::Icmp => {
|
Opcode::Icmp => {
|
||||||
let cond = a.unwrap_condition_code();
|
let cond = a.unwrap_condition_code();
|
||||||
let cond = peepmatic_to_intcc(cond);
|
let cond = peepmatic_to_intcc(cond);
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
@@ -852,7 +1050,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
let val = pos.ins().icmp(cond, b, c);
|
let val = pos.ins().icmp(cond, b, c);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::IcmpImm => {
|
Opcode::IcmpImm => {
|
||||||
let cond = a.unwrap_condition_code();
|
let cond = a.unwrap_condition_code();
|
||||||
let cond = peepmatic_to_intcc(cond);
|
let cond = peepmatic_to_intcc(cond);
|
||||||
let imm = part_to_imm64(pos, b);
|
let imm = part_to_imm64(pos, b);
|
||||||
@@ -860,7 +1058,7 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
let val = pos.ins().icmp_imm(cond, c, imm);
|
let val = pos.ins().icmp_imm(cond, c, imm);
|
||||||
pos.func.dfg.value_def(val).unwrap_inst().into()
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
||||||
}
|
}
|
||||||
Operator::Select => {
|
Opcode::Select => {
|
||||||
let a = part_to_value(pos, root, a).unwrap();
|
let a = part_to_value(pos, root, a).unwrap();
|
||||||
let b = part_to_value(pos, root, b).unwrap();
|
let b = part_to_value(pos, root, b).unwrap();
|
||||||
let c = part_to_value(pos, root, c).unwrap();
|
let c = part_to_value(pos, root, c).unwrap();
|
||||||
@@ -891,3 +1089,21 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||||||
self.pointer_bits()
|
self.pointer_bits()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[cfg(feature = "x86")]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::isa::lookup;
|
||||||
|
use crate::settings::{builder, Flags};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use target_lexicon::triple;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_peepmatic_preopt() {
|
||||||
|
let isa = lookup(triple!("x86_64"))
|
||||||
|
.expect("expect x86 ISA")
|
||||||
|
.finish(Flags::new(builder()));
|
||||||
|
let _ = preopt(&*isa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -10,8 +10,13 @@ description = "DSL and compiler for generating peephole optimizers"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.27"
|
anyhow = "1.0.27"
|
||||||
peepmatic-automata = { version = "0.2.0", path = "crates/automata", features = ["dot"] }
|
peepmatic-automata = { version = "0.66.0", path = "crates/automata", features = ["dot"] }
|
||||||
peepmatic-macro = { version = "0.2.0", path = "crates/macro" }
|
peepmatic-macro = { version = "0.66.0", path = "crates/macro" }
|
||||||
peepmatic-runtime = { version = "0.2.0", path = "crates/runtime", features = ["construct"] }
|
peepmatic-runtime = { version = "0.66.0", path = "crates/runtime", features = ["construct"] }
|
||||||
|
peepmatic-traits = { version = "0.66.0", path = "crates/traits" }
|
||||||
|
serde = { version = "1.0.105", features = ["derive"] }
|
||||||
wast = "15.0.0"
|
wast = "15.0.0"
|
||||||
z3 = { version = "0.6.0", features = ["static-link-z3"] }
|
z3 = { version = "0.6.0", features = ["static-link-z3"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
peepmatic-test-operator = { version = "0.66.0", path = "crates/test-operator" }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peepmatic-automata"
|
name = "peepmatic-automata"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0 WITH LLVM-exception"
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peepmatic-fuzzing"
|
name = "peepmatic-fuzzing"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
@@ -17,6 +17,8 @@ peepmatic = { path = "../.." }
|
|||||||
peepmatic-automata = { path = "../automata", features = ["serde"] }
|
peepmatic-automata = { path = "../automata", features = ["serde"] }
|
||||||
peepmatic-runtime = { path = "../runtime", features = ["construct"] }
|
peepmatic-runtime = { path = "../runtime", features = ["construct"] }
|
||||||
peepmatic-test = { path = "../test" }
|
peepmatic-test = { path = "../test" }
|
||||||
|
peepmatic-test-operator = { path = "../test-operator" }
|
||||||
|
peepmatic-traits = { path = "../traits" }
|
||||||
rand = { version = "0.7.3", features = ["small_rng"] }
|
rand = { version = "0.7.3", features = ["small_rng"] }
|
||||||
serde = "1.0.106"
|
serde = "1.0.106"
|
||||||
wast = "15.0.0"
|
wast = "15.0.0"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Fuzz testing utilities related to AST pattern matching.
|
//! Fuzz testing utilities related to AST pattern matching.
|
||||||
|
|
||||||
use peepmatic_runtime::PeepholeOptimizations;
|
use peepmatic_runtime::PeepholeOptimizations;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
@@ -19,18 +20,18 @@ pub fn compile(data: &[u8]) {
|
|||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt = match peepmatic::compile_str(source, Path::new("fuzz")) {
|
let opt = match peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz")) {
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Should be able to serialize and deserialize the peephole optimizer.
|
// Should be able to serialize and deserialize the peephole optimizer.
|
||||||
let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK");
|
let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK");
|
||||||
let _: PeepholeOptimizations =
|
let _: PeepholeOptimizations<TestOperator> =
|
||||||
bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK");
|
bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK");
|
||||||
|
|
||||||
// Compiling the same source text again should be deterministic.
|
// Compiling the same source text again should be deterministic.
|
||||||
let opt2 = peepmatic::compile_str(source, Path::new("fuzz"))
|
let opt2 = peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz"))
|
||||||
.expect("should be able to compile source text again, if it compiled OK the first time");
|
.expect("should be able to compile source text again, if it compiled OK the first time");
|
||||||
let opt2_bytes =
|
let opt2_bytes =
|
||||||
bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK");
|
bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK");
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ use peepmatic::{
|
|||||||
};
|
};
|
||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
cc::ConditionCode,
|
cc::ConditionCode,
|
||||||
operator::TypingContext as TypingContextTrait,
|
|
||||||
part::Constant,
|
part::Constant,
|
||||||
r#type::BitWidth,
|
r#type::BitWidth,
|
||||||
r#type::{Kind, Type},
|
r#type::{Kind, Type},
|
||||||
};
|
};
|
||||||
use peepmatic_test::{Program, TestIsa};
|
use peepmatic_test::{Program, TestIsa};
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules};
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str;
|
use std::str;
|
||||||
@@ -37,7 +38,7 @@ pub fn interp(data: &[u8]) {
|
|||||||
|
|
||||||
// Okay, we know it compiles and verifies alright, so (re)parse the AST.
|
// Okay, we know it compiles and verifies alright, so (re)parse the AST.
|
||||||
let buf = wast::parser::ParseBuffer::new(&source).unwrap();
|
let buf = wast::parser::ParseBuffer::new(&source).unwrap();
|
||||||
let ast = wast::parser::parse::<Optimizations>(&buf).unwrap();
|
let ast = wast::parser::parse::<Optimizations<TestOperator>>(&buf).unwrap();
|
||||||
|
|
||||||
// And we need access to the assigned types, so re-verify it as well.
|
// And we need access to the assigned types, so re-verify it as well.
|
||||||
peepmatic::verify(&ast).unwrap();
|
peepmatic::verify(&ast).unwrap();
|
||||||
@@ -87,7 +88,7 @@ pub fn interp(data: &[u8]) {
|
|||||||
// Generate this operation's immediates.
|
// Generate this operation's immediates.
|
||||||
let mut imm_tys = vec![];
|
let mut imm_tys = vec![];
|
||||||
op.operator
|
op.operator
|
||||||
.immediate_types(&mut TypingContext, op.span(), &mut imm_tys);
|
.immediate_types((), &mut TypingContext, &mut imm_tys);
|
||||||
let imms: Vec<_> = op
|
let imms: Vec<_> = op
|
||||||
.operands
|
.operands
|
||||||
.iter()
|
.iter()
|
||||||
@@ -121,7 +122,7 @@ pub fn interp(data: &[u8]) {
|
|||||||
// this operation's arguments.
|
// this operation's arguments.
|
||||||
let mut arg_tys = vec![];
|
let mut arg_tys = vec![];
|
||||||
op.operator
|
op.operator
|
||||||
.param_types(&mut TypingContext, op.span(), &mut arg_tys);
|
.parameter_types((), &mut TypingContext, &mut arg_tys);
|
||||||
let args: Vec<_> = op
|
let args: Vec<_> = op
|
||||||
.operands
|
.operands
|
||||||
.iter()
|
.iter()
|
||||||
@@ -165,7 +166,7 @@ pub fn interp(data: &[u8]) {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let ty = match op.operator.result_type(&mut TypingContext, op.span()) {
|
let ty = match op.operator.result_type((), &mut TypingContext) {
|
||||||
TypeOrConditionCode::Type(ty) => ty,
|
TypeOrConditionCode::Type(ty) => ty,
|
||||||
TypeOrConditionCode::ConditionCode => {
|
TypeOrConditionCode::ConditionCode => {
|
||||||
unreachable!("condition codes cannot be operation results")
|
unreachable!("condition codes cannot be operation results")
|
||||||
@@ -206,41 +207,42 @@ enum TypeOrConditionCode {
|
|||||||
struct TypingContext;
|
struct TypingContext;
|
||||||
|
|
||||||
impl<'a> TypingContextTrait<'a> for TypingContext {
|
impl<'a> TypingContextTrait<'a> for TypingContext {
|
||||||
|
type Span = ();
|
||||||
type TypeVariable = TypeOrConditionCode;
|
type TypeVariable = TypeOrConditionCode;
|
||||||
|
|
||||||
fn cc(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn cc(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::ConditionCode
|
TypeOrConditionCode::ConditionCode
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bNN(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn bNN(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::b1())
|
TypeOrConditionCode::Type(Type::b1())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iNN(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn iNN(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::i32())
|
TypeOrConditionCode::Type(Type::i32())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iMM(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn iMM(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::i32())
|
TypeOrConditionCode::Type(Type::i32())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cpu_flags(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn cpu_flags(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::cpu_flags())
|
TypeOrConditionCode::Type(Type::cpu_flags())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn b1(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn b1(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::b1())
|
TypeOrConditionCode::Type(Type::b1())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn void(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::void())
|
TypeOrConditionCode::Type(Type::void())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool_or_int(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn bool_or_int(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::b1())
|
TypeOrConditionCode::Type(Type::b1())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any_t(&mut self, _: wast::Span) -> Self::TypeVariable {
|
fn any_t(&mut self, _: ()) -> Self::TypeVariable {
|
||||||
TypeOrConditionCode::Type(Type::i32())
|
TypeOrConditionCode::Type(Type::i32())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Utilities for fuzzing our DSL's parser.
|
//! Utilities for fuzzing our DSL's parser.
|
||||||
|
|
||||||
use peepmatic::Optimizations;
|
use peepmatic::Optimizations;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
/// Attempt to parse the given string as if it were a snippet of our DSL.
|
/// Attempt to parse the given string as if it were a snippet of our DSL.
|
||||||
@@ -15,7 +16,7 @@ pub fn parse(data: &[u8]) {
|
|||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = wast::parser::parse::<Optimizations>(&buf);
|
let _ = wast::parser::parse::<Optimizations<TestOperator>>(&buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peepmatic-macro"
|
name = "peepmatic-macro"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0 WITH LLVM-exception"
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ pub fn derive_child_nodes(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
|||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl #impl_generics ChildNodes<'a, 'a> for #name #ty_generics #where_clause {
|
impl #impl_generics ChildNodes<'a, 'a, TOperator> for #name #ty_generics #where_clause {
|
||||||
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a>>) {
|
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a, TOperator>>) {
|
||||||
#children
|
#children
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,12 @@ fn get_child_nodes(data: &syn::Data) -> Result<impl quote::ToTokens> {
|
|||||||
fn add_trait_bounds(mut generics: Generics) -> Generics {
|
fn add_trait_bounds(mut generics: Generics) -> Generics {
|
||||||
for param in &mut generics.params {
|
for param in &mut generics.params {
|
||||||
if let GenericParam::Type(type_param) = param {
|
if let GenericParam::Type(type_param) = param {
|
||||||
type_param.bounds.push(parse_quote!(ChildNodes<'a, 'a>));
|
if type_param.ident == "TOperator" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
type_param
|
||||||
|
.bounds
|
||||||
|
.push(parse_quote!(ChildNodes<'a, 'a, TOperator>));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generics
|
generics
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub fn derive_into_dyn_ast_ref(input: &DeriveInput) -> Result<impl quote::ToToke
|
|||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a> #where_clause {
|
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a, TOperator> #where_clause {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(x: &'a #ty #ty_generics) -> Self {
|
fn from(x: &'a #ty #ty_generics) -> Self {
|
||||||
Self::#ty(x)
|
Self::#ty(x)
|
||||||
|
|||||||
@@ -11,14 +11,8 @@ use syn::{parse_macro_input, Ident, Result};
|
|||||||
|
|
||||||
mod child_nodes;
|
mod child_nodes;
|
||||||
mod into_dyn_ast_ref;
|
mod into_dyn_ast_ref;
|
||||||
mod operator;
|
|
||||||
mod span;
|
mod span;
|
||||||
|
|
||||||
#[proc_macro_derive(PeepmaticOperator, attributes(peepmatic))]
|
|
||||||
pub fn operator(input: TokenStream) -> TokenStream {
|
|
||||||
operator::derive_operator(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(Ast, attributes(peepmatic))]
|
#[proc_macro_derive(Ast, attributes(peepmatic))]
|
||||||
pub fn derive_ast(input: TokenStream) -> TokenStream {
|
pub fn derive_ast(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|||||||
@@ -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 {
|
fn add_span_trait_bounds(mut generics: Generics) -> Generics {
|
||||||
for param in &mut generics.params {
|
for param in &mut generics.params {
|
||||||
if let GenericParam::Type(ref mut type_param) = *param {
|
if let GenericParam::Type(ref mut type_param) = *param {
|
||||||
|
if type_param.ident == "TOperator" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
type_param.bounds.push(parse_quote!(Span));
|
type_param.bounds.push(parse_quote!(Span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peepmatic-runtime"
|
name = "peepmatic-runtime"
|
||||||
version = "0.2.0"
|
version = "0.66.0"
|
||||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0 WITH LLVM-exception"
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
@@ -12,13 +12,14 @@ description = "Runtime support for peepmatic peephole optimizers"
|
|||||||
bincode = "1.2.1"
|
bincode = "1.2.1"
|
||||||
bumpalo = "3.2.0"
|
bumpalo = "3.2.0"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
peepmatic-automata = { version = "0.2.0", path = "../automata", features = ["serde"] }
|
peepmatic-automata = { version = "0.66.0", path = "../automata", features = ["serde"] }
|
||||||
peepmatic-macro = { version = "0.2.0", path = "../macro" }
|
peepmatic-traits = { version = "0.66.0", path = "../traits" }
|
||||||
serde = { version = "1.0.105", features = ["derive"] }
|
serde = { version = "1.0.105", features = ["derive"] }
|
||||||
thiserror = "1.0.15"
|
thiserror = "1.0.15"
|
||||||
wast = { version = "15.0.0", optional = true }
|
wast = { version = "15.0.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
peepmatic-test-operator = { version = "0.66.0", path = "../test-operator" }
|
||||||
serde_test = "1.0.114"
|
serde_test = "1.0.114"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
//! Interfacing with actual instructions.
|
//! Interfacing with actual instructions.
|
||||||
|
|
||||||
use crate::operator::Operator;
|
|
||||||
use crate::part::{Constant, Part};
|
use crate::part::{Constant, Part};
|
||||||
use crate::paths::Path;
|
use crate::paths::Path;
|
||||||
use crate::r#type::Type;
|
use crate::r#type::Type;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
/// A trait for interfacing with actual instruction sequences.
|
/// A trait for interfacing with actual instruction sequences.
|
||||||
///
|
///
|
||||||
@@ -32,6 +33,9 @@ pub unsafe trait InstructionSet<'a> {
|
|||||||
/// implementation.
|
/// implementation.
|
||||||
type Context;
|
type Context;
|
||||||
|
|
||||||
|
/// An operator.
|
||||||
|
type Operator: 'static + Copy + Debug + Eq + Hash + Into<NonZeroU32>;
|
||||||
|
|
||||||
/// An instruction (or identifier for an instruction).
|
/// An instruction (or identifier for an instruction).
|
||||||
type Instruction: Copy + Debug + Eq;
|
type Instruction: Copy + Debug + Eq;
|
||||||
|
|
||||||
@@ -64,10 +68,12 @@ pub unsafe trait InstructionSet<'a> {
|
|||||||
|
|
||||||
/// Get the given instruction's operator.
|
/// Get the given instruction's operator.
|
||||||
///
|
///
|
||||||
/// If the instruction's opcode does not have an associated
|
/// If the instruction isn't supported, then `None` should be returned.
|
||||||
/// `peepmatic_runtime::operator::Operator` variant (i.e. that instruction
|
fn operator(
|
||||||
/// isn't supported by `peepmatic` yet) then `None` should be returned.
|
&self,
|
||||||
fn operator(&self, context: &mut Self::Context, instr: Self::Instruction) -> Option<Operator>;
|
context: &mut Self::Context,
|
||||||
|
instr: Self::Instruction,
|
||||||
|
) -> Option<Self::Operator>;
|
||||||
|
|
||||||
/// Make a unary instruction.
|
/// Make a unary instruction.
|
||||||
///
|
///
|
||||||
@@ -76,7 +82,7 @@ pub unsafe trait InstructionSet<'a> {
|
|||||||
&self,
|
&self,
|
||||||
context: &mut Self::Context,
|
context: &mut Self::Context,
|
||||||
root: Self::Instruction,
|
root: Self::Instruction,
|
||||||
operator: Operator,
|
operator: Self::Operator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<Self::Instruction>,
|
a: Part<Self::Instruction>,
|
||||||
) -> Self::Instruction;
|
) -> Self::Instruction;
|
||||||
@@ -92,7 +98,7 @@ pub unsafe trait InstructionSet<'a> {
|
|||||||
&self,
|
&self,
|
||||||
context: &mut Self::Context,
|
context: &mut Self::Context,
|
||||||
root: Self::Instruction,
|
root: Self::Instruction,
|
||||||
operator: Operator,
|
operator: Self::Operator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<Self::Instruction>,
|
a: Part<Self::Instruction>,
|
||||||
b: Part<Self::Instruction>,
|
b: Part<Self::Instruction>,
|
||||||
@@ -108,7 +114,7 @@ pub unsafe trait InstructionSet<'a> {
|
|||||||
&self,
|
&self,
|
||||||
context: &mut Self::Context,
|
context: &mut Self::Context,
|
||||||
root: Self::Instruction,
|
root: Self::Instruction,
|
||||||
operator: Operator,
|
operator: Self::Operator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<Self::Instruction>,
|
a: Part<Self::Instruction>,
|
||||||
b: Part<Self::Instruction>,
|
b: Part<Self::Instruction>,
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ pub mod error;
|
|||||||
pub mod instruction_set;
|
pub mod instruction_set;
|
||||||
pub mod integer_interner;
|
pub mod integer_interner;
|
||||||
pub mod linear;
|
pub mod linear;
|
||||||
pub mod operator;
|
|
||||||
pub mod optimizations;
|
pub mod optimizations;
|
||||||
pub mod optimizer;
|
pub mod optimizer;
|
||||||
pub mod part;
|
pub mod part;
|
||||||
pub mod paths;
|
pub mod paths;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
|
pub mod unquote;
|
||||||
|
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
pub use optimizations::PeepholeOptimizations;
|
pub use optimizations::PeepholeOptimizations;
|
||||||
|
|||||||
@@ -7,17 +7,22 @@
|
|||||||
|
|
||||||
use crate::cc::ConditionCode;
|
use crate::cc::ConditionCode;
|
||||||
use crate::integer_interner::{IntegerId, IntegerInterner};
|
use crate::integer_interner::{IntegerId, IntegerInterner};
|
||||||
use crate::operator::{Operator, UnquoteOperator};
|
|
||||||
use crate::paths::{PathId, PathInterner};
|
use crate::paths::{PathId, PathInterner};
|
||||||
use crate::r#type::{BitWidth, Type};
|
use crate::r#type::{BitWidth, Type};
|
||||||
|
use crate::unquote::UnquoteOperator;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
/// A set of linear optimizations.
|
/// A set of linear optimizations.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Optimizations {
|
pub struct Optimizations<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// The linear optimizations.
|
/// The linear optimizations.
|
||||||
pub optimizations: Vec<Optimization>,
|
pub optimizations: Vec<Optimization<TOperator>>,
|
||||||
|
|
||||||
/// The de-duplicated paths referenced by these optimizations.
|
/// The de-duplicated paths referenced by these optimizations.
|
||||||
pub paths: PathInterner,
|
pub paths: PathInterner,
|
||||||
@@ -28,9 +33,12 @@ pub struct Optimizations {
|
|||||||
|
|
||||||
/// A linearized optimization.
|
/// A linearized optimization.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Optimization {
|
pub struct Optimization<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// The chain of increments for this optimization.
|
/// The chain of increments for this optimization.
|
||||||
pub increments: Vec<Increment>,
|
pub increments: Vec<Increment<TOperator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Match any value.
|
/// Match any value.
|
||||||
@@ -63,7 +71,10 @@ pub fn bool_to_match_result(b: bool) -> MatchResult {
|
|||||||
/// basically become a state and a transition edge out of that state in the
|
/// basically become a state and a transition edge out of that state in the
|
||||||
/// final automata.
|
/// final automata.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Increment {
|
pub struct Increment<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// The matching operation to perform.
|
/// The matching operation to perform.
|
||||||
pub operation: MatchOp,
|
pub operation: MatchOp,
|
||||||
|
|
||||||
@@ -74,7 +85,7 @@ pub struct Increment {
|
|||||||
|
|
||||||
/// Actions to perform, given that the operation resulted in the expected
|
/// Actions to perform, given that the operation resulted in the expected
|
||||||
/// value.
|
/// value.
|
||||||
pub actions: Vec<Action>,
|
pub actions: Vec<Action<TOperator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A matching operation to be performed on some Cranelift instruction as part
|
/// A matching operation to be performed on some Cranelift instruction as part
|
||||||
@@ -163,7 +174,7 @@ pub struct RhsId(pub u16);
|
|||||||
/// When evaluating actions, the `i^th` action implicitly defines the
|
/// When evaluating actions, the `i^th` action implicitly defines the
|
||||||
/// `RhsId(i)`.
|
/// `RhsId(i)`.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Action {
|
pub enum Action<TOperator> {
|
||||||
/// Reuse something from the left-hand side.
|
/// Reuse something from the left-hand side.
|
||||||
GetLhs {
|
GetLhs {
|
||||||
/// The path to the instruction or value.
|
/// The path to the instruction or value.
|
||||||
@@ -215,13 +226,13 @@ pub enum Action {
|
|||||||
/// The type of this instruction's result.
|
/// The type of this instruction's result.
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
/// The operator for this instruction.
|
/// The operator for this instruction.
|
||||||
operator: Operator,
|
operator: TOperator,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Make a binary instruction.
|
/// Make a binary instruction.
|
||||||
MakeBinaryInst {
|
MakeBinaryInst {
|
||||||
/// The opcode for this instruction.
|
/// The opcode for this instruction.
|
||||||
operator: Operator,
|
operator: TOperator,
|
||||||
/// The type of this instruction's result.
|
/// The type of this instruction's result.
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
/// The operands for this instruction.
|
/// The operands for this instruction.
|
||||||
@@ -231,7 +242,7 @@ pub enum Action {
|
|||||||
/// Make a ternary instruction.
|
/// Make a ternary instruction.
|
||||||
MakeTernaryInst {
|
MakeTernaryInst {
|
||||||
/// The opcode for this instruction.
|
/// The opcode for this instruction.
|
||||||
operator: Operator,
|
operator: TOperator,
|
||||||
/// The type of this instruction's result.
|
/// The type of this instruction's result.
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
/// The operands for this instruction.
|
/// The operands for this instruction.
|
||||||
@@ -242,6 +253,7 @@ pub enum Action {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
|
||||||
// These types all end up in the automaton, so we should take care that they
|
// These types all end up in the automaton, so we should take care that they
|
||||||
// are small and don't fill up the data cache (or take up too much
|
// are small and don't fill up the data cache (or take up too much
|
||||||
@@ -259,6 +271,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn action_size() {
|
fn action_size() {
|
||||||
assert_eq!(std::mem::size_of::<Action>(), 16);
|
assert_eq!(std::mem::size_of::<Action<TestOperator>>(), 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 crate::paths::PathInterner;
|
||||||
use peepmatic_automata::Automaton;
|
use peepmatic_automata::Automaton;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
#[cfg(feature = "construct")]
|
#[cfg(feature = "construct")]
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -19,7 +21,10 @@ use std::path::Path;
|
|||||||
/// This is the compilation result of the `peepmatic` crate, after its taken a
|
/// This is the compilation result of the `peepmatic` crate, after its taken a
|
||||||
/// bunch of optimizations written in the DSL and lowered and combined them.
|
/// bunch of optimizations written in the DSL and lowered and combined them.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct PeepholeOptimizations {
|
pub struct PeepholeOptimizations<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// The instruction paths referenced by the peephole optimizations.
|
/// The instruction paths referenced by the peephole optimizations.
|
||||||
pub paths: PathInterner,
|
pub paths: PathInterner,
|
||||||
|
|
||||||
@@ -29,12 +34,18 @@ pub struct PeepholeOptimizations {
|
|||||||
|
|
||||||
/// The underlying automata for matching optimizations' left-hand sides, and
|
/// The underlying automata for matching optimizations' left-hand sides, and
|
||||||
/// building up the corresponding right-hand side.
|
/// building up the corresponding right-hand side.
|
||||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action]>>,
|
pub automata: Automaton<MatchResult, MatchOp, Box<[Action<TOperator>]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PeepholeOptimizations {
|
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// Deserialize a `PeepholeOptimizations` from bytes.
|
/// Deserialize a `PeepholeOptimizations` from bytes.
|
||||||
pub fn deserialize(serialized: &[u8]) -> Result<Self> {
|
pub fn deserialize<'a>(serialized: &'a [u8]) -> Result<Self>
|
||||||
|
where
|
||||||
|
TOperator: serde::Deserialize<'a>,
|
||||||
|
{
|
||||||
let peep_opt: Self = bincode::deserialize(serialized)?;
|
let peep_opt: Self = bincode::deserialize(serialized)?;
|
||||||
Ok(peep_opt)
|
Ok(peep_opt)
|
||||||
}
|
}
|
||||||
@@ -43,12 +54,20 @@ impl PeepholeOptimizations {
|
|||||||
///
|
///
|
||||||
/// Requires that the `"construct"` cargo feature is enabled.
|
/// Requires that the `"construct"` cargo feature is enabled.
|
||||||
#[cfg(feature = "construct")]
|
#[cfg(feature = "construct")]
|
||||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()> {
|
pub fn serialize_to_file(&self, path: &Path) -> Result<()>
|
||||||
|
where
|
||||||
|
TOperator: serde::Serialize,
|
||||||
|
{
|
||||||
let file = fs::File::create(path)?;
|
let file = fs::File::create(path)?;
|
||||||
bincode::serialize_into(file, self)?;
|
bincode::serialize_into(file, self)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// Create a new peephole optimizer instance from this set of peephole
|
/// Create a new peephole optimizer instance from this set of peephole
|
||||||
/// optimizations.
|
/// optimizations.
|
||||||
///
|
///
|
||||||
@@ -58,9 +77,13 @@ impl PeepholeOptimizations {
|
|||||||
/// instance, rather than create a new one for each instruction. Reusing the
|
/// instance, rather than create a new one for each instruction. Reusing the
|
||||||
/// peephole optimizer instance allows the reuse of a few internal
|
/// peephole optimizer instance allows the reuse of a few internal
|
||||||
/// allocations.
|
/// allocations.
|
||||||
pub fn optimizer<'peep, 'ctx, I>(&'peep self, instr_set: I) -> PeepholeOptimizer<'peep, 'ctx, I>
|
pub fn optimizer<'peep, 'ctx, TInstructionSet>(
|
||||||
|
&'peep self,
|
||||||
|
instr_set: TInstructionSet,
|
||||||
|
) -> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||||
where
|
where
|
||||||
I: InstructionSet<'ctx>,
|
TInstructionSet: InstructionSet<'ctx, Operator = TOperator>,
|
||||||
|
TOperator: Into<std::num::NonZeroU32>,
|
||||||
{
|
{
|
||||||
PeepholeOptimizer {
|
PeepholeOptimizer {
|
||||||
peep_opt: self,
|
peep_opt: self,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
use crate::instruction_set::InstructionSet;
|
use crate::instruction_set::InstructionSet;
|
||||||
use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult};
|
use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult};
|
||||||
use crate::operator::UnquoteOperator;
|
|
||||||
use crate::optimizations::PeepholeOptimizations;
|
use crate::optimizations::PeepholeOptimizations;
|
||||||
use crate::part::{Constant, Part};
|
use crate::part::{Constant, Part};
|
||||||
use crate::r#type::{BitWidth, Type};
|
use crate::r#type::{BitWidth, Type};
|
||||||
|
use crate::unquote::UnquoteOperator;
|
||||||
use peepmatic_automata::State;
|
use peepmatic_automata::State;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
@@ -21,20 +21,20 @@ use std::num::NonZeroU32;
|
|||||||
/// Reusing an instance when applying peephole optimizations to different
|
/// Reusing an instance when applying peephole optimizations to different
|
||||||
/// instruction sequences means that you reuse internal allocations that are
|
/// instruction sequences means that you reuse internal allocations that are
|
||||||
/// used to match left-hand sides and build up right-hand sides.
|
/// used to match left-hand sides and build up right-hand sides.
|
||||||
pub struct PeepholeOptimizer<'peep, 'ctx, I>
|
pub struct PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||||
where
|
where
|
||||||
I: InstructionSet<'ctx>,
|
TInstructionSet: InstructionSet<'ctx>,
|
||||||
{
|
{
|
||||||
pub(crate) peep_opt: &'peep PeepholeOptimizations,
|
pub(crate) peep_opt: &'peep PeepholeOptimizations<TInstructionSet::Operator>,
|
||||||
pub(crate) instr_set: I,
|
pub(crate) instr_set: TInstructionSet,
|
||||||
pub(crate) right_hand_sides: Vec<Part<I::Instruction>>,
|
pub(crate) right_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
|
||||||
pub(crate) actions: Vec<Action>,
|
pub(crate) actions: Vec<Action<TInstructionSet::Operator>>,
|
||||||
pub(crate) backtracking_states: Vec<(State, usize)>,
|
pub(crate) backtracking_states: Vec<(State, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'peep, 'ctx, I> Debug for PeepholeOptimizer<'peep, 'ctx, I>
|
impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||||
where
|
where
|
||||||
I: InstructionSet<'ctx>,
|
TInstructionSet: InstructionSet<'ctx>,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let PeepholeOptimizer {
|
let PeepholeOptimizer {
|
||||||
@@ -54,9 +54,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'peep, 'ctx, I> PeepholeOptimizer<'peep, 'ctx, I>
|
impl<'peep, 'ctx, TInstructionSet> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||||
where
|
where
|
||||||
I: InstructionSet<'ctx>,
|
TInstructionSet: InstructionSet<'ctx>,
|
||||||
{
|
{
|
||||||
fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant {
|
fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant {
|
||||||
use Constant::*;
|
use Constant::*;
|
||||||
@@ -107,7 +107,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_actions(&mut self, context: &mut I::Context, root: I::Instruction) {
|
fn eval_actions(
|
||||||
|
&mut self,
|
||||||
|
context: &mut TInstructionSet::Context,
|
||||||
|
root: TInstructionSet::Instruction,
|
||||||
|
) {
|
||||||
let mut actions = mem::replace(&mut self.actions, vec![]);
|
let mut actions = mem::replace(&mut self.actions, vec![]);
|
||||||
|
|
||||||
for action in actions.drain(..) {
|
for action in actions.drain(..) {
|
||||||
@@ -272,8 +276,8 @@ where
|
|||||||
|
|
||||||
fn eval_match_op(
|
fn eval_match_op(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut I::Context,
|
context: &mut TInstructionSet::Context,
|
||||||
root: I::Instruction,
|
root: TInstructionSet::Instruction,
|
||||||
match_op: MatchOp,
|
match_op: MatchOp,
|
||||||
) -> MatchResult {
|
) -> MatchResult {
|
||||||
use crate::linear::MatchOp::*;
|
use crate::linear::MatchOp::*;
|
||||||
@@ -288,13 +292,7 @@ where
|
|||||||
.ok_or(Else)?;
|
.ok_or(Else)?;
|
||||||
let inst = part.as_instruction().ok_or(Else)?;
|
let inst = part.as_instruction().ok_or(Else)?;
|
||||||
let op = self.instr_set.operator(context, inst).ok_or(Else)?;
|
let op = self.instr_set.operator(context, inst).ok_or(Else)?;
|
||||||
let op = op as u32;
|
Ok(op.into())
|
||||||
debug_assert!(
|
|
||||||
op != 0,
|
|
||||||
"`Operator` doesn't have any variant represented
|
|
||||||
with zero"
|
|
||||||
);
|
|
||||||
Ok(unsafe { NonZeroU32::new_unchecked(op as u32) })
|
|
||||||
}
|
}
|
||||||
IsConst { path } => {
|
IsConst { path } => {
|
||||||
let path = self.peep_opt.paths.lookup(path);
|
let path = self.peep_opt.paths.lookup(path);
|
||||||
@@ -477,9 +475,9 @@ where
|
|||||||
/// untouched and `None` is returned.
|
/// untouched and `None` is returned.
|
||||||
pub fn apply_one(
|
pub fn apply_one(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut I::Context,
|
context: &mut TInstructionSet::Context,
|
||||||
root: I::Instruction,
|
root: TInstructionSet::Instruction,
|
||||||
) -> Option<I::Instruction> {
|
) -> Option<TInstructionSet::Instruction> {
|
||||||
log::trace!("PeepholeOptimizer::apply_one");
|
log::trace!("PeepholeOptimizer::apply_one");
|
||||||
|
|
||||||
self.backtracking_states.clear();
|
self.backtracking_states.clear();
|
||||||
@@ -566,7 +564,11 @@ where
|
|||||||
|
|
||||||
/// Keep applying peephole optimizations to the given instruction until none
|
/// Keep applying peephole optimizations to the given instruction until none
|
||||||
/// can be applied anymore.
|
/// can be applied anymore.
|
||||||
pub fn apply_all(&mut self, context: &mut I::Context, mut inst: I::Instruction) {
|
pub fn apply_all(
|
||||||
|
&mut self,
|
||||||
|
context: &mut TInstructionSet::Context,
|
||||||
|
mut inst: TInstructionSet::Instruction,
|
||||||
|
) {
|
||||||
loop {
|
loop {
|
||||||
if let Some(new_inst) = self.apply_one(context, inst) {
|
if let Some(new_inst) = self.apply_one(context, inst) {
|
||||||
inst = new_inst;
|
inst = new_inst;
|
||||||
|
|||||||
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"
|
log = "0.4.8"
|
||||||
peepmatic = { path = "../.." }
|
peepmatic = { path = "../.." }
|
||||||
peepmatic-runtime = { path = "../runtime" }
|
peepmatic-runtime = { path = "../runtime" }
|
||||||
|
peepmatic-test-operator = { path = "../test-operator" }
|
||||||
|
peepmatic-traits = { path = "../traits" }
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
cc::ConditionCode,
|
cc::ConditionCode,
|
||||||
instruction_set::InstructionSet,
|
instruction_set::InstructionSet,
|
||||||
operator::Operator,
|
|
||||||
part::{Constant, Part},
|
part::{Constant, Part},
|
||||||
paths::Path,
|
paths::Path,
|
||||||
r#type::{BitWidth, Kind, Type},
|
r#type::{BitWidth, Kind, Type},
|
||||||
};
|
};
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
use peepmatic_traits::TypingRules;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@@ -19,7 +20,7 @@ pub struct Instruction(pub usize);
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InstructionData {
|
pub struct InstructionData {
|
||||||
pub operator: Operator,
|
pub operator: TestOperator,
|
||||||
pub r#type: Type,
|
pub r#type: Type,
|
||||||
pub immediates: Vec<Immediate>,
|
pub immediates: Vec<Immediate>,
|
||||||
pub arguments: Vec<Instruction>,
|
pub arguments: Vec<Instruction>,
|
||||||
@@ -174,7 +175,7 @@ impl Program {
|
|||||||
|
|
||||||
pub fn new_instruction(
|
pub fn new_instruction(
|
||||||
&mut self,
|
&mut self,
|
||||||
operator: Operator,
|
operator: TestOperator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
immediates: Vec<Immediate>,
|
immediates: Vec<Immediate>,
|
||||||
arguments: Vec<Instruction>,
|
arguments: Vec<Instruction>,
|
||||||
@@ -188,11 +189,11 @@ impl Program {
|
|||||||
immediates.len(),
|
immediates.len(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
operator.params_arity() as usize,
|
operator.parameters_arity() as usize,
|
||||||
arguments.len(),
|
arguments.len(),
|
||||||
"wrong number of arguments for {:?}: expected {}, found {}",
|
"wrong number of arguments for {:?}: expected {}, found {}",
|
||||||
operator,
|
operator,
|
||||||
operator.params_arity(),
|
operator.parameters_arity(),
|
||||||
arguments.len(),
|
arguments.len(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -222,7 +223,7 @@ impl Program {
|
|||||||
assert!(!root_bit_width.is_polymorphic());
|
assert!(!root_bit_width.is_polymorphic());
|
||||||
match c {
|
match c {
|
||||||
Constant::Bool(_, bit_width) => self.new_instruction(
|
Constant::Bool(_, bit_width) => self.new_instruction(
|
||||||
Operator::Bconst,
|
TestOperator::Bconst,
|
||||||
if bit_width.is_polymorphic() {
|
if bit_width.is_polymorphic() {
|
||||||
Type {
|
Type {
|
||||||
kind: Kind::Bool,
|
kind: Kind::Bool,
|
||||||
@@ -238,7 +239,7 @@ impl Program {
|
|||||||
vec![],
|
vec![],
|
||||||
),
|
),
|
||||||
Constant::Int(_, bit_width) => self.new_instruction(
|
Constant::Int(_, bit_width) => self.new_instruction(
|
||||||
Operator::Iconst,
|
TestOperator::Iconst,
|
||||||
if bit_width.is_polymorphic() {
|
if bit_width.is_polymorphic() {
|
||||||
Type {
|
Type {
|
||||||
kind: Kind::Int,
|
kind: Kind::Int,
|
||||||
@@ -259,12 +260,12 @@ impl Program {
|
|||||||
fn instruction_to_constant(&mut self, inst: Instruction) -> Option<Constant> {
|
fn instruction_to_constant(&mut self, inst: Instruction) -> Option<Constant> {
|
||||||
match self.data(inst) {
|
match self.data(inst) {
|
||||||
InstructionData {
|
InstructionData {
|
||||||
operator: Operator::Iconst,
|
operator: TestOperator::Iconst,
|
||||||
immediates,
|
immediates,
|
||||||
..
|
..
|
||||||
} => Some(immediates[0].unwrap_constant()),
|
} => Some(immediates[0].unwrap_constant()),
|
||||||
InstructionData {
|
InstructionData {
|
||||||
operator: Operator::Bconst,
|
operator: TestOperator::Bconst,
|
||||||
immediates,
|
immediates,
|
||||||
..
|
..
|
||||||
} => Some(immediates[0].unwrap_constant()),
|
} => Some(immediates[0].unwrap_constant()),
|
||||||
@@ -310,6 +311,8 @@ pub struct TestIsa {
|
|||||||
// Unsafe because we must ensure that `instruction_result_bit_width` never
|
// Unsafe because we must ensure that `instruction_result_bit_width` never
|
||||||
// returns zero.
|
// returns zero.
|
||||||
unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||||
|
type Operator = TestOperator;
|
||||||
|
|
||||||
type Context = Program;
|
type Context = Program;
|
||||||
|
|
||||||
type Instruction = Instruction;
|
type Instruction = Instruction;
|
||||||
@@ -360,7 +363,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
Some(part)
|
Some(part)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operator(&self, program: &mut Program, instr: Instruction) -> Option<Operator> {
|
fn operator(&self, program: &mut Program, instr: Instruction) -> Option<TestOperator> {
|
||||||
log::debug!("operator({:?})", instr);
|
log::debug!("operator({:?})", instr);
|
||||||
let data = program.data(instr);
|
let data = program.data(instr);
|
||||||
Some(data.operator)
|
Some(data.operator)
|
||||||
@@ -370,7 +373,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
&self,
|
&self,
|
||||||
program: &mut Program,
|
program: &mut Program,
|
||||||
root: Instruction,
|
root: Instruction,
|
||||||
operator: Operator,
|
operator: TestOperator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<Instruction>,
|
a: Part<Instruction>,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
@@ -383,11 +386,11 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
|
|
||||||
let (imms, args) = match operator.immediates_arity() {
|
let (imms, args) = match operator.immediates_arity() {
|
||||||
0 => {
|
0 => {
|
||||||
assert_eq!(operator.params_arity(), 1);
|
assert_eq!(operator.parameters_arity(), 1);
|
||||||
(vec![], vec![program.part_to_instruction(root, a).unwrap()])
|
(vec![], vec![program.part_to_instruction(root, a).unwrap()])
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
assert_eq!(operator.params_arity(), 0);
|
assert_eq!(operator.parameters_arity(), 0);
|
||||||
(vec![program.part_to_immediate(a).unwrap()], vec![])
|
(vec![program.part_to_immediate(a).unwrap()], vec![])
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -399,7 +402,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
&self,
|
&self,
|
||||||
program: &mut Program,
|
program: &mut Program,
|
||||||
root: Instruction,
|
root: Instruction,
|
||||||
operator: Operator,
|
operator: TestOperator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<Instruction>,
|
a: Part<Instruction>,
|
||||||
b: Part<Instruction>,
|
b: Part<Instruction>,
|
||||||
@@ -414,7 +417,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
|
|
||||||
let (imms, args) = match operator.immediates_arity() {
|
let (imms, args) = match operator.immediates_arity() {
|
||||||
0 => {
|
0 => {
|
||||||
assert_eq!(operator.params_arity(), 2);
|
assert_eq!(operator.parameters_arity(), 2);
|
||||||
(
|
(
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
@@ -424,14 +427,14 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
assert_eq!(operator.params_arity(), 1);
|
assert_eq!(operator.parameters_arity(), 1);
|
||||||
(
|
(
|
||||||
vec![program.part_to_immediate(a).unwrap()],
|
vec![program.part_to_immediate(a).unwrap()],
|
||||||
vec![program.part_to_instruction(root, b).unwrap()],
|
vec![program.part_to_instruction(root, b).unwrap()],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
assert_eq!(operator.params_arity(), 0);
|
assert_eq!(operator.parameters_arity(), 0);
|
||||||
(
|
(
|
||||||
vec![
|
vec![
|
||||||
program.part_to_immediate(a).unwrap(),
|
program.part_to_immediate(a).unwrap(),
|
||||||
@@ -449,7 +452,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
&self,
|
&self,
|
||||||
program: &mut Program,
|
program: &mut Program,
|
||||||
root: Instruction,
|
root: Instruction,
|
||||||
operator: Operator,
|
operator: TestOperator,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
a: Part<Instruction>,
|
a: Part<Instruction>,
|
||||||
b: Part<Instruction>,
|
b: Part<Instruction>,
|
||||||
@@ -465,7 +468,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
);
|
);
|
||||||
let (imms, args) = match operator.immediates_arity() {
|
let (imms, args) = match operator.immediates_arity() {
|
||||||
0 => {
|
0 => {
|
||||||
assert_eq!(operator.params_arity(), 3);
|
assert_eq!(operator.parameters_arity(), 3);
|
||||||
(
|
(
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
@@ -476,7 +479,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
assert_eq!(operator.params_arity(), 2);
|
assert_eq!(operator.parameters_arity(), 2);
|
||||||
(
|
(
|
||||||
vec![program.part_to_immediate(a).unwrap()],
|
vec![program.part_to_immediate(a).unwrap()],
|
||||||
vec![
|
vec![
|
||||||
@@ -486,7 +489,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
assert_eq!(operator.params_arity(), 1);
|
assert_eq!(operator.parameters_arity(), 1);
|
||||||
(
|
(
|
||||||
vec![
|
vec![
|
||||||
program.part_to_immediate(a).unwrap(),
|
program.part_to_immediate(a).unwrap(),
|
||||||
@@ -496,7 +499,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
assert_eq!(operator.params_arity(), 0);
|
assert_eq!(operator.parameters_arity(), 0);
|
||||||
(
|
(
|
||||||
vec![
|
vec![
|
||||||
program.part_to_immediate(a).unwrap(),
|
program.part_to_immediate(a).unwrap(),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
cc::ConditionCode,
|
cc::ConditionCode,
|
||||||
operator::Operator,
|
|
||||||
part::Constant,
|
part::Constant,
|
||||||
r#type::{BitWidth, Type},
|
r#type::{BitWidth, Type},
|
||||||
};
|
};
|
||||||
use peepmatic_test::*;
|
use peepmatic_test::*;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
|
||||||
const TEST_ISA: TestIsa = TestIsa {
|
const TEST_ISA: TestIsa = TestIsa {
|
||||||
native_word_size_in_bits: 32,
|
native_word_size_in_bits: 32,
|
||||||
@@ -26,13 +26,13 @@ fn opcode() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
||||||
|
|
||||||
let new = optimizer.apply_one(&mut program, add);
|
let new = optimizer.apply_one(&mut program, add);
|
||||||
let new = new.expect("optimization should have applied");
|
let new = new.expect("optimization should have applied");
|
||||||
assert!(program.structurally_eq(new, five));
|
assert!(program.structurally_eq(new, five));
|
||||||
|
|
||||||
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, five]);
|
let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, five]);
|
||||||
let replacement = optimizer.apply_one(&mut program, add);
|
let replacement = optimizer.apply_one(&mut program, add);
|
||||||
assert!(replacement.is_none());
|
assert!(replacement.is_none());
|
||||||
}
|
}
|
||||||
@@ -45,10 +45,10 @@ fn constant() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
||||||
|
|
||||||
let expected = program.new_instruction(
|
let expected = program.new_instruction(
|
||||||
Operator::IaddImm,
|
TestOperator::IaddImm,
|
||||||
Type::i32(),
|
Type::i32(),
|
||||||
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
||||||
vec![zero],
|
vec![zero],
|
||||||
@@ -58,8 +58,8 @@ fn constant() {
|
|||||||
let new = new.expect("optimization should have applied");
|
let new = new.expect("optimization should have applied");
|
||||||
assert!(program.structurally_eq(new, expected));
|
assert!(program.structurally_eq(new, expected));
|
||||||
|
|
||||||
let mul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, zero]);
|
let mul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, zero]);
|
||||||
let add = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![mul, five]);
|
let add = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![mul, five]);
|
||||||
let replacement = optimizer.apply_one(&mut program, add);
|
let replacement = optimizer.apply_one(&mut program, add);
|
||||||
assert!(replacement.is_none());
|
assert!(replacement.is_none());
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ fn boolean() {
|
|||||||
|
|
||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let t = program.r#const(Constant::Bool(true, BitWidth::One), BitWidth::One);
|
let t = program.r#const(Constant::Bool(true, BitWidth::One), BitWidth::One);
|
||||||
let bint = program.new_instruction(Operator::Bint, Type::i1(), vec![], vec![t]);
|
let bint = program.new_instruction(TestOperator::Bint, Type::i1(), vec![], vec![t]);
|
||||||
let one = program.r#const(Constant::Int(1, BitWidth::One), BitWidth::ThirtyTwo);
|
let one = program.r#const(Constant::Int(1, BitWidth::One), BitWidth::ThirtyTwo);
|
||||||
|
|
||||||
let new = optimizer.apply_one(&mut program, bint);
|
let new = optimizer.apply_one(&mut program, bint);
|
||||||
@@ -79,7 +79,7 @@ fn boolean() {
|
|||||||
assert!(program.structurally_eq(new, one));
|
assert!(program.structurally_eq(new, one));
|
||||||
|
|
||||||
let f = program.r#const(Constant::Bool(false, BitWidth::One), BitWidth::One);
|
let f = program.r#const(Constant::Bool(false, BitWidth::One), BitWidth::One);
|
||||||
let bint = program.new_instruction(Operator::Bint, Type::i1(), vec![], vec![f]);
|
let bint = program.new_instruction(TestOperator::Bint, Type::i1(), vec![], vec![f]);
|
||||||
let replacement = optimizer.apply_one(&mut program, bint);
|
let replacement = optimizer.apply_one(&mut program, bint);
|
||||||
assert!(replacement.is_none());
|
assert!(replacement.is_none());
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ fn condition_codes() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::One);
|
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::One);
|
||||||
let icmp_eq = program.new_instruction(
|
let icmp_eq = program.new_instruction(
|
||||||
Operator::Icmp,
|
TestOperator::Icmp,
|
||||||
Type::b1(),
|
Type::b1(),
|
||||||
vec![ConditionCode::Eq.into()],
|
vec![ConditionCode::Eq.into()],
|
||||||
vec![five, five],
|
vec![five, five],
|
||||||
@@ -104,7 +104,7 @@ fn condition_codes() {
|
|||||||
assert!(program.structurally_eq(new, t));
|
assert!(program.structurally_eq(new, t));
|
||||||
|
|
||||||
let icmp_ne = program.new_instruction(
|
let icmp_ne = program.new_instruction(
|
||||||
Operator::Icmp,
|
TestOperator::Icmp,
|
||||||
Type::b1(),
|
Type::b1(),
|
||||||
vec![ConditionCode::Ne.into()],
|
vec![ConditionCode::Ne.into()],
|
||||||
vec![five, five],
|
vec![five, five],
|
||||||
@@ -128,17 +128,17 @@ fn is_power_of_two() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||||
|
|
||||||
let one = program.r#const(Constant::Int(1, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let one = program.r#const(Constant::Int(1, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let ishl = program.new_instruction(Operator::Ishl, Type::i32(), vec![], vec![five, one]);
|
let ishl = program.new_instruction(TestOperator::Ishl, Type::i32(), vec![], vec![five, one]);
|
||||||
|
|
||||||
let new = optimizer.apply_one(&mut program, imul);
|
let new = optimizer.apply_one(&mut program, imul);
|
||||||
let new = new.expect("optimization should have applied");
|
let new = new.expect("optimization should have applied");
|
||||||
assert!(program.structurally_eq(new, ishl));
|
assert!(program.structurally_eq(new, ishl));
|
||||||
|
|
||||||
let three = program.r#const(Constant::Int(3, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let three = program.r#const(Constant::Int(3, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, three]);
|
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, three]);
|
||||||
|
|
||||||
let replacement = optimizer.apply_one(&mut program, imul);
|
let replacement = optimizer.apply_one(&mut program, imul);
|
||||||
assert!(replacement.is_none());
|
assert!(replacement.is_none());
|
||||||
@@ -159,10 +159,10 @@ fn bit_width() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||||
|
|
||||||
let imul_imm = program.new_instruction(
|
let imul_imm = program.new_instruction(
|
||||||
Operator::ImulImm,
|
TestOperator::ImulImm,
|
||||||
Type::i32(),
|
Type::i32(),
|
||||||
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
||||||
vec![two],
|
vec![two],
|
||||||
@@ -174,7 +174,7 @@ fn bit_width() {
|
|||||||
|
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||||
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||||
|
|
||||||
let replacement = optimizer.apply_one(&mut program, imul);
|
let replacement = optimizer.apply_one(&mut program, imul);
|
||||||
assert!(replacement.is_none());
|
assert!(replacement.is_none());
|
||||||
@@ -195,10 +195,10 @@ fn fits_in_native_word() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||||
|
|
||||||
let imul_imm = program.new_instruction(
|
let imul_imm = program.new_instruction(
|
||||||
Operator::ImulImm,
|
TestOperator::ImulImm,
|
||||||
Type::i32(),
|
Type::i32(),
|
||||||
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
||||||
vec![two],
|
vec![two],
|
||||||
@@ -210,7 +210,7 @@ fn fits_in_native_word() {
|
|||||||
|
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||||
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||||
let imul = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![five, two]);
|
let imul = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![five, two]);
|
||||||
|
|
||||||
let replacement = optimizer.apply_one(&mut program, imul);
|
let replacement = optimizer.apply_one(&mut program, imul);
|
||||||
assert!(replacement.is_none());
|
assert!(replacement.is_none());
|
||||||
@@ -230,10 +230,10 @@ fn unquote_neg() {
|
|||||||
let mut program = Program::default();
|
let mut program = Program::default();
|
||||||
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||||
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||||
let isub = program.new_instruction(Operator::Isub, Type::i64(), vec![], vec![five, two]);
|
let isub = program.new_instruction(TestOperator::Isub, Type::i64(), vec![], vec![five, two]);
|
||||||
|
|
||||||
let iadd_imm = program.new_instruction(
|
let iadd_imm = program.new_instruction(
|
||||||
Operator::IaddImm,
|
TestOperator::IaddImm,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![Constant::Int(-2 as _, BitWidth::SixtyFour).into()],
|
vec![Constant::Int(-2 as _, BitWidth::SixtyFour).into()],
|
||||||
vec![five],
|
vec![five],
|
||||||
@@ -276,13 +276,13 @@ fn subsumption() {
|
|||||||
|
|
||||||
log::debug!("(iadd (iadd (iadd w x) y) z) => (iadd (iadd w x) (iadd y z))");
|
log::debug!("(iadd (iadd (iadd w x) y) z) => (iadd (iadd w x) (iadd y z))");
|
||||||
|
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, y]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, y]);
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, z]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, z]);
|
||||||
let expected_lhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]);
|
let expected_lhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||||
let expected_rhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]);
|
let expected_rhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]);
|
||||||
let expected = program.new_instruction(
|
let expected = program.new_instruction(
|
||||||
Operator::Iadd,
|
TestOperator::Iadd,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![],
|
vec![],
|
||||||
vec![expected_lhs, expected_rhs],
|
vec![expected_lhs, expected_rhs],
|
||||||
@@ -294,17 +294,17 @@ fn subsumption() {
|
|||||||
|
|
||||||
log::debug!("(iadd w x) => y");
|
log::debug!("(iadd w x) => y");
|
||||||
|
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||||
let new = optimizer.apply_one(&mut program, iadd);
|
let new = optimizer.apply_one(&mut program, iadd);
|
||||||
let new = new.expect("optimization should have applied");
|
let new = new.expect("optimization should have applied");
|
||||||
assert!(program.structurally_eq(new, y));
|
assert!(program.structurally_eq(new, y));
|
||||||
|
|
||||||
log::debug!("(iadd x (iadd y z)) => (iadd_imm x (iadd y z))");
|
log::debug!("(iadd x (iadd y z)) => (iadd_imm x (iadd y z))");
|
||||||
|
|
||||||
let iadd_y_z = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]);
|
let iadd_y_z = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]);
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]);
|
||||||
let iadd_imm = program.new_instruction(
|
let iadd_imm = program.new_instruction(
|
||||||
Operator::IaddImm,
|
TestOperator::IaddImm,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![Constant::Int(22, BitWidth::SixtyFour).into()],
|
vec![Constant::Int(22, BitWidth::SixtyFour).into()],
|
||||||
vec![iadd_y_z],
|
vec![iadd_y_z],
|
||||||
@@ -316,19 +316,19 @@ fn subsumption() {
|
|||||||
log::debug!("(iadd (imul_imm x 1) (imul_imm x 1)) => (ishl_imm 1 (imul_imm x 1))");
|
log::debug!("(iadd (imul_imm x 1) (imul_imm x 1)) => (ishl_imm 1 (imul_imm x 1))");
|
||||||
|
|
||||||
let imul_imm = program.new_instruction(
|
let imul_imm = program.new_instruction(
|
||||||
Operator::ImulImm,
|
TestOperator::ImulImm,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![Constant::Int(1, BitWidth::SixtyFour).into()],
|
vec![Constant::Int(1, BitWidth::SixtyFour).into()],
|
||||||
vec![x],
|
vec![x],
|
||||||
);
|
);
|
||||||
let iadd = program.new_instruction(
|
let iadd = program.new_instruction(
|
||||||
Operator::Iadd,
|
TestOperator::Iadd,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![],
|
vec![],
|
||||||
vec![imul_imm, imul_imm],
|
vec![imul_imm, imul_imm],
|
||||||
);
|
);
|
||||||
let ishl_imm = program.new_instruction(
|
let ishl_imm = program.new_instruction(
|
||||||
Operator::IshlImm,
|
TestOperator::IshlImm,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![Constant::Int(1, BitWidth::SixtyFour).into()],
|
vec![Constant::Int(1, BitWidth::SixtyFour).into()],
|
||||||
vec![imul_imm],
|
vec![imul_imm],
|
||||||
@@ -339,10 +339,10 @@ fn subsumption() {
|
|||||||
|
|
||||||
log::debug!("(iadd (imul w x) (imul y z)) does not match any optimization.");
|
log::debug!("(iadd (imul w x) (imul y z)) does not match any optimization.");
|
||||||
|
|
||||||
let imul_w_x = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![w, x]);
|
let imul_w_x = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![w, x]);
|
||||||
let imul_y_z = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![y, z]);
|
let imul_y_z = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![y, z]);
|
||||||
let iadd = program.new_instruction(
|
let iadd = program.new_instruction(
|
||||||
Operator::Iadd,
|
TestOperator::Iadd,
|
||||||
Type::i64(),
|
Type::i64(),
|
||||||
vec![],
|
vec![],
|
||||||
vec![imul_w_x, imul_y_z],
|
vec![imul_w_x, imul_y_z],
|
||||||
@@ -363,9 +363,9 @@ fn polymorphic_bit_widths() {
|
|||||||
|
|
||||||
let x = program.r#const(Constant::Int(42, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let x = program.r#const(Constant::Int(42, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let y = program.r#const(Constant::Int(420, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
let y = program.r#const(Constant::Int(420, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![x, y]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![x, y]);
|
||||||
let iadd_imm = program.new_instruction(
|
let iadd_imm = program.new_instruction(
|
||||||
Operator::IaddImm,
|
TestOperator::IaddImm,
|
||||||
Type::i32(),
|
Type::i32(),
|
||||||
vec![Constant::Int(42, BitWidth::ThirtyTwo).into()],
|
vec![Constant::Int(42, BitWidth::ThirtyTwo).into()],
|
||||||
vec![y],
|
vec![y],
|
||||||
@@ -379,9 +379,9 @@ fn polymorphic_bit_widths() {
|
|||||||
|
|
||||||
let x = program.r#const(Constant::Int(42, BitWidth::Sixteen), BitWidth::Sixteen);
|
let x = program.r#const(Constant::Int(42, BitWidth::Sixteen), BitWidth::Sixteen);
|
||||||
let y = program.r#const(Constant::Int(420, BitWidth::Sixteen), BitWidth::Sixteen);
|
let y = program.r#const(Constant::Int(420, BitWidth::Sixteen), BitWidth::Sixteen);
|
||||||
let iadd = program.new_instruction(Operator::Iadd, Type::i16(), vec![], vec![x, y]);
|
let iadd = program.new_instruction(TestOperator::Iadd, Type::i16(), vec![], vec![x, y]);
|
||||||
let iadd_imm = program.new_instruction(
|
let iadd_imm = program.new_instruction(
|
||||||
Operator::IaddImm,
|
TestOperator::IaddImm,
|
||||||
Type::i16(),
|
Type::i16(),
|
||||||
vec![Constant::Int(42, BitWidth::Sixteen).into()],
|
vec![Constant::Int(42, BitWidth::Sixteen).into()],
|
||||||
vec![y],
|
vec![y],
|
||||||
|
|||||||
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_macro::Ast;
|
||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
operator::{Operator, UnquoteOperator},
|
|
||||||
r#type::{BitWidth, Type},
|
r#type::{BitWidth, Type},
|
||||||
|
unquote::UnquoteOperator,
|
||||||
};
|
};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@@ -32,58 +32,58 @@ use wast::Id;
|
|||||||
|
|
||||||
/// A reference to any AST node.
|
/// A reference to any AST node.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum DynAstRef<'a> {
|
pub enum DynAstRef<'a, TOperator> {
|
||||||
/// A reference to an `Optimizations`.
|
/// A reference to an `Optimizations`.
|
||||||
Optimizations(&'a Optimizations<'a>),
|
Optimizations(&'a Optimizations<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to an `Optimization`.
|
/// A reference to an `Optimization`.
|
||||||
Optimization(&'a Optimization<'a>),
|
Optimization(&'a Optimization<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to an `Lhs`.
|
/// A reference to an `Lhs`.
|
||||||
Lhs(&'a Lhs<'a>),
|
Lhs(&'a Lhs<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to an `Rhs`.
|
/// A reference to an `Rhs`.
|
||||||
Rhs(&'a Rhs<'a>),
|
Rhs(&'a Rhs<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `Pattern`.
|
/// A reference to a `Pattern`.
|
||||||
Pattern(&'a Pattern<'a>),
|
Pattern(&'a Pattern<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `Precondition`.
|
/// A reference to a `Precondition`.
|
||||||
Precondition(&'a Precondition<'a>),
|
Precondition(&'a Precondition<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `ConstraintOperand`.
|
/// A reference to a `ConstraintOperand`.
|
||||||
ConstraintOperand(&'a ConstraintOperand<'a>),
|
ConstraintOperand(&'a ConstraintOperand<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `ValueLiteral`.
|
/// A reference to a `ValueLiteral`.
|
||||||
ValueLiteral(&'a ValueLiteral<'a>),
|
ValueLiteral(&'a ValueLiteral<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `Constant`.
|
/// A reference to a `Constant`.
|
||||||
Constant(&'a Constant<'a>),
|
Constant(&'a Constant<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `PatternOperation`.
|
/// A reference to a `PatternOperation`.
|
||||||
PatternOperation(&'a Operation<'a, Pattern<'a>>),
|
PatternOperation(&'a Operation<'a, TOperator, Pattern<'a, TOperator>>),
|
||||||
|
|
||||||
/// A reference to a `Variable`.
|
/// A reference to a `Variable`.
|
||||||
Variable(&'a Variable<'a>),
|
Variable(&'a Variable<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to an `Integer`.
|
/// A reference to an `Integer`.
|
||||||
Integer(&'a Integer<'a>),
|
Integer(&'a Integer<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `Boolean`.
|
/// A reference to a `Boolean`.
|
||||||
Boolean(&'a Boolean<'a>),
|
Boolean(&'a Boolean<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to a `ConditionCode`.
|
/// A reference to a `ConditionCode`.
|
||||||
ConditionCode(&'a ConditionCode<'a>),
|
ConditionCode(&'a ConditionCode<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to an `Unquote`.
|
/// A reference to an `Unquote`.
|
||||||
Unquote(&'a Unquote<'a>),
|
Unquote(&'a Unquote<'a, TOperator>),
|
||||||
|
|
||||||
/// A reference to an `RhsOperation`.
|
/// A reference to an `RhsOperation`.
|
||||||
RhsOperation(&'a Operation<'a, Rhs<'a>>),
|
RhsOperation(&'a Operation<'a, TOperator, Rhs<'a, TOperator>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ChildNodes<'a, 'b> for DynAstRef<'a> {
|
impl<'a, 'b, TOperator> ChildNodes<'a, 'b, TOperator> for DynAstRef<'a, TOperator> {
|
||||||
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a>>) {
|
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a, TOperator>>) {
|
||||||
match self {
|
match self {
|
||||||
Self::Optimizations(x) => x.child_nodes(sink),
|
Self::Optimizations(x) => x.child_nodes(sink),
|
||||||
Self::Optimization(x) => x.child_nodes(sink),
|
Self::Optimization(x) => x.child_nodes(sink),
|
||||||
@@ -118,23 +118,28 @@ impl<'a, 'b> ChildNodes<'a, 'b> for DynAstRef<'a> {
|
|||||||
/// This trait is blanked implemented for everything that does those three
|
/// This trait is blanked implemented for everything that does those three
|
||||||
/// things, and in practice those three thrings are all implemented by the
|
/// things, and in practice those three thrings are all implemented by the
|
||||||
/// `derive(Ast)` macro.
|
/// `derive(Ast)` macro.
|
||||||
pub trait Ast<'a>: 'a + ChildNodes<'a, 'a> + Span
|
pub trait Ast<'a, TOperator>: 'a + ChildNodes<'a, 'a, TOperator> + Span
|
||||||
where
|
where
|
||||||
DynAstRef<'a>: From<&'a Self>,
|
DynAstRef<'a, TOperator>: From<&'a Self>,
|
||||||
|
TOperator: 'a,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Ast<'a> for T
|
impl<'a, T, TOperator> Ast<'a, TOperator> for T
|
||||||
where
|
where
|
||||||
T: 'a + ?Sized + ChildNodes<'a, 'a> + Span,
|
T: 'a + ?Sized + ChildNodes<'a, 'a, TOperator> + Span,
|
||||||
DynAstRef<'a>: From<&'a Self>,
|
DynAstRef<'a, TOperator>: From<&'a Self>,
|
||||||
|
TOperator: 'a,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerate the child AST nodes of a given node.
|
/// Enumerate the child AST nodes of a given node.
|
||||||
pub trait ChildNodes<'a, 'b> {
|
pub trait ChildNodes<'a, 'b, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'a,
|
||||||
|
{
|
||||||
/// Get each of this AST node's children, in order.
|
/// Get each of this AST node's children, in order.
|
||||||
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a>>);
|
fn child_nodes(&'b self, sink: &mut impl Extend<DynAstRef<'a, TOperator>>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for getting the span where an AST node was defined.
|
/// A trait for getting the span where an AST node was defined.
|
||||||
@@ -147,30 +152,30 @@ pub trait Span {
|
|||||||
///
|
///
|
||||||
/// This is the root AST node.
|
/// This is the root AST node.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Optimizations<'a> {
|
pub struct Optimizations<'a, TOperator> {
|
||||||
/// Where these `Optimizations` were defined.
|
/// Where these `Optimizations` were defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
|
|
||||||
/// The optimizations.
|
/// The optimizations.
|
||||||
#[peepmatic(flatten)]
|
#[peepmatic(flatten)]
|
||||||
pub optimizations: Vec<Optimization<'a>>,
|
pub optimizations: Vec<Optimization<'a, TOperator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A complete optimization: a left-hand side to match against and a right-hand
|
/// A complete optimization: a left-hand side to match against and a right-hand
|
||||||
/// side replacement.
|
/// side replacement.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Optimization<'a> {
|
pub struct Optimization<'a, TOperator> {
|
||||||
/// Where this `Optimization` was defined.
|
/// Where this `Optimization` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
|
|
||||||
/// The left-hand side that matches when this optimization applies.
|
/// The left-hand side that matches when this optimization applies.
|
||||||
pub lhs: Lhs<'a>,
|
pub lhs: Lhs<'a, TOperator>,
|
||||||
|
|
||||||
/// The new sequence of instructions to replace an old sequence that matches
|
/// The new sequence of instructions to replace an old sequence that matches
|
||||||
/// the left-hand side with.
|
/// the left-hand side with.
|
||||||
pub rhs: Rhs<'a>,
|
pub rhs: Rhs<'a, TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A left-hand side describes what is required for a particular optimization to
|
/// A left-hand side describes what is required for a particular optimization to
|
||||||
@@ -180,58 +185,58 @@ pub struct Optimization<'a> {
|
|||||||
/// candidate instruction sequences, and zero or more preconditions that add
|
/// candidate instruction sequences, and zero or more preconditions that add
|
||||||
/// additional constraints upon instruction sequences matched by the pattern.
|
/// additional constraints upon instruction sequences matched by the pattern.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Lhs<'a> {
|
pub struct Lhs<'a, TOperator> {
|
||||||
/// Where this `Lhs` was defined.
|
/// Where this `Lhs` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
|
|
||||||
/// A pattern that describes sequences of instructions to match.
|
/// A pattern that describes sequences of instructions to match.
|
||||||
pub pattern: Pattern<'a>,
|
pub pattern: Pattern<'a, TOperator>,
|
||||||
|
|
||||||
/// Additional constraints that a match must satisfy in addition to
|
/// Additional constraints that a match must satisfy in addition to
|
||||||
/// structually matching the pattern, e.g. some constant must be a power of
|
/// structually matching the pattern, e.g. some constant must be a power of
|
||||||
/// two.
|
/// two.
|
||||||
#[peepmatic(flatten)]
|
#[peepmatic(flatten)]
|
||||||
pub preconditions: Vec<Precondition<'a>>,
|
pub preconditions: Vec<Precondition<'a, TOperator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A structural pattern, potentially with wildcard variables for matching whole
|
/// A structural pattern, potentially with wildcard variables for matching whole
|
||||||
/// subtrees.
|
/// subtrees.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub enum Pattern<'a> {
|
pub enum Pattern<'a, TOperator> {
|
||||||
/// A specific value. These are written as `1234` or `0x1234` or `true` or
|
/// A specific value. These are written as `1234` or `0x1234` or `true` or
|
||||||
/// `false`.
|
/// `false`.
|
||||||
ValueLiteral(ValueLiteral<'a>),
|
ValueLiteral(ValueLiteral<'a, TOperator>),
|
||||||
|
|
||||||
/// A constant that matches any constant value. This subsumes value
|
/// A constant that matches any constant value. This subsumes value
|
||||||
/// patterns. These are upper-case identifiers like `$C`.
|
/// patterns. These are upper-case identifiers like `$C`.
|
||||||
Constant(Constant<'a>),
|
Constant(Constant<'a, TOperator>),
|
||||||
|
|
||||||
/// An operation pattern with zero or more operand patterns. These are
|
/// An operation pattern with zero or more operand patterns. These are
|
||||||
/// s-expressions like `(iadd $x $y)`.
|
/// s-expressions like `(iadd $x $y)`.
|
||||||
Operation(Operation<'a, Pattern<'a>>),
|
Operation(Operation<'a, TOperator, Pattern<'a, TOperator>>),
|
||||||
|
|
||||||
/// A variable that matches any kind of subexpression. This subsumes all
|
/// A variable that matches any kind of subexpression. This subsumes all
|
||||||
/// other patterns. These are lower-case identifiers like `$x`.
|
/// other patterns. These are lower-case identifiers like `$x`.
|
||||||
Variable(Variable<'a>),
|
Variable(Variable<'a, TOperator>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An integer or boolean value literal.
|
/// An integer or boolean value literal.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub enum ValueLiteral<'a> {
|
pub enum ValueLiteral<'a, TOperator> {
|
||||||
/// An integer value.
|
/// An integer value.
|
||||||
Integer(Integer<'a>),
|
Integer(Integer<'a, TOperator>),
|
||||||
|
|
||||||
/// A boolean value: `true` or `false`.
|
/// A boolean value: `true` or `false`.
|
||||||
Boolean(Boolean<'a>),
|
Boolean(Boolean<'a, TOperator>),
|
||||||
|
|
||||||
/// A condition code: `eq`, `ne`, etc...
|
/// A condition code: `eq`, `ne`, etc...
|
||||||
ConditionCode(ConditionCode<'a>),
|
ConditionCode(ConditionCode<'a, TOperator>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An integer literal.
|
/// An integer literal.
|
||||||
#[derive(Debug, PartialEq, Eq, Ast)]
|
#[derive(Debug, PartialEq, Eq, Ast)]
|
||||||
pub struct Integer<'a> {
|
pub struct Integer<'a, TOperator> {
|
||||||
/// Where this `Integer` was defined.
|
/// Where this `Integer` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -255,10 +260,10 @@ pub struct Integer<'a> {
|
|||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub marker: PhantomData<&'a ()>,
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Integer<'_> {
|
impl<TOperator> Hash for Integer<'_, TOperator> {
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where
|
where
|
||||||
H: Hasher,
|
H: Hasher,
|
||||||
@@ -278,7 +283,7 @@ impl Hash for Integer<'_> {
|
|||||||
|
|
||||||
/// A boolean literal.
|
/// A boolean literal.
|
||||||
#[derive(Debug, PartialEq, Eq, Ast)]
|
#[derive(Debug, PartialEq, Eq, Ast)]
|
||||||
pub struct Boolean<'a> {
|
pub struct Boolean<'a, TOperator> {
|
||||||
/// Where this `Boolean` was defined.
|
/// Where this `Boolean` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -299,10 +304,10 @@ pub struct Boolean<'a> {
|
|||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub marker: PhantomData<&'a ()>,
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Boolean<'_> {
|
impl<TOperator> Hash for Boolean<'_, TOperator> {
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where
|
where
|
||||||
H: Hasher,
|
H: Hasher,
|
||||||
@@ -322,7 +327,7 @@ impl Hash for Boolean<'_> {
|
|||||||
|
|
||||||
/// A condition code.
|
/// A condition code.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct ConditionCode<'a> {
|
pub struct ConditionCode<'a, TOperator> {
|
||||||
/// Where this `ConditionCode` was defined.
|
/// Where this `ConditionCode` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -333,7 +338,7 @@ pub struct ConditionCode<'a> {
|
|||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub marker: PhantomData<&'a ()>,
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A symbolic constant.
|
/// A symbolic constant.
|
||||||
@@ -341,7 +346,7 @@ pub struct ConditionCode<'a> {
|
|||||||
/// These are identifiers containing uppercase letters: `$C`, `$MY-CONST`,
|
/// These are identifiers containing uppercase letters: `$C`, `$MY-CONST`,
|
||||||
/// `$CONSTANT1`.
|
/// `$CONSTANT1`.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Constant<'a> {
|
pub struct Constant<'a, TOperator> {
|
||||||
/// Where this `Constant` was defined.
|
/// Where this `Constant` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -349,6 +354,10 @@ pub struct Constant<'a> {
|
|||||||
/// This constant's identifier.
|
/// This constant's identifier.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub id: Id<'a>,
|
pub id: Id<'a>,
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[peepmatic(skip_child)]
|
||||||
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A variable that matches any subtree.
|
/// A variable that matches any subtree.
|
||||||
@@ -357,7 +366,7 @@ pub struct Constant<'a> {
|
|||||||
/// being the same as each other occurrence as well, e.g. `(iadd $x $x)` matches
|
/// being the same as each other occurrence as well, e.g. `(iadd $x $x)` matches
|
||||||
/// `(iadd 5 5)` but not `(iadd 1 2)`.
|
/// `(iadd 5 5)` but not `(iadd 1 2)`.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Variable<'a> {
|
pub struct Variable<'a, TOperator> {
|
||||||
/// Where this `Variable` was defined.
|
/// Where this `Variable` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -365,15 +374,20 @@ pub struct Variable<'a> {
|
|||||||
/// This variable's identifier.
|
/// This variable's identifier.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub id: Id<'a>,
|
pub id: Id<'a>,
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[peepmatic(skip_child)]
|
||||||
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An operation with an operator, and operands of type `T`.
|
/// An operation with an operator, and operands of type `T`.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
#[peepmatic(no_into_dyn_node)]
|
#[peepmatic(no_into_dyn_node)]
|
||||||
pub struct Operation<'a, T>
|
pub struct Operation<'a, TOperator, TOperand>
|
||||||
where
|
where
|
||||||
T: 'a + Ast<'a>,
|
TOperator: 'a,
|
||||||
DynAstRef<'a>: From<&'a T>,
|
TOperand: 'a + Ast<'a, TOperator>,
|
||||||
|
DynAstRef<'a, TOperator>: From<&'a TOperand>,
|
||||||
{
|
{
|
||||||
/// The span where this operation was written.
|
/// The span where this operation was written.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
@@ -381,7 +395,7 @@ where
|
|||||||
|
|
||||||
/// The operator for this operation, e.g. `imul` or `iadd`.
|
/// The operator for this operation, e.g. `imul` or `iadd`.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub operator: Operator,
|
pub operator: TOperator,
|
||||||
|
|
||||||
/// An optional ascribed or inferred type for the operator.
|
/// An optional ascribed or inferred type for the operator.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
@@ -393,23 +407,27 @@ where
|
|||||||
/// the operands. When `Operation is used in a right-hand side replacement,
|
/// the operands. When `Operation is used in a right-hand side replacement,
|
||||||
/// these are the sub-replacements for the operands.
|
/// these are the sub-replacements for the operands.
|
||||||
#[peepmatic(flatten)]
|
#[peepmatic(flatten)]
|
||||||
pub operands: Vec<T>,
|
pub operands: Vec<TOperand>,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub marker: PhantomData<&'a ()>,
|
pub marker: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Operation<'a, Pattern<'a>>> for DynAstRef<'a> {
|
impl<'a, TOperator> From<&'a Operation<'a, TOperator, Pattern<'a, TOperator>>>
|
||||||
|
for DynAstRef<'a, TOperator>
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(o: &'a Operation<'a, Pattern<'a>>) -> DynAstRef<'a> {
|
fn from(o: &'a Operation<'a, TOperator, Pattern<'a, TOperator>>) -> DynAstRef<'a, TOperator> {
|
||||||
DynAstRef::PatternOperation(o)
|
DynAstRef::PatternOperation(o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Operation<'a, Rhs<'a>>> for DynAstRef<'a> {
|
impl<'a, TOperator> From<&'a Operation<'a, TOperator, Rhs<'a, TOperator>>>
|
||||||
|
for DynAstRef<'a, TOperator>
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(o: &'a Operation<'a, Rhs<'a>>) -> DynAstRef<'a> {
|
fn from(o: &'a Operation<'a, TOperator, Rhs<'a, TOperator>>) -> DynAstRef<'a, TOperator> {
|
||||||
DynAstRef::RhsOperation(o)
|
DynAstRef::RhsOperation(o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,7 +435,7 @@ impl<'a> From<&'a Operation<'a, Rhs<'a>>> for DynAstRef<'a> {
|
|||||||
/// A precondition adds additional constraints to a pattern, such as "$C must be
|
/// A precondition adds additional constraints to a pattern, such as "$C must be
|
||||||
/// a power of two".
|
/// a power of two".
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Precondition<'a> {
|
pub struct Precondition<'a, TOperator> {
|
||||||
/// Where this `Precondition` was defined.
|
/// Where this `Precondition` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -428,7 +446,11 @@ pub struct Precondition<'a> {
|
|||||||
|
|
||||||
/// The operands of the constraint.
|
/// The operands of the constraint.
|
||||||
#[peepmatic(flatten)]
|
#[peepmatic(flatten)]
|
||||||
pub operands: Vec<ConstraintOperand<'a>>,
|
pub operands: Vec<ConstraintOperand<'a, TOperator>>,
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[peepmatic(skip_child)]
|
||||||
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contraint operators.
|
/// Contraint operators.
|
||||||
@@ -446,40 +468,40 @@ pub enum Constraint {
|
|||||||
|
|
||||||
/// An operand of a precondition's constraint.
|
/// An operand of a precondition's constraint.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub enum ConstraintOperand<'a> {
|
pub enum ConstraintOperand<'a, TOperator> {
|
||||||
/// A value literal operand.
|
/// A value literal operand.
|
||||||
ValueLiteral(ValueLiteral<'a>),
|
ValueLiteral(ValueLiteral<'a, TOperator>),
|
||||||
|
|
||||||
/// A constant operand.
|
/// A constant operand.
|
||||||
Constant(Constant<'a>),
|
Constant(Constant<'a, TOperator>),
|
||||||
|
|
||||||
/// A variable operand.
|
/// A variable operand.
|
||||||
Variable(Variable<'a>),
|
Variable(Variable<'a, TOperator>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The right-hand side of an optimization that contains the instructions to
|
/// The right-hand side of an optimization that contains the instructions to
|
||||||
/// replace any matched left-hand side with.
|
/// replace any matched left-hand side with.
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub enum Rhs<'a> {
|
pub enum Rhs<'a, TOperator> {
|
||||||
/// A value literal right-hand side.
|
/// A value literal right-hand side.
|
||||||
ValueLiteral(ValueLiteral<'a>),
|
ValueLiteral(ValueLiteral<'a, TOperator>),
|
||||||
|
|
||||||
/// A constant right-hand side (the constant must have been matched and
|
/// A constant right-hand side (the constant must have been matched and
|
||||||
/// bound in the left-hand side's pattern).
|
/// bound in the left-hand side's pattern).
|
||||||
Constant(Constant<'a>),
|
Constant(Constant<'a, TOperator>),
|
||||||
|
|
||||||
/// A variable right-hand side (the variable must have been matched and
|
/// A variable right-hand side (the variable must have been matched and
|
||||||
/// bound in the left-hand side's pattern).
|
/// bound in the left-hand side's pattern).
|
||||||
Variable(Variable<'a>),
|
Variable(Variable<'a, TOperator>),
|
||||||
|
|
||||||
/// An unquote expression that is evaluated while replacing the left-hand
|
/// An unquote expression that is evaluated while replacing the left-hand
|
||||||
/// side with the right-hand side. The result of the evaluation is used in
|
/// side with the right-hand side. The result of the evaluation is used in
|
||||||
/// the replacement.
|
/// the replacement.
|
||||||
Unquote(Unquote<'a>),
|
Unquote(Unquote<'a, TOperator>),
|
||||||
|
|
||||||
/// A compound right-hand side consisting of an operation and subsequent
|
/// A compound right-hand side consisting of an operation and subsequent
|
||||||
/// right-hand side operands.
|
/// right-hand side operands.
|
||||||
Operation(Operation<'a, Rhs<'a>>),
|
Operation(Operation<'a, TOperator, Rhs<'a, TOperator>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An unquote operation.
|
/// An unquote operation.
|
||||||
@@ -493,7 +515,7 @@ pub enum Rhs<'a> {
|
|||||||
/// instructions that match its left-hand side with the compile-time result of
|
/// instructions that match its left-hand side with the compile-time result of
|
||||||
/// `log2($C)` (the left-hand side must match and bind the constant `$C`).
|
/// `log2($C)` (the left-hand side must match and bind the constant `$C`).
|
||||||
#[derive(Debug, Ast)]
|
#[derive(Debug, Ast)]
|
||||||
pub struct Unquote<'a> {
|
pub struct Unquote<'a, TOperator> {
|
||||||
/// Where this `Unquote` was defined.
|
/// Where this `Unquote` was defined.
|
||||||
#[peepmatic(skip_child)]
|
#[peepmatic(skip_child)]
|
||||||
pub span: wast::Span,
|
pub span: wast::Span,
|
||||||
@@ -504,5 +526,9 @@ pub struct Unquote<'a> {
|
|||||||
|
|
||||||
/// The operands for this unquote operation.
|
/// The operands for this unquote operation.
|
||||||
#[peepmatic(flatten)]
|
#[peepmatic(flatten)]
|
||||||
pub operands: Vec<Rhs<'a>>,
|
pub operands: Vec<Rhs<'a, TOperator>>,
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[peepmatic(skip_child)]
|
||||||
|
pub marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,20 @@
|
|||||||
|
|
||||||
use peepmatic_automata::{Automaton, Builder};
|
use peepmatic_automata::{Automaton, Builder};
|
||||||
use peepmatic_runtime::linear;
|
use peepmatic_runtime::linear;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// Construct an automaton from a set of linear optimizations.
|
/// Construct an automaton from a set of linear optimizations.
|
||||||
pub fn automatize(
|
pub fn automatize<TOperator>(
|
||||||
opts: &linear::Optimizations,
|
opts: &linear::Optimizations<TOperator>,
|
||||||
) -> Automaton<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> {
|
) -> Automaton<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
debug_assert!(crate::linear_passes::is_sorted_lexicographically(opts));
|
debug_assert!(crate::linear_passes::is_sorted_lexicographically(opts));
|
||||||
|
|
||||||
let mut builder = Builder::<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>>::new();
|
let mut builder =
|
||||||
|
Builder::<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>::new();
|
||||||
|
|
||||||
for opt in &opts.optimizations {
|
for opt in &opts.optimizations {
|
||||||
let mut insertion = builder.insert();
|
let mut insertion = builder.insert();
|
||||||
|
|||||||
@@ -7,17 +7,21 @@ use peepmatic_runtime::{
|
|||||||
cc::ConditionCode,
|
cc::ConditionCode,
|
||||||
integer_interner::{IntegerId, IntegerInterner},
|
integer_interner::{IntegerId, IntegerInterner},
|
||||||
linear,
|
linear,
|
||||||
operator::Operator,
|
|
||||||
paths::{PathId, PathInterner},
|
paths::{PathId, PathInterner},
|
||||||
};
|
};
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::num::NonZeroU16;
|
use std::num::{NonZeroU16, NonZeroU32};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct PeepholeDotFmt<'a>(pub(crate) &'a PathInterner, pub(crate) &'a IntegerInterner);
|
pub(crate) struct PeepholeDotFmt<'a>(pub(crate) &'a PathInterner, pub(crate) &'a IntegerInterner);
|
||||||
|
|
||||||
impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for PeepholeDotFmt<'_> {
|
impl<TOperator> DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
|
||||||
|
for PeepholeDotFmt<'_>
|
||||||
|
where
|
||||||
|
TOperator: Debug + TryFrom<NonZeroU32>,
|
||||||
|
{
|
||||||
fn fmt_transition(
|
fn fmt_transition(
|
||||||
&self,
|
&self,
|
||||||
w: &mut impl Write,
|
w: &mut impl Write,
|
||||||
@@ -26,22 +30,23 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
|||||||
_to: Option<&linear::MatchOp>,
|
_to: Option<&linear::MatchOp>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let from = from.expect("we should have match op for every state");
|
let from = from.expect("we should have match op for every state");
|
||||||
if let Some(x) = input.ok().map(|x| x.get()) {
|
if let Some(x) = input.ok() {
|
||||||
match from {
|
match from {
|
||||||
linear::MatchOp::Opcode { .. } => {
|
linear::MatchOp::Opcode { .. } => {
|
||||||
let opcode =
|
let opcode = TOperator::try_from(x)
|
||||||
Operator::try_from(x).expect("we shouldn't generate non-opcode edges");
|
.map_err(|_| ())
|
||||||
write!(w, "{}", opcode)
|
.expect("we shouldn't generate non-opcode edges");
|
||||||
|
write!(w, "{:?}", opcode)
|
||||||
}
|
}
|
||||||
linear::MatchOp::ConditionCode { .. } => {
|
linear::MatchOp::ConditionCode { .. } => {
|
||||||
let cc =
|
let cc = ConditionCode::try_from(x.get())
|
||||||
ConditionCode::try_from(x).expect("we shouldn't generate non-CC edges");
|
.expect("we shouldn't generate non-CC edges");
|
||||||
write!(w, "{}", cc)
|
write!(w, "{}", cc)
|
||||||
}
|
}
|
||||||
linear::MatchOp::IntegerValue { .. } => {
|
linear::MatchOp::IntegerValue { .. } => {
|
||||||
let x = self
|
let x = self.1.lookup(IntegerId(
|
||||||
.1
|
NonZeroU16::new(x.get().try_into().unwrap()).unwrap(),
|
||||||
.lookup(IntegerId(NonZeroU16::new(x.try_into().unwrap()).unwrap()));
|
));
|
||||||
write!(w, "{}", x)
|
write!(w, "{}", x)
|
||||||
}
|
}
|
||||||
_ => write!(w, "Ok({})", x),
|
_ => write!(w, "Ok({})", x),
|
||||||
@@ -73,7 +78,11 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
|||||||
writeln!(w, "</font>")
|
writeln!(w, "</font>")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_output(&self, w: &mut impl Write, actions: &Box<[linear::Action]>) -> io::Result<()> {
|
fn fmt_output(
|
||||||
|
&self,
|
||||||
|
w: &mut impl Write,
|
||||||
|
actions: &Box<[linear::Action<TOperator>]>,
|
||||||
|
) -> io::Result<()> {
|
||||||
use linear::Action::*;
|
use linear::Action::*;
|
||||||
|
|
||||||
if actions.is_empty() {
|
if actions.is_empty() {
|
||||||
@@ -88,11 +97,11 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
|||||||
match a {
|
match a {
|
||||||
GetLhs { path } => write!(w, "get-lhs @ {}<br/>", p(path))?,
|
GetLhs { path } => write!(w, "get-lhs @ {}<br/>", p(path))?,
|
||||||
UnaryUnquote { operator, operand } => {
|
UnaryUnquote { operator, operand } => {
|
||||||
write!(w, "eval {} $rhs{}<br/>", operator, operand.0)?
|
write!(w, "eval {:?} $rhs{}<br/>", operator, operand.0)?
|
||||||
}
|
}
|
||||||
BinaryUnquote { operator, operands } => write!(
|
BinaryUnquote { operator, operands } => write!(
|
||||||
w,
|
w,
|
||||||
"eval {} $rhs{}, $rhs{}<br/>",
|
"eval {:?} $rhs{}, $rhs{}<br/>",
|
||||||
operator, operands[0].0, operands[1].0,
|
operator, operands[0].0, operands[1].0,
|
||||||
)?,
|
)?,
|
||||||
MakeIntegerConst {
|
MakeIntegerConst {
|
||||||
@@ -108,14 +117,14 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
|||||||
operand,
|
operand,
|
||||||
operator,
|
operator,
|
||||||
r#type: _,
|
r#type: _,
|
||||||
} => write!(w, "make {} $rhs{}<br/>", operator, operand.0,)?,
|
} => write!(w, "make {:?} $rhs{}<br/>", operator, operand.0,)?,
|
||||||
MakeBinaryInst {
|
MakeBinaryInst {
|
||||||
operator,
|
operator,
|
||||||
operands,
|
operands,
|
||||||
r#type: _,
|
r#type: _,
|
||||||
} => write!(
|
} => write!(
|
||||||
w,
|
w,
|
||||||
"make {} $rhs{}, $rhs{}<br/>",
|
"make {:?} $rhs{}, $rhs{}<br/>",
|
||||||
operator, operands[0].0, operands[1].0,
|
operator, operands[0].0, operands[1].0,
|
||||||
)?,
|
)?,
|
||||||
MakeTernaryInst {
|
MakeTernaryInst {
|
||||||
@@ -124,7 +133,7 @@ impl DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action]>> for Pee
|
|||||||
r#type: _,
|
r#type: _,
|
||||||
} => write!(
|
} => write!(
|
||||||
w,
|
w,
|
||||||
"make {} $rhs{}, $rhs{}, $rhs{}<br/>",
|
"make {:?} $rhs{}, $rhs{}, $rhs{}<br/>",
|
||||||
operator, operands[0].0, operands[1].0, operands[2].0,
|
operator, operands[0].0, operands[1].0, operands[2].0,
|
||||||
)?,
|
)?,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,20 +23,25 @@ pub use self::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use peepmatic_runtime::PeepholeOptimizations;
|
use peepmatic_runtime::PeepholeOptimizations;
|
||||||
|
use peepmatic_traits::TypingRules;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Compile the given DSL file into a compact peephole optimizations automaton!
|
/// Compile the given DSL file into a compact peephole optimizations automaton!
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```ignore
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
/// use std::path::Path;
|
/// use std::path::Path;
|
||||||
///
|
///
|
||||||
/// let peep_opts = peepmatic::compile_file(Path::new(
|
/// let peep_opts = peepmatic::compile_file::<cranelift_codegen::ir::Opcode>(
|
||||||
/// "path/to/optimizations.peepmatic"
|
/// Path::new("path/to/optimizations.peepmatic")
|
||||||
/// ))?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// // Use the peephole optimizations or serialize them into bytes here...
|
/// // Use the peephole optimizations or serialize them into bytes here...
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
@@ -49,9 +54,19 @@ use std::path::Path;
|
|||||||
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
|
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
|
||||||
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
|
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
|
||||||
/// peephole optimizer's automaton will be written to that file path.
|
/// peephole optimizer's automaton will be written to that file path.
|
||||||
pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
|
pub fn compile_file<TOperator>(filename: &Path) -> anyhow::Result<PeepholeOptimizations<TOperator>>
|
||||||
|
where
|
||||||
|
TOperator: Copy
|
||||||
|
+ Debug
|
||||||
|
+ Eq
|
||||||
|
+ Hash
|
||||||
|
+ for<'a> wast::parser::Parse<'a>
|
||||||
|
+ TypingRules
|
||||||
|
+ Into<NonZeroU32>
|
||||||
|
+ TryFrom<NonZeroU32>,
|
||||||
|
{
|
||||||
let source = fs::read_to_string(filename)?;
|
let source = fs::read_to_string(filename)?;
|
||||||
compile_str(&source, filename)
|
compile_str::<TOperator>(&source, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile the given DSL source text down into a compact peephole optimizations
|
/// Compile the given DSL source text down into a compact peephole optimizations
|
||||||
@@ -64,11 +79,11 @@ pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
|
|||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```ignore
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
/// use std::path::Path;
|
/// use std::path::Path;
|
||||||
///
|
///
|
||||||
/// let peep_opts = peepmatic::compile_str(
|
/// let peep_opts = peepmatic::compile_str::<cranelift_codegen::ir::Opcode>(
|
||||||
/// "
|
/// "
|
||||||
/// (=> (iadd $x 0) $x)
|
/// (=> (iadd $x 0) $x)
|
||||||
/// (=> (imul $x 0) 0)
|
/// (=> (imul $x 0) 0)
|
||||||
@@ -88,14 +103,27 @@ pub fn compile_file(filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
|
|||||||
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
|
/// `PEEPMATIC_DOT` environment variable to a file path. A [GraphViz
|
||||||
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
|
/// Dot]((https://graphviz.gitlab.io/_pages/pdf/dotguide.pdf)) file showing the
|
||||||
/// peephole optimizer's automaton will be written to that file path.
|
/// peephole optimizer's automaton will be written to that file path.
|
||||||
pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result<PeepholeOptimizations> {
|
pub fn compile_str<TOperator>(
|
||||||
|
source: &str,
|
||||||
|
filename: &Path,
|
||||||
|
) -> anyhow::Result<PeepholeOptimizations<TOperator>>
|
||||||
|
where
|
||||||
|
TOperator: Copy
|
||||||
|
+ Debug
|
||||||
|
+ Eq
|
||||||
|
+ Hash
|
||||||
|
+ for<'a> wast::parser::Parse<'a>
|
||||||
|
+ TypingRules
|
||||||
|
+ Into<NonZeroU32>
|
||||||
|
+ TryFrom<NonZeroU32>,
|
||||||
|
{
|
||||||
let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| {
|
let buf = wast::parser::ParseBuffer::new(source).map_err(|mut e| {
|
||||||
e.set_path(filename);
|
e.set_path(filename);
|
||||||
e.set_text(source);
|
e.set_text(source);
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let opts = wast::parser::parse::<Optimizations>(&buf).map_err(|mut e| {
|
let opts = wast::parser::parse::<Optimizations<'_, TOperator>>(&buf).map_err(|mut e| {
|
||||||
e.set_path(filename);
|
e.set_path(filename);
|
||||||
e.set_text(source);
|
e.set_text(source);
|
||||||
e
|
e
|
||||||
@@ -137,9 +165,10 @@ pub fn compile_str(source: &str, filename: &Path) -> anyhow::Result<PeepholeOpti
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
|
||||||
fn assert_compiles(path: &str) {
|
fn assert_compiles(path: &str) {
|
||||||
match compile_file(Path::new(path)) {
|
match compile_file::<TestOperator>(Path::new(path)) {
|
||||||
Ok(_) => return,
|
Ok(_) => return,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("error: {}", e);
|
eprintln!("error: {}", e);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use peepmatic_runtime::{
|
|||||||
paths::{PathId, PathInterner},
|
paths::{PathId, PathInterner},
|
||||||
};
|
};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// Sort a set of optimizations from least to most general.
|
/// Sort a set of optimizations from least to most general.
|
||||||
///
|
///
|
||||||
@@ -25,7 +27,10 @@ use std::cmp::Ordering;
|
|||||||
///
|
///
|
||||||
/// and we are matching `(imul 4 (..))`, then we want to apply the second
|
/// and we are matching `(imul 4 (..))`, then we want to apply the second
|
||||||
/// optimization, because it is more specific than the first.
|
/// optimization, because it is more specific than the first.
|
||||||
pub fn sort_least_to_most_general(opts: &mut linear::Optimizations) {
|
pub fn sort_least_to_most_general<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
let linear::Optimizations {
|
let linear::Optimizations {
|
||||||
ref mut optimizations,
|
ref mut optimizations,
|
||||||
ref paths,
|
ref paths,
|
||||||
@@ -41,7 +46,10 @@ pub fn sort_least_to_most_general(opts: &mut linear::Optimizations) {
|
|||||||
/// Sort the linear optimizations lexicographically.
|
/// Sort the linear optimizations lexicographically.
|
||||||
///
|
///
|
||||||
/// This sort order is required for automata construction.
|
/// This sort order is required for automata construction.
|
||||||
pub fn sort_lexicographically(opts: &mut linear::Optimizations) {
|
pub fn sort_lexicographically<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
let linear::Optimizations {
|
let linear::Optimizations {
|
||||||
ref mut optimizations,
|
ref mut optimizations,
|
||||||
ref paths,
|
ref paths,
|
||||||
@@ -53,12 +61,15 @@ pub fn sort_lexicographically(opts: &mut linear::Optimizations) {
|
|||||||
.sort_by(|a, b| compare_optimizations(paths, a, b, |a_len, b_len| a_len.cmp(&b_len)));
|
.sort_by(|a, b| compare_optimizations(paths, a, b, |a_len, b_len| a_len.cmp(&b_len)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_optimizations(
|
fn compare_optimizations<TOperator>(
|
||||||
paths: &PathInterner,
|
paths: &PathInterner,
|
||||||
a: &linear::Optimization,
|
a: &linear::Optimization<TOperator>,
|
||||||
b: &linear::Optimization,
|
b: &linear::Optimization<TOperator>,
|
||||||
compare_lengths: impl Fn(usize, usize) -> Ordering,
|
compare_lengths: impl Fn(usize, usize) -> Ordering,
|
||||||
) -> Ordering {
|
) -> Ordering
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
for (a, b) in a.increments.iter().zip(b.increments.iter()) {
|
for (a, b) in a.increments.iter().zip(b.increments.iter()) {
|
||||||
let c = compare_match_op_generality(paths, a.operation, b.operation);
|
let c = compare_match_op_generality(paths, a.operation, b.operation);
|
||||||
if c != Ordering::Equal {
|
if c != Ordering::Equal {
|
||||||
@@ -79,11 +90,14 @@ fn compare_optimizations(
|
|||||||
compare_lengths(a.increments.len(), b.increments.len())
|
compare_lengths(a.increments.len(), b.increments.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_optimization_generality(
|
fn compare_optimization_generality<TOperator>(
|
||||||
paths: &PathInterner,
|
paths: &PathInterner,
|
||||||
a: &linear::Optimization,
|
a: &linear::Optimization<TOperator>,
|
||||||
b: &linear::Optimization,
|
b: &linear::Optimization<TOperator>,
|
||||||
) -> Ordering {
|
) -> Ordering
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
compare_optimizations(paths, a, b, |a_len, b_len| {
|
compare_optimizations(paths, a, b, |a_len, b_len| {
|
||||||
// If they shared equivalent prefixes, then compare lengths and invert the
|
// If they shared equivalent prefixes, then compare lengths and invert the
|
||||||
// result because longer patterns are less general than shorter patterns.
|
// result because longer patterns are less general than shorter patterns.
|
||||||
@@ -158,14 +172,22 @@ fn compare_paths(paths: &PathInterner, a: PathId, b: PathId) -> Ordering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Are the given optimizations sorted from least to most general?
|
/// Are the given optimizations sorted from least to most general?
|
||||||
pub(crate) fn is_sorted_by_generality(opts: &linear::Optimizations) -> bool {
|
pub(crate) fn is_sorted_by_generality<TOperator>(opts: &linear::Optimizations<TOperator>) -> bool
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
opts.optimizations
|
opts.optimizations
|
||||||
.windows(2)
|
.windows(2)
|
||||||
.all(|w| compare_optimization_generality(&opts.paths, &w[0], &w[1]) <= Ordering::Equal)
|
.all(|w| compare_optimization_generality(&opts.paths, &w[0], &w[1]) <= Ordering::Equal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Are the given optimizations sorted lexicographically?
|
/// Are the given optimizations sorted lexicographically?
|
||||||
pub(crate) fn is_sorted_lexicographically(opts: &linear::Optimizations) -> bool {
|
pub(crate) fn is_sorted_lexicographically<TOperator>(
|
||||||
|
opts: &linear::Optimizations<TOperator>,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
opts.optimizations.windows(2).all(|w| {
|
opts.optimizations.windows(2).all(|w| {
|
||||||
compare_optimizations(&opts.paths, &w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len))
|
compare_optimizations(&opts.paths, &w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len))
|
||||||
<= Ordering::Equal
|
<= Ordering::Equal
|
||||||
@@ -207,7 +229,10 @@ pub(crate) fn is_sorted_lexicographically(opts: &linear::Optimizations) -> bool
|
|||||||
/// opcode @ 0 --iadd-->
|
/// opcode @ 0 --iadd-->
|
||||||
/// opcode @ 0 --(else)--> is-const? @ 0 --true-->
|
/// opcode @ 0 --(else)--> is-const? @ 0 --true-->
|
||||||
/// ```
|
/// ```
|
||||||
pub fn match_in_same_order(opts: &mut linear::Optimizations) {
|
pub fn match_in_same_order<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
assert!(!opts.optimizations.is_empty());
|
assert!(!opts.optimizations.is_empty());
|
||||||
|
|
||||||
let mut prefix = vec![];
|
let mut prefix = vec![];
|
||||||
@@ -267,7 +292,10 @@ pub fn match_in_same_order(opts: &mut linear::Optimizations) {
|
|||||||
/// existence completely. So we just emit nop match operations for all variable
|
/// existence completely. So we just emit nop match operations for all variable
|
||||||
/// patterns, and then in this post-processing pass, we fuse them and their
|
/// patterns, and then in this post-processing pass, we fuse them and their
|
||||||
/// actions with their preceding increment.
|
/// actions with their preceding increment.
|
||||||
pub fn remove_unnecessary_nops(opts: &mut linear::Optimizations) {
|
pub fn remove_unnecessary_nops<TOperator>(opts: &mut linear::Optimizations<TOperator>)
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
for opt in &mut opts.optimizations {
|
for opt in &mut opts.optimizations {
|
||||||
if opt.increments.len() < 2 {
|
if opt.increments.len() < 2 {
|
||||||
debug_assert!(!opt.increments.is_empty());
|
debug_assert!(!opt.increments.is_empty());
|
||||||
@@ -289,9 +317,9 @@ mod tests {
|
|||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
linear::{bool_to_match_result, Else, MatchOp::*, MatchResult},
|
linear::{bool_to_match_result, Else, MatchOp::*, MatchResult},
|
||||||
operator::Operator,
|
|
||||||
paths::*,
|
paths::*,
|
||||||
};
|
};
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -306,7 +334,7 @@ mod tests {
|
|||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
|
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
|
||||||
|
|
||||||
let opts = match wast::parser::parse::<Optimizations>(&buf) {
|
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
|
||||||
Ok(opts) => opts,
|
Ok(opts) => opts,
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
e.set_path(std::path::Path::new(stringify!($test_name)));
|
e.set_path(std::path::Path::new(stringify!($test_name)));
|
||||||
@@ -383,7 +411,7 @@ mod tests {
|
|||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
|
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
|
||||||
|
|
||||||
let opts = match wast::parser::parse::<Optimizations>(&buf) {
|
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
|
||||||
Ok(opts) => opts,
|
Ok(opts) => opts,
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
e.set_path(std::path::Path::new(stringify!($test_name)));
|
e.set_path(std::path::Path::new(stringify!($test_name)));
|
||||||
@@ -468,31 +496,19 @@ mod tests {
|
|||||||
",
|
",
|
||||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(
|
(Opcode { path: p(&[0, 1]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0, 1]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IntegerValue { path: p(&[0, 1]) }, i(42))
|
(IntegerValue { path: p(&[0, 1]) }, i(42))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
|
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
|
||||||
(
|
(
|
||||||
@@ -501,10 +517,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
|
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
|
||||||
(
|
(
|
||||||
@@ -513,18 +526,12 @@ mod tests {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true))
|
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(
|
(
|
||||||
Eq {
|
Eq {
|
||||||
@@ -535,10 +542,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
],
|
],
|
||||||
@@ -558,50 +562,32 @@ mod tests {
|
|||||||
",
|
",
|
||||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
|
|
||||||
),
|
|
||||||
(IntegerValue { path: p(&[0, 0]) }, i(2)),
|
(IntegerValue { path: p(&[0, 0]) }, i(2)),
|
||||||
(Nop, Err(Else))
|
(Nop, Err(Else))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
|
|
||||||
),
|
|
||||||
(IntegerValue { path: p(&[0, 0]) }, i(1)),
|
(IntegerValue { path: p(&[0, 0]) }, i(1)),
|
||||||
(Nop, Err(Else))
|
(Nop, Err(Else))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IntegerValue { path: p(&[0, 1]) }, i(2))
|
(IntegerValue { path: p(&[0, 1]) }, i(2))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Imul as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IntegerValue { path: p(&[0, 1]) }, i(1))
|
(IntegerValue { path: p(&[0, 1]) }, i(1))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(IntegerValue { path: p(&[0, 0]) }, i(0)),
|
(IntegerValue { path: p(&[0, 0]) }, i(0)),
|
||||||
(Nop, Err(Else))
|
(Nop, Err(Else))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||||
Opcode { path: p(&[0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Iadd as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(IntegerValue { path: p(&[0, 1]) }, i(0))
|
(IntegerValue { path: p(&[0, 1]) }, i(0))
|
||||||
]
|
]
|
||||||
@@ -619,14 +605,8 @@ mod tests {
|
|||||||
",
|
",
|
||||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Opcode { path: p(&[0]) },
|
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Opcode { path: p(&[0, 0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(
|
(
|
||||||
@@ -638,14 +618,8 @@ mod tests {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Opcode { path: p(&[0]) },
|
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Opcode { path: p(&[0, 0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(
|
(
|
||||||
@@ -670,14 +644,8 @@ mod tests {
|
|||||||
",
|
",
|
||||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Opcode { path: p(&[0]) },
|
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Opcode { path: p(&[0, 0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(
|
(
|
||||||
@@ -689,14 +657,8 @@ mod tests {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(
|
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Opcode { path: p(&[0]) },
|
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Opcode { path: p(&[0, 0]) },
|
|
||||||
Ok(NonZeroU32::new(Operator::Bor as _).unwrap())
|
|
||||||
),
|
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(Nop, Err(Else)),
|
(Nop, Err(Else)),
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -93,11 +93,17 @@ use peepmatic_runtime::{
|
|||||||
};
|
};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use wast::Id;
|
use wast::Id;
|
||||||
|
|
||||||
/// Translate the given AST optimizations into linear optimizations.
|
/// Translate the given AST optimizations into linear optimizations.
|
||||||
pub fn linearize(opts: &Optimizations) -> linear::Optimizations {
|
pub fn linearize<TOperator>(opts: &Optimizations<TOperator>) -> linear::Optimizations<TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
|
||||||
|
{
|
||||||
let mut optimizations = vec![];
|
let mut optimizations = vec![];
|
||||||
let mut paths = PathInterner::new();
|
let mut paths = PathInterner::new();
|
||||||
let mut integers = IntegerInterner::new();
|
let mut integers = IntegerInterner::new();
|
||||||
@@ -113,12 +119,15 @@ pub fn linearize(opts: &Optimizations) -> linear::Optimizations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Translate an AST optimization into a linear optimization!
|
/// Translate an AST optimization into a linear optimization!
|
||||||
fn linearize_optimization(
|
fn linearize_optimization<TOperator>(
|
||||||
paths: &mut PathInterner,
|
paths: &mut PathInterner,
|
||||||
integers: &mut IntegerInterner,
|
integers: &mut IntegerInterner,
|
||||||
opt: &Optimization,
|
opt: &Optimization<TOperator>,
|
||||||
) -> linear::Optimization {
|
) -> linear::Optimization<TOperator>
|
||||||
let mut increments: Vec<linear::Increment> = vec![];
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
|
||||||
|
{
|
||||||
|
let mut increments: Vec<linear::Increment<_>> = vec![];
|
||||||
|
|
||||||
let mut lhs_id_to_path = LhsIdToPath::new();
|
let mut lhs_id_to_path = LhsIdToPath::new();
|
||||||
|
|
||||||
@@ -175,20 +184,26 @@ fn linearize_optimization(
|
|||||||
///
|
///
|
||||||
/// Does not maintain any extra state about the traversal, such as where in the
|
/// Does not maintain any extra state about the traversal, such as where in the
|
||||||
/// tree each yielded node comes from.
|
/// tree each yielded node comes from.
|
||||||
struct RhsPostOrder<'a> {
|
struct RhsPostOrder<'a, TOperator> {
|
||||||
dfs: Dfs<'a>,
|
dfs: Dfs<'a, TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RhsPostOrder<'a> {
|
impl<'a, TOperator> RhsPostOrder<'a, TOperator>
|
||||||
fn new(rhs: &'a Rhs<'a>) -> Self {
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
|
fn new(rhs: &'a Rhs<'a, TOperator>) -> Self {
|
||||||
Self { dfs: Dfs::new(rhs) }
|
Self { dfs: Dfs::new(rhs) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for RhsPostOrder<'a> {
|
impl<'a, TOperator> Iterator for RhsPostOrder<'a, TOperator>
|
||||||
type Item = &'a Rhs<'a>;
|
where
|
||||||
|
TOperator: Copy,
|
||||||
|
{
|
||||||
|
type Item = &'a Rhs<'a, TOperator>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a Rhs<'a>> {
|
fn next(&mut self) -> Option<&'a Rhs<'a, TOperator>> {
|
||||||
use crate::traversals::TraversalEvent as TE;
|
use crate::traversals::TraversalEvent as TE;
|
||||||
loop {
|
loop {
|
||||||
match self.dfs.next()? {
|
match self.dfs.next()? {
|
||||||
@@ -203,14 +218,17 @@ impl<'a> Iterator for RhsPostOrder<'a> {
|
|||||||
///
|
///
|
||||||
/// Keeps track of the path to each pattern, and yields it along side the
|
/// Keeps track of the path to each pattern, and yields it along side the
|
||||||
/// pattern AST node.
|
/// pattern AST node.
|
||||||
struct PatternPreOrder<'a> {
|
struct PatternPreOrder<'a, TOperator> {
|
||||||
last_child: Option<u8>,
|
last_child: Option<u8>,
|
||||||
path: Vec<u8>,
|
path: Vec<u8>,
|
||||||
dfs: Dfs<'a>,
|
dfs: Dfs<'a, TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PatternPreOrder<'a> {
|
impl<'a, TOperator> PatternPreOrder<'a, TOperator>
|
||||||
fn new(pattern: &'a Pattern<'a>) -> Self {
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
|
fn new(pattern: &'a Pattern<'a, TOperator>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
last_child: None,
|
last_child: None,
|
||||||
path: vec![],
|
path: vec![],
|
||||||
@@ -218,7 +236,7 @@ impl<'a> PatternPreOrder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, paths: &mut PathInterner) -> Option<(PathId, &'a Pattern<'a>)> {
|
fn next(&mut self, paths: &mut PathInterner) -> Option<(PathId, &'a Pattern<'a, TOperator>)> {
|
||||||
use crate::traversals::TraversalEvent as TE;
|
use crate::traversals::TraversalEvent as TE;
|
||||||
loop {
|
loop {
|
||||||
match self.dfs.next()? {
|
match self.dfs.next()? {
|
||||||
@@ -252,15 +270,17 @@ impl<'a> PatternPreOrder<'a> {
|
|||||||
|
|
||||||
/// A map from left-hand side identifiers to the path in the left-hand side
|
/// A map from left-hand side identifiers to the path in the left-hand side
|
||||||
/// where they first occurred.
|
/// where they first occurred.
|
||||||
struct LhsIdToPath<'a> {
|
struct LhsIdToPath<'a, TOperator> {
|
||||||
id_to_path: BTreeMap<&'a str, PathId>,
|
id_to_path: BTreeMap<&'a str, PathId>,
|
||||||
|
_marker: PhantomData<&'a TOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LhsIdToPath<'a> {
|
impl<'a, TOperator> LhsIdToPath<'a, TOperator> {
|
||||||
/// Construct a new, empty `LhsIdToPath`.
|
/// Construct a new, empty `LhsIdToPath`.
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id_to_path: Default::default(),
|
id_to_path: Default::default(),
|
||||||
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +300,7 @@ impl<'a> LhsIdToPath<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remember the path to any LHS ids used in the given pattern.
|
/// Remember the path to any LHS ids used in the given pattern.
|
||||||
fn remember_path_to_pattern_ids(&mut self, pattern: &'a Pattern<'a>, path: PathId) {
|
fn remember_path_to_pattern_ids(&mut self, pattern: &'a Pattern<'a, TOperator>, path: PathId) {
|
||||||
match pattern {
|
match pattern {
|
||||||
// If this is the first time we've seen an identifier defined on the
|
// If this is the first time we've seen an identifier defined on the
|
||||||
// left-hand side, remember it.
|
// left-hand side, remember it.
|
||||||
@@ -294,10 +314,10 @@ impl<'a> LhsIdToPath<'a> {
|
|||||||
|
|
||||||
/// An `RhsBuilder` emits the actions for building the right-hand side
|
/// An `RhsBuilder` emits the actions for building the right-hand side
|
||||||
/// instructions.
|
/// instructions.
|
||||||
struct RhsBuilder<'a> {
|
struct RhsBuilder<'a, TOperator> {
|
||||||
// We do a post order traversal of the RHS because an RHS instruction cannot
|
// We do a post order traversal of the RHS because an RHS instruction cannot
|
||||||
// be created until after all of its operands are created.
|
// be created until after all of its operands are created.
|
||||||
rhs_post_order: RhsPostOrder<'a>,
|
rhs_post_order: RhsPostOrder<'a, TOperator>,
|
||||||
|
|
||||||
// A map from a right-hand side's span to its `linear::RhsId`. This is used
|
// A map from a right-hand side's span to its `linear::RhsId`. This is used
|
||||||
// by RHS-construction actions to reference operands. In practice the
|
// by RHS-construction actions to reference operands. In practice the
|
||||||
@@ -306,9 +326,12 @@ struct RhsBuilder<'a> {
|
|||||||
rhs_span_to_id: BTreeMap<wast::Span, linear::RhsId>,
|
rhs_span_to_id: BTreeMap<wast::Span, linear::RhsId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RhsBuilder<'a> {
|
impl<'a, TOperator> RhsBuilder<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// Create a new builder for the given right-hand side.
|
/// Create a new builder for the given right-hand side.
|
||||||
fn new(rhs: &'a Rhs<'a>) -> Self {
|
fn new(rhs: &'a Rhs<'a, TOperator>) -> Self {
|
||||||
let rhs_post_order = RhsPostOrder::new(rhs);
|
let rhs_post_order = RhsPostOrder::new(rhs);
|
||||||
let rhs_span_to_id = Default::default();
|
let rhs_span_to_id = Default::default();
|
||||||
Self {
|
Self {
|
||||||
@@ -323,7 +346,7 @@ impl<'a> RhsBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// Panics if we haven't already emitted the action for building this RHS's
|
/// Panics if we haven't already emitted the action for building this RHS's
|
||||||
/// instruction.
|
/// instruction.
|
||||||
fn get_rhs_id(&self, rhs: &Rhs) -> linear::RhsId {
|
fn get_rhs_id(&self, rhs: &Rhs<TOperator>) -> linear::RhsId {
|
||||||
self.rhs_span_to_id[&rhs.span()]
|
self.rhs_span_to_id[&rhs.span()]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,8 +358,8 @@ impl<'a> RhsBuilder<'a> {
|
|||||||
fn add_rhs_build_actions(
|
fn add_rhs_build_actions(
|
||||||
&mut self,
|
&mut self,
|
||||||
integers: &mut IntegerInterner,
|
integers: &mut IntegerInterner,
|
||||||
lhs_id_to_path: &LhsIdToPath,
|
lhs_id_to_path: &LhsIdToPath<TOperator>,
|
||||||
actions: &mut Vec<linear::Action>,
|
actions: &mut Vec<linear::Action<TOperator>>,
|
||||||
) {
|
) {
|
||||||
while let Some(rhs) = self.rhs_post_order.next() {
|
while let Some(rhs) = self.rhs_post_order.next() {
|
||||||
actions.push(self.rhs_to_linear_action(integers, lhs_id_to_path, rhs));
|
actions.push(self.rhs_to_linear_action(integers, lhs_id_to_path, rhs));
|
||||||
@@ -348,9 +371,9 @@ impl<'a> RhsBuilder<'a> {
|
|||||||
fn rhs_to_linear_action(
|
fn rhs_to_linear_action(
|
||||||
&self,
|
&self,
|
||||||
integers: &mut IntegerInterner,
|
integers: &mut IntegerInterner,
|
||||||
lhs_id_to_path: &LhsIdToPath,
|
lhs_id_to_path: &LhsIdToPath<TOperator>,
|
||||||
rhs: &Rhs,
|
rhs: &Rhs<TOperator>,
|
||||||
) -> linear::Action {
|
) -> linear::Action<TOperator> {
|
||||||
match rhs {
|
match rhs {
|
||||||
Rhs::ValueLiteral(ValueLiteral::Integer(i)) => linear::Action::MakeIntegerConst {
|
Rhs::ValueLiteral(ValueLiteral::Integer(i)) => linear::Action::MakeIntegerConst {
|
||||||
value: integers.intern(i.value as u64),
|
value: integers.intern(i.value as u64),
|
||||||
@@ -425,9 +448,15 @@ impl<'a> RhsBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Precondition<'_> {
|
impl<TOperator> Precondition<'_, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash + Into<NonZeroU32>,
|
||||||
|
{
|
||||||
/// Convert this precondition into a `linear::Increment`.
|
/// Convert this precondition into a `linear::Increment`.
|
||||||
fn to_linear_increment(&self, lhs_id_to_path: &LhsIdToPath) -> linear::Increment {
|
fn to_linear_increment(
|
||||||
|
&self,
|
||||||
|
lhs_id_to_path: &LhsIdToPath<TOperator>,
|
||||||
|
) -> linear::Increment<TOperator> {
|
||||||
match self.constraint {
|
match self.constraint {
|
||||||
Constraint::IsPowerOfTwo => {
|
Constraint::IsPowerOfTwo => {
|
||||||
let id = match &self.operands[0] {
|
let id = match &self.operands[0] {
|
||||||
@@ -484,7 +513,10 @@ impl Precondition<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pattern<'_> {
|
impl<TOperator> Pattern<'_, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Copy,
|
||||||
|
{
|
||||||
/// Convert this pattern into its linear match operation and the expected
|
/// Convert this pattern into its linear match operation and the expected
|
||||||
/// result of that operation.
|
/// result of that operation.
|
||||||
///
|
///
|
||||||
@@ -493,9 +525,12 @@ impl Pattern<'_> {
|
|||||||
fn to_linear_match_op(
|
fn to_linear_match_op(
|
||||||
&self,
|
&self,
|
||||||
integers: &mut IntegerInterner,
|
integers: &mut IntegerInterner,
|
||||||
lhs_id_to_path: &LhsIdToPath,
|
lhs_id_to_path: &LhsIdToPath<TOperator>,
|
||||||
path: PathId,
|
path: PathId,
|
||||||
) -> (linear::MatchOp, linear::MatchResult) {
|
) -> (linear::MatchOp, linear::MatchResult)
|
||||||
|
where
|
||||||
|
TOperator: Into<NonZeroU32>,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
Pattern::ValueLiteral(ValueLiteral::Integer(Integer { value, .. })) => (
|
Pattern::ValueLiteral(ValueLiteral::Integer(Integer { value, .. })) => (
|
||||||
linear::MatchOp::IntegerValue { path },
|
linear::MatchOp::IntegerValue { path },
|
||||||
@@ -543,9 +578,7 @@ impl Pattern<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pattern::Operation(op) => {
|
Pattern::Operation(op) => {
|
||||||
let op = op.operator as u32;
|
let expected = Ok(op.operator.into());
|
||||||
debug_assert!(op != 0, "no `Operator` variants are zero");
|
|
||||||
let expected = Ok(unsafe { NonZeroU32::new_unchecked(op) });
|
|
||||||
(linear::MatchOp::Opcode { path }, expected)
|
(linear::MatchOp::Opcode { path }, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -558,9 +591,9 @@ mod tests {
|
|||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::{
|
||||||
integer_interner::IntegerId,
|
integer_interner::IntegerId,
|
||||||
linear::{bool_to_match_result, Action::*, Else, MatchOp::*},
|
linear::{bool_to_match_result, Action::*, Else, MatchOp::*},
|
||||||
operator::Operator,
|
|
||||||
r#type::{BitWidth, Kind, Type},
|
r#type::{BitWidth, Kind, Type},
|
||||||
};
|
};
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
|
||||||
macro_rules! linearizes_to {
|
macro_rules! linearizes_to {
|
||||||
($name:ident, $source:expr, $make_expected:expr $(,)* ) => {
|
($name:ident, $source:expr, $make_expected:expr $(,)* ) => {
|
||||||
@@ -568,7 +601,7 @@ mod tests {
|
|||||||
fn $name() {
|
fn $name() {
|
||||||
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
|
let buf = wast::parser::ParseBuffer::new($source).expect("should lex OK");
|
||||||
|
|
||||||
let opts = match wast::parser::parse::<Optimizations>(&buf) {
|
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
|
||||||
Ok(opts) => opts,
|
Ok(opts) => opts,
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
e.set_path(std::path::Path::new(stringify!($name)));
|
e.set_path(std::path::Path::new(stringify!($name)));
|
||||||
@@ -602,7 +635,7 @@ mod tests {
|
|||||||
let make_expected: fn(
|
let make_expected: fn(
|
||||||
&mut dyn FnMut(&[u8]) -> PathId,
|
&mut dyn FnMut(&[u8]) -> PathId,
|
||||||
&mut dyn FnMut(u64) -> IntegerId,
|
&mut dyn FnMut(u64) -> IntegerId,
|
||||||
) -> Vec<linear::Increment> = $make_expected;
|
) -> Vec<linear::Increment<TestOperator>> = $make_expected;
|
||||||
let expected = make_expected(&mut p, &mut i);
|
let expected = make_expected(&mut p, &mut i);
|
||||||
dbg!(&expected);
|
dbg!(&expected);
|
||||||
|
|
||||||
@@ -624,12 +657,12 @@ mod tests {
|
|||||||
|p, i| vec![
|
|p, i| vec![
|
||||||
linear::Increment {
|
linear::Increment {
|
||||||
operation: Opcode { path: p(&[0]) },
|
operation: Opcode { path: p(&[0]) },
|
||||||
expected: Ok(NonZeroU32::new(Operator::Imul as _).unwrap()),
|
expected: Ok(TestOperator::Imul.into()),
|
||||||
actions: vec![
|
actions: vec![
|
||||||
GetLhs { path: p(&[0, 0]) },
|
GetLhs { path: p(&[0, 0]) },
|
||||||
GetLhs { path: p(&[0, 1]) },
|
GetLhs { path: p(&[0, 1]) },
|
||||||
MakeBinaryInst {
|
MakeBinaryInst {
|
||||||
operator: Operator::Ishl,
|
operator: TestOperator::Ishl,
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
kind: Kind::Int,
|
kind: Kind::Int,
|
||||||
bit_width: BitWidth::Polymorphic,
|
bit_width: BitWidth::Polymorphic,
|
||||||
@@ -702,11 +735,11 @@ mod tests {
|
|||||||
|p, i| vec![
|
|p, i| vec![
|
||||||
linear::Increment {
|
linear::Increment {
|
||||||
operation: Opcode { path: p(&[0]) },
|
operation: Opcode { path: p(&[0]) },
|
||||||
expected: Ok(NonZeroU32::new(Operator::Iconst as _).unwrap()),
|
expected: Ok(TestOperator::Iconst.into()),
|
||||||
actions: vec![
|
actions: vec![
|
||||||
GetLhs { path: p(&[0, 0]) },
|
GetLhs { path: p(&[0, 0]) },
|
||||||
MakeUnaryInst {
|
MakeUnaryInst {
|
||||||
operator: Operator::Iconst,
|
operator: TestOperator::Iconst,
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
kind: Kind::Int,
|
kind: Kind::Int,
|
||||||
bit_width: BitWidth::Polymorphic,
|
bit_width: BitWidth::Polymorphic,
|
||||||
@@ -729,14 +762,14 @@ mod tests {
|
|||||||
|p, i| vec![
|
|p, i| vec![
|
||||||
linear::Increment {
|
linear::Increment {
|
||||||
operation: Opcode { path: p(&[0]) },
|
operation: Opcode { path: p(&[0]) },
|
||||||
expected: Ok(NonZeroU32::new(Operator::Bor as _).unwrap()),
|
expected: Ok(TestOperator::Bor.into()),
|
||||||
actions: vec![
|
actions: vec![
|
||||||
GetLhs { path: p(&[0, 0]) },
|
GetLhs { path: p(&[0, 0]) },
|
||||||
GetLhs {
|
GetLhs {
|
||||||
path: p(&[0, 1, 1]),
|
path: p(&[0, 1, 1]),
|
||||||
},
|
},
|
||||||
MakeBinaryInst {
|
MakeBinaryInst {
|
||||||
operator: Operator::Bor,
|
operator: TestOperator::Bor,
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
kind: Kind::Int,
|
kind: Kind::Int,
|
||||||
bit_width: BitWidth::Polymorphic,
|
bit_width: BitWidth::Polymorphic,
|
||||||
@@ -752,7 +785,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
linear::Increment {
|
linear::Increment {
|
||||||
operation: Opcode { path: p(&[0, 1]) },
|
operation: Opcode { path: p(&[0, 1]) },
|
||||||
expected: Ok(NonZeroU32::new(Operator::Bor as _).unwrap()),
|
expected: Ok(TestOperator::Bor.into()),
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
},
|
},
|
||||||
linear::Increment {
|
linear::Increment {
|
||||||
@@ -791,7 +824,7 @@ mod tests {
|
|||||||
|p, i| vec![
|
|p, i| vec![
|
||||||
linear::Increment {
|
linear::Increment {
|
||||||
operation: Opcode { path: p(&[0]) },
|
operation: Opcode { path: p(&[0]) },
|
||||||
expected: Ok(NonZeroU32::new(Operator::Ireduce as _).unwrap()),
|
expected: Ok(TestOperator::Ireduce.into()),
|
||||||
actions: vec![MakeIntegerConst {
|
actions: vec![MakeIntegerConst {
|
||||||
value: i(0),
|
value: i(0),
|
||||||
bit_width: BitWidth::ThirtyTwo,
|
bit_width: BitWidth::ThirtyTwo,
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ mod tok {
|
|||||||
custom_keyword!(nof);
|
custom_keyword!(nof);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Optimizations<'a> {
|
impl<'a, TOperator> Parse<'a> for Optimizations<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Parse<'a>,
|
||||||
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
let mut optimizations = vec![];
|
let mut optimizations = vec![];
|
||||||
@@ -98,7 +101,10 @@ impl<'a> Parse<'a> for Optimizations<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Optimization<'a> {
|
impl<'a, TOperator> Parse<'a> for Optimization<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Parse<'a>,
|
||||||
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
p.parens(|p| {
|
p.parens(|p| {
|
||||||
@@ -110,7 +116,10 @@ impl<'a> Parse<'a> for Optimization<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Lhs<'a> {
|
impl<'a, TOperator> Parse<'a> for Lhs<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Parse<'a>,
|
||||||
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
let mut preconditions = vec![];
|
let mut preconditions = vec![];
|
||||||
@@ -139,30 +148,36 @@ impl<'a> Parse<'a> for Lhs<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Pattern<'a> {
|
impl<'a, TOperator> Parse<'a> for Pattern<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Parse<'a>,
|
||||||
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
if p.peek::<ValueLiteral>() {
|
if p.peek::<ValueLiteral<TOperator>>() {
|
||||||
return Ok(Pattern::ValueLiteral(p.parse()?));
|
return Ok(Pattern::ValueLiteral(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Constant>() {
|
if p.peek::<Constant<TOperator>>() {
|
||||||
return Ok(Pattern::Constant(p.parse()?));
|
return Ok(Pattern::Constant(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Operation<Self>>() {
|
if p.peek::<Operation<TOperator, Self>>() {
|
||||||
return Ok(Pattern::Operation(p.parse()?));
|
return Ok(Pattern::Operation(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Variable>() {
|
if p.peek::<Variable<TOperator>>() {
|
||||||
return Ok(Pattern::Variable(p.parse()?));
|
return Ok(Pattern::Variable(p.parse()?));
|
||||||
}
|
}
|
||||||
Err(p.error("expected a left-hand side pattern"))
|
Err(p.error("expected a left-hand side pattern"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for Pattern<'a> {
|
impl<'a, TOperator> Peek for Pattern<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'a,
|
||||||
|
{
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
ValueLiteral::peek(c)
|
ValueLiteral::<TOperator>::peek(c)
|
||||||
|| Constant::peek(c)
|
|| Constant::<TOperator>::peek(c)
|
||||||
|| Variable::peek(c)
|
|| Variable::<TOperator>::peek(c)
|
||||||
|| Operation::<Self>::peek(c)
|
|| Operation::<TOperator, Self>::peek(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display() -> &'static str {
|
fn display() -> &'static str {
|
||||||
@@ -170,24 +185,26 @@ impl<'a> Peek for Pattern<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for ValueLiteral<'a> {
|
impl<'a, TOperator> Parse<'a> for ValueLiteral<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
if let Ok(b) = p.parse::<Boolean>() {
|
if let Ok(b) = p.parse::<Boolean<TOperator>>() {
|
||||||
return Ok(ValueLiteral::Boolean(b));
|
return Ok(ValueLiteral::Boolean(b));
|
||||||
}
|
}
|
||||||
if let Ok(i) = p.parse::<Integer>() {
|
if let Ok(i) = p.parse::<Integer<TOperator>>() {
|
||||||
return Ok(ValueLiteral::Integer(i));
|
return Ok(ValueLiteral::Integer(i));
|
||||||
}
|
}
|
||||||
if let Ok(cc) = p.parse::<ConditionCode>() {
|
if let Ok(cc) = p.parse::<ConditionCode<TOperator>>() {
|
||||||
return Ok(ValueLiteral::ConditionCode(cc));
|
return Ok(ValueLiteral::ConditionCode(cc));
|
||||||
}
|
}
|
||||||
Err(p.error("expected an integer or boolean or condition code literal"))
|
Err(p.error("expected an integer or boolean or condition code literal"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for ValueLiteral<'a> {
|
impl<'a, TOperator> Peek for ValueLiteral<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
c.integer().is_some() || Boolean::peek(c) || ConditionCode::peek(c)
|
c.integer().is_some()
|
||||||
|
|| Boolean::<TOperator>::peek(c)
|
||||||
|
|| ConditionCode::<TOperator>::peek(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display() -> &'static str {
|
fn display() -> &'static str {
|
||||||
@@ -195,7 +212,7 @@ impl<'a> Peek for ValueLiteral<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Integer<'a> {
|
impl<'a, TOperator> Parse<'a> for Integer<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
p.step(|c| {
|
p.step(|c| {
|
||||||
@@ -221,7 +238,7 @@ impl<'a> Parse<'a> for Integer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Boolean<'a> {
|
impl<'a, TOperator> Parse<'a> for Boolean<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
if p.parse::<tok::r#true>().is_ok() {
|
if p.parse::<tok::r#true>().is_ok() {
|
||||||
@@ -244,7 +261,7 @@ impl<'a> Parse<'a> for Boolean<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for Boolean<'a> {
|
impl<'a, TOperator> Peek for Boolean<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
<tok::r#true as Peek>::peek(c) || <tok::r#false as Peek>::peek(c)
|
<tok::r#true as Peek>::peek(c) || <tok::r#false as Peek>::peek(c)
|
||||||
}
|
}
|
||||||
@@ -254,7 +271,7 @@ impl<'a> Peek for Boolean<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for ConditionCode<'a> {
|
impl<'a, TOperator> Parse<'a> for ConditionCode<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
|
|
||||||
@@ -292,7 +309,7 @@ impl<'a> Parse<'a> for ConditionCode<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for ConditionCode<'a> {
|
impl<'a, TOperator> Peek for ConditionCode<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
macro_rules! peek_cc {
|
macro_rules! peek_cc {
|
||||||
( $( $token:ident, )* ) => {
|
( $( $token:ident, )* ) => {
|
||||||
@@ -321,7 +338,7 @@ impl<'a> Peek for ConditionCode<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Constant<'a> {
|
impl<'a, TOperator> Parse<'a> for Constant<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
let id = Id::parse(p)?;
|
let id = Id::parse(p)?;
|
||||||
@@ -330,7 +347,11 @@ impl<'a> Parse<'a> for Constant<'a> {
|
|||||||
.chars()
|
.chars()
|
||||||
.all(|c| !c.is_alphabetic() || c.is_uppercase())
|
.all(|c| !c.is_alphabetic() || c.is_uppercase())
|
||||||
{
|
{
|
||||||
Ok(Constant { span, id })
|
Ok(Constant {
|
||||||
|
span,
|
||||||
|
id,
|
||||||
|
marker: PhantomData,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let upper = id
|
let upper = id
|
||||||
.name()
|
.name()
|
||||||
@@ -345,7 +366,7 @@ impl<'a> Parse<'a> for Constant<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for Constant<'a> {
|
impl<'a, TOperator> Peek for Constant<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
if let Some((id, _rest)) = c.id() {
|
if let Some((id, _rest)) = c.id() {
|
||||||
id.chars().all(|c| !c.is_alphabetic() || c.is_uppercase())
|
id.chars().all(|c| !c.is_alphabetic() || c.is_uppercase())
|
||||||
@@ -359,7 +380,7 @@ impl<'a> Peek for Constant<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Variable<'a> {
|
impl<'a, TOperator> Parse<'a> for Variable<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
let id = Id::parse(p)?;
|
let id = Id::parse(p)?;
|
||||||
@@ -368,7 +389,11 @@ impl<'a> Parse<'a> for Variable<'a> {
|
|||||||
.chars()
|
.chars()
|
||||||
.all(|c| !c.is_alphabetic() || c.is_lowercase())
|
.all(|c| !c.is_alphabetic() || c.is_lowercase())
|
||||||
{
|
{
|
||||||
Ok(Variable { span, id })
|
Ok(Variable {
|
||||||
|
span,
|
||||||
|
id,
|
||||||
|
marker: PhantomData,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let lower = id
|
let lower = id
|
||||||
.name()
|
.name()
|
||||||
@@ -383,7 +408,7 @@ impl<'a> Parse<'a> for Variable<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for Variable<'a> {
|
impl<'a, TOperator> Peek for Variable<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
if let Some((id, _rest)) = c.id() {
|
if let Some((id, _rest)) = c.id() {
|
||||||
id.chars().all(|c| !c.is_alphabetic() || c.is_lowercase())
|
id.chars().all(|c| !c.is_alphabetic() || c.is_lowercase())
|
||||||
@@ -397,10 +422,11 @@ impl<'a> Peek for Variable<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Parse<'a> for Operation<'a, T>
|
impl<'a, TOperator, TOperand> Parse<'a> for Operation<'a, TOperator, TOperand>
|
||||||
where
|
where
|
||||||
T: 'a + Ast<'a> + Peek + Parse<'a>,
|
TOperator: Parse<'a>,
|
||||||
DynAstRef<'a>: From<&'a T>,
|
TOperand: 'a + Ast<'a, TOperator> + Peek + Parse<'a>,
|
||||||
|
DynAstRef<'a, TOperator>: From<&'a TOperand>,
|
||||||
{
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
@@ -417,7 +443,7 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut operands = vec![];
|
let mut operands = vec![];
|
||||||
while p.peek::<T>() {
|
while p.peek::<TOperand>() {
|
||||||
operands.push(p.parse()?);
|
operands.push(p.parse()?);
|
||||||
}
|
}
|
||||||
Ok(Operation {
|
Ok(Operation {
|
||||||
@@ -431,10 +457,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Peek for Operation<'a, T>
|
impl<'a, TOperator, TOperand> Peek for Operation<'a, TOperator, TOperand>
|
||||||
where
|
where
|
||||||
T: 'a + Ast<'a>,
|
TOperand: 'a + Ast<'a, TOperator>,
|
||||||
DynAstRef<'a>: From<&'a T>,
|
DynAstRef<'a, TOperator>: From<&'a TOperand>,
|
||||||
{
|
{
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
wast::LParen::peek(c)
|
wast::LParen::peek(c)
|
||||||
@@ -445,19 +471,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Precondition<'a> {
|
impl<'a, TOperator> Parse<'a> for Precondition<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
p.parens(|p| {
|
p.parens(|p| {
|
||||||
let constraint = p.parse()?;
|
let constraint = p.parse()?;
|
||||||
let mut operands = vec![];
|
let mut operands = vec![];
|
||||||
while p.peek::<ConstraintOperand>() {
|
while p.peek::<ConstraintOperand<TOperator>>() {
|
||||||
operands.push(p.parse()?);
|
operands.push(p.parse()?);
|
||||||
}
|
}
|
||||||
Ok(Precondition {
|
Ok(Precondition {
|
||||||
span,
|
span,
|
||||||
constraint,
|
constraint,
|
||||||
operands,
|
operands,
|
||||||
|
marker: PhantomData,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -481,24 +508,26 @@ impl<'a> Parse<'a> for Constraint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for ConstraintOperand<'a> {
|
impl<'a, TOperator> Parse<'a> for ConstraintOperand<'a, TOperator> {
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
if p.peek::<ValueLiteral>() {
|
if p.peek::<ValueLiteral<TOperator>>() {
|
||||||
return Ok(ConstraintOperand::ValueLiteral(p.parse()?));
|
return Ok(ConstraintOperand::ValueLiteral(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Constant>() {
|
if p.peek::<Constant<TOperator>>() {
|
||||||
return Ok(ConstraintOperand::Constant(p.parse()?));
|
return Ok(ConstraintOperand::Constant(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Variable>() {
|
if p.peek::<Variable<TOperator>>() {
|
||||||
return Ok(ConstraintOperand::Variable(p.parse()?));
|
return Ok(ConstraintOperand::Variable(p.parse()?));
|
||||||
}
|
}
|
||||||
Err(p.error("expected an operand for precondition constraint"))
|
Err(p.error("expected an operand for precondition constraint"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for ConstraintOperand<'a> {
|
impl<'a, TOperator> Peek for ConstraintOperand<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
ValueLiteral::peek(c) || Constant::peek(c) || Variable::peek(c)
|
ValueLiteral::<TOperator>::peek(c)
|
||||||
|
|| Constant::<TOperator>::peek(c)
|
||||||
|
|| Variable::<TOperator>::peek(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display() -> &'static str {
|
fn display() -> &'static str {
|
||||||
@@ -506,34 +535,40 @@ impl<'a> Peek for ConstraintOperand<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Rhs<'a> {
|
impl<'a, TOperator> Parse<'a> for Rhs<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Parse<'a>,
|
||||||
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
if p.peek::<ValueLiteral>() {
|
if p.peek::<ValueLiteral<TOperator>>() {
|
||||||
return Ok(Rhs::ValueLiteral(p.parse()?));
|
return Ok(Rhs::ValueLiteral(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Constant>() {
|
if p.peek::<Constant<TOperator>>() {
|
||||||
return Ok(Rhs::Constant(p.parse()?));
|
return Ok(Rhs::Constant(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Variable>() {
|
if p.peek::<Variable<TOperator>>() {
|
||||||
return Ok(Rhs::Variable(p.parse()?));
|
return Ok(Rhs::Variable(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Unquote>() {
|
if p.peek::<Unquote<TOperator>>() {
|
||||||
return Ok(Rhs::Unquote(p.parse()?));
|
return Ok(Rhs::Unquote(p.parse()?));
|
||||||
}
|
}
|
||||||
if p.peek::<Operation<Self>>() {
|
if p.peek::<Operation<TOperator, Self>>() {
|
||||||
return Ok(Rhs::Operation(p.parse()?));
|
return Ok(Rhs::Operation(p.parse()?));
|
||||||
}
|
}
|
||||||
Err(p.error("expected a right-hand side replacement"))
|
Err(p.error("expected a right-hand side replacement"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for Rhs<'a> {
|
impl<'a, TOperator> Peek for Rhs<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: 'a,
|
||||||
|
{
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
ValueLiteral::peek(c)
|
ValueLiteral::<TOperator>::peek(c)
|
||||||
|| Constant::peek(c)
|
|| Constant::<TOperator>::peek(c)
|
||||||
|| Variable::peek(c)
|
|| Variable::<TOperator>::peek(c)
|
||||||
|| Unquote::peek(c)
|
|| Unquote::<TOperator>::peek(c)
|
||||||
|| Operation::<Self>::peek(c)
|
|| Operation::<TOperator, Self>::peek(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display() -> &'static str {
|
fn display() -> &'static str {
|
||||||
@@ -541,26 +576,30 @@ impl<'a> Peek for Rhs<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parse<'a> for Unquote<'a> {
|
impl<'a, TOperator> Parse<'a> for Unquote<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Parse<'a>,
|
||||||
|
{
|
||||||
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
fn parse(p: Parser<'a>) -> ParseResult<Self> {
|
||||||
let span = p.cur_span();
|
let span = p.cur_span();
|
||||||
p.parse::<tok::dollar>()?;
|
p.parse::<tok::dollar>()?;
|
||||||
p.parens(|p| {
|
p.parens(|p| {
|
||||||
let operator = p.parse()?;
|
let operator = p.parse()?;
|
||||||
let mut operands = vec![];
|
let mut operands = vec![];
|
||||||
while p.peek::<Rhs>() {
|
while p.peek::<Rhs<TOperator>>() {
|
||||||
operands.push(p.parse()?);
|
operands.push(p.parse()?);
|
||||||
}
|
}
|
||||||
Ok(Unquote {
|
Ok(Unquote {
|
||||||
span,
|
span,
|
||||||
operator,
|
operator,
|
||||||
operands,
|
operands,
|
||||||
|
marker: PhantomData,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Peek for Unquote<'a> {
|
impl<'a, TOperator> Peek for Unquote<'a, TOperator> {
|
||||||
fn peek(c: Cursor) -> bool {
|
fn peek(c: Cursor) -> bool {
|
||||||
tok::dollar::peek(c)
|
tok::dollar::peek(c)
|
||||||
}
|
}
|
||||||
@@ -573,7 +612,7 @@ impl<'a> Peek for Unquote<'a> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use peepmatic_runtime::operator::Operator;
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
|
||||||
macro_rules! test_parse {
|
macro_rules! test_parse {
|
||||||
(
|
(
|
||||||
@@ -623,7 +662,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_parse! {
|
test_parse! {
|
||||||
parse_boolean<Boolean> {
|
parse_boolean<Boolean<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"true",
|
"true",
|
||||||
"false",
|
"false",
|
||||||
@@ -641,7 +680,7 @@ mod test {
|
|||||||
"falsezzz",
|
"falsezzz",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_cc<ConditionCode> {
|
parse_cc<ConditionCode<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"eq",
|
"eq",
|
||||||
"ne",
|
"ne",
|
||||||
@@ -661,7 +700,7 @@ mod test {
|
|||||||
"neq",
|
"neq",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_constant<Constant> {
|
parse_constant<Constant<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"$C",
|
"$C",
|
||||||
"$C1",
|
"$C1",
|
||||||
@@ -693,7 +732,7 @@ mod test {
|
|||||||
"imul",
|
"imul",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_constraint_operand<ConstraintOperand> {
|
parse_constraint_operand<ConstraintOperand<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"1234",
|
"1234",
|
||||||
"true",
|
"true",
|
||||||
@@ -707,7 +746,7 @@ mod test {
|
|||||||
"(iadd 1 2)",
|
"(iadd 1 2)",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_integer<Integer> {
|
parse_integer<Integer<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"0",
|
"0",
|
||||||
"1",
|
"1",
|
||||||
@@ -746,7 +785,7 @@ mod test {
|
|||||||
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_lhs<Lhs> {
|
parse_lhs<Lhs<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"(when (imul $C1 $C2) (is-power-of-two $C1) (is-power-of-two $C2))",
|
"(when (imul $C1 $C2) (is-power-of-two $C1) (is-power-of-two $C2))",
|
||||||
"(when (imul $x $C) (is-power-of-two $C))",
|
"(when (imul $x $C) (is-power-of-two $C))",
|
||||||
@@ -762,7 +801,7 @@ mod test {
|
|||||||
"abc",
|
"abc",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_operation_pattern<Operation<Pattern>> {
|
parse_operation_pattern<Operation<TestOperator, Pattern<TestOperator>>> {
|
||||||
ok {
|
ok {
|
||||||
"(iadd)",
|
"(iadd)",
|
||||||
"(iadd 1)",
|
"(iadd 1)",
|
||||||
@@ -779,7 +818,7 @@ mod test {
|
|||||||
"(ishl $x $(log2 $C))",
|
"(ishl $x $(log2 $C))",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_operation_rhs<Operation<Rhs>> {
|
parse_operation_rhs<Operation<TestOperator, Rhs<TestOperator>>> {
|
||||||
ok {
|
ok {
|
||||||
"(iadd)",
|
"(iadd)",
|
||||||
"(iadd 1)",
|
"(iadd 1)",
|
||||||
@@ -793,7 +832,7 @@ mod test {
|
|||||||
"$CONST",
|
"$CONST",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_operator<Operator> {
|
parse_operator<TestOperator> {
|
||||||
ok {
|
ok {
|
||||||
"bor",
|
"bor",
|
||||||
"iadd",
|
"iadd",
|
||||||
@@ -812,7 +851,7 @@ mod test {
|
|||||||
"iadd{i32}",
|
"iadd{i32}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_optimization<Optimization> {
|
parse_optimization<Optimization<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"(=> (when (iadd $x $C) (is-power-of-two $C) (is-power-of-two $C)) (iadd $C $x))",
|
"(=> (when (iadd $x $C) (is-power-of-two $C) (is-power-of-two $C)) (iadd $C $x))",
|
||||||
"(=> (when (iadd $x $C)) (iadd $C $x))",
|
"(=> (when (iadd $x $C)) (iadd $C $x))",
|
||||||
@@ -825,7 +864,7 @@ mod test {
|
|||||||
"(=> () ())",
|
"(=> () ())",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_optimizations<Optimizations> {
|
parse_optimizations<Optimizations<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"",
|
"",
|
||||||
r#"
|
r#"
|
||||||
@@ -844,7 +883,7 @@ mod test {
|
|||||||
"#,
|
"#,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_pattern<Pattern> {
|
parse_pattern<Pattern<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"1234",
|
"1234",
|
||||||
"$C",
|
"$C",
|
||||||
@@ -857,7 +896,7 @@ mod test {
|
|||||||
"abc",
|
"abc",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_precondition<Precondition> {
|
parse_precondition<Precondition<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"(is-power-of-two)",
|
"(is-power-of-two)",
|
||||||
"(is-power-of-two $C)",
|
"(is-power-of-two $C)",
|
||||||
@@ -871,7 +910,7 @@ mod test {
|
|||||||
"$CONST",
|
"$CONST",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_rhs<Rhs> {
|
parse_rhs<Rhs<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"5",
|
"5",
|
||||||
"$C",
|
"$C",
|
||||||
@@ -884,7 +923,7 @@ mod test {
|
|||||||
"()",
|
"()",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_unquote<Unquote> {
|
parse_unquote<Unquote<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"$(log2)",
|
"$(log2)",
|
||||||
"$(log2 $C)",
|
"$(log2 $C)",
|
||||||
@@ -899,7 +938,7 @@ mod test {
|
|||||||
"$()",
|
"$()",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_value_literal<ValueLiteral> {
|
parse_value_literal<ValueLiteral<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"12345",
|
"12345",
|
||||||
"true",
|
"true",
|
||||||
@@ -911,7 +950,7 @@ mod test {
|
|||||||
"12.34",
|
"12.34",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_variable<Variable> {
|
parse_variable<Variable<TestOperator>> {
|
||||||
ok {
|
ok {
|
||||||
"$v",
|
"$v",
|
||||||
"$v1",
|
"$v1",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! Traversals over the AST.
|
//! Traversals over the AST.
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// A low-level DFS traversal event: either entering or exiting the traversal of
|
/// A low-level DFS traversal event: either entering or exiting the traversal of
|
||||||
/// an AST node.
|
/// an AST node.
|
||||||
@@ -32,13 +34,16 @@ pub enum TraversalEvent {
|
|||||||
/// the AST, because the `new` constructor takes anything that can convert into
|
/// the AST, because the `new` constructor takes anything that can convert into
|
||||||
/// a `DynAstRef`.
|
/// a `DynAstRef`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Dfs<'a> {
|
pub struct Dfs<'a, TOperator> {
|
||||||
stack: Vec<(TraversalEvent, DynAstRef<'a>)>,
|
stack: Vec<(TraversalEvent, DynAstRef<'a, TOperator>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Dfs<'a> {
|
impl<'a, TOperator> Dfs<'a, TOperator>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
/// Construct a new `Dfs` traversal starting at the given `start` AST node.
|
/// Construct a new `Dfs` traversal starting at the given `start` AST node.
|
||||||
pub fn new(start: impl Into<DynAstRef<'a>>) -> Self {
|
pub fn new(start: impl Into<DynAstRef<'a, TOperator>>) -> Self {
|
||||||
let start = start.into();
|
let start = start.into();
|
||||||
Dfs {
|
Dfs {
|
||||||
stack: vec![
|
stack: vec![
|
||||||
@@ -49,15 +54,18 @@ impl<'a> Dfs<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Peek at the next traversal event and AST node pair, if any.
|
/// Peek at the next traversal event and AST node pair, if any.
|
||||||
pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a>)> {
|
pub fn peek(&self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> {
|
||||||
self.stack.last().cloned()
|
self.stack.last().copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Dfs<'a> {
|
impl<'a, TOperator> Iterator for Dfs<'a, TOperator>
|
||||||
type Item = (TraversalEvent, DynAstRef<'a>);
|
where
|
||||||
|
TOperator: Copy,
|
||||||
|
{
|
||||||
|
type Item = (TraversalEvent, DynAstRef<'a, TOperator>);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(TraversalEvent, DynAstRef<'a>)> {
|
fn next(&mut self) -> Option<(TraversalEvent, DynAstRef<'a, TOperator>)> {
|
||||||
let (event, node) = self.stack.pop()?;
|
let (event, node) = self.stack.pop()?;
|
||||||
if let TraversalEvent::Enter = event {
|
if let TraversalEvent::Enter = event {
|
||||||
let mut enqueue_children = EnqueueChildren(self);
|
let mut enqueue_children = EnqueueChildren(self);
|
||||||
@@ -65,15 +73,16 @@ impl<'a> Iterator for Dfs<'a> {
|
|||||||
}
|
}
|
||||||
return Some((event, node));
|
return Some((event, node));
|
||||||
|
|
||||||
struct EnqueueChildren<'a, 'b>(&'b mut Dfs<'a>)
|
struct EnqueueChildren<'a, 'b, TOperator>(&'b mut Dfs<'a, TOperator>)
|
||||||
where
|
where
|
||||||
'a: 'b;
|
'a: 'b;
|
||||||
|
|
||||||
impl<'a, 'b> Extend<DynAstRef<'a>> for EnqueueChildren<'a, 'b>
|
impl<'a, 'b, TOperator> Extend<DynAstRef<'a, TOperator>> for EnqueueChildren<'a, 'b, TOperator>
|
||||||
where
|
where
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
|
TOperator: Copy,
|
||||||
{
|
{
|
||||||
fn extend<T: IntoIterator<Item = DynAstRef<'a>>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = DynAstRef<'a, TOperator>>>(&mut self, iter: T) {
|
||||||
let iter = iter.into_iter();
|
let iter = iter.into_iter();
|
||||||
|
|
||||||
let (min, max) = iter.size_hint();
|
let (min, max) = iter.size_hint();
|
||||||
@@ -97,6 +106,7 @@ impl<'a> Iterator for Dfs<'a> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
use DynAstRef::*;
|
use DynAstRef::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -107,7 +117,7 @@ mod tests {
|
|||||||
(ishl $x $(log2 $C)))
|
(ishl $x $(log2 $C)))
|
||||||
";
|
";
|
||||||
let buf = wast::parser::ParseBuffer::new(input).expect("input should lex OK");
|
let buf = wast::parser::ParseBuffer::new(input).expect("input should lex OK");
|
||||||
let ast = match wast::parser::parse::<crate::ast::Optimizations>(&buf) {
|
let ast = match wast::parser::parse::<crate::ast::Optimizations<TestOperator>>(&buf) {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(e) => panic!("expected to parse OK, got error:\n\n{}", e),
|
Err(e) => panic!("expected to parse OK, got error:\n\n{}", e),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,14 +14,13 @@
|
|||||||
|
|
||||||
use crate::ast::{Span as _, *};
|
use crate::ast::{Span as _, *};
|
||||||
use crate::traversals::{Dfs, TraversalEvent};
|
use crate::traversals::{Dfs, TraversalEvent};
|
||||||
use peepmatic_runtime::{
|
use peepmatic_runtime::r#type::{BitWidth, Kind, Type};
|
||||||
operator::{Operator, TypingContext as TypingContextTrait},
|
use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules};
|
||||||
r#type::{BitWidth, Kind, Type},
|
|
||||||
};
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@@ -94,7 +93,10 @@ impl VerifyError {
|
|||||||
pub type VerifyResult<T> = Result<T, VerifyError>;
|
pub type VerifyResult<T> = Result<T, VerifyError>;
|
||||||
|
|
||||||
/// Verify and type check a set of optimizations.
|
/// Verify and type check a set of optimizations.
|
||||||
pub fn verify(opts: &Optimizations) -> VerifyResult<()> {
|
pub fn verify<TOperator>(opts: &Optimizations<TOperator>) -> VerifyResult<()>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash + TypingRules,
|
||||||
|
{
|
||||||
if opts.optimizations.is_empty() {
|
if opts.optimizations.is_empty() {
|
||||||
return Err(anyhow::anyhow!("no optimizations").into());
|
return Err(anyhow::anyhow!("no optimizations").into());
|
||||||
}
|
}
|
||||||
@@ -113,7 +115,10 @@ pub fn verify(opts: &Optimizations) -> VerifyResult<()> {
|
|||||||
/// If there were duplicates, then it would be nondeterministic which one we
|
/// If there were duplicates, then it would be nondeterministic which one we
|
||||||
/// applied and would make automata construction more difficult. It is better to
|
/// applied and would make automata construction more difficult. It is better to
|
||||||
/// check for duplicates and reject them if found.
|
/// check for duplicates and reject them if found.
|
||||||
fn verify_unique_left_hand_sides(opts: &Optimizations) -> VerifyResult<()> {
|
fn verify_unique_left_hand_sides<TOperator>(opts: &Optimizations<TOperator>) -> VerifyResult<()>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Eq + Debug + Hash,
|
||||||
|
{
|
||||||
let mut lefts = HashMap::new();
|
let mut lefts = HashMap::new();
|
||||||
for opt in &opts.optimizations {
|
for opt in &opts.optimizations {
|
||||||
let canon_lhs = canonicalized_lhs_key(&opt.lhs);
|
let canon_lhs = canonicalized_lhs_key(&opt.lhs);
|
||||||
@@ -146,7 +151,10 @@ fn verify_unique_left_hand_sides(opts: &Optimizations) -> VerifyResult<()> {
|
|||||||
///
|
///
|
||||||
/// This function creates an opaque, canonicalized hash key for left-hand sides
|
/// This function creates an opaque, canonicalized hash key for left-hand sides
|
||||||
/// that sees through identifier renaming.
|
/// that sees through identifier renaming.
|
||||||
fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq {
|
fn canonicalized_lhs_key<TOperator>(lhs: &Lhs<TOperator>) -> impl Hash + Eq
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash,
|
||||||
|
{
|
||||||
let mut var_to_canon = HashMap::new();
|
let mut var_to_canon = HashMap::new();
|
||||||
let mut const_to_canon = HashMap::new();
|
let mut const_to_canon = HashMap::new();
|
||||||
let mut canonicalized = vec![];
|
let mut canonicalized = vec![];
|
||||||
@@ -183,19 +191,20 @@ fn canonicalized_lhs_key(lhs: &Lhs) -> impl Hash + Eq {
|
|||||||
return canonicalized;
|
return canonicalized;
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq)]
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
enum CanonicalBit {
|
enum CanonicalBit<TOperator> {
|
||||||
Var(u32),
|
Var(u32),
|
||||||
Const(u32),
|
Const(u32),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
ConditionCode(peepmatic_runtime::cc::ConditionCode),
|
ConditionCode(peepmatic_runtime::cc::ConditionCode),
|
||||||
Operation(Operator, Option<Type>),
|
Operation(TOperator, Option<Type>),
|
||||||
Precondition(Constraint),
|
Precondition(Constraint),
|
||||||
Other(&'static str),
|
Other(&'static str),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TypingContext<'a> {
|
#[derive(Debug)]
|
||||||
|
struct TypingContext<'a, TOperator> {
|
||||||
z3: &'a z3::Context,
|
z3: &'a z3::Context,
|
||||||
type_kind_sort: z3::DatatypeSort<'a>,
|
type_kind_sort: z3::DatatypeSort<'a>,
|
||||||
solver: z3::Solver<'a>,
|
solver: z3::Solver<'a>,
|
||||||
@@ -218,12 +227,15 @@ pub(crate) struct TypingContext<'a> {
|
|||||||
// Keep track of AST nodes that need to have their types assigned to
|
// Keep track of AST nodes that need to have their types assigned to
|
||||||
// them. For these AST nodes, we know what bit width to use when
|
// them. For these AST nodes, we know what bit width to use when
|
||||||
// interpreting peephole optimization actions.
|
// interpreting peephole optimization actions.
|
||||||
boolean_literals: Vec<(&'a Boolean<'a>, TypeVar<'a>)>,
|
boolean_literals: Vec<(&'a Boolean<'a, TOperator>, TypeVar<'a>)>,
|
||||||
integer_literals: Vec<(&'a Integer<'a>, TypeVar<'a>)>,
|
integer_literals: Vec<(&'a Integer<'a, TOperator>, TypeVar<'a>)>,
|
||||||
rhs_operations: Vec<(&'a Operation<'a, Rhs<'a>>, TypeVar<'a>)>,
|
rhs_operations: Vec<(
|
||||||
|
&'a Operation<'a, TOperator, Rhs<'a, TOperator>>,
|
||||||
|
TypeVar<'a>,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TypingContext<'a> {
|
impl<'a, TOperator> TypingContext<'a, TOperator> {
|
||||||
fn new(z3: &'a z3::Context) -> Self {
|
fn new(z3: &'a z3::Context) -> Self {
|
||||||
let type_kind_sort = z3::DatatypeBuilder::new(z3)
|
let type_kind_sort = z3::DatatypeBuilder::new(z3)
|
||||||
.variant("int", &[])
|
.variant("int", &[])
|
||||||
@@ -301,51 +313,55 @@ impl<'a> TypingContext<'a> {
|
|||||||
// and similar refer to the same type variables.
|
// and similar refer to the same type variables.
|
||||||
fn enter_operation_scope<'b>(
|
fn enter_operation_scope<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
) -> impl DerefMut<Target = TypingContext<'a>> + Drop + 'b {
|
) -> impl DerefMut<Target = TypingContext<'a, TOperator>> + Drop + 'b {
|
||||||
assert!(self.operation_scope.is_empty());
|
assert!(self.operation_scope.is_empty());
|
||||||
return Scope(self);
|
return Scope(self);
|
||||||
|
|
||||||
struct Scope<'a, 'b>(&'b mut TypingContext<'a>)
|
struct Scope<'a, 'b, TOperator>(&'b mut TypingContext<'a, TOperator>)
|
||||||
where
|
where
|
||||||
'a: 'b;
|
'a: 'b;
|
||||||
|
|
||||||
impl<'a, 'b> Deref for Scope<'a, 'b>
|
impl<'a, 'b, TOperator> Deref for Scope<'a, 'b, TOperator>
|
||||||
where
|
where
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
{
|
{
|
||||||
type Target = TypingContext<'a>;
|
type Target = TypingContext<'a, TOperator>;
|
||||||
fn deref(&self) -> &TypingContext<'a> {
|
fn deref(&self) -> &TypingContext<'a, TOperator> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> DerefMut for Scope<'a, 'b>
|
impl<'a, 'b, TOperator> DerefMut for Scope<'a, 'b, TOperator>
|
||||||
where
|
where
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
{
|
{
|
||||||
fn deref_mut(&mut self) -> &mut TypingContext<'a> {
|
fn deref_mut(&mut self) -> &mut TypingContext<'a, TOperator> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Scope<'_, '_> {
|
impl<TOperator> Drop for Scope<'_, '_, TOperator> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.0.operation_scope.clear();
|
self.0.operation_scope.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remember_boolean_literal(&mut self, b: &'a Boolean<'a>, ty: TypeVar<'a>) {
|
fn remember_boolean_literal(&mut self, b: &'a Boolean<'a, TOperator>, ty: TypeVar<'a>) {
|
||||||
self.assert_is_bool(b.span, &ty);
|
self.assert_is_bool(b.span, &ty);
|
||||||
self.boolean_literals.push((b, ty));
|
self.boolean_literals.push((b, ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remember_integer_literal(&mut self, i: &'a Integer<'a>, ty: TypeVar<'a>) {
|
fn remember_integer_literal(&mut self, i: &'a Integer<'a, TOperator>, ty: TypeVar<'a>) {
|
||||||
self.assert_is_integer(i.span, &ty);
|
self.assert_is_integer(i.span, &ty);
|
||||||
self.integer_literals.push((i, ty));
|
self.integer_literals.push((i, ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remember_rhs_operation(&mut self, op: &'a Operation<'a, Rhs<'a>>, ty: TypeVar<'a>) {
|
fn remember_rhs_operation(
|
||||||
|
&mut self,
|
||||||
|
op: &'a Operation<'a, TOperator, Rhs<'a, TOperator>>,
|
||||||
|
ty: TypeVar<'a>,
|
||||||
|
) {
|
||||||
self.rhs_operations.push((op, ty));
|
self.rhs_operations.push((op, ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +654,8 @@ impl<'a> TypingContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TypingContextTrait<'a> for TypingContext<'a> {
|
impl<'a, TOperator> TypingContextTrait<'a> for TypingContext<'a, TOperator> {
|
||||||
|
type Span = Span;
|
||||||
type TypeVariable = TypeVar<'a>;
|
type TypeVariable = TypeVar<'a>;
|
||||||
|
|
||||||
fn cc(&mut self, span: Span) -> TypeVar<'a> {
|
fn cc(&mut self, span: Span) -> TypeVar<'a> {
|
||||||
@@ -737,13 +754,19 @@ impl<'a> TypingContextTrait<'a> for TypingContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct TypeVar<'a> {
|
struct TypeVar<'a> {
|
||||||
kind: z3::ast::Datatype<'a>,
|
kind: z3::ast::Datatype<'a>,
|
||||||
width: z3::ast::BV<'a>,
|
width: z3::ast::BV<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_optimization(z3: &z3::Context, opt: &Optimization) -> VerifyResult<()> {
|
fn verify_optimization<TOperator>(
|
||||||
|
z3: &z3::Context,
|
||||||
|
opt: &Optimization<TOperator>,
|
||||||
|
) -> VerifyResult<()>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash + TypingRules,
|
||||||
|
{
|
||||||
let mut context = TypingContext::new(z3);
|
let mut context = TypingContext::new(z3);
|
||||||
collect_type_constraints(&mut context, opt)?;
|
collect_type_constraints(&mut context, opt)?;
|
||||||
context.type_check(opt.span)?;
|
context.type_check(opt.span)?;
|
||||||
@@ -755,10 +778,13 @@ fn verify_optimization(z3: &z3::Context, opt: &Optimization) -> VerifyResult<()>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_type_constraints<'a>(
|
fn collect_type_constraints<'a, TOperator>(
|
||||||
context: &mut TypingContext<'a>,
|
context: &mut TypingContext<'a, TOperator>,
|
||||||
opt: &'a Optimization<'a>,
|
opt: &'a Optimization<'a, TOperator>,
|
||||||
) -> VerifyResult<()> {
|
) -> VerifyResult<()>
|
||||||
|
where
|
||||||
|
TOperator: Copy + Debug + Eq + Hash + TypingRules,
|
||||||
|
{
|
||||||
use crate::traversals::TraversalEvent as TE;
|
use crate::traversals::TraversalEvent as TE;
|
||||||
|
|
||||||
let lhs_ty = context.new_type_var();
|
let lhs_ty = context.new_type_var();
|
||||||
@@ -780,8 +806,22 @@ fn collect_type_constraints<'a>(
|
|||||||
// Build up the type constraints for the left-hand side.
|
// Build up the type constraints for the left-hand side.
|
||||||
for (event, node) in Dfs::new(&opt.lhs) {
|
for (event, node) in Dfs::new(&opt.lhs) {
|
||||||
match (event, node) {
|
match (event, node) {
|
||||||
(TE::Enter, DynAstRef::Pattern(Pattern::Constant(Constant { id, span })))
|
(
|
||||||
| (TE::Enter, DynAstRef::Pattern(Pattern::Variable(Variable { id, span }))) => {
|
TE::Enter,
|
||||||
|
DynAstRef::Pattern(Pattern::Constant(Constant {
|
||||||
|
id,
|
||||||
|
span,
|
||||||
|
marker: _,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
TE::Enter,
|
||||||
|
DynAstRef::Pattern(Pattern::Variable(Variable {
|
||||||
|
id,
|
||||||
|
span,
|
||||||
|
marker: _,
|
||||||
|
})),
|
||||||
|
) => {
|
||||||
let id = context.get_or_create_type_var_for_id(*id);
|
let id = context.get_or_create_type_var_for_id(*id);
|
||||||
context.assert_type_eq(*span, expected_types.last().unwrap(), &id, None);
|
context.assert_type_eq(*span, expected_types.last().unwrap(), &id, None);
|
||||||
}
|
}
|
||||||
@@ -805,11 +845,11 @@ fn collect_type_constraints<'a>(
|
|||||||
let mut operand_types = vec![];
|
let mut operand_types = vec![];
|
||||||
{
|
{
|
||||||
let mut scope = context.enter_operation_scope();
|
let mut scope = context.enter_operation_scope();
|
||||||
result_ty = op.operator.result_type(&mut *scope, op.span);
|
result_ty = op.operator.result_type(op.span, &mut *scope);
|
||||||
op.operator
|
op.operator
|
||||||
.immediate_types(&mut *scope, op.span, &mut operand_types);
|
.immediate_types(op.span, &mut *scope, &mut operand_types);
|
||||||
op.operator
|
op.operator
|
||||||
.param_types(&mut *scope, op.span, &mut operand_types);
|
.parameter_types(op.span, &mut *scope, &mut operand_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.operands.len() != operand_types.len() {
|
if op.operands.len() != operand_types.len() {
|
||||||
@@ -841,9 +881,8 @@ fn collect_type_constraints<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match op.operator {
|
if (op.operator.is_reduce() || op.operator.is_extend()) && op.r#type.get().is_none()
|
||||||
Operator::Ireduce | Operator::Uextend | Operator::Sextend => {
|
{
|
||||||
if op.r#type.get().is_none() {
|
|
||||||
return Err(WastError::new(
|
return Err(WastError::new(
|
||||||
op.span,
|
op.span,
|
||||||
"`ireduce`, `sextend`, and `uextend` require an ascribed type, \
|
"`ireduce`, `sextend`, and `uextend` require an ascribed type, \
|
||||||
@@ -852,19 +891,13 @@ fn collect_type_constraints<'a>(
|
|||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match op.operator {
|
if op.operator.is_extend() {
|
||||||
Operator::Uextend | Operator::Sextend => {
|
|
||||||
context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]);
|
context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]);
|
||||||
}
|
}
|
||||||
Operator::Ireduce => {
|
if op.operator.is_reduce() {
|
||||||
context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]);
|
context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ty) = op.r#type.get() {
|
if let Some(ty) = op.r#type.get() {
|
||||||
match ty.kind {
|
match ty.kind {
|
||||||
@@ -916,8 +949,22 @@ fn collect_type_constraints<'a>(
|
|||||||
let ty = expected_types.last().unwrap();
|
let ty = expected_types.last().unwrap();
|
||||||
context.assert_is_cc(cc.span, ty);
|
context.assert_is_cc(cc.span, ty);
|
||||||
}
|
}
|
||||||
(TE::Enter, DynAstRef::Rhs(Rhs::Constant(Constant { span, id })))
|
(
|
||||||
| (TE::Enter, DynAstRef::Rhs(Rhs::Variable(Variable { span, id }))) => {
|
TE::Enter,
|
||||||
|
DynAstRef::Rhs(Rhs::Constant(Constant {
|
||||||
|
span,
|
||||||
|
id,
|
||||||
|
marker: _,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
TE::Enter,
|
||||||
|
DynAstRef::Rhs(Rhs::Variable(Variable {
|
||||||
|
span,
|
||||||
|
id,
|
||||||
|
marker: _,
|
||||||
|
})),
|
||||||
|
) => {
|
||||||
let id_ty = context.get_type_var_for_id(*id)?;
|
let id_ty = context.get_type_var_for_id(*id)?;
|
||||||
context.assert_type_eq(*span, expected_types.last().unwrap(), &id_ty, None);
|
context.assert_type_eq(*span, expected_types.last().unwrap(), &id_ty, None);
|
||||||
}
|
}
|
||||||
@@ -926,11 +973,11 @@ fn collect_type_constraints<'a>(
|
|||||||
let mut operand_types = vec![];
|
let mut operand_types = vec![];
|
||||||
{
|
{
|
||||||
let mut scope = context.enter_operation_scope();
|
let mut scope = context.enter_operation_scope();
|
||||||
result_ty = op.operator.result_type(&mut *scope, op.span);
|
result_ty = op.operator.result_type(op.span, &mut *scope);
|
||||||
op.operator
|
op.operator
|
||||||
.immediate_types(&mut *scope, op.span, &mut operand_types);
|
.immediate_types(op.span, &mut *scope, &mut operand_types);
|
||||||
op.operator
|
op.operator
|
||||||
.param_types(&mut *scope, op.span, &mut operand_types);
|
.parameter_types(op.span, &mut *scope, &mut operand_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.operands.len() != operand_types.len() {
|
if op.operands.len() != operand_types.len() {
|
||||||
@@ -965,9 +1012,8 @@ fn collect_type_constraints<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match op.operator {
|
if (op.operator.is_reduce() || op.operator.is_extend()) && op.r#type.get().is_none()
|
||||||
Operator::Ireduce | Operator::Uextend | Operator::Sextend => {
|
{
|
||||||
if op.r#type.get().is_none() {
|
|
||||||
return Err(WastError::new(
|
return Err(WastError::new(
|
||||||
op.span,
|
op.span,
|
||||||
"`ireduce`, `sextend`, and `uextend` require an ascribed type, \
|
"`ireduce`, `sextend`, and `uextend` require an ascribed type, \
|
||||||
@@ -976,19 +1022,13 @@ fn collect_type_constraints<'a>(
|
|||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match op.operator {
|
if op.operator.is_extend() {
|
||||||
Operator::Uextend | Operator::Sextend => {
|
|
||||||
context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]);
|
context.assert_bit_width_gt(op.span, &result_ty, &operand_types[0]);
|
||||||
}
|
}
|
||||||
Operator::Ireduce => {
|
if op.operator.is_reduce() {
|
||||||
context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]);
|
context.assert_bit_width_lt(op.span, &result_ty, &operand_types[0]);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ty) = op.r#type.get() {
|
if let Some(ty) = op.r#type.get() {
|
||||||
match ty.kind {
|
match ty.kind {
|
||||||
@@ -1017,11 +1057,11 @@ fn collect_type_constraints<'a>(
|
|||||||
let mut operand_types = vec![];
|
let mut operand_types = vec![];
|
||||||
{
|
{
|
||||||
let mut scope = context.enter_operation_scope();
|
let mut scope = context.enter_operation_scope();
|
||||||
result_ty = unq.operator.result_type(&mut *scope, unq.span);
|
result_ty = unq.operator.result_type(unq.span, &mut *scope);
|
||||||
unq.operator
|
unq.operator
|
||||||
.immediate_types(&mut *scope, unq.span, &mut operand_types);
|
.immediate_types(unq.span, &mut *scope, &mut operand_types);
|
||||||
unq.operator
|
unq.operator
|
||||||
.param_types(&mut *scope, unq.span, &mut operand_types);
|
.parameter_types(unq.span, &mut *scope, &mut operand_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
if unq.operands.len() != operand_types.len() {
|
if unq.operands.len() != operand_types.len() {
|
||||||
@@ -1068,9 +1108,9 @@ fn collect_type_constraints<'a>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_constrain_precondition<'a>(
|
fn type_constrain_precondition<'a, TOperator>(
|
||||||
context: &mut TypingContext<'a>,
|
context: &mut TypingContext<'a, TOperator>,
|
||||||
pre: &Precondition<'a>,
|
pre: &Precondition<'a, TOperator>,
|
||||||
) -> VerifyResult<()> {
|
) -> VerifyResult<()> {
|
||||||
match pre.constraint {
|
match pre.constraint {
|
||||||
Constraint::BitWidth => {
|
Constraint::BitWidth => {
|
||||||
@@ -1182,13 +1222,14 @@ fn type_constrain_precondition<'a>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use peepmatic_test_operator::TestOperator;
|
||||||
|
|
||||||
macro_rules! verify_ok {
|
macro_rules! verify_ok {
|
||||||
($name:ident, $src:expr) => {
|
($name:ident, $src:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK");
|
let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK");
|
||||||
let opts = match wast::parser::parse::<Optimizations>(&buf) {
|
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
|
||||||
Ok(opts) => opts,
|
Ok(opts) => opts,
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
e.set_path(Path::new(stringify!($name)));
|
e.set_path(Path::new(stringify!($name)));
|
||||||
@@ -1215,7 +1256,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK");
|
let buf = wast::parser::ParseBuffer::new($src).expect("should lex OK");
|
||||||
let opts = match wast::parser::parse::<Optimizations>(&buf) {
|
let opts = match wast::parser::parse::<Optimizations<TestOperator>>(&buf) {
|
||||||
Ok(opts) => opts,
|
Ok(opts) => opts,
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
e.set_path(Path::new(stringify!($name)));
|
e.set_path(Path::new(stringify!($name)));
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ use std::process::{Command, Stdio};
|
|||||||
// note that this list must be topologically sorted by dependencies
|
// note that this list must be topologically sorted by dependencies
|
||||||
const CRATES_TO_PUBLISH: &[&str] = &[
|
const CRATES_TO_PUBLISH: &[&str] = &[
|
||||||
// peepmatic
|
// peepmatic
|
||||||
|
"peepmatic-traits",
|
||||||
"peepmatic-macro",
|
"peepmatic-macro",
|
||||||
"peepmatic-automata",
|
"peepmatic-automata",
|
||||||
|
"peepmatic-test-operator",
|
||||||
"peepmatic-runtime",
|
"peepmatic-runtime",
|
||||||
"peepmatic",
|
"peepmatic",
|
||||||
// cranelift
|
// cranelift
|
||||||
|
|||||||
Reference in New Issue
Block a user