cargo workspace layout, multithreaded fuzzer
This commit is contained in:
parent
3fb8a76a2f
commit
7d2b8c16ca
|
@ -8,6 +8,21 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
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]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -17,9 +32,27 @@ dependencies = [
|
||||||
"autocfg",
|
"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]]
|
[[package]]
|
||||||
name = "ppc750cl"
|
name = "ppc750cl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppc750cl-fuzz"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"num_cpus",
|
||||||
|
"ppc750cl",
|
||||||
|
]
|
||||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -1,8 +1,5 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "ppc750cl"
|
members = [
|
||||||
version = "0.1.0"
|
"lib",
|
||||||
edition = "2018"
|
"fuzz",
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
]
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
num-traits = "0.2.14"
|
|
||||||
|
|
|
@ -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" }
|
|
@ -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);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
|
@ -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::ops::Range;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
use std::sync::Arc;
|
use num_traits::AsPrimitive;
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
enum Opcode {
|
pub enum Opcode {
|
||||||
Illegal = -1,
|
Illegal = -1,
|
||||||
// 750CL extensions
|
// 750CL extensions
|
||||||
Twi,
|
Twi,
|
||||||
|
@ -244,9 +238,9 @@ impl Default for Opcode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
struct Ins {
|
pub struct Ins {
|
||||||
code: u32, // 0..32
|
pub code: u32, // 0..32
|
||||||
op: Opcode,
|
pub op: Opcode,
|
||||||
// TODO move these struct members out to functions
|
// TODO move these struct members out to functions
|
||||||
bd: u16, // 16..30
|
bd: u16, // 16..30
|
||||||
p2: u8, // 6..11: S, D, TO, crbD, BO
|
p2: u8, // 6..11: S, D, TO, crbD, BO
|
||||||
|
@ -264,9 +258,11 @@ fn bit(x: u32, idx: usize) -> bool {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn bits<F>(x: u32, range: Range<usize>) -> F
|
fn bits<F>(x: u32, range: Range<usize>) -> F
|
||||||
where
|
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)]
|
#[inline(always)]
|
||||||
|
@ -294,6 +290,12 @@ macro_rules! ins_bit {
|
||||||
macro_rules! ins_field {
|
macro_rules! ins_field {
|
||||||
($func:ident, $return_type:tt, $range:expr) => {
|
($func:ident, $return_type:tt, $range:expr) => {
|
||||||
fn $func(&self) -> $return_type {
|
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)
|
bits(self.code, $range)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -350,7 +352,7 @@ impl Ins {
|
||||||
ins_field!(ps_l, u8, 17..20);
|
ins_field!(ps_l, u8, 17..20);
|
||||||
ins_field!(ps_d, u16, 20..32);
|
ins_field!(ps_d, u16, 20..32);
|
||||||
|
|
||||||
fn disasm(x: u32) -> Self {
|
pub fn disasm(x: u32) -> Self {
|
||||||
let family = bits(x, 0..6);
|
let family = bits(x, 0..6);
|
||||||
match family {
|
match family {
|
||||||
0b000011 => {
|
0b000011 => {
|
||||||
|
@ -1328,7 +1330,7 @@ impl Ins {
|
||||||
Opcode::Sthux => "sthux",
|
Opcode::Sthux => "sthux",
|
||||||
Opcode::Sthx => "sthx",
|
Opcode::Sthx => "sthx",
|
||||||
Opcode::Sthbrx => "sthbrx",
|
Opcode::Sthbrx => "sthbrx",
|
||||||
Opcode::Stswi => "stswx",
|
Opcode::Stswx => "stswx",
|
||||||
Opcode::Stwbrx => "stwbrx",
|
Opcode::Stwbrx => "stwbrx",
|
||||||
Opcode::Stwcx_ => "stwcx.",
|
Opcode::Stwcx_ => "stwcx.",
|
||||||
Opcode::Stwx => "stwx",
|
Opcode::Stwx => "stwx",
|
||||||
|
@ -1631,6 +1633,8 @@ impl Ins {
|
||||||
Opcode::PsNmadd => "ps_nmadd",
|
Opcode::PsNmadd => "ps_nmadd",
|
||||||
Opcode::PsNmsub => "ps_nmsub",
|
Opcode::PsNmsub => "ps_nmsub",
|
||||||
Opcode::PsSel => "ps_sel",
|
Opcode::PsSel => "ps_sel",
|
||||||
|
Opcode::PsSum0 => "ps_sum0",
|
||||||
|
Opcode::PsSum1 => "ps_sum1",
|
||||||
_ => disasm_unreachable!(self.code),
|
_ => disasm_unreachable!(self.code),
|
||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
|
@ -1649,7 +1653,7 @@ impl Ins {
|
||||||
let name = match self.op {
|
let name = match self.op {
|
||||||
Opcode::Fmul => "fmul",
|
Opcode::Fmul => "fmul",
|
||||||
Opcode::Fmuls => "fmuls",
|
Opcode::Fmuls => "fmuls",
|
||||||
Opcode::PsMadd => "ps_madd",
|
Opcode::PsMul => "ps_mul",
|
||||||
Opcode::PsMuls0 => "ps_muls0",
|
Opcode::PsMuls0 => "ps_muls0",
|
||||||
Opcode::PsMuls1 => "ps_muls1",
|
Opcode::PsMuls1 => "ps_muls1",
|
||||||
_ => disasm_unreachable!(self.code),
|
_ => disasm_unreachable!(self.code),
|
||||||
|
@ -2018,10 +2022,10 @@ impl ToString for Ins {
|
||||||
| Opcode::Icbi => self.to_string_form_reg23(),
|
| Opcode::Icbi => self.to_string_form_reg23(),
|
||||||
Opcode::Eciwx
|
Opcode::Eciwx
|
||||||
| Opcode::Ecowx
|
| Opcode::Ecowx
|
||||||
| Opcode::Lbzux
|
|
||||||
| Opcode::Lbzx
|
|
||||||
| Opcode::Lhaux
|
| Opcode::Lhaux
|
||||||
| Opcode::Lhax
|
| Opcode::Lhax
|
||||||
|
| Opcode::Lbzux
|
||||||
|
| Opcode::Lbzx
|
||||||
| Opcode::Lhbrx
|
| Opcode::Lhbrx
|
||||||
| Opcode::Lhzux
|
| Opcode::Lhzux
|
||||||
| Opcode::Lhzx
|
| 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -2303,15 +2282,16 @@ mod tests {
|
||||||
Ins::disasm(0b000100_00001_00000_00000_0000000111_1).op,
|
Ins::disasm(0b000100_00001_00000_00000_0000000111_1).op,
|
||||||
Opcode::Illegal
|
Opcode::Illegal
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(Ins::disasm(0x7c000278).op, Opcode::Xor);
|
||||||
Ins::disasm(0b1111100000000000000001001111000).op,
|
|
||||||
Opcode::Xor,
|
|
||||||
);
|
|
||||||
// TODO more tests
|
// TODO more tests
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_string() {
|
fn test_to_string() {
|
||||||
assert_eq!(Ins::disasm(0x4c000000).to_string(), "mcrf crf0, crf0");
|
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(), "");
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue