File: domtree.rs

package info (click to toggle)
rust-regalloc2 0.10.2-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 680 kB
  • sloc: makefile: 22; sh: 1
file content (129 lines) | stat: -rw-r--r-- 3,862 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * Released under the terms of the Apache 2.0 license with LLVM
 * exception. See `LICENSE` for details.
 */

#![no_main]
use regalloc2::fuzzing::arbitrary::{Arbitrary, Result, Unstructured};
use regalloc2::fuzzing::{domtree, fuzz_target, postorder};
use regalloc2::Block;
use std::collections::HashSet;

#[derive(Clone, Debug)]
struct CFG {
    num_blocks: usize,
    preds: Vec<Vec<Block>>,
    succs: Vec<Vec<Block>>,
}

impl Arbitrary<'_> for CFG {
    fn arbitrary(u: &mut Unstructured) -> Result<CFG> {
        let num_blocks = u.int_in_range(1..=1000)?;
        let mut succs = vec![];
        for _ in 0..num_blocks {
            let mut block_succs = vec![];
            for _ in 0..u.int_in_range(0..=5)? {
                block_succs.push(Block::new(u.int_in_range(0..=(num_blocks - 1))?));
            }
            succs.push(block_succs);
        }
        let mut preds = vec![];
        for _ in 0..num_blocks {
            preds.push(vec![]);
        }
        for from in 0..num_blocks {
            for succ in &succs[from] {
                preds[succ.index()].push(Block::new(from));
            }
        }
        Ok(CFG {
            num_blocks,
            preds,
            succs,
        })
    }
}

#[derive(Clone, Debug)]
struct Path {
    blocks: Vec<Block>,
}

impl Path {
    fn choose_from_cfg(cfg: &CFG, u: &mut Unstructured) -> Result<Path> {
        let succs = u.int_in_range(0..=(2 * cfg.num_blocks))?;
        let mut block = Block::new(0);
        let mut blocks = vec![];
        blocks.push(block);
        for _ in 0..succs {
            if cfg.succs[block.index()].is_empty() {
                break;
            }
            block = *u.choose(&cfg.succs[block.index()])?;
            blocks.push(block);
        }
        Ok(Path { blocks })
    }
}

fn check_idom_violations(idom: &[Block], path: &Path) {
    // "a dom b" means that any path from the entry block through the CFG that
    // contains a and b will contain a before b.
    //
    // To test this, for any given block b_i, we have the set S of b_0 .. b_{i-1},
    // and we walk up the domtree from b_i to get all blocks that dominate b_i;
    // each such block must appear in S. (Otherwise, we have a counterexample
    // for which dominance says it should appear in the path prefix, but it does
    // not.)
    let mut visited = HashSet::new();
    visited.insert(Block::new(0));
    for block in &path.blocks {
        let mut parent = idom[block.index()];
        let mut domset = HashSet::new();
        domset.insert(*block);
        while parent.is_valid() {
            assert!(visited.contains(&parent));
            domset.insert(parent);
            let next = idom[parent.index()];
            parent = next;
        }

        // Check that `dominates()` returns true for every block in domset,
        // and false for every other block.
        for domblock in 0..idom.len() {
            let domblock = Block::new(domblock);
            assert_eq!(
                domset.contains(&domblock),
                domtree::dominates(idom, domblock, *block)
            );
        }
        visited.insert(*block);
    }
}

#[derive(Clone, Debug)]
struct TestCase {
    cfg: CFG,
    path: Path,
}

impl Arbitrary<'_> for TestCase {
    fn arbitrary(u: &mut Unstructured) -> Result<TestCase> {
        let cfg = CFG::arbitrary(u)?;
        let path = Path::choose_from_cfg(&cfg, u)?;
        Ok(TestCase { cfg, path })
    }
}

fuzz_target!(|testcase: TestCase| {
    let postord = postorder::calculate(testcase.cfg.num_blocks, Block::new(0), |block| {
        &testcase.cfg.succs[block.index()]
    });
    let idom = domtree::calculate(
        testcase.cfg.num_blocks,
        |block| &testcase.cfg.preds[block.index()],
        &postord[..],
        Block::new(0),
    );
    check_idom_violations(&idom[..], &testcase.path);
});