Coverage Report

Created: 2024-10-16 07:58

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