/src/regalloc.rs/bin/validator.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::test_framework::*; |
2 | | use regalloc::*; |
3 | | use std::collections::{HashMap, HashSet}; |
4 | | |
5 | | #[derive(PartialEq, Debug)] |
6 | | pub enum RegRef { |
7 | | Use, |
8 | | Def, |
9 | | } |
10 | | |
11 | | pub struct Context<'rru> { |
12 | | pub num_vregs: usize, |
13 | | pub num_blocks: u32, |
14 | | real_reg_universe: &'rru RealRegUniverse, |
15 | | vreg_types: HashMap<usize, RegClass>, |
16 | | used_regs: HashSet<Reg>, |
17 | | } |
18 | | |
19 | | impl<'rru> Context<'rru> { |
20 | | fn new(func: &Func, real_reg_universe: &'rru RealRegUniverse) -> Self { |
21 | | Self { |
22 | | num_vregs: func.num_virtual_regs as usize, |
23 | | real_reg_universe, |
24 | | num_blocks: func.blocks.len(), |
25 | | vreg_types: HashMap::new(), |
26 | | used_regs: HashSet::new(), |
27 | | } |
28 | | } |
29 | | |
30 | 0 | pub fn check_reg(&mut self, reg: Reg, reg_ref: RegRef) -> bool { |
31 | 0 | let rc = reg.get_class(); |
32 | 0 | let index = reg.get_index(); |
33 | 0 |
|
34 | 0 | if !self.used_regs.contains(®) { |
35 | 0 | if reg_ref == RegRef::Use { |
36 | | // Use before def. |
37 | 0 | return false; |
38 | 0 | } |
39 | 0 | self.used_regs.insert(reg); |
40 | 0 | } |
41 | | |
42 | 0 | if reg.is_virtual() { |
43 | | // If the register has been mentioned earlier, check that it didn't change type in the |
44 | | // meanwhile. |
45 | 0 | if let Some(prev_rc) = self.vreg_types.insert(index, rc) { |
46 | 0 | if prev_rc != rc { |
47 | 0 | return false; |
48 | 0 | } |
49 | 0 | } |
50 | | |
51 | | // If it's virtual, it must be in range. |
52 | 0 | index < self.num_vregs |
53 | | } else { |
54 | | debug_assert!(reg.is_real()); |
55 | | // If it's real, it must: |
56 | | // - exist in the array of real registers, |
57 | | // - have the same encoding as in the real register universe, |
58 | | // - be in the range of its register class. |
59 | 0 | if index >= self.real_reg_universe.regs.len() { |
60 | 0 | return false; |
61 | 0 | } |
62 | 0 | if self.real_reg_universe.regs[index].0 != reg.as_real_reg().unwrap() { |
63 | 0 | return false; |
64 | 0 | } |
65 | 0 | match self.real_reg_universe.allocable_by_class[rc as usize] { |
66 | 0 | Some(ref reg_info) => index >= reg_info.first && index <= reg_info.last, |
67 | 0 | None => false, |
68 | | } |
69 | | } |
70 | 0 | } |
71 | | |
72 | | pub fn check_reg_rc(&mut self, reg: &Reg, reg_ref: RegRef, expected_class: RegClass) -> bool { |
73 | | reg.get_class() == expected_class && self.check_reg(*reg, reg_ref) |
74 | | } |
75 | | } |
76 | | |
77 | 0 | pub fn validate(func: &Func, real_reg_universe: &RealRegUniverse) -> Result<(), String> { |
78 | 0 | let mut cx = Context::new(func, real_reg_universe); |
79 | 0 |
|
80 | 0 | // Function entry must exist and point to a valid block. |
81 | 0 | match &func.entry { |
82 | 0 | None => { |
83 | 0 | return Err("missing entry label".into()); |
84 | | } |
85 | 0 | Some(label) => { |
86 | 0 | if !label.type_checks(&cx) { |
87 | 0 | return Err("invalid or unresolved entry label".into()); |
88 | 0 | } |
89 | | } |
90 | | }; |
91 | | |
92 | | // Blocks must not be empty. |
93 | 0 | for b in func.blocks.iter() { |
94 | 0 | if b.len == 0 { |
95 | 0 | return Err(format!("block {} is empty", b.name)); |
96 | 0 | } |
97 | 0 | if b.start.get().checked_add(b.len).is_none() { |
98 | 0 | return Err(format!("too many instructions in block {}", b.name)); |
99 | 0 | } |
100 | | } |
101 | | |
102 | 0 | if func.blocks[BlockIx::new(0)].start.get() != 0 { |
103 | 0 | return Err(format!("first block must start at first instruction")); |
104 | 0 | } |
105 | 0 |
|
106 | 0 | let last_block = &func.blocks[BlockIx::new(func.blocks.len() - 1)]; |
107 | 0 | if func.insns.len() != last_block.start.get() + last_block.len { |
108 | 0 | return Err(format!("unused instructions")); |
109 | 0 | } |
110 | | |
111 | | // Check that blocks are ordered in increasing block start. |
112 | 0 | for i in 1..func.blocks.len() { |
113 | 0 | let prev = BlockIx::new(i - 1); |
114 | 0 | let cur = BlockIx::new(i); |
115 | 0 |
|
116 | 0 | let prev_block = &func.blocks[prev]; |
117 | 0 | if prev_block.start >= func.blocks[cur].start { |
118 | 0 | return Err(format!( |
119 | 0 | "blocks {:?} and {:?} not ordered in strictly increasing start", |
120 | 0 | prev, cur |
121 | 0 | )); |
122 | 0 | } |
123 | 0 |
|
124 | 0 | let prev_start: u32 = prev_block.start.into(); |
125 | 0 | let cur_start: u32 = func.blocks[cur].start.into(); |
126 | 0 | if prev_start + prev_block.len != cur_start { |
127 | 0 | return Err(format!("block {:?} is incorrectly specified", prev)); |
128 | 0 | } |
129 | | } |
130 | | |
131 | | // Check instructions. |
132 | 0 | for b in func.blocks.iter() { |
133 | 0 | if b.start.get().checked_add(b.len).is_none() { |
134 | 0 | return Err("too many block instructions".into()); |
135 | 0 | } |
136 | 0 | for i in b.start.dotdot(b.start.plus(b.len)) { |
137 | 0 | if i.get() >= func.insns.len() { |
138 | 0 | return Err(format!( |
139 | 0 | "invalid instruction number {:?} in block {}", |
140 | 0 | i, b.name |
141 | 0 | )); |
142 | 0 | } |
143 | 0 |
|
144 | 0 | let inst = &func.insns[i]; |
145 | 0 |
|
146 | 0 | if !inst.is_user() { |
147 | 0 | return Err(format!( |
148 | 0 | "unexpected regalloc inst {:?} in block {}", |
149 | 0 | inst, b.name |
150 | 0 | )); |
151 | 0 | } |
152 | 0 |
|
153 | 0 | // This is a poor man's liveness analysis, since it doesn't take control flow into account. |
154 | 0 | if !inst.type_checks(&mut cx) { |
155 | 0 | return Err(format!( |
156 | 0 | "inst {:?} in block {} does not type check", |
157 | 0 | inst, b.name |
158 | 0 | )); |
159 | 0 | } |
160 | 0 |
|
161 | 0 | // No control flow instructions in the middle, but it must be one at the end. |
162 | 0 | if i == b.start.plus(b.len - 1) { |
163 | 0 | if !inst.is_control_flow() { |
164 | 0 | return Err(format!( |
165 | 0 | "final inst {:?} of block {} must be a control flow inst", |
166 | 0 | inst, b.name |
167 | 0 | )); |
168 | 0 | } |
169 | | } else { |
170 | 0 | if inst.is_control_flow() { |
171 | 0 | return Err(format!( |
172 | 0 | "control flow inst {:?} in the middle of block {}", |
173 | 0 | inst, b.name |
174 | 0 | )); |
175 | 0 | } |
176 | | } |
177 | | } |
178 | | } |
179 | | |
180 | 0 | if let Err(err) = regalloc::analysis_main::run_analysis( |
181 | 0 | func, |
182 | 0 | real_reg_universe, |
183 | 0 | // The next four params merely ensure that we get all possible analysis results from |
184 | 0 | // `run_analysis`. There's no implied claim about which algorithm we're using or |
185 | 0 | // validating. |
186 | 0 | AlgorithmWithDefaults::Backtracking, |
187 | 0 | /*client_wants_stackmaps=*/ true, |
188 | 0 | /*reftype_class=*/ RegClass::I64, |
189 | 0 | /*reftyped_vregs=*/ &vec![], |
190 | 0 | ) { |
191 | 0 | return Err(err.to_string()); |
192 | 0 | } |
193 | 0 |
|
194 | 0 | Ok(()) |
195 | 0 | } |
196 | | |
197 | 0 | pub fn check_results( |
198 | 0 | before_regalloc_result: &Result<RunResult, String>, |
199 | 0 | after_regalloc_result: &Result<RunResult, String>, |
200 | 0 | ) { |
201 | 0 | match before_regalloc_result { |
202 | 0 | Ok(before_regalloc_result) => { |
203 | 0 | let after_regalloc_result = after_regalloc_result |
204 | 0 | .as_ref() |
205 | 0 | .expect("code after regalloc should have succeeded"); |
206 | | |
207 | | // The interpreted result number of dynamic steps is a lower bound on the |
208 | | // number of dynamic steps executed after regalloc. |
209 | | assert!( |
210 | 0 | before_regalloc_result.num_steps <= after_regalloc_result.num_steps, |
211 | | "inconsistent trace" |
212 | | ); |
213 | | |
214 | | assert_eq!( |
215 | | before_regalloc_result.ret_value, after_regalloc_result.ret_value, |
216 | 0 | "Incorrect interpreter result: expected {:?}, observed {:?}", |
217 | 0 | before_regalloc_result.ret_value, after_regalloc_result.ret_value |
218 | | ); |
219 | | |
220 | | assert_eq!( |
221 | | before_regalloc_result.stdout, after_regalloc_result.stdout, |
222 | 0 | r#"Different stdout values before/after regalloc: |
223 | 0 | - before: |
224 | 0 | {} |
225 | 0 | -after: |
226 | 0 | {} |
227 | 0 | "#, |
228 | 0 | before_regalloc_result.stdout, after_regalloc_result.stdout |
229 | | ); |
230 | | } |
231 | | |
232 | 0 | Err(err) => { |
233 | 0 | assert_eq!(err, after_regalloc_result.as_ref().unwrap_err()); |
234 | | } |
235 | | } |
236 | 0 | } |