use std::collections::BTreeMap;
use std::fmt::Display;
use disc_v::{Decoder, Extension, Extensions};
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Rng(seed.wrapping_mul(0x9e37_79b9_7f4a_7c15) | 1)
}
pub fn next_u32(&mut self) -> u32 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
(self.0 >> 32) as u32
}
}
#[derive(Default)]
pub struct Failures(BTreeMap<String, (usize, String)>);
impl Failures {
pub fn add(&mut self, kind: impl Display, example: impl Display) {
self.0
.entry(kind.to_string())
.or_insert((0, example.to_string()))
.0 += 1;
}
pub fn assert_none(self) {
if self.0.is_empty() {
return;
}
let total: usize = self.0.values().map(|(n, _)| n).sum();
let mut msg = format!("{total} failures of {} kinds:\n", self.0.len());
for (kind, (count, example)) in &self.0 {
msg += &format!("{count:>7} x {kind}\n e.g. {example}\n");
}
panic!("{msg}");
}
}
pub fn riscv_opcodes_samples(rng: &mut Rng, per_entry: usize) -> Vec<u32> {
entries()
.iter()
.flat_map(|e| e.samples(rng, per_entry))
.collect()
}
pub struct Entry {
pub rv32: bool,
pub rv64: bool,
pub name: &'static str,
pub base: Option<&'static str>,
pub bits: u32,
pub mask: u32,
pub nonzero: Vec<u32>,
pub not_two: u32,
pub extensions: Vec<Vec<&'static str>>,
}
impl Entry {
pub fn applies_to(&self, isa: disc_v::Isa) -> bool {
match isa {
disc_v::Isa::Rv32 => self.rv32,
disc_v::Isa::Rv64 => self.rv64,
disc_v::Isa::Rv128 => false,
}
}
pub fn matches(&self, inst: u32) -> bool {
inst & self.mask == self.bits
&& self.nonzero.iter().all(|&m| inst & m != 0)
&& (self.not_two == 0 || (inst & self.not_two) >> self.not_two.trailing_zeros() != 2)
}
pub fn is_named(&self, name: &str) -> bool {
self.name == name || self.base == Some(name)
}
pub fn samples(&self, rng: &mut Rng, n: usize) -> Vec<u32> {
let width = if self.bits & 3 == 3 { u32::MAX } else { 0xffff };
(0..n * 8)
.map(|_| (rng.next_u32() & !self.mask & width) | self.bits)
.filter(|&x| self.matches(x))
.take(n)
.collect()
}
}
pub fn entries() -> Vec<Entry> {
let hex = |s: &str| u32::from_str_radix(s, 16).unwrap();
include_str!("../data/riscv-opcodes.txt")
.lines()
.filter(|l| !l.starts_with('#'))
.map(|l| {
let f: Vec<&'static str> = l.split(' ').collect();
Entry {
rv32: f[0].contains("32"),
rv64: f[0].contains("64"),
name: f[1],
base: Some(f[2]).filter(|&b| b != "-"),
bits: hex(f[3]),
mask: hex(f[4]),
nonzero: match f[5] {
"-" => vec![],
s => s.split(',').map(hex).collect(),
},
not_two: if f[6] == "-" { 0 } else { hex(f[6]) },
extensions: match f[7] {
"-" => vec![],
s => s.split('+').map(|g| g.split('|').collect()).collect(),
},
}
})
.collect()
}
pub fn embedded() -> Extensions {
use Extension::*;
Extensions::from([
M, A, F, Zca, Zcb, Zcmp, Zcmt, Zicsr, Zifencei, Zba, Zbb, Zbs,
])
}
pub fn rve() -> Extensions {
use Extension::*;
Extensions::from([
E, M, A, Zca, Zcb, Zcmp, Zcmt, Zicsr, Zifencei, Zilsd, Zclsd, Zdinx, Zhinx, Zba, Zbb, Zbs,
])
}
pub fn inx() -> Extensions {
use Extension::*;
Extensions::DEFAULT
.without(F)
.without(V)
.with(Zdinx)
.with(Zhinx)
}
pub fn thead() -> Extensions {
Extensions::DEFAULT | Extensions::XTHEAD
}
pub fn corev() -> Extensions {
Extensions::DEFAULT | Extensions::XCV
}
pub fn with_resolving_conflicts(mut dec: Decoder, ext: Extension) -> Decoder {
loop {
match dec.with(ext) {
Ok(dec) => return dec,
Err(conflict) => dec = dec.without(conflict.conflicts_with()),
}
}
}