/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.91.1/src/inst_predicates.rs
Line | Count | Source (jump to first uncovered line) |
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 cranelift_entity::EntityRef; |
6 | | |
7 | | /// Preserve instructions with used result values. |
8 | 1.18M | pub fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool { |
9 | 1.18M | dfg.inst_results(inst).iter().any(|v| live[v.index()]) |
10 | 1.18M | } |
11 | | |
12 | | /// Test whether the given opcode is unsafe to even consider as side-effect-free. |
13 | | #[inline(always)] |
14 | 4.15M | fn trivially_has_side_effects(opcode: Opcode) -> bool { |
15 | 4.15M | opcode.is_call() |
16 | 3.82M | || opcode.is_branch() |
17 | 3.25M | || opcode.is_terminator() |
18 | 2.76M | || opcode.is_return() |
19 | 2.76M | || opcode.can_trap() |
20 | 2.68M | || opcode.other_side_effects() |
21 | 2.66M | || opcode.can_store() |
22 | 4.15M | } |
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 | | #[inline(always)] |
28 | 2.58M | fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool { |
29 | 2.58M | if !opcode.can_load() { |
30 | 1.90M | return false; |
31 | 683k | } |
32 | 683k | match *data { |
33 | 0 | InstructionData::StackLoad { .. } => false, |
34 | 683k | InstructionData::Load { flags, .. } => !flags.notrap(), |
35 | 0 | _ => true, |
36 | | } |
37 | 2.58M | } |
38 | | |
39 | | /// Does the given instruction have any side-effect that would preclude it from being removed when |
40 | | /// its value is unused? |
41 | | #[inline(always)] |
42 | 4.15M | pub fn has_side_effect(func: &Function, inst: Inst) -> bool { |
43 | 4.15M | let data = &func.dfg[inst]; |
44 | 4.15M | let opcode = data.opcode(); |
45 | 4.15M | trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data) |
46 | 4.15M | } |
47 | | |
48 | | /// Does the given instruction have any side-effect as per [has_side_effect], or else is a load, |
49 | | /// but not the get_pinned_reg opcode? |
50 | 2.40M | pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool { |
51 | 2.40M | let op = func.dfg[inst].opcode(); |
52 | 2.40M | op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load()) |
53 | 2.40M | } |
54 | | |
55 | | /// Is the given instruction a constant value (`iconst`, `fconst`) that can be |
56 | | /// represented in 64 bits? |
57 | 1.03M | pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> { |
58 | 1.03M | let data = &func.dfg[inst]; |
59 | 1.03M | if data.opcode() == Opcode::Null { |
60 | 0 | return Some(0); |
61 | 1.03M | } |
62 | 1.03M | match data { |
63 | 14.6k | &InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64), |
64 | 5.74k | &InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64), |
65 | 4.27k | &InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()), |
66 | 1.01M | _ => None, |
67 | | } |
68 | 1.03M | } |
69 | | |
70 | | /// Get the address, offset, and access type from the given instruction, if any. |
71 | 1.03M | pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> { |
72 | 1.03M | let data = &func.dfg[inst]; |
73 | 1.03M | match data { |
74 | 182k | InstructionData::Load { arg, offset, .. } => { |
75 | 182k | let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]); |
76 | 182k | Some((*arg, *offset, ty)) |
77 | | } |
78 | 3.30k | InstructionData::LoadNoOffset { arg, .. } => { |
79 | 3.30k | let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]); |
80 | 3.30k | Some((*arg, 0.into(), ty)) |
81 | | } |
82 | 27.0k | InstructionData::Store { args, offset, .. } => { |
83 | 27.0k | let ty = func.dfg.value_type(args[0]); |
84 | 27.0k | Some((args[1], *offset, ty)) |
85 | | } |
86 | 0 | InstructionData::StoreNoOffset { args, .. } => { |
87 | 0 | let ty = func.dfg.value_type(args[0]); |
88 | 0 | Some((args[1], 0.into(), ty)) |
89 | | } |
90 | 825k | _ => None, |
91 | | } |
92 | 1.03M | } |
93 | | |
94 | | /// Get the store data, if any, from an instruction. |
95 | 27.0k | pub fn inst_store_data(func: &Function, inst: Inst) -> Option<Value> { |
96 | 27.0k | let data = &func.dfg[inst]; |
97 | 27.0k | match data { |
98 | 27.0k | InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => { |
99 | 27.0k | Some(args[0]) |
100 | | } |
101 | 0 | _ => None, |
102 | | } |
103 | 27.0k | } |
104 | | |
105 | | /// Determine whether this opcode behaves as a memory fence, i.e., |
106 | | /// prohibits any moving of memory accesses across it. |
107 | 2.08M | pub fn has_memory_fence_semantics(op: Opcode) -> bool { |
108 | 1.87M | match op { |
109 | | Opcode::AtomicRmw |
110 | | | Opcode::AtomicCas |
111 | | | Opcode::AtomicLoad |
112 | | | Opcode::AtomicStore |
113 | | | Opcode::Fence |
114 | 0 | | Opcode::Debugtrap => true, |
115 | 210k | Opcode::Call | Opcode::CallIndirect => true, |
116 | 1.87M | op if op.can_trap() => true, |
117 | 1.69M | _ => false, |
118 | | } |
119 | 2.08M | } |
120 | | |
121 | | /// Visit all successors of a block with a given visitor closure. The closure |
122 | | /// arguments are the branch instruction that is used to reach the successor, |
123 | | /// the successor block itself, and a flag indicating whether the block is |
124 | | /// branched to via a table entry. |
125 | 658k | pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>( |
126 | 658k | f: &Function, |
127 | 658k | block: Block, |
128 | 658k | mut visit: F, |
129 | 658k | ) { |
130 | 851k | for inst in f.layout.block_likely_branches(block) { |
131 | 851k | if f.dfg[inst].opcode().is_branch() { |
132 | 387k | visit_branch_targets(f, inst, &mut visit); |
133 | 464k | } |
134 | | } |
135 | 658k | } cranelift_codegen::inst_predicates::visit_block_succs::<<cranelift_codegen::alias_analysis::AliasAnalysis>::compute_block_input_states::{closure#1}> Line | Count | Source | 125 | 329k | pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>( | 126 | 329k | f: &Function, | 127 | 329k | block: Block, | 128 | 329k | mut visit: F, | 129 | 329k | ) { | 130 | 426k | for inst in f.layout.block_likely_branches(block) { | 131 | 426k | if f.dfg[inst].opcode().is_branch() { | 132 | 194k | visit_branch_targets(f, inst, &mut visit); | 133 | 232k | } | 134 | | } | 135 | 329k | } |
cranelift_codegen::inst_predicates::visit_block_succs::<<cranelift_codegen::machinst::blockorder::BlockLoweringOrder>::new::{closure#0}> Line | Count | Source | 125 | 328k | pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>( | 126 | 328k | f: &Function, | 127 | 328k | block: Block, | 128 | 328k | mut visit: F, | 129 | 328k | ) { | 130 | 424k | for inst in f.layout.block_likely_branches(block) { | 131 | 424k | if f.dfg[inst].opcode().is_branch() { | 132 | 192k | visit_branch_targets(f, inst, &mut visit); | 133 | 231k | } | 134 | | } | 135 | 328k | } |
|
136 | | |
137 | 387k | fn visit_branch_targets<F: FnMut(Inst, Block, bool)>(f: &Function, inst: Inst, visit: &mut F) { |
138 | 387k | match f.dfg[inst].analyze_branch(&f.dfg.value_lists) { |
139 | 0 | BranchInfo::NotABranch => {} |
140 | 385k | BranchInfo::SingleDest(dest, _) => { |
141 | 385k | visit(inst, dest, false); |
142 | 385k | } |
143 | 1.52k | BranchInfo::Table(table, maybe_dest) => { |
144 | 1.52k | if let Some(dest) = maybe_dest { |
145 | 1.52k | // The default block is reached via a direct conditional branch, |
146 | 1.52k | // so it is not part of the table. |
147 | 1.52k | visit(inst, dest, false); |
148 | 1.52k | } |
149 | 6.05k | for &dest in f.jump_tables[table].as_slice() { |
150 | 6.05k | visit(inst, dest, true); |
151 | 6.05k | } |
152 | | } |
153 | | } |
154 | 387k | } cranelift_codegen::inst_predicates::visit_branch_targets::<<cranelift_codegen::alias_analysis::AliasAnalysis>::compute_block_input_states::{closure#1}> Line | Count | Source | 137 | 194k | fn visit_branch_targets<F: FnMut(Inst, Block, bool)>(f: &Function, inst: Inst, visit: &mut F) { | 138 | 194k | match f.dfg[inst].analyze_branch(&f.dfg.value_lists) { | 139 | 0 | BranchInfo::NotABranch => {} | 140 | 193k | BranchInfo::SingleDest(dest, _) => { | 141 | 193k | visit(inst, dest, false); | 142 | 193k | } | 143 | 818 | BranchInfo::Table(table, maybe_dest) => { | 144 | 818 | if let Some(dest) = maybe_dest { | 145 | 818 | // The default block is reached via a direct conditional branch, | 146 | 818 | // so it is not part of the table. | 147 | 818 | visit(inst, dest, false); | 148 | 818 | } | 149 | 3.34k | for &dest in f.jump_tables[table].as_slice() { | 150 | 3.34k | visit(inst, dest, true); | 151 | 3.34k | } | 152 | | } | 153 | | } | 154 | 194k | } |
cranelift_codegen::inst_predicates::visit_branch_targets::<<cranelift_codegen::machinst::blockorder::BlockLoweringOrder>::new::{closure#0}> Line | Count | Source | 137 | 192k | fn visit_branch_targets<F: FnMut(Inst, Block, bool)>(f: &Function, inst: Inst, visit: &mut F) { | 138 | 192k | match f.dfg[inst].analyze_branch(&f.dfg.value_lists) { | 139 | 0 | BranchInfo::NotABranch => {} | 140 | 192k | BranchInfo::SingleDest(dest, _) => { | 141 | 192k | visit(inst, dest, false); | 142 | 192k | } | 143 | 702 | BranchInfo::Table(table, maybe_dest) => { | 144 | 702 | if let Some(dest) = maybe_dest { | 145 | 702 | // The default block is reached via a direct conditional branch, | 146 | 702 | // so it is not part of the table. | 147 | 702 | visit(inst, dest, false); | 148 | 702 | } | 149 | 2.71k | for &dest in f.jump_tables[table].as_slice() { | 150 | 2.71k | visit(inst, dest, true); | 151 | 2.71k | } | 152 | | } | 153 | | } | 154 | 192k | } |
|