Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Build
run: cargo build
- name: Run tests
run: cargo test --all --verbose
run: cargo test --all --all-features --verbose

# Make sure the code typechecks with non-default features enabled.
features:
Expand Down Expand Up @@ -72,11 +72,7 @@ jobs:
run: rustup toolchain install nightly
- name: Install cargo-fuzz
run: cargo +nightly install cargo-fuzz
- name: Build ssagen fuzzing target
run: cargo +nightly fuzz build ssagen
- name: Build moves fuzzing target
run: cargo +nightly fuzz build moves
- name: Build ion fuzzing target
run: cargo +nightly fuzz build ion
- name: Build and smoke-test ion_checker fuzzing target
run: cargo +nightly fuzz run ion_checker ./fuzz/smoketest/ion_checker.bin
- name: Build all fuzz targets
run: cargo +nightly fuzz build
# Note: all fuzzers are run with `arbtest` during `cargo test` with the
# `fuzzing` feature enabled.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ serde = { version = "1.0.136", features = [
], default-features = false, optional = true }

# The below are only needed for fuzzing.
libfuzzer-sys = { version = "0.4.2", optional = true }
arbitrary = { version = "1.4.2", optional = true }
arbtest = { version = "0.3.2", optional = true }
bumpalo = { version = "3.16.0", features = ["allocator-api2"] }
allocator-api2 = { version = "0.2.18", default-features = false, features = ["alloc"] }

# When testing regalloc2 by itself, enable debug assertions and overflow checks
# When testing regalloc2 by itself, enable debug assertions and overflow checks.
[profile.release]
debug = true
debug-assertions = true
Expand All @@ -49,7 +50,7 @@ checker = []
trace-log = []

# Exposes the internal API for fuzzing.
fuzzing = ["libfuzzer-sys", "checker", "trace-log"]
fuzzing = ["arbitrary", "arbtest", "checker", "trace-log"]

# Enables serde for exposed types.
enable-serde = ["serde"]
16 changes: 2 additions & 14 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ test = false
doc = false

[[bin]]
name = "ssagen"
path = "fuzz_targets/ssagen.rs"
name = "fastalloc"
path = "fuzz_targets/fastalloc.rs"
test = false
doc = false

Expand All @@ -43,18 +43,6 @@ path = "fuzz_targets/moves.rs"
test = false
doc = false

[[bin]]
name = "ion_checker"
path = "fuzz_targets/ion_checker.rs"
test = false
doc = false

[[bin]]
name = "fastalloc_checker"
path = "fuzz_targets/fastalloc_checker.rs"
test = false
doc = false

# Enable debug assertions and overflow checks when fuzzing
[profile.release]
debug = true
Expand Down
134 changes: 5 additions & 129 deletions fuzz/fuzz_targets/domtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,134 +4,10 @@
*/

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

#[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 mut postorder = vec![];
postorder::calculate(
testcase.cfg.num_blocks,
Block::new(0),
&mut vec![],
&mut postorder,
|block| &testcase.cfg.succs[block.index()],
);
let mut idom = vec![];
domtree::calculate(
testcase.cfg.num_blocks,
|block| &testcase.cfg.preds[block.index()],
&postorder[..],
&mut vec![],
&mut idom,
Block::new(0),
);
check_idom_violations(&idom[..], &testcase.path);
fuzz_target!(|test_case: domtree::TestCase| {
let _ = env_logger::try_init();
domtree::check(test_case);
});
13 changes: 13 additions & 0 deletions fuzz/fuzz_targets/fastalloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Released under the terms of the Apache 2.0 license with LLVM
* exception. See `LICENSE` for details.
*/

#![no_main]
use libfuzzer_sys::fuzz_target;
use regalloc2::fuzzing::fastalloc;

fuzz_target!(|test_case: fastalloc::TestCase| {
let _ = env_logger::try_init();
fastalloc::check(test_case);
});
47 changes: 0 additions & 47 deletions fuzz/fuzz_targets/fastalloc_checker.rs

This file was deleted.

19 changes: 4 additions & 15 deletions fuzz/fuzz_targets/ion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,10 @@
*/

#![no_main]
use regalloc2::fuzzing::func::Func;
use regalloc2::fuzzing::fuzz_target;
use libfuzzer_sys::fuzz_target;
use regalloc2::fuzzing::ion;

fuzz_target!(|func: Func| {
fuzz_target!(|test_case: ion::TestCase| {
let _ = env_logger::try_init();
log::trace!("func:\n{:?}", func);
let env = regalloc2::fuzzing::func::machine_env();

thread_local! {
// We test that ctx is cleared properly between runs.
static CTX: std::cell::RefCell<regalloc2::fuzzing::ion::Ctx> = std::cell::RefCell::default();
}

CTX.with(|ctx| {
let _out = regalloc2::fuzzing::ion::run(&func, &env, &mut *ctx.borrow_mut(), false, false)
.expect("regalloc did not succeed");
});
ion::check(test_case);
});
55 changes: 0 additions & 55 deletions fuzz/fuzz_targets/ion_checker.rs

This file was deleted.

Loading