Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.91.1/src/verifier/flags.rs
Line
Count
Source (jump to first uncovered line)
1
//! Verify CPU flags values.
2
3
use crate::entity::{EntitySet, SecondaryMap};
4
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
5
use crate::ir;
6
use crate::ir::instructions::BranchInfo;
7
use crate::packed_option::PackedOption;
8
use crate::timing;
9
use crate::verifier::{VerifierErrors, VerifierStepResult};
10
11
/// Verify that CPU flags are used correctly.
12
///
13
/// The value types `iflags` and `fflags` represent CPU flags which usually live in a
14
/// special-purpose register, so they can't be used as freely as other value types that can live in
15
/// any register.
16
///
17
/// We verify the following conditions:
18
///
19
/// - At most one flags value can be live at a time.
20
/// - A flags value can not be live across an instruction that clobbers the flags.
21
///
22
///
23
1.39M
pub fn verify_flags(
24
1.39M
    func: &ir::Function,
25
1.39M
    cfg: &ControlFlowGraph,
26
1.39M
    errors: &mut VerifierErrors,
27
1.39M
) -> VerifierStepResult<()> {
28
1.39M
    let _tt = timing::verify_flags();
29
1.39M
    let mut verifier = FlagsVerifier {
30
1.39M
        func,
31
1.39M
        cfg,
32
1.39M
        livein: SecondaryMap::new(),
33
1.39M
    };
34
1.39M
    verifier.check(errors)
35
1.39M
}
36
37
struct FlagsVerifier<'a> {
38
    func: &'a ir::Function,
39
    cfg: &'a ControlFlowGraph,
40
41
    /// The single live-in flags value (if any) for each block.
42
    livein: SecondaryMap<ir::Block, PackedOption<ir::Value>>,
43
}
44
45
impl<'a> FlagsVerifier<'a> {
46
1.39M
    fn check(&mut self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
47
1.39M
        // List of blocks that need to be processed. blocks may be re-added to this list when we detect
48
1.39M
        // that one of their successor blocks needs a live-in flags value.
49
1.39M
        let mut worklist = EntitySet::with_capacity(self.func.layout.block_capacity());
50
3.17M
        for block in self.func.layout.blocks() {
51
3.17M
            worklist.insert(block);
52
3.17M
        }
53
54
4.57M
        while let Some(block) = worklist.pop() {
55
3.17M
            if let Some(value) = self.visit_block(block, errors)? {
56
                // The block has live-in flags. Check if the value changed.
57
0
                match self.livein[block].expand() {
58
                    // Revisit any predecessor blocks the first time we see a live-in for `block`.
59
                    None => {
60
0
                        self.livein[block] = value.into();
61
0
                        for BlockPredecessor { block: pred, .. } in self.cfg.pred_iter(block) {
62
0
                            worklist.insert(pred);
63
0
                        }
64
                    }
65
0
                    Some(old) if old != value => {
66
0
                        return errors.fatal((
67
0
                            block,
68
0
                            format!("conflicting live-in CPU flags: {} and {}", old, value),
69
0
                        ));
70
                    }
71
0
                    x => assert_eq!(x, Some(value)),
72
                }
73
            } else {
74
                // Existing live-in flags should never be able to disappear.
75
3.17M
                assert_eq!(self.livein[block].expand(), None);
76
            }
77
        }
78
79
1.39M
        Ok(())
80
1.39M
    }
81
82
    /// Check flags usage in `block` and return the live-in flags value, if any.
83
3.17M
    fn visit_block(
84
3.17M
        &self,
85
3.17M
        block: ir::Block,
86
3.17M
        errors: &mut VerifierErrors,
87
3.17M
    ) -> VerifierStepResult<Option<ir::Value>> {
88
3.17M
        // The single currently live flags value.
89
3.17M
        let mut live_val = None;
90
91
        // Visit instructions backwards so we can track liveness accurately.
92
19.8M
        for inst in self.func.layout.block_insts(block).rev() {
93
            // Check if `inst` interferes with existing live flags.
94
19.8M
            if let Some(live) = live_val {
95
0
                for &res in self.func.dfg.inst_results(inst) {
96
0
                    if res == live {
97
0
                        // We've reached the def of `live_flags`, so it is no longer live above.
98
0
                        live_val = None;
99
0
                    } else if self.func.dfg.value_type(res).is_flags() {
100
0
                        errors
101
0
                            .report((inst, format!("{} clobbers live CPU flags in {}", res, live)));
102
0
                        return Err(());
103
0
                    }
104
                }
105
19.8M
            }
106
107
            // Now look for live ranges of CPU flags that end here.
108
19.8M
            for &arg in self.func.dfg.inst_args(inst) {
109
16.8M
                if self.func.dfg.value_type(arg).is_flags() {
110
0
                    merge(&mut live_val, arg, inst, errors)?;
111
16.8M
                }
112
            }
113
114
            // Include live-in flags to successor blocks.
115
19.8M
            match self.func.dfg.analyze_branch(inst) {
116
17.9M
                BranchInfo::NotABranch => {}
117
1.81M
                BranchInfo::SingleDest(dest, _) => {
118
1.81M
                    if let Some(val) = self.livein[dest].expand() {
119
0
                        merge(&mut live_val, val, inst, errors)?;
120
1.81M
                    }
121
                }
122
7.02k
                BranchInfo::Table(jt, dest) => {
123
7.02k
                    if let Some(dest) = dest {
124
7.02k
                        if let Some(val) = self.livein[dest].expand() {
125
0
                            merge(&mut live_val, val, inst, errors)?;
126
7.02k
                        }
127
0
                    }
128
27.1k
                    for dest in self.func.jump_tables[jt].iter() {
129
27.1k
                        if let Some(val) = self.livein[*dest].expand() {
130
0
                            merge(&mut live_val, val, inst, errors)?;
131
27.1k
                        }
132
                    }
133
                }
134
            }
135
        }
136
137
        // Return the required live-in flags value.
138
3.17M
        Ok(live_val)
139
3.17M
    }
140
}
141
142
// Merge live flags values, or return an error on conflicting values.
143
0
fn merge(
144
0
    a: &mut Option<ir::Value>,
145
0
    b: ir::Value,
146
0
    inst: ir::Inst,
147
0
    errors: &mut VerifierErrors,
148
0
) -> VerifierStepResult<()> {
149
0
    if let Some(va) = *a {
150
0
        if b != va {
151
0
            return errors.fatal((
152
0
                inst,
153
0
                format!("conflicting live CPU flags: {} and {}", va, b),
154
0
            ));
155
0
        }
156
0
    } else {
157
0
        *a = Some(b);
158
0
    }
159
160
0
    Ok(())
161
0
}