use crate::ir::immediates::Offset32;
use crate::ir::{self, Block, Function, Inst, InstructionData, Opcode, Type, Value};
#[inline(always)]
fn trivially_has_side_effects(opcode: Opcode) -> bool {
opcode.is_call()
|| opcode.is_branch()
|| opcode.is_terminator()
|| opcode.is_return()
|| opcode.can_trap()
|| opcode.other_side_effects()
|| opcode.can_store()
}
#[inline(always)]
fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool {
if !opcode.can_load() {
return false;
}
match *data {
InstructionData::StackLoad { .. } => false,
InstructionData::Load { flags, .. } => !flags.notrap(),
_ => true,
}
}
#[inline(always)]
fn has_side_effect(func: &Function, inst: Inst) -> bool {
let data = &func.dfg.insts[inst];
let opcode = data.opcode();
trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
}
pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool {
let is_pure_load = match func.dfg.insts[inst] {
InstructionData::Load {
opcode: Opcode::Load,
flags,
..
} => flags.readonly() && flags.notrap() && flags.can_move(),
_ => false,
};
let has_one_result = func.dfg.inst_results(inst).len() == 1;
let op = func.dfg.insts[inst].opcode();
has_one_result && (is_pure_load || (!op.can_load() && !trivially_has_side_effects(op)))
}
pub fn is_mergeable_for_egraph(func: &Function, inst: Inst) -> bool {
let op = func.dfg.insts[inst].opcode();
func.dfg.inst_results(inst).len() <= 1
&& !op.can_load()
&& !op.can_store()
&& (!has_side_effect(func, inst) || op.side_effects_idempotent())
}
pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool {
let op = func.dfg.insts[inst].opcode();
op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load())
}
pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
match &func.dfg.insts[inst] {
&InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64),
&InstructionData::UnaryIeee16 { imm, .. } => Some(imm.bits() as u64),
&InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64),
&InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()),
_ => None,
}
}
pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> {
match &func.dfg.insts[inst] {
InstructionData::Load { arg, offset, .. } => {
let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
Some((*arg, *offset, ty))
}
InstructionData::LoadNoOffset { arg, .. } => {
let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
Some((*arg, 0.into(), ty))
}
InstructionData::Store { args, offset, .. } => {
let ty = func.dfg.value_type(args[0]);
Some((args[1], *offset, ty))
}
InstructionData::StoreNoOffset { args, .. } => {
let ty = func.dfg.value_type(args[0]);
Some((args[1], 0.into(), ty))
}
_ => None,
}
}
pub fn inst_store_data(func: &Function, inst: Inst) -> Option<Value> {
match &func.dfg.insts[inst] {
InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => {
Some(args[0])
}
_ => None,
}
}
pub fn has_memory_fence_semantics(op: Opcode) -> bool {
match op {
Opcode::AtomicRmw
| Opcode::AtomicCas
| Opcode::AtomicLoad
| Opcode::AtomicStore
| Opcode::Fence
| Opcode::Debugtrap
| Opcode::SequencePoint => true,
Opcode::Call | Opcode::CallIndirect | Opcode::TryCall | Opcode::TryCallIndirect => true,
op if op.can_trap() => true,
_ => false,
}
}
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
f: &Function,
block: Block,
mut visit: F,
) {
if let Some(inst) = f.layout.last_inst(block) {
match &f.dfg.insts[inst] {
ir::InstructionData::Jump {
destination: dest, ..
} => {
visit(inst, dest.block(&f.dfg.value_lists), false);
}
ir::InstructionData::Brif {
blocks: [block_then, block_else],
..
} => {
visit(inst, block_then.block(&f.dfg.value_lists), false);
visit(inst, block_else.block(&f.dfg.value_lists), false);
}
ir::InstructionData::BranchTable { table, .. } => {
let pool = &f.dfg.value_lists;
let table = &f.stencil.dfg.jump_tables[*table];
visit(inst, table.default_block().block(pool), false);
for dest in table.as_slice() {
visit(inst, dest.block(pool), true);
}
}
ir::InstructionData::TryCall { exception, .. }
| ir::InstructionData::TryCallIndirect { exception, .. } => {
let pool = &f.dfg.value_lists;
let exdata = &f.stencil.dfg.exception_tables[*exception];
for dest in exdata.all_branches() {
visit(inst, dest.block(pool), false);
}
}
inst => debug_assert!(!inst.opcode().is_branch()),
}
}
}