Initial public commit of regalloc2.
This commit is contained in:
4
fuzz/.gitignore
vendored
Normal file
4
fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
target
|
||||
corpus
|
||||
artifacts
|
||||
54
fuzz/Cargo.toml
Normal file
54
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
[package]
|
||||
name = "regalloc2-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["Chris Fallin <chris@cfallin.org>"]
|
||||
license = "MPL-2.0 AND Apache-2.0 WITH LLVM-exception"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.3"
|
||||
arbitrary = { version = "^0.4.6", features = ["derive"] }
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
env_logger = "0.8.3"
|
||||
|
||||
[dependencies.regalloc2]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "domtree"
|
||||
path = "fuzz_targets/domtree.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "ssagen"
|
||||
path = "fuzz_targets/ssagen.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "ion"
|
||||
path = "fuzz_targets/ion.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "moves"
|
||||
path = "fuzz_targets/moves.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "ion_checker"
|
||||
path = "fuzz_targets/ion_checker.rs"
|
||||
test = false
|
||||
doc = false
|
||||
128
fuzz/fuzz_targets/domtree.rs
Normal file
128
fuzz/fuzz_targets/domtree.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use regalloc2::{domtree, postorder, Block};
|
||||
|
||||
#[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);
|
||||
loop {
|
||||
assert!(parent.is_valid());
|
||||
assert!(visited.contains(&parent));
|
||||
domset.insert(parent);
|
||||
let next = idom[parent.index()];
|
||||
if next == parent {
|
||||
break;
|
||||
}
|
||||
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);
|
||||
});
|
||||
11
fuzz/fuzz_targets/ion.rs
Normal file
11
fuzz/fuzz_targets/ion.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use regalloc2::fuzzing::func::Func;
|
||||
|
||||
fuzz_target!(|func: Func| {
|
||||
let _ = env_logger::try_init();
|
||||
log::debug!("func:\n{:?}", func);
|
||||
let env = regalloc2::fuzzing::func::machine_env();
|
||||
let _out = regalloc2::ion::run(&func, &env).expect("regalloc did not succeed");
|
||||
});
|
||||
39
fuzz/fuzz_targets/ion_checker.rs
Normal file
39
fuzz/fuzz_targets/ion_checker.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured, Result};
|
||||
|
||||
use regalloc2::fuzzing::func::{Func, Options};
|
||||
use regalloc2::checker::Checker;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TestCase {
|
||||
func: Func,
|
||||
}
|
||||
|
||||
impl Arbitrary for TestCase {
|
||||
fn arbitrary(u: &mut Unstructured) -> Result<TestCase> {
|
||||
Ok(TestCase {
|
||||
func: Func::arbitrary_with_options(u, &Options {
|
||||
reused_inputs: true,
|
||||
fixed_regs: true,
|
||||
clobbers: true,
|
||||
control_flow: true,
|
||||
reducible: false,
|
||||
block_params: true,
|
||||
always_local_uses: false,
|
||||
})?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fuzz_target!(|testcase: TestCase| {
|
||||
let func = testcase.func;
|
||||
let _ = env_logger::try_init();
|
||||
log::debug!("func:\n{:?}", func);
|
||||
let env = regalloc2::fuzzing::func::machine_env();
|
||||
let out = regalloc2::ion::run(&func, &env).expect("regalloc did not succeed");
|
||||
|
||||
let mut checker = Checker::new(&func);
|
||||
checker.prepare(&out);
|
||||
checker.run().expect("checker failed");
|
||||
});
|
||||
76
fuzz/fuzz_targets/moves.rs
Normal file
76
fuzz/fuzz_targets/moves.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use regalloc2::moves::ParallelMoves;
|
||||
use regalloc2::{Allocation, PReg, RegClass};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TestCase {
|
||||
moves: Vec<(Allocation, Allocation)>,
|
||||
}
|
||||
|
||||
impl Arbitrary for TestCase {
|
||||
fn arbitrary(u: &mut Unstructured) -> Result<Self> {
|
||||
let mut ret = TestCase { moves: vec![] };
|
||||
let mut written = HashSet::new();
|
||||
while bool::arbitrary(u)? {
|
||||
let reg1 = u.int_in_range(0..=30)?;
|
||||
let reg2 = u.int_in_range(0..=30)?;
|
||||
if written.contains(®2) {
|
||||
break;
|
||||
}
|
||||
written.insert(reg2);
|
||||
ret.moves.push((
|
||||
Allocation::reg(PReg::new(reg1, RegClass::Int)),
|
||||
Allocation::reg(PReg::new(reg2, RegClass::Int)),
|
||||
));
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
fuzz_target!(|testcase: TestCase| {
|
||||
let _ = env_logger::try_init();
|
||||
let scratch = Allocation::reg(PReg::new(31, RegClass::Int));
|
||||
let mut par = ParallelMoves::new(scratch);
|
||||
for &(src, dst) in &testcase.moves {
|
||||
par.add(src, dst);
|
||||
}
|
||||
let moves = par.resolve();
|
||||
|
||||
// Compute the final source reg for each dest reg in the original
|
||||
// parallel-move set.
|
||||
let mut final_src_per_dest: Vec<Option<usize>> = vec![None; 32];
|
||||
for &(src, dst) in &testcase.moves {
|
||||
if let (Some(preg_src), Some(preg_dst)) = (src.as_reg(), dst.as_reg()) {
|
||||
final_src_per_dest[preg_dst.hw_enc()] = Some(preg_src.hw_enc());
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate the sequence of moves.
|
||||
let mut regfile: Vec<Option<usize>> = vec![None; 32];
|
||||
for i in 0..32 {
|
||||
regfile[i] = Some(i);
|
||||
}
|
||||
for (src, dst) in moves {
|
||||
if let (Some(preg_src), Some(preg_dst)) = (src.as_reg(), dst.as_reg()) {
|
||||
let data = regfile[preg_src.hw_enc()];
|
||||
regfile[preg_dst.hw_enc()] = data;
|
||||
} else {
|
||||
panic!("Bad allocation in move list");
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that the expected register-moves occurred.
|
||||
// N.B.: range up to 31 (not 32) to skip scratch register.
|
||||
for i in 0..31 {
|
||||
if let Some(orig_src) = final_src_per_dest[i] {
|
||||
assert_eq!(regfile[i], Some(orig_src));
|
||||
} else {
|
||||
// Should be untouched.
|
||||
assert_eq!(regfile[i], Some(i));
|
||||
}
|
||||
}
|
||||
});
|
||||
35
fuzz/fuzz_targets/ssagen.rs
Normal file
35
fuzz/fuzz_targets/ssagen.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use regalloc2::cfg::CFGInfo;
|
||||
use regalloc2::fuzzing::func::{Func, Options};
|
||||
use regalloc2::ssa::validate_ssa;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestCase {
|
||||
f: Func,
|
||||
}
|
||||
|
||||
impl Arbitrary for TestCase {
|
||||
fn arbitrary(u: &mut Unstructured) -> Result<Self> {
|
||||
Ok(TestCase {
|
||||
f: Func::arbitrary_with_options(
|
||||
u,
|
||||
&Options {
|
||||
reused_inputs: true,
|
||||
fixed_regs: true,
|
||||
clobbers: true,
|
||||
control_flow: true,
|
||||
reducible: false,
|
||||
always_local_uses: false,
|
||||
},
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fuzz_target!(|t: TestCase| {
|
||||
let cfginfo = CFGInfo::new(&t.f);
|
||||
validate_ssa(&t.f, &cfginfo).expect("invalid SSA");
|
||||
});
|
||||
Reference in New Issue
Block a user