Path: blob/main/cranelift/codegen/src/opts.rs
1693 views
//! Optimization driver using ISLE rewrite rules on an egraph.12mod div_const;34use crate::egraph::{NewOrExistingInst, OptimizeCtx};5pub use crate::ir::condcodes::{FloatCC, IntCC};6use crate::ir::dfg::ValueDef;7pub use crate::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128, Imm64, Offset32, Uimm8, V128Imm};8use crate::ir::instructions::InstructionFormat;9pub use crate::ir::types::*;10pub use crate::ir::{11AtomicRmwOp, BlockCall, Constant, DynamicStackSlot, FuncRef, GlobalValue, Immediate,12InstructionData, MemFlags, Opcode, StackSlot, TrapCode, Type, Value,13};14use crate::isle_common_prelude_methods;15use crate::machinst::isle::*;16use crate::trace;17use cranelift_entity::packed_option::ReservedValue;18use smallvec::{SmallVec, smallvec};19use std::marker::PhantomData;2021pub type Unit = ();22pub type ValueArray2 = [Value; 2];23pub type ValueArray3 = [Value; 3];2425const MAX_ISLE_RETURNS: usize = 8;2627pub type ConstructorVec<T> = SmallVec<[T; MAX_ISLE_RETURNS]>;2829type TypeAndInstructionData = (Type, InstructionData);3031impl<T: smallvec::Array> generated_code::Length for SmallVec<T> {32#[inline]33fn len(&self) -> usize {34SmallVec::len(self)35}36}3738pub(crate) mod generated_code;39use generated_code::{ContextIter, IntoContextIter};4041pub(crate) struct IsleContext<'a, 'b, 'c> {42pub(crate) ctx: &'a mut OptimizeCtx<'b, 'c>,43}4445impl IsleContext<'_, '_, '_> {46#[allow(dead_code, reason = "dead code, only on nightly rust at this time")]47pub(crate) fn dfg(&self) -> &crate::ir::DataFlowGraph {48&self.ctx.func.dfg49}50}5152pub(crate) struct InstDataEtorIter<'a, 'b, 'c> {53stack: SmallVec<[Value; 8]>,54_phantom1: PhantomData<&'a ()>,55_phantom2: PhantomData<&'b ()>,56_phantom3: PhantomData<&'c ()>,57}5859impl Default for InstDataEtorIter<'_, '_, '_> {60fn default() -> Self {61InstDataEtorIter {62stack: SmallVec::default(),63_phantom1: PhantomData,64_phantom2: PhantomData,65_phantom3: PhantomData,66}67}68}6970impl<'a, 'b, 'c> InstDataEtorIter<'a, 'b, 'c> {71fn new(root: Value) -> Self {72debug_assert_ne!(root, Value::reserved_value());73trace!("new iter from root {root}");74Self {75stack: smallvec![root],76_phantom1: PhantomData,77_phantom2: PhantomData,78_phantom3: PhantomData,79}80}81}8283impl<'a, 'b, 'c> ContextIter for InstDataEtorIter<'a, 'b, 'c>84where85'b: 'a,86'c: 'b,87{88type Context = IsleContext<'a, 'b, 'c>;89type Output = (Type, InstructionData);9091fn next(&mut self, ctx: &mut IsleContext<'a, 'b, 'c>) -> Option<Self::Output> {92while let Some(value) = self.stack.pop() {93debug_assert!(ctx.ctx.func.dfg.value_is_real(value));94trace!("iter: value {:?}", value);95match ctx.ctx.func.dfg.value_def(value) {96ValueDef::Union(x, y) => {97debug_assert_ne!(x, Value::reserved_value());98debug_assert_ne!(y, Value::reserved_value());99trace!(" -> {}, {}", x, y);100self.stack.push(x);101self.stack.push(y);102continue;103}104ValueDef::Result(inst, _) if ctx.ctx.func.dfg.inst_results(inst).len() == 1 => {105let ty = ctx.ctx.func.dfg.value_type(value);106trace!(" -> value of type {}", ty);107return Some((ty, ctx.ctx.func.dfg.insts[inst]));108}109_ => {}110}111}112None113}114}115116impl<'a, 'b, 'c> IntoContextIter for InstDataEtorIter<'a, 'b, 'c>117where118'b: 'a,119'c: 'b,120{121type Context = IsleContext<'a, 'b, 'c>;122type Output = (Type, InstructionData);123type IntoIter = Self;124125fn into_context_iter(self) -> Self {126self127}128}129130#[derive(Default)]131pub(crate) struct MaybeUnaryEtorIter<'a, 'b, 'c> {132opcode: Option<Opcode>,133inner: InstDataEtorIter<'a, 'b, 'c>,134fallback: Option<Value>,135}136137impl MaybeUnaryEtorIter<'_, '_, '_> {138fn new(opcode: Opcode, value: Value) -> Self {139debug_assert_eq!(opcode.format(), InstructionFormat::Unary);140Self {141opcode: Some(opcode),142inner: InstDataEtorIter::new(value),143fallback: Some(value),144}145}146}147148impl<'a, 'b, 'c> ContextIter for MaybeUnaryEtorIter<'a, 'b, 'c>149where150'b: 'a,151'c: 'b,152{153type Context = IsleContext<'a, 'b, 'c>;154type Output = (Type, Value);155156fn next(&mut self, ctx: &mut IsleContext<'a, 'b, 'c>) -> Option<Self::Output> {157debug_assert_ne!(self.opcode, None);158while let Some((ty, inst_def)) = self.inner.next(ctx) {159let InstructionData::Unary { opcode, arg } = inst_def else {160continue;161};162if Some(opcode) == self.opcode {163self.fallback = None;164return Some((ty, arg));165}166}167168self.fallback.take().map(|value| {169let ty = generated_code::Context::value_type(ctx, value);170(ty, value)171})172}173}174175impl<'a, 'b, 'c> IntoContextIter for MaybeUnaryEtorIter<'a, 'b, 'c>176where177'b: 'a,178'c: 'b,179{180type Context = IsleContext<'a, 'b, 'c>;181type Output = (Type, Value);182type IntoIter = Self;183184fn into_context_iter(self) -> Self {185self186}187}188189impl<'a, 'b, 'c> generated_code::Context for IsleContext<'a, 'b, 'c> {190isle_common_prelude_methods!();191192type inst_data_value_etor_returns = InstDataEtorIter<'a, 'b, 'c>;193194fn inst_data_value_etor(&mut self, eclass: Value, returns: &mut InstDataEtorIter<'a, 'b, 'c>) {195*returns = InstDataEtorIter::new(eclass);196}197198type inst_data_value_tupled_etor_returns = InstDataEtorIter<'a, 'b, 'c>;199200fn inst_data_value_tupled_etor(201&mut self,202eclass: Value,203returns: &mut InstDataEtorIter<'a, 'b, 'c>,204) {205// Literally identical to `inst_data_value_etor`, just a different nominal type in ISLE206self.inst_data_value_etor(eclass, returns);207}208209fn make_inst_ctor(&mut self, ty: Type, op: &InstructionData) -> Value {210trace!("make_inst_ctor: creating {:?}", op);211let value = self.ctx.insert_pure_enode(NewOrExistingInst::New(*op, ty));212trace!("make_inst_ctor: {:?} -> {}", op, value);213value214}215216fn make_skeleton_inst_ctor(&mut self, data: &InstructionData) -> Inst {217let inst = self.ctx.func.dfg.make_inst(*data);218self.ctx219.func220.dfg221.make_inst_results(inst, Default::default());222inst223}224225fn inst_data_etor(&mut self, inst: Inst) -> Option<InstructionData> {226Some(self.ctx.func.dfg.insts[inst])227}228229fn value_array_2_ctor(&mut self, arg0: Value, arg1: Value) -> ValueArray2 {230[arg0, arg1]231}232233fn value_array_3_ctor(&mut self, arg0: Value, arg1: Value, arg2: Value) -> ValueArray3 {234[arg0, arg1, arg2]235}236237#[inline]238fn value_type(&mut self, val: Value) -> Type {239self.ctx.func.dfg.value_type(val)240}241242fn iconst_sextend_etor(243&mut self,244(ty, inst_data): (Type, InstructionData),245) -> Option<(Type, i64)> {246if let InstructionData::UnaryImm {247opcode: Opcode::Iconst,248imm,249} = inst_data250{251Some((ty, self.i64_sextend_imm64(ty, imm)))252} else {253None254}255}256257fn remat(&mut self, value: Value) -> Value {258trace!("remat: {}", value);259self.ctx.remat_values.insert(value);260self.ctx.stats.remat += 1;261value262}263264fn subsume(&mut self, value: Value) -> Value {265trace!("subsume: {}", value);266self.ctx.subsume_values.insert(value);267self.ctx.stats.subsume += 1;268value269}270271fn splat64(&mut self, val: u64) -> Constant {272let val = u128::from(val);273let val = val | (val << 64);274let imm = V128Imm(val.to_le_bytes());275self.ctx.func.dfg.constants.insert(imm.into())276}277278type sextend_maybe_etor_returns = MaybeUnaryEtorIter<'a, 'b, 'c>;279fn sextend_maybe_etor(&mut self, value: Value, returns: &mut Self::sextend_maybe_etor_returns) {280*returns = MaybeUnaryEtorIter::new(Opcode::Sextend, value);281}282283type uextend_maybe_etor_returns = MaybeUnaryEtorIter<'a, 'b, 'c>;284fn uextend_maybe_etor(&mut self, value: Value, returns: &mut Self::uextend_maybe_etor_returns) {285*returns = MaybeUnaryEtorIter::new(Opcode::Uextend, value);286}287288// NB: Cranelift's defined semantics for `fcvt_from_{s,u}int` match Rust's289// own semantics for converting an integer to a float, so these are all290// implemented with `as` conversions in Rust.291fn f32_from_uint(&mut self, n: u64) -> Ieee32 {292Ieee32::with_float(n as f32)293}294295fn f64_from_uint(&mut self, n: u64) -> Ieee64 {296Ieee64::with_float(n as f64)297}298299fn f32_from_sint(&mut self, n: i64) -> Ieee32 {300Ieee32::with_float(n as f32)301}302303fn f64_from_sint(&mut self, n: i64) -> Ieee64 {304Ieee64::with_float(n as f64)305}306307fn u64_bswap16(&mut self, n: u64) -> u64 {308(n as u16).swap_bytes() as u64309}310311fn u64_bswap32(&mut self, n: u64) -> u64 {312(n as u32).swap_bytes() as u64313}314315fn u64_bswap64(&mut self, n: u64) -> u64 {316n.swap_bytes()317}318319fn ieee128_constant_extractor(&mut self, n: Constant) -> Option<Ieee128> {320self.ctx.func.dfg.constants.get(n).try_into().ok()321}322323fn ieee128_constant(&mut self, n: Ieee128) -> Constant {324self.ctx.func.dfg.constants.insert(n.into())325}326327fn div_const_magic_u32(&mut self, d: u32) -> generated_code::DivConstMagicU32 {328let div_const::MU32 {329mul_by,330do_add,331shift_by,332} = div_const::magic_u32(d);333generated_code::DivConstMagicU32::U32 {334mul_by,335do_add,336shift_by: shift_by.try_into().unwrap(),337}338}339340fn div_const_magic_u64(&mut self, d: u64) -> generated_code::DivConstMagicU64 {341let div_const::MU64 {342mul_by,343do_add,344shift_by,345} = div_const::magic_u64(d);346generated_code::DivConstMagicU64::U64 {347mul_by,348do_add,349shift_by: shift_by.try_into().unwrap(),350}351}352353fn div_const_magic_s32(&mut self, d: i32) -> generated_code::DivConstMagicS32 {354let div_const::MS32 { mul_by, shift_by } = div_const::magic_s32(d);355generated_code::DivConstMagicS32::S32 {356mul_by,357shift_by: shift_by.try_into().unwrap(),358}359}360361fn div_const_magic_s64(&mut self, d: i64) -> generated_code::DivConstMagicS64 {362let div_const::MS64 { mul_by, shift_by } = div_const::magic_s64(d);363generated_code::DivConstMagicS64::S64 {364mul_by,365shift_by: shift_by.try_into().unwrap(),366}367}368}369370371