cranelift_codegen/
inst_predicates.rs1use crate::ir::immediates::Offset32;
3use crate::ir::{self, Block, DataFlowGraph, Function, Inst, InstructionData, Opcode, Type, Value};
4use cranelift_entity::EntityRef;
5
6pub fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool {
8 dfg.inst_results(inst).iter().any(|v| live[v.index()])
9}
10
11#[inline(always)]
13fn 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#[inline(always)]
27fn 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#[inline(always)]
41pub fn has_side_effect(func: &Function, inst: Inst) -> bool {
42 let data = &func.dfg.insts[inst];
43 let opcode = data.opcode();
44 trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
45}
46
47pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool {
53 let is_readonly_load = match func.dfg.insts[inst] {
54 InstructionData::Load {
55 opcode: Opcode::Load,
56 flags,
57 ..
58 } => flags.readonly() && flags.notrap(),
59 _ => false,
60 };
61 let has_one_result = func.dfg.inst_results(inst).len() == 1;
70
71 let op = func.dfg.insts[inst].opcode();
72
73 has_one_result && (is_readonly_load || (!op.can_load() && !trivially_has_side_effects(op)))
74}
75
76pub fn is_mergeable_for_egraph(func: &Function, inst: Inst) -> bool {
82 let op = func.dfg.insts[inst].opcode();
83 let has_one_result = func.dfg.inst_results(inst).len() == 1;
86 has_one_result
87 && !op.can_load()
90 && !op.can_store()
91 && (!has_side_effect(func, inst) || op.side_effects_idempotent())
93}
94
95pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool {
98 let op = func.dfg.insts[inst].opcode();
99 op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load())
100}
101
102pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
105 let data = &func.dfg.insts[inst];
106 if data.opcode() == Opcode::Null {
107 return Some(0);
108 }
109 match data {
110 &InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64),
111 &InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64),
112 &InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()),
113 _ => None,
114 }
115}
116
117pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> {
119 let data = &func.dfg.insts[inst];
120 match data {
121 InstructionData::Load { arg, offset, .. } => {
122 let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
123 Some((*arg, *offset, ty))
124 }
125 InstructionData::LoadNoOffset { arg, .. } => {
126 let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
127 Some((*arg, 0.into(), ty))
128 }
129 InstructionData::Store { args, offset, .. } => {
130 let ty = func.dfg.value_type(args[0]);
131 Some((args[1], *offset, ty))
132 }
133 InstructionData::StoreNoOffset { args, .. } => {
134 let ty = func.dfg.value_type(args[0]);
135 Some((args[1], 0.into(), ty))
136 }
137 _ => None,
138 }
139}
140
141pub fn inst_store_data(func: &Function, inst: Inst) -> Option<Value> {
143 let data = &func.dfg.insts[inst];
144 match data {
145 InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => {
146 Some(args[0])
147 }
148 _ => None,
149 }
150}
151
152pub fn has_memory_fence_semantics(op: Opcode) -> bool {
155 match op {
156 Opcode::AtomicRmw
157 | Opcode::AtomicCas
158 | Opcode::AtomicLoad
159 | Opcode::AtomicStore
160 | Opcode::Fence
161 | Opcode::Debugtrap => true,
162 Opcode::Call | Opcode::CallIndirect => true,
163 op if op.can_trap() => true,
164 _ => false,
165 }
166}
167
168pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
173 f: &Function,
174 block: Block,
175 mut visit: F,
176) {
177 if let Some(inst) = f.layout.last_inst(block) {
178 match &f.dfg.insts[inst] {
179 ir::InstructionData::Jump {
180 destination: dest, ..
181 } => {
182 visit(inst, dest.block(&f.dfg.value_lists), false);
183 }
184
185 ir::InstructionData::Brif {
186 blocks: [block_then, block_else],
187 ..
188 } => {
189 visit(inst, block_then.block(&f.dfg.value_lists), false);
190 visit(inst, block_else.block(&f.dfg.value_lists), false);
191 }
192
193 ir::InstructionData::BranchTable { table, .. } => {
194 let pool = &f.dfg.value_lists;
195 let table = &f.stencil.dfg.jump_tables[*table];
196
197 visit(inst, table.default_block().block(pool), false);
208
209 for dest in table.as_slice() {
210 visit(inst, dest.block(pool), true);
211 }
212 }
213
214 inst => debug_assert!(!inst.opcode().is_branch()),
215 }
216 }
217}