/src/regalloc2/src/fuzzing/domtree.rs
Line | Count | Source |
1 | | //! Fuzz the dominator-tree calculation. |
2 | | |
3 | | use crate::{domtree, postorder, Block}; |
4 | | use arbitrary::{Arbitrary, Result, Unstructured}; |
5 | | use std::collections::HashSet; |
6 | | use std::{vec, vec::Vec}; |
7 | | |
8 | | #[derive(Clone, Debug)] |
9 | | struct CFG { |
10 | | num_blocks: usize, |
11 | | preds: Vec<Vec<Block>>, |
12 | | succs: Vec<Vec<Block>>, |
13 | | } |
14 | | |
15 | | impl Arbitrary<'_> for CFG { |
16 | 0 | fn arbitrary(u: &mut Unstructured) -> Result<CFG> { |
17 | 0 | let num_blocks = u.int_in_range(1..=1000)?; |
18 | 0 | let mut succs = vec![]; |
19 | 0 | for _ in 0..num_blocks { |
20 | 0 | let mut block_succs = vec![]; |
21 | 0 | for _ in 0..u.int_in_range(0..=5)? { |
22 | 0 | block_succs.push(Block::new(u.int_in_range(0..=(num_blocks - 1))?)); |
23 | | } |
24 | 0 | succs.push(block_succs); |
25 | | } |
26 | 0 | let mut preds = vec![]; |
27 | 0 | for _ in 0..num_blocks { |
28 | 0 | preds.push(vec![]); |
29 | 0 | } |
30 | 0 | for from in 0..num_blocks { |
31 | 0 | for succ in &succs[from] { |
32 | 0 | preds[succ.index()].push(Block::new(from)); |
33 | 0 | } |
34 | | } |
35 | 0 | Ok(CFG { |
36 | 0 | num_blocks, |
37 | 0 | preds, |
38 | 0 | succs, |
39 | 0 | }) |
40 | 0 | } |
41 | | } |
42 | | |
43 | | #[derive(Clone, Debug)] |
44 | | struct Path { |
45 | | blocks: Vec<Block>, |
46 | | } |
47 | | |
48 | | impl Path { |
49 | 0 | fn choose_from_cfg(cfg: &CFG, u: &mut Unstructured) -> Result<Path> { |
50 | 0 | let succs = u.int_in_range(0..=(2 * cfg.num_blocks))?; |
51 | 0 | let mut block = Block::new(0); |
52 | 0 | let mut blocks = vec![]; |
53 | 0 | blocks.push(block); |
54 | 0 | for _ in 0..succs { |
55 | 0 | if cfg.succs[block.index()].is_empty() { |
56 | 0 | break; |
57 | 0 | } |
58 | 0 | block = *u.choose(&cfg.succs[block.index()])?; |
59 | 0 | blocks.push(block); |
60 | | } |
61 | 0 | Ok(Path { blocks }) |
62 | 0 | } |
63 | | } |
64 | | |
65 | 0 | fn check_idom_violations(idom: &[Block], path: &Path) { |
66 | | // "a dom b" means that any path from the entry block through the CFG that |
67 | | // contains a and b will contain a before b. |
68 | | // |
69 | | // To test this, for any given block b_i, we have the set S of b_0 .. |
70 | | // b_{i-1}, and we walk up the domtree from b_i to get all blocks that |
71 | | // dominate b_i; each such block must appear in S. (Otherwise, we have a |
72 | | // counterexample for which dominance says it should appear in the path |
73 | | // prefix, but it does not.) |
74 | 0 | let mut visited = HashSet::new(); |
75 | 0 | visited.insert(Block::new(0)); |
76 | 0 | for block in &path.blocks { |
77 | 0 | let mut parent = idom[block.index()]; |
78 | 0 | let mut domset = HashSet::new(); |
79 | 0 | domset.insert(*block); |
80 | 0 | while parent.is_valid() { |
81 | 0 | assert!(visited.contains(&parent)); |
82 | 0 | domset.insert(parent); |
83 | 0 | let next = idom[parent.index()]; |
84 | 0 | parent = next; |
85 | | } |
86 | | |
87 | | // Check that `dominates()` returns true for every block in domset, |
88 | | // and false for every other block. |
89 | 0 | for domblock in 0..idom.len() { |
90 | 0 | let domblock = Block::new(domblock); |
91 | 0 | assert_eq!( |
92 | 0 | domset.contains(&domblock), |
93 | 0 | domtree::dominates(idom, domblock, *block) |
94 | | ); |
95 | | } |
96 | 0 | visited.insert(*block); |
97 | | } |
98 | 0 | } |
99 | | |
100 | | /// A control-flow graph ([`CFG`]) and a [`Path`] through it. |
101 | | #[derive(Clone, Debug)] |
102 | | pub struct TestCase { |
103 | | cfg: CFG, |
104 | | path: Path, |
105 | | } |
106 | | |
107 | | impl Arbitrary<'_> for TestCase { |
108 | 0 | fn arbitrary(u: &mut Unstructured) -> Result<TestCase> { |
109 | 0 | let cfg = CFG::arbitrary(u)?; |
110 | 0 | let path = Path::choose_from_cfg(&cfg, u)?; |
111 | 0 | Ok(TestCase { cfg, path }) |
112 | 0 | } |
113 | | } |
114 | | |
115 | 0 | pub fn check(t: TestCase) { |
116 | 0 | let mut postorder = vec![]; |
117 | 0 | postorder::calculate( |
118 | 0 | t.cfg.num_blocks, |
119 | 0 | Block::new(0), |
120 | 0 | &mut vec![], |
121 | 0 | &mut postorder, |
122 | 0 | |block| &t.cfg.succs[block.index()], |
123 | | ) |
124 | 0 | .unwrap(); |
125 | | |
126 | 0 | let mut idom = vec![]; |
127 | 0 | domtree::calculate( |
128 | 0 | t.cfg.num_blocks, |
129 | 0 | |block| &t.cfg.preds[block.index()], |
130 | 0 | &postorder[..], |
131 | 0 | &mut vec![], |
132 | 0 | &mut idom, |
133 | 0 | Block::new(0), |
134 | | ); |
135 | 0 | check_idom_violations(&idom[..], &t.path); |
136 | 0 | } |
137 | | |
138 | | #[test] |
139 | | fn smoke() { |
140 | | arbtest::arbtest(|u| { |
141 | | let test_case = TestCase::arbitrary(u)?; |
142 | | check(test_case); |
143 | | Ok(()) |
144 | | }) |
145 | | .budget_ms(1_000); |
146 | | } |