/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 | } | 
 |