This change means that running
cargo test --features "enable-peepmatic rebuild-peephole-optimizers"
inside `cranelift/codegen` will rebuild peephole optimizers on not only x86_64
but also aarch64.
1343 lines
45 KiB
Rust
1343 lines
45 KiB
Rust
//! Glue for working with `peepmatic`-generated peephole optimizers.
|
|
|
|
use crate::cursor::{Cursor, FuncCursor};
|
|
use crate::ir::{
|
|
dfg::DataFlowGraph,
|
|
entities::{Inst, Value},
|
|
immediates::{Imm64, Uimm64},
|
|
instructions::{InstructionData, Opcode},
|
|
types, InstBuilder,
|
|
};
|
|
use crate::isa::TargetIsa;
|
|
use cranelift_codegen_shared::condcodes::IntCC;
|
|
use peepmatic_runtime::{
|
|
cc::ConditionCode,
|
|
instruction_set::InstructionSet,
|
|
part::{Constant, Part},
|
|
r#type::{BitWidth, Kind, Type},
|
|
PeepholeOptimizations, PeepholeOptimizer,
|
|
};
|
|
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};
|
|
|
|
peepmatic_traits::define_parse_and_typing_rules_for_operator! {
|
|
Opcode {
|
|
adjust_sp_down => AdjustSpDown {
|
|
parameters(iNN);
|
|
result(void);
|
|
}
|
|
adjust_sp_down_imm => AdjustSpDownImm {
|
|
immediates(iNN);
|
|
result(void);
|
|
}
|
|
band => Band {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
band_imm => BandImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
bconst => Bconst {
|
|
immediates(b1);
|
|
result(bNN);
|
|
}
|
|
bint => Bint {
|
|
parameters(bNN);
|
|
result(iNN);
|
|
}
|
|
bor => Bor {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
bor_imm => BorImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
brnz => Brnz {
|
|
parameters(bool_or_int);
|
|
result(void);
|
|
}
|
|
brz => Brz {
|
|
parameters(bool_or_int);
|
|
result(void);
|
|
}
|
|
bxor => Bxor {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
bxor_imm => BxorImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
iadd => Iadd {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
iadd_imm => IaddImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
icmp => Icmp {
|
|
immediates(cc);
|
|
parameters(iNN, iNN);
|
|
result(b1);
|
|
}
|
|
icmp_imm => IcmpImm {
|
|
immediates(cc, iNN);
|
|
parameters(iNN);
|
|
result(b1);
|
|
}
|
|
iconst => Iconst {
|
|
immediates(iNN);
|
|
result(iNN);
|
|
}
|
|
ifcmp => Ifcmp {
|
|
parameters(iNN, iNN);
|
|
result(cpu_flags);
|
|
}
|
|
ifcmp_imm => IfcmpImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(cpu_flags);
|
|
}
|
|
imul => Imul {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
imul_imm => ImulImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
ireduce => Ireduce {
|
|
parameters(iNN);
|
|
result(iMM);
|
|
is_reduce(true);
|
|
}
|
|
irsub_imm => IrsubImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
ishl => Ishl {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
ishl_imm => IshlImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
isub => Isub {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
rotl => Rotl {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
rotl_imm => RotlImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
rotr => Rotr {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
rotr_imm => RotrImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
sdiv => Sdiv {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
sdiv_imm => SdivImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
select => Select {
|
|
parameters(bool_or_int, any_t, any_t);
|
|
result(any_t);
|
|
}
|
|
sextend => Sextend {
|
|
parameters(iNN);
|
|
result(iMM);
|
|
is_extend(true);
|
|
}
|
|
srem => Srem {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
srem_imm => SremImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
sshr => Sshr {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
sshr_imm => SshrImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
trapnz => Trapnz {
|
|
parameters(bool_or_int);
|
|
result(void);
|
|
}
|
|
trapz => Trapz {
|
|
parameters(bool_or_int);
|
|
result(void);
|
|
}
|
|
udiv => Udiv {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
udiv_imm => UdivImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
uextend => Uextend {
|
|
parameters(iNN);
|
|
result(iMM);
|
|
is_extend(true);
|
|
}
|
|
urem => Urem {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
urem_imm => UremImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
ushr => Ushr {
|
|
parameters(iNN, iNN);
|
|
result(iNN);
|
|
}
|
|
ushr_imm => UshrImm {
|
|
immediates(iNN);
|
|
parameters(iNN);
|
|
result(iNN);
|
|
}
|
|
}
|
|
parse_cfg(feature = "rebuild-peephole-optimizers");
|
|
}
|
|
|
|
/// Code required to rebuild Peepmatic-based peephole optimizers.
|
|
///
|
|
/// This module is used to scope imports and dependencies that are only required
|
|
/// for building peephole optimizers (as opposed to just using pre-built
|
|
/// peephole optimizers). This helps ensure that our regular builds using
|
|
/// pre-built peephole optimizers stay lean.
|
|
#[cfg(feature = "rebuild-peephole-optimizers")]
|
|
mod rebuild {
|
|
use super::*;
|
|
use alloc::vec::Vec;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
/// Rebuild the `preopt.peepmatic` peephole optimizer.
|
|
///
|
|
/// Saves and overwrites the old `preopt.serialized` build and returns a
|
|
/// copy of the result.
|
|
pub fn rebuild_preopt() -> Vec<u8> {
|
|
let codegen_path = Path::new(include_str!(concat!(
|
|
env!("OUT_DIR"),
|
|
"/CRANELIFT_CODEGEN_PATH"
|
|
)));
|
|
let source_path = codegen_path.join("src").join("preopt.peepmatic");
|
|
|
|
let preopt = peepmatic::compile_file::<Opcode>(&source_path)
|
|
.expect("failed to compile `src/preopt.peepmatic`");
|
|
|
|
let serialized_path = codegen_path.join("src").join("preopt.serialized");
|
|
preopt
|
|
.serialize_to_file(&serialized_path)
|
|
.expect("failed to serialize peephole optimizer to `src/preopt.serialized`");
|
|
fs::read(&serialized_path).expect("failed to read `src/preopt.serialized`")
|
|
}
|
|
}
|
|
|
|
/// Get the `preopt.peepmatic` peephole optimizer.
|
|
pub(crate) fn preopt<'a, 'b>(
|
|
isa: &'b dyn TargetIsa,
|
|
) -> PeepholeOptimizer<'static, 'a, &'b dyn TargetIsa> {
|
|
#[cfg(feature = "rebuild-peephole-optimizers")]
|
|
fn get_serialized() -> Cow<'static, [u8]> {
|
|
rebuild::rebuild_preopt().into()
|
|
}
|
|
|
|
#[cfg(not(feature = "rebuild-peephole-optimizers"))]
|
|
fn get_serialized() -> Cow<'static, [u8]> {
|
|
static SERIALIZED: &[u8] = include_bytes!("preopt.serialized");
|
|
SERIALIZED.into()
|
|
}
|
|
|
|
// Once initialized, this must never be re-assigned. The initialized value
|
|
// is semantically "static data" and is intentionally leaked for the whole
|
|
// program's lifetime.
|
|
static DESERIALIZED: AtomicPtr<PeepholeOptimizations<Opcode>> = AtomicPtr::new(ptr::null_mut());
|
|
|
|
// If `DESERIALIZED` has already been initialized, then just use it.
|
|
let ptr = DESERIALIZED.load(Ordering::SeqCst);
|
|
if let Some(peep_opts) = unsafe { ptr.as_ref() } {
|
|
return peep_opts.optimizer(isa);
|
|
}
|
|
|
|
// Otherwise, if `DESERIALIZED` hasn't been initialized, then we need to
|
|
// deserialize the peephole optimizations and initialize it. However,
|
|
// another thread could be doing the same thing concurrently, so there is a
|
|
// race to see who initializes `DESERIALIZED` first, and we need to be
|
|
// prepared to both win or lose that race.
|
|
let peep_opts = PeepholeOptimizations::deserialize(&get_serialized())
|
|
.expect("should always be able to deserialize `preopt.serialized`");
|
|
let peep_opts = Box::into_raw(Box::new(peep_opts));
|
|
|
|
// Only update `DESERIALIZE` if it is still null, attempting to perform the
|
|
// one-time transition from null -> non-null.
|
|
if DESERIALIZED
|
|
.compare_and_swap(ptr::null_mut(), peep_opts, Ordering::SeqCst)
|
|
.is_null()
|
|
{
|
|
// We won the race to initialize `DESERIALIZED`.
|
|
debug_assert_eq!(DESERIALIZED.load(Ordering::SeqCst), peep_opts);
|
|
let peep_opts = unsafe { &*peep_opts };
|
|
return peep_opts.optimizer(isa);
|
|
}
|
|
|
|
// We lost the race to initialize `DESERIALIZED`. Drop our no-longer-needed
|
|
// instance of `peep_opts` and get the pointer to the instance that won the
|
|
// race.
|
|
let _ = unsafe { Box::from_raw(peep_opts) };
|
|
let peep_opts = DESERIALIZED.load(Ordering::SeqCst);
|
|
let peep_opts = unsafe { peep_opts.as_ref().unwrap() };
|
|
peep_opts.optimizer(isa)
|
|
}
|
|
|
|
/// Either a `Value` or an `Inst`.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub enum ValueOrInst {
|
|
Value(Value),
|
|
Inst(Inst),
|
|
}
|
|
|
|
impl ValueOrInst {
|
|
/// Get the underlying `Value` if any.
|
|
pub fn value(&self) -> Option<Value> {
|
|
match *self {
|
|
Self::Value(v) => Some(v),
|
|
Self::Inst(_) => None,
|
|
}
|
|
}
|
|
|
|
/// Get the underlying `Inst` if any.
|
|
pub fn inst(&self) -> Option<Inst> {
|
|
match *self {
|
|
Self::Inst(i) => Some(i),
|
|
Self::Value(_) => None,
|
|
}
|
|
}
|
|
|
|
/// Unwrap the underlying `Value`, panicking if it is not a `Value.
|
|
pub fn unwrap_value(&self) -> Value {
|
|
self.value().unwrap()
|
|
}
|
|
|
|
/// Unwrap the underlying `Inst`, panicking if it is not a `Inst.
|
|
pub fn unwrap_inst(&self) -> Inst {
|
|
self.inst().unwrap()
|
|
}
|
|
|
|
/// Is this a `Value`?
|
|
pub fn is_value(&self) -> bool {
|
|
self.value().is_some()
|
|
}
|
|
|
|
/// Is this an `Inst`?
|
|
pub fn is_inst(&self) -> bool {
|
|
self.inst().is_some()
|
|
}
|
|
|
|
fn resolve_inst(&self, dfg: &DataFlowGraph) -> Option<Inst> {
|
|
match *self {
|
|
ValueOrInst::Inst(i) => Some(i),
|
|
ValueOrInst::Value(v) => dfg.value_def(v).inst(),
|
|
}
|
|
}
|
|
|
|
fn result_bit_width(&self, dfg: &DataFlowGraph) -> u8 {
|
|
match *self {
|
|
ValueOrInst::Value(v) => dfg.value_type(v).bits().try_into().unwrap(),
|
|
ValueOrInst::Inst(inst) => {
|
|
let result = dfg.first_result(inst);
|
|
dfg.value_type(result).bits().try_into().unwrap()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn to_constant(&self, pos: &mut FuncCursor) -> Option<Constant> {
|
|
let inst = self.resolve_inst(&pos.func.dfg)?;
|
|
match pos.func.dfg[inst] {
|
|
InstructionData::UnaryImm {
|
|
opcode: Opcode::Iconst,
|
|
imm,
|
|
} => {
|
|
let width = self.result_bit_width(&pos.func.dfg).try_into().unwrap();
|
|
let x: i64 = imm.into();
|
|
Some(Constant::Int(x as u64, width))
|
|
}
|
|
InstructionData::UnaryBool {
|
|
opcode: Opcode::Bconst,
|
|
imm,
|
|
} => {
|
|
let width = self.result_bit_width(&pos.func.dfg).try_into().unwrap();
|
|
Some(Constant::Bool(imm, width))
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Value> for ValueOrInst {
|
|
fn from(v: Value) -> ValueOrInst {
|
|
ValueOrInst::Value(v)
|
|
}
|
|
}
|
|
|
|
impl From<Inst> for ValueOrInst {
|
|
fn from(i: Inst) -> ValueOrInst {
|
|
ValueOrInst::Inst(i)
|
|
}
|
|
}
|
|
|
|
/// Get the fixed bit width of `bit_width`, or if it is polymorphic, the bit
|
|
/// width of `root`.
|
|
fn bit_width(dfg: &DataFlowGraph, bit_width: BitWidth, root: Inst) -> u8 {
|
|
bit_width.fixed_width().unwrap_or_else(|| {
|
|
let tyvar = dfg.ctrl_typevar(root);
|
|
let ty = dfg.compute_result_type(root, 0, tyvar).unwrap();
|
|
u8::try_from(ty.bits()).unwrap()
|
|
})
|
|
}
|
|
|
|
/// Convert the constant `c` into an instruction.
|
|
fn const_to_value<'a>(builder: impl InstBuilder<'a>, c: Constant, root: Inst) -> Value {
|
|
match c {
|
|
Constant::Bool(b, width) => {
|
|
let width = bit_width(builder.data_flow_graph(), width, root);
|
|
let ty = match width {
|
|
1 => types::B1,
|
|
8 => types::B8,
|
|
16 => types::B16,
|
|
32 => types::B32,
|
|
64 => types::B64,
|
|
128 => types::B128,
|
|
_ => unreachable!(),
|
|
};
|
|
builder.bconst(ty, b)
|
|
}
|
|
Constant::Int(x, width) => {
|
|
let width = bit_width(builder.data_flow_graph(), width, root);
|
|
let ty = match width {
|
|
8 => types::I8,
|
|
16 => types::I16,
|
|
32 => types::I32,
|
|
64 => types::I64,
|
|
128 => types::I128,
|
|
_ => unreachable!(),
|
|
};
|
|
builder.iconst(ty, x as i64)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn part_to_value(pos: &mut FuncCursor, root: Inst, part: Part<ValueOrInst>) -> Option<Value> {
|
|
match part {
|
|
Part::Instruction(ValueOrInst::Inst(inst)) => {
|
|
pos.func.dfg.inst_results(inst).first().copied()
|
|
}
|
|
Part::Instruction(ValueOrInst::Value(v)) => Some(v),
|
|
Part::Constant(c) => Some(const_to_value(pos.ins(), c, root)),
|
|
Part::ConditionCode(_) => None,
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Constant> for Imm64 {
|
|
type Error = &'static str;
|
|
|
|
fn try_from(c: Constant) -> Result<Self, Self::Error> {
|
|
match c {
|
|
Constant::Int(x, _) => Ok(Imm64::from(x as i64)),
|
|
Constant::Bool(..) => Err("cannot create Imm64 from Constant::Bool"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Into<Constant> for Imm64 {
|
|
#[inline]
|
|
fn into(self) -> Constant {
|
|
let x: i64 = self.into();
|
|
Constant::Int(x as _, BitWidth::SixtyFour)
|
|
}
|
|
}
|
|
|
|
impl Into<Part<ValueOrInst>> for Imm64 {
|
|
#[inline]
|
|
fn into(self) -> Part<ValueOrInst> {
|
|
let c: Constant = self.into();
|
|
c.into()
|
|
}
|
|
}
|
|
|
|
fn part_to_imm64(pos: &mut FuncCursor, part: Part<ValueOrInst>) -> Imm64 {
|
|
return match part {
|
|
Part::Instruction(x) => match x.to_constant(pos).unwrap_or_else(|| cannot_convert()) {
|
|
Constant::Int(x, _) => (x as i64).into(),
|
|
Constant::Bool(..) => cannot_convert(),
|
|
},
|
|
Part::Constant(Constant::Int(x, _)) => (x as i64).into(),
|
|
Part::ConditionCode(_) | Part::Constant(Constant::Bool(..)) => cannot_convert(),
|
|
};
|
|
|
|
#[inline(never)]
|
|
#[cold]
|
|
fn cannot_convert() -> ! {
|
|
panic!("cannot convert part into `Imm64`")
|
|
}
|
|
}
|
|
|
|
impl Into<Constant> for Uimm64 {
|
|
#[inline]
|
|
fn into(self) -> Constant {
|
|
let x: u64 = self.into();
|
|
Constant::Int(x, BitWidth::SixtyFour)
|
|
}
|
|
}
|
|
|
|
impl Into<Part<ValueOrInst>> for Uimm64 {
|
|
#[inline]
|
|
fn into(self) -> Part<ValueOrInst> {
|
|
let c: Constant = self.into();
|
|
c.into()
|
|
}
|
|
}
|
|
|
|
fn peepmatic_to_intcc(cc: ConditionCode) -> IntCC {
|
|
match cc {
|
|
ConditionCode::Eq => IntCC::Equal,
|
|
ConditionCode::Ne => IntCC::NotEqual,
|
|
ConditionCode::Slt => IntCC::SignedLessThan,
|
|
ConditionCode::Sle => IntCC::SignedGreaterThanOrEqual,
|
|
ConditionCode::Sgt => IntCC::SignedGreaterThan,
|
|
ConditionCode::Sge => IntCC::SignedLessThanOrEqual,
|
|
ConditionCode::Ult => IntCC::UnsignedLessThan,
|
|
ConditionCode::Uge => IntCC::UnsignedGreaterThanOrEqual,
|
|
ConditionCode::Ugt => IntCC::UnsignedGreaterThan,
|
|
ConditionCode::Ule => IntCC::UnsignedLessThanOrEqual,
|
|
ConditionCode::Of => IntCC::Overflow,
|
|
ConditionCode::Nof => IntCC::NotOverflow,
|
|
}
|
|
}
|
|
|
|
fn intcc_to_peepmatic(cc: IntCC) -> ConditionCode {
|
|
match cc {
|
|
IntCC::Equal => ConditionCode::Eq,
|
|
IntCC::NotEqual => ConditionCode::Ne,
|
|
IntCC::SignedLessThan => ConditionCode::Slt,
|
|
IntCC::SignedGreaterThanOrEqual => ConditionCode::Sle,
|
|
IntCC::SignedGreaterThan => ConditionCode::Sgt,
|
|
IntCC::SignedLessThanOrEqual => ConditionCode::Sge,
|
|
IntCC::UnsignedLessThan => ConditionCode::Ult,
|
|
IntCC::UnsignedGreaterThanOrEqual => ConditionCode::Uge,
|
|
IntCC::UnsignedGreaterThan => ConditionCode::Ugt,
|
|
IntCC::UnsignedLessThanOrEqual => ConditionCode::Ule,
|
|
IntCC::Overflow => ConditionCode::Of,
|
|
IntCC::NotOverflow => ConditionCode::Nof,
|
|
}
|
|
}
|
|
|
|
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,
|
|
(Kind::Int, 16) => types::I16,
|
|
(Kind::Int, 32) => types::I32,
|
|
(Kind::Int, 64) => types::I64,
|
|
(Kind::Int, 128) => types::I128,
|
|
(Kind::Bool, 1) => types::B1,
|
|
(Kind::Bool, 8) => types::I8,
|
|
(Kind::Bool, 16) => types::I16,
|
|
(Kind::Bool, 32) => types::I32,
|
|
(Kind::Bool, 64) => types::I64,
|
|
(Kind::Bool, 128) => types::I128,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
// NB: the unsafe contract we must uphold here is that our implementation of
|
|
// `instruction_result_bit_width` must always return a valid, non-zero bit
|
|
// width.
|
|
unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|
type Context = FuncCursor<'b>;
|
|
|
|
type Operator = Opcode;
|
|
|
|
type Instruction = ValueOrInst;
|
|
|
|
fn replace_instruction(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
old: ValueOrInst,
|
|
new: Part<ValueOrInst>,
|
|
) -> ValueOrInst {
|
|
log::trace!("replace {:?} with {:?}", old, new);
|
|
let old_inst = old.resolve_inst(&pos.func.dfg).unwrap();
|
|
|
|
// Try to convert `new` to an instruction, because we prefer replacing
|
|
// an old instruction with a new one wholesale. However, if the
|
|
// replacement cannot be converted to an instruction (e.g. the
|
|
// right-hand side is a block/function parameter value) then we change
|
|
// the old instruction's result to an alias of the new value.
|
|
let new_inst = match new {
|
|
Part::Instruction(ValueOrInst::Inst(inst)) => Some(inst),
|
|
Part::Instruction(ValueOrInst::Value(_)) => {
|
|
// Do not try and follow the value definition. If we transplant
|
|
// this value's instruction, and there are other uses of this
|
|
// value, then we could mess up ordering between instructions.
|
|
None
|
|
}
|
|
Part::Constant(c) => {
|
|
let v = const_to_value(pos.ins(), c, old_inst);
|
|
let inst = pos.func.dfg.value_def(v).unwrap_inst();
|
|
Some(inst)
|
|
}
|
|
Part::ConditionCode(_) => None,
|
|
};
|
|
|
|
match new_inst {
|
|
Some(new_inst) => {
|
|
pos.func.transplant_inst(old_inst, new_inst);
|
|
debug_assert_eq!(pos.current_inst(), Some(old_inst));
|
|
old_inst.into()
|
|
}
|
|
None => {
|
|
let new_value = part_to_value(pos, old_inst, new).unwrap();
|
|
|
|
let old_results = pos.func.dfg.detach_results(old_inst);
|
|
let old_results = old_results.as_slice(&pos.func.dfg.value_lists);
|
|
assert_eq!(old_results.len(), 1);
|
|
let old_value = old_results[0];
|
|
|
|
pos.func.dfg.change_to_alias(old_value, new_value);
|
|
pos.func.dfg.replace(old_inst).nop();
|
|
|
|
new_value.into()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn operator<E>(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
root: ValueOrInst,
|
|
operator: Opcode,
|
|
r#type: Type,
|
|
a: Part<ValueOrInst>,
|
|
) -> ValueOrInst {
|
|
log::trace!("make_inst_1: {:?}({:?})", operator, a);
|
|
|
|
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
|
match operator {
|
|
Opcode::AdjustSpDown => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
pos.ins().adjust_sp_down(a).into()
|
|
}
|
|
Opcode::AdjustSpDownImm => {
|
|
let c = a.unwrap_constant();
|
|
let imm = Imm64::try_from(c).unwrap();
|
|
pos.ins().adjust_sp_down_imm(imm).into()
|
|
}
|
|
Opcode::Bconst => {
|
|
let c = a.unwrap_constant();
|
|
let val = const_to_value(pos.ins(), c, root);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Bint => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
|
let val = pos.ins().bint(ty, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Bnot => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let val = pos.ins().bnot(a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Brnz => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
|
|
// NB: branching instructions must be the root of an
|
|
// optimization's right-hand side, so we get the destination
|
|
// block and arguments from the left-hand side's root. Peepmatic
|
|
// doesn't currently represent labels or varargs.
|
|
let block = pos.func.dfg[root].branch_destination().unwrap();
|
|
let args = pos.func.dfg.inst_args(root)[1..].to_vec();
|
|
|
|
pos.ins().brnz(a, block, &args).into()
|
|
}
|
|
Opcode::Brz => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
|
|
// See the comment in the `Opcode::Brnz` match argm.
|
|
let block = pos.func.dfg[root].branch_destination().unwrap();
|
|
let args = pos.func.dfg.inst_args(root)[1..].to_vec();
|
|
|
|
pos.ins().brz(a, block, &args).into()
|
|
}
|
|
Opcode::Iconst => {
|
|
let a = a.unwrap_constant();
|
|
let val = const_to_value(pos.ins(), a, root);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Ireduce => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
|
let val = pos.ins().ireduce(ty, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Sextend => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
|
let val = pos.ins().sextend(ty, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Trapnz => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
|
|
// NB: similar to branching instructions (see comment in the
|
|
// `Opcode::Brnz` match arm) trapping instructions must be the
|
|
// root of an optimization's right-hand side, and we get the
|
|
// trap code from the root of the left-hand side. Peepmatic
|
|
// doesn't currently represent trap codes.
|
|
let code = pos.func.dfg[root].trap_code().unwrap();
|
|
|
|
pos.ins().trapnz(a, code).into()
|
|
}
|
|
Opcode::Trapz => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
// See comment in the `Opcode::Trapnz` match arm.
|
|
let code = pos.func.dfg[root].trap_code().unwrap();
|
|
pos.ins().trapz(a, code).into()
|
|
}
|
|
Opcode::Uextend => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let ty = peepmatic_ty_to_ir_ty(r#type, &pos.func.dfg, root);
|
|
let val = pos.ins().uextend(ty, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn make_inst_2(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
root: ValueOrInst,
|
|
operator: Opcode,
|
|
_: Type,
|
|
a: Part<ValueOrInst>,
|
|
b: Part<ValueOrInst>,
|
|
) -> ValueOrInst {
|
|
log::trace!("make_inst_2: {:?}({:?}, {:?})", operator, a, b);
|
|
|
|
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
|
match operator {
|
|
Opcode::Band => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().band(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::BandImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().band_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Bor => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().bor(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::BorImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().bor_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Bxor => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().bxor(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::BxorImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().bxor_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Iadd => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().iadd(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::IaddImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().iadd_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Ifcmp => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().ifcmp(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::IfcmpImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().ifcmp_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Imul => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().imul(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::ImulImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().imul_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::IrsubImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().irsub_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Ishl => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().ishl(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::IshlImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().ishl_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Isub => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().isub(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Rotl => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().rotl(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::RotlImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().rotl_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Rotr => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().rotr(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::RotrImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().rotr_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Sdiv => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().sdiv(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::SdivImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().sdiv_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Srem => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().srem(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::SremImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().srem_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Sshr => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().sshr(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::SshrImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().sshr_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Udiv => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().udiv(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::UdivImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().udiv_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Urem => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().urem(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::UremImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().urem_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Ushr => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().ushr(a, b);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::UshrImm => {
|
|
let a = part_to_imm64(pos, a);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let val = pos.ins().ushr_imm(b, a);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn make_inst_3(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
root: ValueOrInst,
|
|
operator: Opcode,
|
|
_: Type,
|
|
a: Part<ValueOrInst>,
|
|
b: Part<ValueOrInst>,
|
|
c: Part<ValueOrInst>,
|
|
) -> ValueOrInst {
|
|
log::trace!("make_inst_3: {:?}({:?}, {:?}, {:?})", operator, a, b, c);
|
|
|
|
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
|
match operator {
|
|
Opcode::Icmp => {
|
|
let cond = a.unwrap_condition_code();
|
|
let cond = peepmatic_to_intcc(cond);
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let c = part_to_value(pos, root, c).unwrap();
|
|
let val = pos.ins().icmp(cond, b, c);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::IcmpImm => {
|
|
let cond = a.unwrap_condition_code();
|
|
let cond = peepmatic_to_intcc(cond);
|
|
let imm = part_to_imm64(pos, b);
|
|
let c = part_to_value(pos, root, c).unwrap();
|
|
let val = pos.ins().icmp_imm(cond, c, imm);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
Opcode::Select => {
|
|
let a = part_to_value(pos, root, a).unwrap();
|
|
let b = part_to_value(pos, root, b).unwrap();
|
|
let c = part_to_value(pos, root, c).unwrap();
|
|
let val = pos.ins().select(a, b, c);
|
|
pos.func.dfg.value_def(val).unwrap_inst().into()
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn instruction_to_constant(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
value_or_inst: ValueOrInst,
|
|
) -> Option<Constant> {
|
|
value_or_inst.to_constant(pos)
|
|
}
|
|
|
|
fn instruction_result_bit_width(
|
|
&self,
|
|
pos: &mut FuncCursor<'b>,
|
|
value_or_inst: ValueOrInst,
|
|
) -> u8 {
|
|
value_or_inst.result_bit_width(&pos.func.dfg)
|
|
}
|
|
|
|
fn native_word_size_in_bits(&self, _pos: &mut FuncCursor<'b>) -> u8 {
|
|
self.pointer_bits()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[cfg(any(feature = "x64", feature = "x86", feature = "arm64"))]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::isa::{lookup, TargetIsa};
|
|
use crate::settings::{builder, Flags};
|
|
use std::str::FromStr;
|
|
use target_lexicon::triple;
|
|
|
|
fn isa() -> Box<dyn TargetIsa> {
|
|
// We need a triple to instantiate and run the peephole optimizer, but we
|
|
// don't care which one when we're just trying to trigger a rebuild of the
|
|
// peephole optimizer (it doesn't affect the serialized bytes at all).
|
|
let triple = if cfg!(any(feature = "x64", feature = "x86")) {
|
|
triple!("x86_64")
|
|
} else if cfg!(feature = "arm64") {
|
|
triple!("aarch64")
|
|
} else {
|
|
panic!("unknown arch")
|
|
};
|
|
lookup(triple).unwrap().finish(Flags::new(builder()))
|
|
}
|
|
|
|
#[test]
|
|
fn get_peepmatic_preopt() {
|
|
let isa = isa();
|
|
let _ = preopt(&*isa);
|
|
}
|
|
}
|