cargo workspace layout, multithreaded fuzzer

This commit is contained in:
Richard Patel 2021-08-14 00:33:10 +02:00
parent 3fb8a76a2f
commit 7d2b8c16ca
6 changed files with 183 additions and 56 deletions

33
Cargo.lock generated
View File

@ -8,6 +8,21 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
[[package]]
name = "num-traits"
version = "0.2.14"
@ -17,9 +32,27 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "ppc750cl"
version = "0.1.0"
dependencies = [
"num-traits",
]
[[package]]
name = "ppc750cl-fuzz"
version = "0.1.0"
dependencies = [
"num_cpus",
"ppc750cl",
]

View File

@ -1,8 +1,5 @@
[package]
name = "ppc750cl"
version = "0.1.0"
edition = "2018"
authors = ["Richard Patel <me@terorie.dev>"]
[dependencies]
num-traits = "0.2.14"
[workspace]
members = [
"lib",
"fuzz",
]

9
fuzz/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "ppc750cl-fuzz"
version = "0.1.0"
edition = "2018"
authors = ["Richard Patel <me@terorie.dev>"]
[dependencies]
num_cpus = "1.13"
ppc750cl = { path = "../lib" }

100
fuzz/src/main.rs Normal file
View File

@ -0,0 +1,100 @@
#![feature(bench_black_box)]
use std::hint::black_box;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use num_cpus;
use ppc750cl::Ins;
use std::ops::Range;
fn main() {
let start = Instant::now();
let fuzzer = MultiFuzzer::new(num_cpus::get() as u32);
fuzzer.run();
println!("Finished in {:.2}s", start.elapsed().as_secs_f32());
}
#[derive(Clone)]
struct MultiFuzzer {
threads: Vec<Fuzzer>,
}
impl MultiFuzzer {
fn new(num_threads: u32) -> Self {
assert_ne!(num_threads, 0);
let mut threads = Vec::<Fuzzer>::with_capacity(num_threads as usize);
let part_size = 0xFFFF_FFFF / num_threads;
let mut offset = 0u32;
loop {
let next_offset = match offset.checked_add(part_size) {
None => break,
Some(v) => v,
};
threads.push(Fuzzer::new(offset..next_offset));
offset = next_offset;
}
threads.last_mut().unwrap().range.end = 0xFFFF_FFFF;
Self { threads }
}
fn dispatch_progress_monitor(&self) {
let this = self.clone();
std::thread::spawn(move || {
let mut last = 0u32;
loop {
std::thread::sleep(Duration::from_secs(1));
let mut now = 0u32;
for thread in &this.threads {
now += thread.counter.load(Ordering::Relaxed) - thread.range.start;
}
let per_second = now - last;
last = now;
let progress = 100f32 * ((now as f32) / (0x1_0000_0000u64 as f32));
println!("{}/s\t{:05.2}%\tn=0x{:08x}", per_second, progress, now);
}
});
}
fn run(&self) {
self.dispatch_progress_monitor();
let handles: Vec<_> = self.threads.iter().map(|t| t.dispatch()).collect();
for handle in handles {
// TODO This doesn't panic immediately, since we'll block on thread zero
// for most of the time.
handle.join().expect("thread panicked");
}
black_box(Ins::disasm(0xFFFF_FFFF).to_string());
}
}
#[derive(Clone)]
struct Fuzzer {
range: Range<u32>,
counter: Arc<AtomicU32>,
}
impl Fuzzer {
fn new(range: Range<u32>) -> Self {
Self {
range,
counter: Arc::new(AtomicU32::new(0)),
}
}
fn dispatch(&self) -> std::thread::JoinHandle<()> {
let counter = Arc::clone(&self.counter);
let range = self.range.clone();
std::thread::spawn(move || {
for x in range.clone() {
black_box(Ins::disasm(x).to_string());
if x % (1 << 19) == 0 {
counter.store(x, Ordering::Relaxed);
}
}
counter.store(range.end, Ordering::Relaxed);
})
}
}

8
lib/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "ppc750cl"
version = "0.1.0"
edition = "2018"
authors = ["Richard Patel <me@terorie.dev>"]
[dependencies]
num-traits = "0.2.14"

View File

