peepmatic: Do not use paths in linear IR
Rather than using paths from the root instruction to the instruction we are matching against or checking if it is constant or whatever, use temporary variables. When we successfully match an instruction's opcode, we simultaneously define these temporaries for the instruction's operands. This is similar to how open-coding these matches in Rust would use `match` expressions with pattern matching to bind the operands to variables at the same time. This saves about 1.8% of instructions retired when Peepmatic is enabled.
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
//! Passes over the linear IR.
|
||||
|
||||
use peepmatic_runtime::{
|
||||
linear,
|
||||
paths::{PathId, PathInterner},
|
||||
};
|
||||
use peepmatic_runtime::linear;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
@@ -33,13 +30,12 @@ where
|
||||
{
|
||||
let linear::Optimizations {
|
||||
ref mut optimizations,
|
||||
ref paths,
|
||||
..
|
||||
} = opts;
|
||||
|
||||
// NB: we *cannot* use an unstable sort here, because we want deterministic
|
||||
// compilation of optimizations to automata.
|
||||
optimizations.sort_by(|a, b| compare_optimization_generality(paths, a, b));
|
||||
optimizations.sort_by(compare_optimization_generality);
|
||||
debug_assert!(is_sorted_by_generality(opts));
|
||||
}
|
||||
|
||||
@@ -52,17 +48,14 @@ where
|
||||
{
|
||||
let linear::Optimizations {
|
||||
ref mut optimizations,
|
||||
ref paths,
|
||||
..
|
||||
} = opts;
|
||||
|
||||
// NB: we *cannot* use an unstable sort here, same as above.
|
||||
optimizations
|
||||
.sort_by(|a, b| compare_optimizations(paths, a, b, |a_len, b_len| a_len.cmp(&b_len)));
|
||||
optimizations.sort_by(|a, b| compare_optimizations(a, b, |a_len, b_len| a_len.cmp(&b_len)));
|
||||
}
|
||||
|
||||
fn compare_optimizations<TOperator>(
|
||||
paths: &PathInterner,
|
||||
a: &linear::Optimization<TOperator>,
|
||||
b: &linear::Optimization<TOperator>,
|
||||
compare_lengths: impl Fn(usize, usize) -> Ordering,
|
||||
@@ -71,7 +64,7 @@ where
|
||||
TOperator: Copy + Debug + Eq + Hash,
|
||||
{
|
||||
for (a, b) in a.matches.iter().zip(b.matches.iter()) {
|
||||
let c = compare_match_op_generality(paths, a.operation, b.operation);
|
||||
let c = compare_match_op_generality(a.operation, b.operation);
|
||||
if c != Ordering::Equal {
|
||||
return c;
|
||||
}
|
||||
@@ -91,86 +84,62 @@ where
|
||||
}
|
||||
|
||||
fn compare_optimization_generality<TOperator>(
|
||||
paths: &PathInterner,
|
||||
a: &linear::Optimization<TOperator>,
|
||||
b: &linear::Optimization<TOperator>,
|
||||
) -> Ordering
|
||||
where
|
||||
TOperator: Copy + Debug + Eq + Hash,
|
||||
{
|
||||
compare_optimizations(paths, a, b, |a_len, b_len| {
|
||||
compare_optimizations(a, b, |a_len, b_len| {
|
||||
// If they shared equivalent prefixes, then compare lengths and invert the
|
||||
// result because longer patterns are less general than shorter patterns.
|
||||
a_len.cmp(&b_len).reverse()
|
||||
})
|
||||
}
|
||||
|
||||
fn compare_match_op_generality(
|
||||
paths: &PathInterner,
|
||||
a: linear::MatchOp,
|
||||
b: linear::MatchOp,
|
||||
) -> Ordering {
|
||||
fn compare_match_op_generality(a: linear::MatchOp, b: linear::MatchOp) -> Ordering {
|
||||
use linear::MatchOp::*;
|
||||
match (a, b) {
|
||||
(Opcode { path: a }, Opcode { path: b }) => compare_paths(paths, a, b),
|
||||
(Opcode { .. }, _) => Ordering::Less,
|
||||
(_, Opcode { .. }) => Ordering::Greater,
|
||||
(Opcode(a), Opcode(b)) => a.cmp(&b),
|
||||
(Opcode(_), _) => Ordering::Less,
|
||||
(_, Opcode(_)) => Ordering::Greater,
|
||||
|
||||
(IntegerValue { path: a }, IntegerValue { path: b }) => compare_paths(paths, a, b),
|
||||
(IntegerValue { .. }, _) => Ordering::Less,
|
||||
(_, IntegerValue { .. }) => Ordering::Greater,
|
||||
(IntegerValue(a), IntegerValue(b)) => a.cmp(&b),
|
||||
(IntegerValue(_), _) => Ordering::Less,
|
||||
(_, IntegerValue(_)) => Ordering::Greater,
|
||||
|
||||
(BooleanValue { path: a }, BooleanValue { path: b }) => compare_paths(paths, a, b),
|
||||
(BooleanValue { .. }, _) => Ordering::Less,
|
||||
(_, BooleanValue { .. }) => Ordering::Greater,
|
||||
(BooleanValue(a), BooleanValue(b)) => a.cmp(&b),
|
||||
(BooleanValue(_), _) => Ordering::Less,
|
||||
(_, BooleanValue(_)) => Ordering::Greater,
|
||||
|
||||
(ConditionCode { path: a }, ConditionCode { path: b }) => compare_paths(paths, a, b),
|
||||
(ConditionCode { .. }, _) => Ordering::Less,
|
||||
(_, ConditionCode { .. }) => Ordering::Greater,
|
||||
(ConditionCode(a), ConditionCode(b)) => a.cmp(&b),
|
||||
(ConditionCode(_), _) => Ordering::Less,
|
||||
(_, ConditionCode(_)) => Ordering::Greater,
|
||||
|
||||
(IsConst { path: a }, IsConst { path: b }) => compare_paths(paths, a, b),
|
||||
(IsConst { .. }, _) => Ordering::Less,
|
||||
(_, IsConst { .. }) => Ordering::Greater,
|
||||
(IsConst(a), IsConst(b)) => a.cmp(&b),
|
||||
(IsConst(_), _) => Ordering::Less,
|
||||
(_, IsConst(_)) => Ordering::Greater,
|
||||
|
||||
(
|
||||
Eq {
|
||||
path_a: pa1,
|
||||
path_b: pb1,
|
||||
},
|
||||
Eq {
|
||||
path_a: pa2,
|
||||
path_b: pb2,
|
||||
},
|
||||
) => compare_paths(paths, pa1, pa2).then(compare_paths(paths, pb1, pb2)),
|
||||
(Eq { .. }, _) => Ordering::Less,
|
||||
(_, Eq { .. }) => Ordering::Greater,
|
||||
(Eq(a1, b1), Eq(a2, b2)) => a1.cmp(&a2).then(b1.cmp(&b2)),
|
||||
(Eq(..), _) => Ordering::Less,
|
||||
(_, Eq(..)) => Ordering::Greater,
|
||||
|
||||
(IsPowerOfTwo { path: a }, IsPowerOfTwo { path: b }) => compare_paths(paths, a, b),
|
||||
(IsPowerOfTwo { .. }, _) => Ordering::Less,
|
||||
(_, IsPowerOfTwo { .. }) => Ordering::Greater,
|
||||
(IsPowerOfTwo(a), IsPowerOfTwo(b)) => a.cmp(&b),
|
||||
(IsPowerOfTwo(_), _) => Ordering::Less,
|
||||
(_, IsPowerOfTwo(_)) => Ordering::Greater,
|
||||
|
||||
(BitWidth { path: a }, BitWidth { path: b }) => compare_paths(paths, a, b),
|
||||
(BitWidth { .. }, _) => Ordering::Less,
|
||||
(_, BitWidth { .. }) => Ordering::Greater,
|
||||
(BitWidth(a), BitWidth(b)) => a.cmp(&b),
|
||||
(BitWidth(_), _) => Ordering::Less,
|
||||
(_, BitWidth(_)) => Ordering::Greater,
|
||||
|
||||
(FitsInNativeWord { path: a }, FitsInNativeWord { path: b }) => compare_paths(paths, a, b),
|
||||
(FitsInNativeWord { .. }, _) => Ordering::Less,
|
||||
(_, FitsInNativeWord { .. }) => Ordering::Greater,
|
||||
(FitsInNativeWord(a), FitsInNativeWord(b)) => a.cmp(&b),
|
||||
(FitsInNativeWord(_), _) => Ordering::Less,
|
||||
(_, FitsInNativeWord(_)) => Ordering::Greater,
|
||||
|
||||
(Nop, Nop) => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_paths(paths: &PathInterner, a: PathId, b: PathId) -> Ordering {
|
||||
if a == b {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
let a = paths.lookup(a);
|
||||
let b = paths.lookup(b);
|
||||
a.0.cmp(&b.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Are the given optimizations sorted from least to most general?
|
||||
pub(crate) fn is_sorted_by_generality<TOperator>(opts: &linear::Optimizations<TOperator>) -> bool
|
||||
where
|
||||
@@ -178,7 +147,7 @@ where
|
||||
{
|
||||
opts.optimizations
|
||||
.windows(2)
|
||||
.all(|w| compare_optimization_generality(&opts.paths, &w[0], &w[1]) <= Ordering::Equal)
|
||||
.all(|w| compare_optimization_generality(&w[0], &w[1]) <= Ordering::Equal)
|
||||
}
|
||||
|
||||
/// Are the given optimizations sorted lexicographically?
|
||||
@@ -189,8 +158,7 @@ where
|
||||
TOperator: Copy + Debug + Eq + Hash,
|
||||
{
|
||||
opts.optimizations.windows(2).all(|w| {
|
||||
compare_optimizations(&opts.paths, &w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len))
|
||||
<= Ordering::Equal
|
||||
compare_optimizations(&w[0], &w[1], |a_len, b_len| a_len.cmp(&b_len)) <= Ordering::Equal
|
||||
})
|
||||
}
|
||||
|
||||
@@ -309,10 +277,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::*;
|
||||
use peepmatic_runtime::{
|
||||
linear::{bool_to_match_result, Else, MatchOp::*, MatchResult},
|
||||
paths::*,
|
||||
};
|
||||
use peepmatic_runtime::linear::{bool_to_match_result, Else, LhsId, MatchOp::*, MatchResult};
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
@@ -374,7 +339,6 @@ mod tests {
|
||||
eprintln!("after = {:#?}", before);
|
||||
|
||||
let linear::Optimizations {
|
||||
mut paths,
|
||||
mut integers,
|
||||
optimizations,
|
||||
} = opts;
|
||||
@@ -389,9 +353,8 @@ mod tests {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut p = |p: &[u8]| paths.intern(Path::new(&p));
|
||||
let mut i = |i: u64| Ok(integers.intern(i).into());
|
||||
let expected = $make_expected(&mut p, &mut i);
|
||||
let expected = $make_expected(&mut i);
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
@@ -452,7 +415,6 @@ mod tests {
|
||||
eprintln!("after = {:#?}", before);
|
||||
|
||||
let linear::Optimizations {
|
||||
mut paths,
|
||||
mut integers,
|
||||
optimizations,
|
||||
} = opts;
|
||||
@@ -467,9 +429,8 @@ mod tests {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut p = |p: &[u8]| paths.intern(Path::new(&p));
|
||||
let mut i = |i: u64| Ok(integers.intern(i).into());
|
||||
let expected = $make_expected(&mut p, &mut i);
|
||||
let expected = $make_expected(&mut i);
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
@@ -488,55 +449,43 @@ mod tests {
|
||||
(=> (iadd $x 42) 0)
|
||||
(=> (iadd $x (iadd $y $z)) 0)
|
||||
",
|
||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
|i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Opcode { path: p(&[0, 1]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(2)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Nop, Err(Else)),
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IntegerValue { path: p(&[0, 1]) }, i(42))
|
||||
(IntegerValue(LhsId(2)), i(42))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
|
||||
(
|
||||
IsPowerOfTwo { path: p(&[0, 1]) },
|
||||
bool_to_match_result(true)
|
||||
)
|
||||
(IsConst(LhsId(2)), bool_to_match_result(true)),
|
||||
(IsPowerOfTwo(LhsId(2)), bool_to_match_result(true))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true)),
|
||||
(
|
||||
BitWidth { path: p(&[0, 0]) },
|
||||
Ok(NonZeroU32::new(32).unwrap())
|
||||
)
|
||||
(IsConst(LhsId(2)), bool_to_match_result(true)),
|
||||
(BitWidth(LhsId(1)), Ok(NonZeroU32::new(32).unwrap()))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IsConst { path: p(&[0, 1]) }, bool_to_match_result(true))
|
||||
(IsConst(LhsId(2)), bool_to_match_result(true))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(
|
||||
Eq {
|
||||
path_a: p(&[0, 1]),
|
||||
path_b: p(&[0, 0]),
|
||||
},
|
||||
bool_to_match_result(true)
|
||||
)
|
||||
(Eq(LhsId(2), LhsId(1)), bool_to_match_result(true))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Nop, Err(Else)),
|
||||
],
|
||||
@@ -554,36 +503,36 @@ mod tests {
|
||||
(=> (imul 2 $x) (ishl $x 1))
|
||||
(=> (imul $x 2) (ishl $x 1))
|
||||
",
|
||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
|i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||
(IntegerValue { path: p(&[0, 0]) }, i(2)),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Imul.into())),
|
||||
(IntegerValue(LhsId(1)), i(2)),
|
||||
(Nop, Err(Else))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||
(IntegerValue { path: p(&[0, 0]) }, i(1)),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Imul.into())),
|
||||
(IntegerValue(LhsId(1)), i(1)),
|
||||
(Nop, Err(Else))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Imul.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IntegerValue { path: p(&[0, 1]) }, i(2))
|
||||
(IntegerValue(LhsId(2)), i(2))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Imul.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Imul.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IntegerValue { path: p(&[0, 1]) }, i(1))
|
||||
(IntegerValue(LhsId(2)), i(1))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(IntegerValue { path: p(&[0, 0]) }, i(0)),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(IntegerValue(LhsId(1)), i(0)),
|
||||
(Nop, Err(Else))
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Iadd.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Iadd.into())),
|
||||
(Nop, Err(Else)),
|
||||
(IntegerValue { path: p(&[0, 1]) }, i(0))
|
||||
(IntegerValue(LhsId(2)), i(0))
|
||||
]
|
||||
]
|
||||
);
|
||||
@@ -597,32 +546,20 @@ mod tests {
|
||||
(=> (bor (bor $x $y) $y)
|
||||
(bor $x $y))
|
||||
",
|
||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
|i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(1)), Ok(TestOperator::Bor.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Eq(LhsId(3), LhsId(2)), bool_to_match_result(true)),
|
||||
(Nop, Err(Else)),
|
||||
(
|
||||
Eq {
|
||||
path_a: p(&[0, 1]),
|
||||
path_b: p(&[0, 0, 0]),
|
||||
},
|
||||
bool_to_match_result(true)
|
||||
),
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(1)), Ok(TestOperator::Bor.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Nop, Err(Else)),
|
||||
(
|
||||
Eq {
|
||||
path_a: p(&[0, 1]),
|
||||
path_b: p(&[0, 0, 1]),
|
||||
},
|
||||
bool_to_match_result(true)
|
||||
),
|
||||
(Eq(LhsId(4), LhsId(2)), bool_to_match_result(true)),
|
||||
],
|
||||
]
|
||||
);
|
||||
@@ -636,39 +573,21 @@ mod tests {
|
||||
(=> (bor (bor $x $y) $y)
|
||||
(bor $x $y))
|
||||
",
|
||||
|p: &mut dyn FnMut(&[u8]) -> PathId, i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
|i: &mut dyn FnMut(u64) -> MatchResult| vec![
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(1)), Ok(TestOperator::Bor.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Eq(LhsId(3), LhsId(2)), bool_to_match_result(true)),
|
||||
(Nop, Err(Else)),
|
||||
(
|
||||
Eq {
|
||||
path_a: p(&[0, 1]),
|
||||
path_b: p(&[0, 0, 0]),
|
||||
},
|
||||
bool_to_match_result(true)
|
||||
),
|
||||
],
|
||||
vec![
|
||||
(Opcode { path: p(&[0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode { path: p(&[0, 0]) }, Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(0)), Ok(TestOperator::Bor.into())),
|
||||
(Opcode(LhsId(1)), Ok(TestOperator::Bor.into())),
|
||||
(Nop, Err(Else)),
|
||||
(Eq(LhsId(3), LhsId(2)), Err(Else)),
|
||||
(Nop, Err(Else)),
|
||||
(
|
||||
Eq {
|
||||
path_a: p(&[0, 1]),
|
||||
path_b: p(&[0, 0, 0]),
|
||||
},
|
||||
Err(Else),
|
||||
),
|
||||
(
|
||||
Eq {
|
||||
path_a: p(&[0, 1]),
|
||||
path_b: p(&[0, 0, 1]),
|
||||
},
|
||||
bool_to_match_result(true)
|
||||
),
|
||||
(Eq(LhsId(4), LhsId(2)), bool_to_match_result(true)),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user