* Remove unused regalloc2-test crate This code doesn't build, and Chris says it's "a really old harness that existed prior to building the fuzzing and was used mainly to profile and get stats before integration with Cranelift". * Re-export libfuzzer/arbitrary from fuzzing module This avoids needing to keep dependencies on `arbitrary` in sync across the three different Cargo.toml files in this project. However, before version 0.4.2, libfuzzer-sys only supported using its macros if it was available at the top-level `libfuzzer_sys` path, which breaks when re-exporting it. So I'm upgrading to that version (or the newest patch release of it). Upgrading libfuzzer-sys in turn brings in the 1.0 release of the arbitrary crate, with a minor API change along the way.
130 lines
3.8 KiB
Rust
130 lines
3.8 KiB
Rust
/*
|
|
* 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);
|
|
});
|