1 //! Instruction predicates/properties, shared by various analyses. 2 use crate::ir::immediates::Offset32; 3 use crate::ir::instructions::BranchInfo; 4 use crate::ir::{Block, DataFlowGraph, Function, Inst, InstructionData, Opcode, Type, Value}; 5 use crate::machinst::ty_bits; 6 use cranelift_entity::EntityRef; 7 8 /// Preserve instructions with used result values. 9 pub fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool { 10 dfg.inst_results(inst).iter().any(|v| live[v.index()]) 11 } 12 13 /// Test whether the given opcode is unsafe to even consider as side-effect-free. 14 fn trivially_has_side_effects(opcode: Opcode) -> bool { 15 opcode.is_call() 16 || opcode.is_branch() 17 || opcode.is_terminator() 18 || opcode.is_return() 19 || opcode.can_trap() 20 || opcode.other_side_effects() 21 || opcode.can_store() 22 } 23 24 /// Load instructions without the `notrap` flag are defined to trap when 25 /// operating on inaccessible memory, so we can't treat them as side-effect-free even if the loaded 26 /// value is unused. 27 fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool { 28 if !opcode.can_load() { 29 return false; 30 } 31 match *data { 32 InstructionData::StackLoad { .. } => false, 33 InstructionData::Load { flags, .. } => !flags.notrap(), 34 _ => true, 35 } 36 } 37 38 /// Does the given instruction have any side-effect that would preclude it from being removed when 39 /// its value is unused? 40 pub fn has_side_effect(func: &Function, inst: Inst) -> bool { 41 let data = &func.dfg[inst]; 42 let opcode = data.opcode(); 43 trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data) 44 } 45 46 /// Does the given instruction have any side-effect as per [has_side_effect], or else is a load, 47 /// but not the get_pinned_reg opcode? 48 pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool { 49 let op = func.dfg[inst].opcode(); 50 op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load()) 51 } 52 53 /// Is the given instruction a constant value (`iconst`, `fconst`, `bconst`) that can be 54 /// represented in 64 bits? 55 pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> { 56 let data = &func.dfg[inst]; 57 if data.opcode() == Opcode::Null { 58 return Some(0); 59 } 60 match data { 61 &InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64), 62 &InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64), 63 &InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()), 64 &InstructionData::UnaryBool { imm, .. } => { 65 let imm = if imm { 66 let bits = ty_bits(func.dfg.value_type(func.dfg.inst_results(inst)[0])); 67 68 if bits < 64 { 69 (1u64 << bits) - 1 70 } else { 71 u64::MAX 72 } 73 } else { 74 0 75 }; 76 77 Some(imm) 78 } 79 _ => None, 80 } 81 } 82 83 /// Get the address, offset, and access type from the given instruction, if any. 84 pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> { 85 let data = &func.dfg[inst]; 86 match data { 87 InstructionData::Load { arg, offset, .. } => { 88 let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]); 89 Some((*arg, *offset, ty)) 90 } 91 InstructionData::LoadNoOffset { arg, .. } => { 92 let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]); 93 Some((*arg, 0.into(), ty)) 94 } 95 InstructionData::Store { args, offset, .. } => { 96 let ty = func.dfg.value_type(args[0]); 97 Some((args[1], *offset, ty)) 98 } 99 InstructionData::StoreNoOffset { args, .. } => { 100 let ty = func.dfg.value_type(args[0]); 101 Some((args[1], 0.into(), ty)) 102 } 103 _ => None, 104 } 105 } 106 107 /// Get the store data, if any, from an instruction. 108 pub fn inst_store_data(func: &Function, inst: Inst) -> Option<Value> { 109 let data = &func.dfg[inst]; 110 match data { 111 InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => { 112 Some(args[0]) 113 } 114 _ => None, 115 } 116 } 117 118 /// Determine whether this opcode behaves as a memory fence, i.e., 119 /// prohibits any moving of memory accesses across it. 120 pub fn has_memory_fence_semantics(op: Opcode) -> bool { 121 match op { 122 Opcode::AtomicRmw 123 | Opcode::AtomicCas 124 | Opcode::AtomicLoad 125 | Opcode::AtomicStore 126 | Opcode::Fence => true, 127 Opcode::Call | Opcode::CallIndirect => true, 128 _ => false, 129 } 130 } 131 132 /// Visit all successors of a block with a given visitor closure. 133 pub(crate) fn visit_block_succs<F: FnMut(Inst, Block)>(f: &Function, block: Block, mut visit: F) { 134 for inst in f.layout.block_likely_branches(block) { 135 if f.dfg[inst].opcode().is_branch() { 136 visit_branch_targets(f, inst, &mut visit); 137 } 138 } 139 } 140 141 fn visit_branch_targets<F: FnMut(Inst, Block)>(f: &Function, inst: Inst, visit: &mut F) { 142 match f.dfg[inst].analyze_branch(&f.dfg.value_lists) { 143 BranchInfo::NotABranch => {} 144 BranchInfo::SingleDest(dest, _) => { 145 visit(inst, dest); 146 } 147 BranchInfo::Table(table, maybe_dest) => { 148 if let Some(dest) = maybe_dest { 149 visit(inst, dest); 150 } 151 for &dest in f.jump_tables[table].as_slice() { 152 visit(inst, dest); 153 } 154 } 155 } 156 } 157