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:
@@ -27,9 +27,10 @@ where
|
||||
{
|
||||
pub(crate) peep_opt: &'peep PeepholeOptimizations<TInstructionSet::Operator>,
|
||||
pub(crate) instr_set: TInstructionSet,
|
||||
pub(crate) left_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
|
||||
pub(crate) right_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
|
||||
pub(crate) actions: Vec<Action<TInstructionSet::Operator>>,
|
||||
pub(crate) backtracking_states: Vec<(State, usize)>,
|
||||
pub(crate) backtracking_states: Vec<(State, usize, usize)>,
|
||||
}
|
||||
|
||||
impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
@@ -40,6 +41,7 @@ where
|
||||
let PeepholeOptimizer {
|
||||
peep_opt,
|
||||
instr_set: _,
|
||||
left_hand_sides,
|
||||
right_hand_sides,
|
||||
actions,
|
||||
backtracking_states,
|
||||
@@ -47,6 +49,7 @@ where
|
||||
f.debug_struct("PeepholeOptimizer")
|
||||
.field("peep_opt", peep_opt)
|
||||
.field("instr_set", &"_")
|
||||
.field("left_hand_sides", left_hand_sides)
|
||||
.field("right_hand_sides", right_hand_sides)
|
||||
.field("actions", actions)
|
||||
.field("backtracking_states", backtracking_states)
|
||||
@@ -117,12 +120,8 @@ where
|
||||
for action in actions.drain(..) {
|
||||
log::trace!("Evaluating action: {:?}", action);
|
||||
match action {
|
||||
Action::GetLhs { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let lhs = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.expect("should always get part at path OK by the time it is bound");
|
||||
Action::GetLhs { lhs } => {
|
||||
let lhs = self.left_hand_sides[lhs.0 as usize];
|
||||
self.right_hand_sides.push(lhs);
|
||||
}
|
||||
Action::UnaryUnquote { operator, operand } => {
|
||||
@@ -284,22 +283,17 @@ where
|
||||
|
||||
log::trace!("Evaluating match operation: {:?}", match_op);
|
||||
let result: MatchResult = (|| match match_op {
|
||||
Opcode { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
Opcode(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
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, &mut self.left_hand_sides)
|
||||
.ok_or(Else)?;
|
||||
Ok(op.into())
|
||||
}
|
||||
IsConst { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
IsConst(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
let is_const = match part {
|
||||
Part::Instruction(i) => {
|
||||
self.instr_set.instruction_to_constant(context, i).is_some()
|
||||
@@ -308,12 +302,8 @@ where
|
||||
};
|
||||
bool_to_match_result(is_const)
|
||||
}
|
||||
IsPowerOfTwo { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
IsPowerOfTwo(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
match part {
|
||||
Part::Constant(c) => {
|
||||
let is_pow2 = c.as_int().unwrap().is_power_of_two();
|
||||
@@ -327,18 +317,11 @@ where
|
||||
let is_pow2 = c.as_int().unwrap().is_power_of_two();
|
||||
bool_to_match_result(is_pow2)
|
||||
}
|
||||
Part::ConditionCode(_) => unreachable!(
|
||||
"IsPowerOfTwo on a condition
|
||||
code"
|
||||
),
|
||||
Part::ConditionCode(_) => unreachable!("IsPowerOfTwo on a condition code"),
|
||||
}
|
||||
}
|
||||
BitWidth { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
BitWidth(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
let bit_width = match part {
|
||||
Part::Instruction(i) => self.instr_set.instruction_result_bit_width(context, i),
|
||||
Part::Constant(Constant::Int(_, w)) | Part::Constant(Constant::Bool(_, w)) => {
|
||||
@@ -355,15 +338,11 @@ where
|
||||
);
|
||||
Ok(unsafe { NonZeroU32::new_unchecked(bit_width as u32) })
|
||||
}
|
||||
FitsInNativeWord { path } => {
|
||||
FitsInNativeWord(id) => {
|
||||
let native_word_size = self.instr_set.native_word_size_in_bits(context);
|
||||
debug_assert!(native_word_size.is_power_of_two());
|
||||
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
let fits = match part {
|
||||
Part::Instruction(i) => {
|
||||
let size = self.instr_set.instruction_result_bit_width(context, i);
|
||||
@@ -378,17 +357,9 @@ where
|
||||
};
|
||||
bool_to_match_result(fits)
|
||||
}
|
||||
Eq { path_a, path_b } => {
|
||||
let path_a = self.peep_opt.paths.lookup(path_a);
|
||||
let part_a = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path_a)
|
||||
.ok_or(Else)?;
|
||||
let path_b = self.peep_opt.paths.lookup(path_b);
|
||||
let part_b = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path_b)
|
||||
.ok_or(Else)?;
|
||||
Eq(a, b) => {
|
||||
let part_a = self.left_hand_sides[a.0 as usize];
|
||||
let part_b = self.left_hand_sides[b.0 as usize];
|
||||
let eq = match (part_a, part_b) {
|
||||
(Part::Instruction(inst), Part::Constant(c1))
|
||||
| (Part::Constant(c1), Part::Instruction(inst)) => {
|
||||
@@ -401,12 +372,8 @@ where
|
||||
};
|
||||
bool_to_match_result(eq)
|
||||
}
|
||||
IntegerValue { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
IntegerValue(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
match part {
|
||||
Part::Constant(c) => {
|
||||
let x = c.as_int().ok_or(Else)?;
|
||||
@@ -425,12 +392,8 @@ where
|
||||
Part::ConditionCode(_) => unreachable!("IntegerValue on condition code"),
|
||||
}
|
||||
}
|
||||
BooleanValue { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
BooleanValue(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
match part {
|
||||
Part::Constant(c) => {
|
||||
let b = c.as_bool().ok_or(Else)?;
|
||||
@@ -447,12 +410,8 @@ where
|
||||
Part::ConditionCode(_) => unreachable!("IntegerValue on condition code"),
|
||||
}
|
||||
}
|
||||
ConditionCode { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
let part = self
|
||||
.instr_set
|
||||
.get_part_at_path(context, root, path)
|
||||
.ok_or(Else)?;
|
||||
ConditionCode(id) => {
|
||||
let part = self.left_hand_sides[id.0 as usize];
|
||||
let cc = part.as_condition_code().ok_or(Else)?;
|
||||
let cc = cc as u32;
|
||||
debug_assert!(cc != 0);
|
||||
@@ -483,12 +442,20 @@ where
|
||||
self.backtracking_states.clear();
|
||||
self.actions.clear();
|
||||
self.right_hand_sides.clear();
|
||||
self.left_hand_sides.clear();
|
||||
|
||||
// `LhsId(0)` is always the root.
|
||||
self.left_hand_sides.push(Part::Instruction(root));
|
||||
|
||||
let mut r#final = None;
|
||||
|
||||
let mut query = self.peep_opt.automata.query();
|
||||
loop {
|
||||
log::trace!("Current state: {:?}", query.current_state());
|
||||
log::trace!(
|
||||
"self.left_hand_sides = {:#?}",
|
||||
self.left_hand_sides.iter().enumerate().collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
if query.is_in_final_state() {
|
||||
// If we're in a final state (which means an optimization is
|
||||
@@ -507,8 +474,11 @@ where
|
||||
// optimization, we want to be able to backtrack to this state and
|
||||
// then try taking the `Else` transition.
|
||||
if query.has_transition_on(&Err(Else)) {
|
||||
self.backtracking_states
|
||||
.push((query.current_state(), self.actions.len()));
|
||||
self.backtracking_states.push((
|
||||
query.current_state(),
|
||||
self.actions.len(),
|
||||
self.left_hand_sides.len(),
|
||||
));
|
||||
}
|
||||
|
||||
let match_op = match query.current_state_data() {
|
||||
@@ -522,9 +492,10 @@ where
|
||||
actions
|
||||
} else if r#final.is_some() {
|
||||
break;
|
||||
} else if let Some((state, actions_len)) = self.backtracking_states.pop() {
|
||||
} else if let Some((state, actions_len, lhs_len)) = self.backtracking_states.pop() {
|
||||
query.go_to_state(state);
|
||||
self.actions.truncate(actions_len);
|
||||
self.left_hand_sides.truncate(lhs_len);
|
||||
query
|
||||
.next(&Err(Else))
|
||||
.expect("backtracking states always have `Else` transitions")
|
||||
|
||||
Reference in New Issue
Block a user