1 //! Instruction predicates/properties, shared by various analyses.
2 
3 use crate::ir::{DataFlowGraph, Function, Inst, InstructionData, Opcode};
4 use crate::machinst::ty_bits;
5 use cranelift_entity::EntityRef;
6 
7 /// Preserve instructions with used result values.
any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool8 pub fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool {
9     dfg.inst_results(inst).iter().any(|v| live[v.index()])
10 }
11 
12 /// Test whether the given opcode is unsafe to even consider as side-effect-free.
trivially_has_side_effects(opcode: Opcode) -> bool13 fn trivially_has_side_effects(opcode: Opcode) -> bool {
14     opcode.is_call()
15         || opcode.is_branch()
16         || opcode.is_terminator()
17         || opcode.is_return()
18         || opcode.can_trap()
19         || opcode.other_side_effects()
20         || opcode.can_store()
21 }
22 
23 /// Load instructions without the `notrap` flag are defined to trap when
24 /// operating on inaccessible memory, so we can't treat them as side-effect-free even if the loaded
25 /// value is unused.
is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool26 fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool {
27     if !opcode.can_load() {
28         return false;
29     }
30     match *data {
31         InstructionData::StackLoad { .. } => false,
32         InstructionData::Load { flags, .. } => !flags.notrap(),
33         _ => true,
34     }
35 }
36 
37 /// Does the given instruction have any side-effect that would preclude it from being removed when
38 /// its value is unused?
has_side_effect(func: &Function, inst: Inst) -> bool39 pub fn has_side_effect(func: &Function, inst: Inst) -> bool {
40     let data = &func.dfg[inst];
41     let opcode = data.opcode();
42     trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
43 }
44 
45 /// Does the given instruction have any side-effect as per [has_side_effect], or else is a load,
46 /// but not the get_pinned_reg opcode?
has_lowering_side_effect(func: &Function, inst: Inst) -> bool47 pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool {
48     let op = func.dfg[inst].opcode();
49     op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load())
50 }
51 
52 /// Is the given instruction a constant value (`iconst`, `fconst`, `bconst`) that can be
53 /// represented in 64 bits?
is_constant_64bit(func: &Function, inst: Inst) -> Option<u64>54 pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
55     let data = &func.dfg[inst];
56     if data.opcode() == Opcode::Null {
57         return Some(0);
58     }
59     match data {
60         &InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64),
61         &InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64),
62         &InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()),
63         &InstructionData::UnaryBool { imm, .. } => {
64             let imm = if imm {
65                 let bits = ty_bits(func.dfg.value_type(func.dfg.inst_results(inst)[0]));
66 
67                 if bits < 64 {
68                     (1u64 << bits) - 1
69                 } else {
70                     u64::MAX
71                 }
72             } else {
73                 0
74             };
75 
76             Some(imm)
77         }
78         _ => None,
79     }
80 }
81 
82 /// Is the given instruction a safepoint (i.e., potentially causes a GC, depending on the
83 /// embedding, and so requires reftyped values to be enumerated with a stack map)?
is_safepoint(func: &Function, inst: Inst) -> bool84 pub fn is_safepoint(func: &Function, inst: Inst) -> bool {
85     let op = func.dfg[inst].opcode();
86     op.is_resumable_trap() || op.is_call()
87 }
88