cranelift_codegen/
inst_predicates.rs1use crate::ir::immediates::Offset32;
3use crate::ir::{self, Block, Function, Inst, InstructionData, Opcode, Type, Value};
4
5#[inline(always)]
7fn trivially_has_side_effects(opcode: Opcode) -> bool {
8 opcode.is_call()
9 || opcode.is_branch()
10 || opcode.is_terminator()
11 || opcode.is_return()
12 || opcode.can_trap()
13 || opcode.other_side_effects()
14 || opcode.can_store()
15}
16
17#[inline(always)]
21fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool {
22 if !opcode.can_load() {
23 return false;
24 }
25 match *data {
26 InstructionData::StackLoad { .. } => false,
27 InstructionData::Load { flags, .. } => !flags.notrap(),
28 _ => true,
29 }
30}
31
32#[inline(always)]
35fn has_side_effect(func: &Function, inst: Inst) -> bool {
36 let data = &func.dfg.insts[inst];
37 let opcode = data.opcode();
38 trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
39}
40
41pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool {
47 let is_pure_load = match func.dfg.insts[inst] {
48 InstructionData::Load {
49 opcode: Opcode::Load,
50 flags,
51 ..
52 } => flags.readonly() && flags.notrap() && flags.can_move(),
53 _ => false,
54 };
55
56 let has_one_result = func.dfg.inst_results(inst).len() == 1;
65
66 let op = func.dfg.insts[inst].opcode();
67
68 has_one_result && (is_pure_load || (!op.can_load() && !trivially_has_side_effects(op)))
69}
70
71pub fn is_mergeable_for_egraph(func: &Function, inst: Inst) -> bool {
77 let op = func.dfg.insts[inst].opcode();
78 func.dfg.inst_results(inst).len() <= 1
81 && !op.can_load()
84 && !op.can_store()
85 && (!has_side_effect(func, inst) || op.side_effects_idempotent())
87}
88
89pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool {
92 let op = func.dfg.insts[inst].opcode();
93 op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load())
94}
95
96pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
99 match &func.dfg.insts[inst] {
100 &InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64),
101 &InstructionData::UnaryIeee16 { imm, .. } => Some(imm.bits() as u64),
102 &InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64),
103 &InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()),
104 _ => None,
105 }
106}
107
108pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> {
110 match &func.dfg.insts[inst] {
111 InstructionData::Load { arg, offset, .. } => {
112 let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
113 Some((*arg, *offset, ty))
114 }
115 InstructionData::LoadNoOffset { arg, .. } => {
116 let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
117 Some((*arg, 0.into(), ty))
118 }
119 InstructionData::Store { args, offset, .. } => {
120 let ty = func.dfg.value_type(args[0]);
121 Some((args[1], *offset, ty))
122 }
123 InstructionData::StoreNoOffset { args, .. } => {
124 let ty = func.dfg.value_type(args[0]);
125 Some((args[1], 0.into(), ty))
126 }
127 _ => None,
128 }
129}
130
131pub fn inst_store_data(func: &Function, inst: Inst) -> Option<Value> {
133 match &func.dfg.insts[inst] {
134 InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => {
135 Some(args[0])
136 }
137 _ => None,
138 }
139}
140
141pub fn has_memory_fence_semantics(op: Opcode) -> bool {
144 match op {
145 Opcode::AtomicRmw
146 | Opcode::AtomicCas
147 | Opcode::AtomicLoad
148 | Opcode::AtomicStore
149 | Opcode::Fence
150 | Opcode::Debugtrap => true,
151 Opcode::Call | Opcode::CallIndirect => true,
152 op if op.can_trap() => true,
153 _ => false,
154 }
155}
156
157pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
162 f: &Function,
163 block: Block,
164 mut visit: F,
165) {
166 if let Some(inst) = f.layout.last_inst(block) {
167 match &f.dfg.insts[inst] {
168 ir::InstructionData::Jump {
169 destination: dest, ..
170 } => {
171 visit(inst, dest.block(&f.dfg.value_lists), false);
172 }
173
174 ir::InstructionData::Brif {
175 blocks: [block_then, block_else],
176 ..
177 } => {
178 visit(inst, block_then.block(&f.dfg.value_lists), false);
179 visit(inst, block_else.block(&f.dfg.value_lists), false);
180 }
181
182 ir::InstructionData::BranchTable { table, .. } => {
183 let pool = &f.dfg.value_lists;
184 let table = &f.stencil.dfg.jump_tables[*table];
185
186 visit(inst, table.default_block().block(pool), false);
197
198 for dest in table.as_slice() {
199 visit(inst, dest.block(pool), true);
200 }
201 }
202
203 inst => debug_assert!(!inst.opcode().is_branch()),
204 }
205 }
206}