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