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:
@@ -14,14 +14,13 @@ use peepmatic_runtime::{
|
||||
cc::ConditionCode,
|
||||
instruction_set::InstructionSet,
|
||||
part::{Constant, Part},
|
||||
paths::Path,
|
||||
r#type::{BitWidth, Kind, Type},
|
||||
PeepholeOptimizations, PeepholeOptimizer,
|
||||
};
|
||||
use peepmatic_traits::TypingRules;
|
||||
use std::borrow::Cow;
|
||||
use std::boxed::Box;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::iter;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
@@ -573,35 +572,6 @@ fn intcc_to_peepmatic(cc: IntCC) -> ConditionCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_immediate(dfg: &DataFlowGraph, inst: Inst, i: usize) -> Part<ValueOrInst> {
|
||||
return match dfg[inst] {
|
||||
InstructionData::BinaryImm64 { imm, .. } if i == 0 => imm.into(),
|
||||
InstructionData::BranchIcmp { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::BranchInt { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCompare { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCompareImm { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCompareImm { imm, .. } if i == 1 => imm.into(),
|
||||
InstructionData::IntCond { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCondTrap { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntSelect { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::UnaryBool { imm, .. } if i == 0 => {
|
||||
Constant::Bool(imm, BitWidth::Polymorphic).into()
|
||||
}
|
||||
InstructionData::UnaryImm { imm, .. } if i == 0 => imm.into(),
|
||||
ref otherwise => unsupported(otherwise),
|
||||
};
|
||||
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn unsupported(data: &InstructionData) -> ! {
|
||||
panic!("unsupported instruction data: {:?}", data)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_argument(dfg: &DataFlowGraph, inst: Inst, i: usize) -> Option<Value> {
|
||||
dfg.inst_args(inst).get(i).copied()
|
||||
}
|
||||
|
||||
fn peepmatic_ty_to_ir_ty(ty: Type, dfg: &DataFlowGraph, root: Inst) -> types::Type {
|
||||
match (ty.kind, bit_width(dfg, ty.bit_width, root)) {
|
||||
(Kind::Int, 8) => types::I8,
|
||||
@@ -681,39 +651,290 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_part_at_path(
|
||||
fn operator<E>(
|
||||
&self,
|
||||
pos: &mut FuncCursor<'b>,
|
||||
root: ValueOrInst,
|
||||
path: Path,
|
||||
) -> Option<Part<ValueOrInst>> {
|
||||
// The root is path [0].
|
||||
debug_assert!(!path.0.is_empty());
|
||||
debug_assert_eq!(path.0[0], 0);
|
||||
|
||||
let mut part = Part::Instruction(root);
|
||||
for p in path.0[1..].iter().copied() {
|
||||
let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?;
|
||||
let operator = pos.func.dfg[inst].opcode();
|
||||
|
||||
if p < operator.immediates_arity() {
|
||||
part = get_immediate(&pos.func.dfg, inst, p as usize);
|
||||
continue;
|
||||
value_or_inst: ValueOrInst,
|
||||
operands: &mut E,
|
||||
) -> Option<Opcode>
|
||||
where
|
||||
E: Extend<Part<Self::Instruction>>,
|
||||
{
|
||||
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
|
||||
Some(match pos.func.dfg[inst] {
|
||||
InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Band,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Bor,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Bxor,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Iadd,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Ifcmp,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Imul,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Ishl,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Isub,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Rotl,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Rotr,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Sdiv,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Srem,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Sshr,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Udiv,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Urem,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Ushr,
|
||||
args,
|
||||
} => {
|
||||
operands.extend(args.iter().map(|v| Part::Instruction((*v).into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
let arg = p - operator.immediates_arity();
|
||||
let arg = arg as usize;
|
||||
let value = get_argument(&pos.func.dfg, inst, arg)?;
|
||||
part = Part::Instruction(value.into());
|
||||
}
|
||||
InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::BandImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::BorImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::BxorImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IaddImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IfcmpImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::ImulImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IrsubImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IshlImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::RotlImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::RotrImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::SdivImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::SremImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::SshrImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::UdivImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::UremImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::UshrImm,
|
||||
imm,
|
||||
arg,
|
||||
} => {
|
||||
operands.extend(
|
||||
iter::once(imm.into()).chain(iter::once(Part::Instruction(arg.into()))),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
log::trace!("get_part_at_path({:?}) = {:?}", path, part);
|
||||
Some(part)
|
||||
}
|
||||
InstructionData::Branch {
|
||||
opcode: opcode @ Opcode::Brnz,
|
||||
ref args,
|
||||
destination: _,
|
||||
}
|
||||
| InstructionData::Branch {
|
||||
opcode: opcode @ Opcode::Brz,
|
||||
ref args,
|
||||
destination: _,
|
||||
} => {
|
||||
operands.extend(
|
||||
args.as_slice(&pos.func.dfg.value_lists)
|
||||
.iter()
|
||||
.map(|v| Part::Instruction((*v).into()))
|
||||
// NB: Peepmatic only knows about the condition, not any
|
||||
// of the arguments to the block, which are special
|
||||
// cased elsewhere, if/when we actually replace the
|
||||
// instruction.
|
||||
.take(1),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Opcode> {
|
||||
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
|
||||
Some(pos.func.dfg[inst].opcode())
|
||||
InstructionData::CondTrap {
|
||||
opcode: opcode @ Opcode::Trapnz,
|
||||
arg,
|
||||
code: _,
|
||||
}
|
||||
| InstructionData::CondTrap {
|
||||
opcode: opcode @ Opcode::Trapz,
|
||||
arg,
|
||||
code: _,
|
||||
} => {
|
||||
operands.extend(iter::once(Part::Instruction(arg.into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::IntCompare {
|
||||
opcode: opcode @ Opcode::Icmp,
|
||||
cond,
|
||||
args,
|
||||
} => {
|
||||
operands.extend(
|
||||
iter::once(intcc_to_peepmatic(cond).into())
|
||||
.chain(args.iter().map(|v| Part::Instruction((*v).into()))),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::IntCompareImm {
|
||||
opcode: opcode @ Opcode::IcmpImm,
|
||||
cond,
|
||||
imm,
|
||||
arg,
|
||||
} => {
|
||||
operands.extend(
|
||||
iter::once(intcc_to_peepmatic(cond).into())
|
||||
.chain(iter::once(Part::Constant(imm.into())))
|
||||
.chain(iter::once(Part::Instruction(arg.into()))),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::Ternary {
|
||||
opcode: opcode @ Opcode::Select,
|
||||
ref args,
|
||||
} => {
|
||||
operands.extend(args.iter().map(|v| Part::Instruction((*v).into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::AdjustSpDown,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Bint,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Ireduce,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Sextend,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Uextend,
|
||||
arg,
|
||||
} => {
|
||||
operands.extend(iter::once(Part::Instruction(arg.into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::UnaryBool { opcode, imm } => {
|
||||
operands.extend(iter::once(Part::Constant(Constant::Bool(
|
||||
imm,
|
||||
BitWidth::Polymorphic,
|
||||
))));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::UnaryImm {
|
||||
opcode: opcode @ Opcode::AdjustSpDownImm,
|
||||
imm,
|
||||
}
|
||||
| InstructionData::UnaryImm {
|
||||
opcode: opcode @ Opcode::Iconst,
|
||||
imm,
|
||||
} => {
|
||||
operands.extend(iter::once(imm.into()));
|
||||
opcode
|
||||
}
|
||||
ref otherwise => {
|
||||
log::trace!("Not supported by Peepmatic: {:?}", otherwise);
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn make_inst_1(
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user