/src/regalloc.rs/bin/fuzzing.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Implements fuzzing primitives for everything. |
2 | | |
3 | | use arbitrary::{Arbitrary, Result, Unstructured}; |
4 | | use std::collections::{HashMap, HashSet}; |
5 | | use std::iter::FromIterator; |
6 | | |
7 | | use crate::test_framework::{self as ir, *}; |
8 | | use regalloc::*; |
9 | | |
10 | | pub const NUM_REAL_REGS_PER_RC: u8 = 4; |
11 | | const NUM_REG_CLASSES: u32 = 5; |
12 | | |
13 | | /// Maximum number of vregs. |
14 | | const NUM_VREGS: u16 = 16; |
15 | | /// Maximum number of blocks. |
16 | | const NUM_BLOCKS: u8 = 8; |
17 | | /// Maximum number of instructions per block. |
18 | | const NUM_BLOCK_INSTS: u8 = 8; |
19 | | |
20 | | struct FuzzingEnv { |
21 | | num_blocks: u8, |
22 | | num_virtual_regs: u16, |
23 | | num_reftyped_regs: u16, // numbered in vreg space above ordinary vregs. |
24 | | /// Map of virtual register index to register class. None means the register hasn't been ever defined. |
25 | | vregs: HashMap<u16, RegClass>, |
26 | | /// Set of reftyped vregs that have been defined. |
27 | | reftyped_regs: HashSet<u16>, |
28 | | /// Really a hashmap from rc to HashSet<Reg>. |
29 | | regs_by_rc: Vec<HashSet<Reg>>, |
30 | | vregs_by_rc: Vec<HashSet<u16>>, |
31 | | } |
32 | | |
33 | | impl FuzzingEnv { |
34 | 26.5k | fn block(&self, u: &mut Unstructured) -> Result<BlockIx> { |
35 | 26.5k | Ok(BlockIx::new((u8::arbitrary(u)? % self.num_blocks) as u32)) |
36 | 26.5k | } |
37 | | |
38 | 26.5k | fn label(&self, u: &mut Unstructured) -> Result<Label> { |
39 | 26.5k | let bix = self.block(u)?; |
40 | 26.5k | Ok(Label::Resolved { |
41 | 26.5k | name: format!("{:?}", bix), |
42 | 26.5k | bix, |
43 | 26.5k | }) |
44 | 26.5k | } |
45 | | |
46 | | /// Returns true whenever a register of the given register class may be used. |
47 | 697k | fn can_use_reg(&self, rc: RegClass) -> bool { |
48 | 697k | !self.regs_by_rc[rc as usize].is_empty() |
49 | 697k | } |
50 | | |
51 | | /// Returns true whenever a reftyped register may be used. |
52 | 357k | fn can_use_reftyped_reg(&self) -> bool { |
53 | 357k | !self.reftyped_regs.is_empty() |
54 | 357k | } |
55 | | |
56 | | /// Returns true whenever a register of the given register class may be defined. |
57 | 320k | fn can_def_reg(&self, rc: RegClass) -> bool { |
58 | | // If we can use one with the given reg class, then we can redefine it! |
59 | 320k | self.can_use_reg(rc) || self.vregs.len() != (self.num_virtual_regs as usize) |
60 | 320k | } |
61 | | |
62 | | /// Returns true whenever a virtual register of the given register class may be defined. |
63 | 58.0k | fn can_def_vreg(&self, rc: RegClass) -> bool { |
64 | 58.0k | !self.vregs_by_rc[rc as usize].is_empty() |
65 | 15.5k | || self.vregs.len() != (self.num_virtual_regs as usize) |
66 | 58.0k | } |
67 | | |
68 | | /// Returns true whenever a reftyped vreg may be defined. |
69 | 178k | fn can_def_reftyped_reg(&self) -> bool { |
70 | 178k | self.can_use_reftyped_reg() || self.reftyped_regs.len() != (self.num_reftyped_regs as usize) |
71 | 178k | } |
72 | | |
73 | 58.0k | fn def_reg(&mut self, rc: RegClass, u: &mut Unstructured) -> Result<Reg> { |
74 | | debug_assert!(self.can_def_reg(rc)); |
75 | 58.0k | let reg = if self.can_def_vreg(rc) && bool::arbitrary(u)? { |
76 | | // virtual. |
77 | 32.2k | let mut index = u16::arbitrary(u)? % self.num_virtual_regs; |
78 | 41.0k | while self.vregs.contains_key(&index) && self.vregs[&index] != rc { |
79 | 8.79k | // linear probing! |
80 | 8.79k | index = (index + 1) % self.num_virtual_regs; |
81 | 8.79k | } |
82 | 32.2k | self.vregs.insert(index, rc); |
83 | 32.2k | self.vregs_by_rc[rc as usize].insert(index); |
84 | 32.2k | Reg::new_virtual(rc, index as u32) |
85 | | } else { |
86 | | // real. |
87 | | // TODO there's insider knowledge about the real reg universe stuck here. |
88 | 25.8k | let index = match rc { |
89 | 21.9k | RegClass::I32 => 0, |
90 | 3.85k | RegClass::F32 => NUM_REAL_REGS_PER_RC, |
91 | 0 | _ => panic!("unexpected rc"), |
92 | 25.8k | } + u8::arbitrary(u)? % NUM_REAL_REGS_PER_RC; |
93 | 25.8k | Reg::new_real(rc, 0x0, index) |
94 | | }; |
95 | 58.0k | self.regs_by_rc[rc as usize].insert(reg); |
96 | 58.0k | Ok(reg) |
97 | 58.0k | } |
98 | | |
99 | 21.0k | fn def_reftyped_reg(&mut self, u: &mut Unstructured) -> Result<Reg> { |
100 | | debug_assert!(self.can_def_reftyped_reg()); |
101 | 21.0k | if self.reftyped_regs.len() == 0 |
102 | 18.4k | || (self.reftyped_regs.len() < (self.num_reftyped_regs as usize) && bool::arbitrary(u)?) |
103 | | { |
104 | 6.16k | let mut index = u16::arbitrary(u)? % self.num_reftyped_regs; |
105 | 15.6k | while self.reftyped_regs.contains(&index) { |
106 | 9.50k | index = (index + 1) % self.num_reftyped_regs; |
107 | 9.50k | } |
108 | 6.16k | self.reftyped_regs.insert(index); |
109 | 6.16k | let index = index + self.num_virtual_regs; |
110 | 6.16k | Ok(Reg::new_virtual(RegClass::I32, index as u32)) |
111 | | } else { |
112 | 14.8k | assert!(self.reftyped_regs.len() > 0); |
113 | 14.8k | let list_index = usize::arbitrary(u)? % self.reftyped_regs.len(); |
114 | 14.8k | let reg_index = self |
115 | 14.8k | .reftyped_regs |
116 | 14.8k | .iter() |
117 | 14.8k | .skip(list_index) |
118 | 14.8k | .cloned() |
119 | 14.8k | .next() |
120 | 14.8k | .unwrap(); |
121 | 14.8k | let reg_index = reg_index + self.num_virtual_regs; |
122 | 14.8k | Ok(Reg::new_virtual(RegClass::I32, reg_index as u32)) |
123 | | } |
124 | 21.0k | } |
125 | | |
126 | 2.72k | fn mod_reg(&mut self, rc: RegClass, u: &mut Unstructured) -> Result<Reg> { |
127 | 2.72k | // No need to handle the def part! If there was such a register, it was inserted in the first |
128 | 2.72k | // place with the same register class. |
129 | 2.72k | self.get_reg(rc, u) |
130 | 2.72k | } |
131 | | |
132 | 48.2k | fn get_reg(&self, rc: RegClass, u: &mut Unstructured) -> Result<Reg> { |
133 | | debug_assert!(self.can_use_reg(rc)); |
134 | 48.2k | let regs = Vec::from_iter(self.regs_by_rc[rc as usize].iter()); |
135 | 48.2k | let reg = *regs[usize::arbitrary(u)? % regs.len()]; |
136 | 48.2k | Ok(reg) |
137 | 48.2k | } |
138 | | |
139 | 17.7k | fn get_reftyped_reg(&self, u: &mut Unstructured) -> Result<Reg> { |
140 | | debug_assert!(self.can_use_reftyped_reg()); |
141 | 17.7k | let regs = Vec::from_iter(self.reftyped_regs.iter()); |
142 | 17.7k | let reg_index = *regs[usize::arbitrary(u)? % regs.len()]; |
143 | 17.7k | let reg_index = reg_index + self.num_virtual_regs; |
144 | 17.7k | Ok(Reg::new_virtual(RegClass::I32, reg_index as u32)) |
145 | 17.7k | } |
146 | | |
147 | 3.65k | fn get_ri(&self, u: &mut Unstructured) -> Result<RI> { |
148 | 3.65k | Ok(if self.can_use_reg(RegClass::I32) && bool::arbitrary(u)? { |
149 | | RI::Reg { |
150 | 2.31k | reg: self.get_reg(RegClass::I32, u)?, |
151 | | } |
152 | | } else { |
153 | | RI::Imm { |
154 | 1.34k | imm: u32::arbitrary(u)?, |
155 | | } |
156 | | }) |
157 | 3.65k | } |
158 | | |
159 | 7.53k | fn get_am(&self, u: &mut Unstructured) -> Result<AM> { |
160 | | debug_assert!(self.can_use_reg(RegClass::I32)); |
161 | 7.53k | let base = self.get_reg(RegClass::I32, u)?; |
162 | 7.53k | Ok(if bool::arbitrary(u)? { |
163 | | // RI |
164 | | AM::RI { |
165 | 3.92k | base, |
166 | 3.92k | offset: u32::arbitrary(u)?, |
167 | | } |
168 | | } else { |
169 | | // RR |
170 | 3.60k | let offset = self.get_reg(RegClass::I32, u)?; |
171 | 3.60k | AM::RR { base, offset } |
172 | | }) |
173 | 7.53k | } |
174 | | |
175 | 89.3k | fn inst(&mut self, u: &mut Unstructured) -> Result<Inst> { |
176 | | use Inst::*; |
177 | | use RegClass::*; |
178 | | |
179 | | enum AllowedInst { |
180 | | Imm, |
181 | | ImmF, |
182 | | Copy, |
183 | | CopyF, |
184 | | CopyRef, |
185 | | BinOp, |
186 | | BinOpM, |
187 | | BinOpF, |
188 | | Load, |
189 | | LoadF, |
190 | | Store, |
191 | | StoreF, |
192 | | MakeRef, |
193 | | UseRef, |
194 | | } |
195 | | |
196 | 89.3k | let mut allowed_insts = Vec::new(); |
197 | 88.7k | if self.can_def_reg(I32) { |
198 | 88.7k | allowed_insts.push(AllowedInst::Imm); |
199 | 628 | } |
200 | 89.3k | if self.can_def_reg(F32) { |
201 | 84.1k | allowed_insts.push(AllowedInst::ImmF); |
202 | 5.22k | } |
203 | 89.3k | if self.can_use_reg(I32) { |
204 | 83.4k | allowed_insts.push(AllowedInst::Copy); |
205 | 83.4k | allowed_insts.push(AllowedInst::BinOp); |
206 | 83.4k | allowed_insts.push(AllowedInst::BinOpM); |
207 | 83.4k | allowed_insts.push(AllowedInst::Load); |
208 | 83.4k | allowed_insts.push(AllowedInst::Store); |
209 | 78.1k | if self.can_def_reg(F32) { |
210 | 78.1k | allowed_insts.push(AllowedInst::LoadF); |
211 | 5.22k | } |
212 | 83.4k | if self.can_use_reg(F32) { |
213 | 64.4k | allowed_insts.push(AllowedInst::StoreF); |
214 | 18.9k | } |
215 | 5.92k | } |
216 | 89.3k | if self.can_use_reg(F32) { |
217 | 66.1k | allowed_insts.push(AllowedInst::CopyF); |
218 | 66.1k | allowed_insts.push(AllowedInst::BinOpF); |
219 | 23.1k | } |
220 | 89.3k | if self.can_def_reftyped_reg() && self.can_use_reg(I32) { |
221 | 83.4k | allowed_insts.push(AllowedInst::MakeRef); |
222 | 5.92k | } |
223 | 89.3k | if self.can_use_reftyped_reg() && self.can_def_reg(I32) { |
224 | 58.3k | allowed_insts.push(AllowedInst::UseRef); |
225 | 31.0k | } |
226 | 89.3k | if self.can_def_reftyped_reg() && self.can_use_reftyped_reg() { |
227 | 58.3k | allowed_insts.push(AllowedInst::CopyRef); |
228 | 31.0k | } |
229 | | |
230 | | debug_assert!(!allowed_insts.is_empty()); |
231 | | |
232 | 89.3k | let index = u8::arbitrary(u)? as usize % (allowed_insts.len() + 1); |
233 | 89.3k | if index == allowed_insts.len() { |
234 | 2.89k | return self.inst_control_flow(u); |
235 | 86.4k | } |
236 | | |
237 | | // Get uses before defs! |
238 | 86.4k | Ok(match allowed_insts[index] { |
239 | 86.4k | AllowedInst::Imm => Imm { |
240 | 32.9k | dst: self.def_reg(I32, u)?, |
241 | 32.9k | imm: u32::arbitrary(u)?, |
242 | | }, |
243 | | AllowedInst::ImmF => ImmF { |
244 | 4.91k | dst: self.def_reg(F32, u)?, |
245 | 4.91k | imm: f32::arbitrary(u)?, |
246 | | }, |
247 | | AllowedInst::Copy => { |
248 | 11.7k | let src = self.get_reg(I32, u)?; |
249 | | Copy { |
250 | 11.7k | dst: self.def_reg(I32, u)?, |
251 | 11.7k | src, |
252 | | } |
253 | | } |
254 | | AllowedInst::CopyF => { |
255 | 2.05k | let src = self.get_reg(F32, u)?; |
256 | | CopyF { |
257 | 2.05k | dst: self.def_reg(F32, u)?, |
258 | 2.05k | src, |
259 | | } |
260 | | } |
261 | | AllowedInst::CopyRef => { |
262 | 15.4k | let dst = self.def_reftyped_reg(u)?; |
263 | 15.4k | let src = self.get_reftyped_reg(u)?; |
264 | 15.4k | Copy { dst, src } |
265 | | } |
266 | | AllowedInst::BinOp => { |
267 | 932 | let src_left = self.get_reg(I32, u)?; |
268 | 932 | let src_right = self.get_ri(u)?; |
269 | | BinOp { |
270 | 932 | op: ir::BinOp::arbitrary(u)?, |
271 | 932 | dst: self.def_reg(I32, u)?, |
272 | 932 | src_left, |
273 | 932 | src_right, |
274 | | } |
275 | | } |
276 | | AllowedInst::BinOpM => BinOpM { |
277 | 2.72k | op: ir::BinOp::arbitrary(u)?, |
278 | 2.72k | dst: self.mod_reg(I32, u)?, |
279 | 2.72k | src_right: self.get_ri(u)?, |
280 | | }, |
281 | | AllowedInst::BinOpF => { |
282 | 419 | let src_left = self.get_reg(F32, u)?; |
283 | 419 | let src_right = self.get_reg(F32, u)?; |
284 | | BinOpF { |
285 | 419 | op: ir::BinOpF::arbitrary(u)?, |
286 | 419 | dst: self.def_reg(F32, u)?, |
287 | 419 | src_left, |
288 | 419 | src_right, |
289 | | } |
290 | | } |
291 | | AllowedInst::Load => { |
292 | 1.25k | let addr = self.get_am(u)?; |
293 | | Load { |
294 | 1.25k | dst: self.def_reg(I32, u)?, |
295 | 1.25k | addr, |
296 | | } |
297 | | } |
298 | | AllowedInst::LoadF => { |
299 | 1.62k | let addr = self.get_am(u)?; |
300 | | LoadF { |
301 | 1.62k | dst: self.def_reg(F32, u)?, |
302 | 1.62k | addr, |
303 | | } |
304 | | } |
305 | | AllowedInst::Store => Store { |
306 | 1.97k | addr: self.get_am(u)?, |
307 | 1.97k | src: self.get_reg(I32, u)?, |
308 | | }, |
309 | | AllowedInst::StoreF => StoreF { |
310 | 2.66k | addr: self.get_am(u)?, |
311 | 2.66k | src: self.get_reg(F32, u)?, |
312 | | }, |
313 | | AllowedInst::MakeRef => MakeRef { |
314 | 5.51k | dst: self.def_reftyped_reg(u)?, |
315 | 5.51k | src: self.get_reg(I32, u)?, |
316 | | }, |
317 | | AllowedInst::UseRef => UseRef { |
318 | 2.24k | dst: self.def_reg(I32, u)?, |
319 | 2.24k | src: self.get_reftyped_reg(u)?, |
320 | | }, |
321 | | }) |
322 | 89.3k | } |
323 | | |
324 | 21.8k | fn inst_control_flow(&self, u: &mut Unstructured) -> Result<Inst> { |
325 | 21.8k | use Inst::*; |
326 | 21.8k | use RegClass::*; |
327 | 21.8k | |
328 | 21.8k | enum AllowedInst { |
329 | 21.8k | Goto, |
330 | 21.8k | GotoCtf, |
331 | 21.8k | Finish, |
332 | 21.8k | } |
333 | 21.8k | |
334 | 21.8k | let mut allowed_insts = vec![AllowedInst::Goto, AllowedInst::Finish]; |
335 | 20.9k | if self.can_use_reg(I32) { |
336 | 20.9k | allowed_insts.push(AllowedInst::GotoCtf); |
337 | 951 | } |
338 | | |
339 | | Ok( |
340 | 21.8k | match allowed_insts[u8::arbitrary(u)? as usize % allowed_insts.len()] { |
341 | 21.8k | AllowedInst::GotoCtf => GotoCTF { |
342 | 5.87k | cond: self.get_reg(I32, u)?, |
343 | 5.87k | target_true: self.label(u)?, |
344 | 5.87k | target_false: self.label(u)?, |
345 | | }, |
346 | | AllowedInst::Goto => Goto { |
347 | 14.7k | target: self.label(u)?, |
348 | | }, |
349 | | AllowedInst::Finish => { |
350 | 1.21k | let ret_value = if bool::arbitrary(u)? { |
351 | 542 | if self.can_use_reg(I32) { |
352 | 427 | Some(self.get_reg(I32, u)?) |
353 | 115 | } else if self.can_use_reg(F32) { |
354 | 53 | Some(self.get_reg(F32, u)?) |
355 | | } else { |
356 | 62 | None |
357 | | } |
358 | | } else { |
359 | 675 | None |
360 | | }; |
361 | 1.21k | Finish { reg: ret_value } |
362 | | } |
363 | | }, |
364 | | ) |
365 | 21.8k | } |
366 | | } |
367 | | |
368 | | impl Arbitrary for Func { |
369 | 4.26k | fn arbitrary(u: &mut Unstructured) -> arbitrary::Result<Func> { |
370 | 4.26k | let num_virtual_regs = 1 + (u16::arbitrary(u)? % NUM_VREGS); |
371 | 4.26k | let num_reftyped_regs = 1 + (u16::arbitrary(u)? % NUM_VREGS); |
372 | 4.26k | let mut num_blocks = 1 + (u8::arbitrary(u)? % NUM_BLOCKS); |
373 | | |
374 | 4.26k | let mut env = FuzzingEnv { |
375 | 4.26k | num_blocks, |
376 | 4.26k | num_virtual_regs, |
377 | 4.26k | num_reftyped_regs, |
378 | 4.26k | vregs: HashMap::new(), |
379 | 4.26k | reftyped_regs: HashSet::new(), |
380 | 4.26k | regs_by_rc: vec![HashSet::new(); NUM_REG_CLASSES as usize], |
381 | 4.26k | vregs_by_rc: vec![HashSet::new(); NUM_REG_CLASSES as usize], |
382 | 4.26k | }; |
383 | 4.26k | |
384 | 4.26k | let entry = Some(Label::Resolved { |
385 | 4.26k | name: "entry".to_string(), |
386 | 4.26k | bix: BlockIx::new(0), |
387 | 4.26k | }); |
388 | 4.26k | |
389 | 4.26k | let mut insts = TypedIxVec::new(); |
390 | 4.26k | let mut blocks = TypedIxVec::new(); |
391 | 4.26k | |
392 | 4.26k | let mut cur_block = 0; |
393 | | |
394 | 26.1k | while num_blocks > 0 { |
395 | 21.8k | let start = insts.len(); |
396 | | |
397 | 21.8k | let mut num_block_insts = 1 + (u8::arbitrary(u)? % NUM_BLOCK_INSTS); |
398 | | |
399 | 21.8k | if bool::arbitrary(u)? { |
400 | 14.7k | insts.push(Inst::Safepoint); |
401 | 7.10k | } |
402 | 108k | while num_block_insts > 0 { |
403 | 108k | let inst = if num_block_insts == 1 { |
404 | 18.9k | env.inst_control_flow(u)? |
405 | | } else { |
406 | 89.3k | env.inst(u)? |
407 | | }; |
408 | 108k | let is_control_flow = inst.is_control_flow(); |
409 | 108k | insts.push(inst); |
410 | 108k | num_block_insts -= 1; |
411 | 108k | if is_control_flow { |
412 | 21.8k | break; |
413 | 86.4k | } |
414 | | } |
415 | | |
416 | | debug_assert!(insts.len() > start); |
417 | 21.8k | let len = insts.len() - start; |
418 | 21.8k | let block = Block { |
419 | 21.8k | name: format!("b{}", cur_block), |
420 | 21.8k | start: InstIx::new(start), |
421 | 21.8k | len, |
422 | 21.8k | estimated_execution_frequency: 0, |
423 | 21.8k | }; |
424 | 21.8k | blocks.push(block); |
425 | 21.8k | |
426 | 21.8k | cur_block += 1; |
427 | 21.8k | num_blocks -= 1; |
428 | | } |
429 | | |
430 | 4.26k | Ok(Func { |
431 | 4.26k | name: "funk".to_string(), |
432 | 4.26k | entry, |
433 | 4.26k | num_virtual_regs: (num_virtual_regs + num_reftyped_regs) as u32, |
434 | 4.26k | reftype_reg_start: Some(num_virtual_regs as u32), |
435 | 4.26k | insns: insts, |
436 | 4.26k | blocks, |
437 | 4.26k | }) |
438 | 4.26k | } |
439 | | } |