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