@ -1,15 +1,9 @@
#![feature(bench_black_box)]
use num_traits::cast::cast;
use num_traits::PrimInt;
use std::hint::black_box;
use std::ops::Range;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use num_traits::AsPrimitive;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum Opcode {
pub enum Opcode {
Illegal = -1,
// 750CL extensions
Twi,
@ -244,9 +238,9 @@ impl Default for Opcode {
}
#[derive(Default, Clone)]
struct Ins {
code: u32, // 0..32
op: Opcode,
pub struct Ins {
pub code: u32, // 0..32
pub op: Opcode,
// TODO move these struct members out to functions
bd: u16, // 16..30
p2: u8, // 6..11: S, D, TO, crbD, BO
@ -264,9 +258,11 @@ fn bit(x: u32, idx: usize) -> bool {
#[inline(always)]
fn bits<F>(x: u32, range: Range<usize>) -> F
where
F: PrimInt,
F: 'static + std::marker::Copy,
u32: AsPrimitive<F>,
{
cast((x >> (32 - range.end)) & ((1 << range.len()) - 1)).expect("extracted bits do not fit")
let masked: u32 = (x >> (32 - range.end)) & ((1 << range.len()) - 1);
masked.as_()
}
#[inline(always)]
@ -294,6 +290,12 @@ macro_rules! ins_bit {
macro_rules! ins_field {
($func:ident, $return_type:tt, $range:expr) => {
fn $func(&self) -> $return_type {
debug_assert!(
($range).len() / 8 <= (std::mem::size_of::<$return_type>()),
"{:?} does not fit in {}",
$range,
stringify!($return_type)
);
bits(self.code, $range)
}
};
@ -350,7 +352,7 @@ impl Ins {
ins_field!(ps_l, u8, 17..20);
ins_field!(ps_d, u16, 20..32);
fn disasm(x: u32) -> Self {
pub fn disasm(x: u32) -> Self {
let family = bits(x, 0..6);
match family {
0b000011 => {
@ -1328,7 +1330,7 @@ impl Ins {
Opcode::Sthux => "sthux",
Opcode::Sthx => "sthx",
Opcode::Sthbrx => "sthbrx",
Opcode::Stswi => "stswx",
Opcode::Stswx => "stswx",
Opcode::Stwbrx => "stwbrx",
Opcode::Stwcx_ => "stwcx.",
Opcode::Stwx => "stwx",
@ -1631,6 +1633,8 @@ impl Ins {
Opcode::PsNmadd => "ps_nmadd",
Opcode::PsNmsub => "ps_nmsub",
Opcode::PsSel => "ps_sel",
Opcode::PsSum0 => "ps_sum0",
Opcode::PsSum1 => "ps_sum1",
_ => disasm_unreachable!(self.code),
};
format!(
@ -1649,7 +1653,7 @@ impl Ins {
let name = match self.op {
Opcode::Fmul => "fmul",
Opcode::Fmuls => "fmuls",
Opcode::PsMadd => "ps_madd",
Opcode::PsMul => "ps_mul",
Opcode::PsMuls0 => "ps_muls0",
Opcode::PsMuls1 => "ps_muls1",
_ => disasm_unreachable!(self.code),
@ -2018,10 +2022,10 @@ impl ToString for Ins {
| Opcode::Icbi => self.to_string_form_reg23(),
Opcode::Eciwx
| Opcode::Ecowx
| Opcode::Lbzux
| Opcode::Lbzx
| Opcode::Lhaux
| Opcode::Lhax
| Opcode::Lbzux
| Opcode::Lbzx
| Opcode::Lhbrx
| Opcode::Lhzux
| Opcode::Lhzx
@ -2228,31 +2232,6 @@ impl ToString for Ins {
}
}
fn main() {
// We don't have much to do. For now, just run a dumb microbenchmark / fuzzer.
let start = Instant::now();
let counter = Arc::new(AtomicU32::new(0));
let counter_inner = Arc::clone(&counter);
std::thread::spawn(move || {
let mut last = 0u32;
loop {
std::thread::sleep(Duration::from_secs(1));
let now = counter_inner.load(Ordering::Relaxed);
let per_second = now - last;
last = now;
let progress = 100f32 * ((now as f32) / (0x1_0000_0000u64 as f32));
println!("{}/s\t{:05.2}%\tn=0x{:08x}", per_second, progress, now);
}
});
for x in 0x4800_0000u32..0xFFFF_FFFFu32 {
black_box(Ins::disasm(x).to_string());
if x % (1 << 19) == 0 {
counter.store(x, Ordering::Relaxed);
}
}
println!("Finished in {:.2}s", start.elapsed().as_secs_f32());
}
#[cfg(test)]
mod tests {
use super::*;
@ -2303,15 +2282,16 @@ mod tests {
Ins::disasm(0b000100_00001_00000_00000_0000000111_1).op,
Opcode::Illegal
);
assert_eq!(
Ins::disasm(0b1111100000000000000001001111000).op,
Opcode::Xor,
);
assert_eq!(Ins::disasm(0x7c000278).op, Opcode::Xor);
// TODO more tests
}
#[test]
fn test_to_string() {
assert_eq!(Ins::disasm(0x4c000000).to_string(), "mcrf crf0, crf0");
assert_eq!(Ins::disasm(0x7c000278).to_string(), "xor r0, r0, r0");
assert_eq!(Ins::disasm(0x10000014).to_string(), "ps_sum0 fr0, fr0, fr0, fr0");
assert_eq!(Ins::disasm(0x10000032).to_string(), "ps_mul fr0, fr0, fr0");
assert_eq!(Ins::disasm(0x7c00052a).to_string(), "");
}
}