diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/cranelift-codegen/src/peepmatic.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/peepmatic.rs')
-rw-r--r-- | third_party/rust/cranelift-codegen/src/peepmatic.rs | 1330 |
1 files changed, 1330 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/peepmatic.rs b/third_party/rust/cranelift-codegen/src/peepmatic.rs new file mode 100644 index 0000000000..bf0f440865 --- /dev/null +++ b/third_party/rust/cranelift-codegen/src/peepmatic.rs @@ -0,0 +1,1330 @@ +//! 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(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); + } +} |