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