Compare commits

..

No commits in common. "8d01c9f1d70adbc84c3b39a865ab3468af36d6c8" and "e9a689aa1a67146528b4d89d6659da74253a22f6" have entirely different histories.

7 changed files with 69 additions and 357 deletions

52
Cargo.lock generated
View file

@ -29,37 +29,6 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.10" version = "0.3.10"
@ -164,26 +133,6 @@ version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.1" version = "1.0.1"
@ -222,7 +171,6 @@ name = "rvdc"
version = "0.1.1" version = "0.1.1"
dependencies = [ dependencies = [
"object", "object",
"rayon",
"tempfile", "tempfile",
] ]

View file

@ -15,5 +15,4 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(slow_tests)'] }
[dev-dependencies] [dev-dependencies]
object = "0.36.7" object = "0.36.7"
rayon = "1.10.0"
tempfile = "3.19.1" tempfile = "3.19.1"

View file

@ -38,7 +38,7 @@ More extensions may be implemented in the future.
let x = 0x1101_u32; let x = 0x1101_u32;
let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP }; let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP };
let (inst, is_compressed) = rvdc::Inst::decode(x, rvdc::Xlen::Rv32).unwrap(); let (inst, is_compressed) = rvdc::Inst::decode(x).unwrap();
assert_eq!(inst, expected); assert_eq!(inst, expected);
assert_eq!(is_compressed, rvdc::IsCompressed::Yes); assert_eq!(is_compressed, rvdc::IsCompressed::Yes);
assert_eq!(format!("{inst}"), "addi sp, sp, -32") assert_eq!(format!("{inst}"), "addi sp, sp, -32")
@ -49,7 +49,7 @@ assert_eq!(format!("{inst}"), "addi sp, sp, -32")
let x = 0x0000a317; let x = 0x0000a317;
let expected = rvdc::Inst::Auipc { uimm: rvdc::Imm::new_u32(0xa << 12), dest: rvdc::Reg::T1 }; let expected = rvdc::Inst::Auipc { uimm: rvdc::Imm::new_u32(0xa << 12), dest: rvdc::Reg::T1 };
let (inst, is_compressed) = rvdc::Inst::decode(x, rvdc::Xlen::Rv32).unwrap(); let (inst, is_compressed) = rvdc::Inst::decode(x).unwrap();
assert_eq!(inst, expected); assert_eq!(inst, expected);
assert_eq!(is_compressed, rvdc::IsCompressed::No); assert_eq!(is_compressed, rvdc::IsCompressed::No);
assert_eq!(format!("{inst}"), "auipc t1, 10") assert_eq!(format!("{inst}"), "auipc t1, 10")

View file

@ -6,7 +6,7 @@ use core::fmt::{self, Debug, Display};
use core::ops::RangeInclusive; use core::ops::RangeInclusive;
/// The register size of the ISA, RV32 or RV64. /// The register size of the ISA, RV32 or RV64.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Xlen { pub enum Xlen {
/// 32 bit /// 32 bit
Rv32, Rv32,
@ -274,16 +274,10 @@ pub enum Inst {
/// Add /// Add
Add { dest: Reg, src1: Reg, src2: Reg }, Add { dest: Reg, src1: Reg, src2: Reg },
/// Add 32-bit (**RV64 only**)
AddW { dest: Reg, src1: Reg, src2: Reg },
/// Subtract /// Subtract
Sub { dest: Reg, src1: Reg, src2: Reg }, Sub { dest: Reg, src1: Reg, src2: Reg },
/// Subtract 32-bit (**RV64 only**)
SubW { dest: Reg, src1: Reg, src2: Reg },
/// Shift Left Logical /// Shift Left Logical
Sll { dest: Reg, src1: Reg, src2: Reg }, Sll { dest: Reg, src1: Reg, src2: Reg },
/// Shift Left Logical 32-bit (**RV64 only**)
SllW { dest: Reg, src1: Reg, src2: Reg },
/// Set Less Than (signed) /// Set Less Than (signed)
Slt { dest: Reg, src1: Reg, src2: Reg }, Slt { dest: Reg, src1: Reg, src2: Reg },
/// Set Less Than Unsigned /// Set Less Than Unsigned
@ -292,12 +286,8 @@ pub enum Inst {
Xor { dest: Reg, src1: Reg, src2: Reg }, Xor { dest: Reg, src1: Reg, src2: Reg },
/// Shift Right Logical (unsigned) /// Shift Right Logical (unsigned)
Srl { dest: Reg, src1: Reg, src2: Reg }, Srl { dest: Reg, src1: Reg, src2: Reg },
/// Shift Right Logical (unsigned) 32-bit (**RV64 only**)
SrlW { dest: Reg, src1: Reg, src2: Reg },
/// Shift Right Arithmetic (unsigned) /// Shift Right Arithmetic (unsigned)
Sra { dest: Reg, src1: Reg, src2: Reg }, Sra { dest: Reg, src1: Reg, src2: Reg },
/// Shift Right Arithmetic (unsigned) 32-bit (**RV64 only**)
SraW { dest: Reg, src1: Reg, src2: Reg },
/// OR /// OR
Or { dest: Reg, src1: Reg, src2: Reg }, Or { dest: Reg, src1: Reg, src2: Reg },
/// AND /// AND
@ -313,8 +303,6 @@ pub enum Inst {
// ------------- M extension ------------- // ------------- M extension -------------
/// Multiply /// Multiply
Mul { dest: Reg, src1: Reg, src2: Reg }, Mul { dest: Reg, src1: Reg, src2: Reg },
/// Multiply 32-bit (**RV64 only**)
MulW { dest: Reg, src1: Reg, src2: Reg },
/// Mul Upper Half Signed-Signed /// Mul Upper Half Signed-Signed
Mulh { dest: Reg, src1: Reg, src2: Reg }, Mulh { dest: Reg, src1: Reg, src2: Reg },
/// Mul Upper Half Signed-Unsigned /// Mul Upper Half Signed-Unsigned
@ -323,20 +311,12 @@ pub enum Inst {
Mulhu { dest: Reg, src1: Reg, src2: Reg }, Mulhu { dest: Reg, src1: Reg, src2: Reg },
/// Divide (signed) /// Divide (signed)
Div { dest: Reg, src1: Reg, src2: Reg }, Div { dest: Reg, src1: Reg, src2: Reg },
/// Divide (signed) 32-bit (**RV64 only**)
DivW { dest: Reg, src1: Reg, src2: Reg },
/// Divide Unsigned /// Divide Unsigned
Divu { dest: Reg, src1: Reg, src2: Reg }, Divu { dest: Reg, src1: Reg, src2: Reg },
/// Divide Unsigned 32-bit (**RV64 only**)
DivuW { dest: Reg, src1: Reg, src2: Reg },
/// Remainder (signed) /// Remainder (signed)
Rem { dest: Reg, src1: Reg, src2: Reg }, Rem { dest: Reg, src1: Reg, src2: Reg },
/// Remainder (signed) 32-bit (**RV64 only**)
RemW { dest: Reg, src1: Reg, src2: Reg },
/// Remainder Unsigned /// Remainder Unsigned
Remu { dest: Reg, src1: Reg, src2: Reg }, Remu { dest: Reg, src1: Reg, src2: Reg },
/// Remainder Unsigned 32-bit (**RV64 only**)
RemuW { dest: Reg, src1: Reg, src2: Reg },
// ------------- A extension ------------- // ------------- A extension -------------
/// Load-Reserved Word /// Load-Reserved Word
@ -565,7 +545,7 @@ impl Display for Inst {
if imm.as_u32() == 0 { if imm.as_u32() == 0 {
write!(f, "sext.w {dest}, {src1}") write!(f, "sext.w {dest}, {src1}")
} else { } else {
write!(f, "addiw {dest}, {src1}, {}", imm.as_i32()) write!(f, "addi.w {dest}, {src1}, {}", imm.as_i32())
} }
} }
Inst::Slti { Inst::Slti {
@ -626,20 +606,13 @@ impl Display for Inst {
Inst::Add { dest, src1, src2 } => { Inst::Add { dest, src1, src2 } => {
write!(f, "add {dest}, {src1}, {src2}") write!(f, "add {dest}, {src1}, {src2}")
} }
Inst::AddW { dest, src1, src2 } => {
write!(f, "addw {dest}, {src1}, {src2}")
}
Inst::Sub { dest, src1, src2 } => write!(f, "sub {dest}, {src1}, {src2}"), Inst::Sub { dest, src1, src2 } => write!(f, "sub {dest}, {src1}, {src2}"),
Inst::SubW { dest, src1, src2 } => write!(f, "subw {dest}, {src1}, {src2}"),
Inst::Sll { dest, src1, src2 } => write!(f, "sll {dest}, {src1}, {src2}"), Inst::Sll { dest, src1, src2 } => write!(f, "sll {dest}, {src1}, {src2}"),
Inst::SllW { dest, src1, src2 } => write!(f, "sllw {dest}, {src1}, {src2}"),
Inst::Slt { dest, src1, src2 } => write!(f, "slt {dest}, {src1}, {src2}"), Inst::Slt { dest, src1, src2 } => write!(f, "slt {dest}, {src1}, {src2}"),
Inst::Sltu { dest, src1, src2 } => write!(f, "sltu {dest}, {src1}, {src2}"), Inst::Sltu { dest, src1, src2 } => write!(f, "sltu {dest}, {src1}, {src2}"),
Inst::Xor { dest, src1, src2 } => write!(f, "xor {dest}, {src1}, {src2}"), Inst::Xor { dest, src1, src2 } => write!(f, "xor {dest}, {src1}, {src2}"),
Inst::Srl { dest, src1, src2 } => write!(f, "srl {dest}, {src1}, {src2}"), Inst::Srl { dest, src1, src2 } => write!(f, "srl {dest}, {src1}, {src2}"),
Inst::SrlW { dest, src1, src2 } => write!(f, "srlw {dest}, {src1}, {src2}"),
Inst::Sra { dest, src1, src2 } => write!(f, "sra {dest}, {src1}, {src2}"), Inst::Sra { dest, src1, src2 } => write!(f, "sra {dest}, {src1}, {src2}"),
Inst::SraW { dest, src1, src2 } => write!(f, "sraw {dest}, {src1}, {src2}"),
Inst::Or { dest, src1, src2 } => write!(f, "or {dest}, {src1}, {src2}"), Inst::Or { dest, src1, src2 } => write!(f, "or {dest}, {src1}, {src2}"),
Inst::And { dest, src1, src2 } => write!(f, "and {dest}, {src1}, {src2}"), Inst::And { dest, src1, src2 } => write!(f, "and {dest}, {src1}, {src2}"),
Inst::Fence { fence } => match fence.fm { Inst::Fence { fence } => match fence.fm {
@ -652,18 +625,13 @@ impl Display for Inst {
Inst::Ecall => write!(f, "ecall"), Inst::Ecall => write!(f, "ecall"),
Inst::Ebreak => write!(f, "ebreak"), Inst::Ebreak => write!(f, "ebreak"),
Inst::Mul { dest, src1, src2 } => write!(f, "mul {dest}, {src1}, {src2}"), Inst::Mul { dest, src1, src2 } => write!(f, "mul {dest}, {src1}, {src2}"),
Inst::MulW { dest, src1, src2 } => write!(f, "mulw {dest}, {src1}, {src2}"),
Inst::Mulh { dest, src1, src2 } => write!(f, "mulh {dest}, {src1}, {src2}"), Inst::Mulh { dest, src1, src2 } => write!(f, "mulh {dest}, {src1}, {src2}"),
Inst::Mulhsu { dest, src1, src2 } => write!(f, "mulhsu {dest}, {src1}, {src2}"), Inst::Mulhsu { dest, src1, src2 } => write!(f, "mulhsu {dest}, {src1}, {src2}"),
Inst::Mulhu { dest, src1, src2 } => write!(f, "mulhu {dest}, {src1}, {src2}"), Inst::Mulhu { dest, src1, src2 } => write!(f, "mulhu {dest}, {src1}, {src2}"),
Inst::Div { dest, src1, src2 } => write!(f, "div {dest}, {src1}, {src2}"), Inst::Div { dest, src1, src2 } => write!(f, "div {dest}, {src1}, {src2}"),
Inst::DivW { dest, src1, src2 } => write!(f, "divw {dest}, {src1}, {src2}"),
Inst::Divu { dest, src1, src2 } => write!(f, "divu {dest}, {src1}, {src2}"), Inst::Divu { dest, src1, src2 } => write!(f, "divu {dest}, {src1}, {src2}"),
Inst::DivuW { dest, src1, src2 } => write!(f, "divuw {dest}, {src1}, {src2}"),
Inst::Rem { dest, src1, src2 } => write!(f, "rem {dest}, {src1}, {src2}"), Inst::Rem { dest, src1, src2 } => write!(f, "rem {dest}, {src1}, {src2}"),
Inst::RemW { dest, src1, src2 } => write!(f, "remw {dest}, {src1}, {src2}"),
Inst::Remu { dest, src1, src2 } => write!(f, "remu {dest}, {src1}, {src2}"), Inst::Remu { dest, src1, src2 } => write!(f, "remu {dest}, {src1}, {src2}"),
Inst::RemuW { dest, src1, src2 } => write!(f, "remuw {dest}, {src1}, {src2}"),
Inst::LrW { order, dest, addr } => write!(f, "lr.w{order} {dest}, ({addr})",), Inst::LrW { order, dest, addr } => write!(f, "lr.w{order} {dest}, ({addr})",),
Inst::ScW { Inst::ScW {
order, order,
@ -769,6 +737,14 @@ impl InstCode {
let end_span = 32 - (range.end() + 1); let end_span = 32 - (range.end() + 1);
(self.0 << (end_span)) >> (end_span + range.start()) (self.0 << (end_span)) >> (end_span + range.start())
} }
fn immediate_u(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
let mut imm = 0;
for (from, to) in mappings {
let value = self.extract(from.clone());
imm |= value << to;
}
Imm::new_u32(imm)
}
fn immediate_s(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm { fn immediate_s(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
let mut imm = 0; let mut imm = 0;
let mut size = 0; let mut size = 0;
@ -799,10 +775,6 @@ impl InstCode {
fn rs2_imm(self) -> u32 { fn rs2_imm(self) -> u32 {
self.extract(20..=24) self.extract(20..=24)
} }
// shifts on RV64 have one extra bit
fn rs2_imm_plus(self) -> u32 {
self.extract(20..=25)
}
fn rd(self) -> Reg { fn rd(self) -> Reg {
Reg(self.extract(7..=11) as u8) Reg(self.extract(7..=11) as u8)
} }
@ -816,8 +788,7 @@ impl InstCode {
self.immediate_s(&[(31..=31, 12), (7..=7, 11), (25..=30, 5), (8..=11, 1)]) self.immediate_s(&[(31..=31, 12), (7..=7, 11), (25..=30, 5), (8..=11, 1)])
} }
fn imm_u(self) -> Imm { fn imm_u(self) -> Imm {
// Don't be fooled by the "u", LUI/AUIPC immediates are sign-extended on RV64. self.immediate_u(&[(12..=31, 12)])
self.immediate_s(&[(12..=31, 12)])
} }
fn imm_j(self) -> Imm { fn imm_j(self) -> Imm {
self.immediate_s(&[(31..=31, 20), (21..=30, 1), (20..=20, 11), (12..=19, 12)]) self.immediate_s(&[(31..=31, 20), (21..=30, 1), (20..=20, 11), (12..=19, 12)])
@ -957,10 +928,10 @@ impl Inst {
/// let x = 0x1101_u16; /// let x = 0x1101_u16;
/// let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP }; /// let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP };
/// ///
/// let inst = rvdc::Inst::decode_compressed(x, rvdc::Xlen::Rv32).unwrap(); /// let inst = rvdc::Inst::decode_compressed(x).unwrap();
/// assert_eq!(inst, expected); /// assert_eq!(inst, expected);
/// ``` /// ```
pub fn decode_compressed(code: u16, _xlen: Xlen) -> Result<Inst, DecodeError> { pub fn decode_compressed(code: u16, xlen: Xlen) -> Result<Inst, DecodeError> {
let code = InstCodeC(code); let code = InstCodeC(code);
if code.0 == 0 { if code.0 == 0 {
return Err(decode_error(code, "null instruction")); return Err(decode_error(code, "null instruction"));
@ -1125,7 +1096,7 @@ impl Inst {
_ => { _ => {
let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]); let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]);
if uimm.as_u32() == 0 { if uimm.as_u32() == 0 {
return Err(decode_error(code, "C.LUI zero immediate")); return Err(decode_error(code, "C.LUI imm"));
} }
Inst::Lui { Inst::Lui {
uimm, uimm,
@ -1381,13 +1352,7 @@ impl Inst {
src1: code.rs1(), src1: code.rs1(),
}, },
0b001 => { 0b001 => {
// For RV32, bit 25 must be zero as well. if code.funct7() != 0 {
let left_zeroes = code.funct7()
>> match xlen {
Xlen::Rv32 => 0,
Xlen::Rv64 => 1,
};
if left_zeroes != 0 {
return Err(decode_error(code, "slli shift overflow")); return Err(decode_error(code, "slli shift overflow"));
} }
Inst::Slli { Inst::Slli {
@ -1396,42 +1361,24 @@ impl Inst {
src1: code.rs1(), src1: code.rs1(),
} }
} }
0b101 => match xlen { 0b101 => match code.funct7() {
Xlen::Rv32 => match code.funct7() { 0b0000000 => Inst::Srli {
0b0000000 => Inst::Srli { imm: Imm::new_u32(code.rs2_imm()),
imm: Imm::new_u32(code.rs2_imm()), dest: code.rd(),
dest: code.rd(), src1: code.rs1(),
src1: code.rs1(),
},
0b0100000 => Inst::Srai {
imm: Imm::new_u32(code.rs2_imm()),
dest: code.rd(),
src1: code.rs1(),
},
_ => return Err(decode_error(code, "srli shift overflow")),
}, },
Xlen::Rv64 => { 0b0100000 => Inst::Srai {
let upper = code.funct7() >> 1; imm: Imm::new_u32(code.rs2_imm()),
match upper { dest: code.rd(),
0b010000 => Inst::Srai { src1: code.rs1(),
imm: Imm::new_u32(code.rs2_imm_plus()), },
dest: code.rd(), _ => return Err(decode_error(code, "OP-IMM funct7")),
src1: code.rs1(),
},
0b000000 => Inst::Srli {
imm: Imm::new_u32(code.rs2_imm_plus()),
dest: code.rd(),
src1: code.rs1(),
},
_ => return Err(decode_error(code, "srai/srli upper bits")),
}
}
}, },
_ => return Err(decode_error(code, "OP-IMM funct3")), _ => return Err(decode_error(code, "OP-IMM funct3")),
}, },
// OP-IMM-32 // OP-IMM-32
0b0011011 => { 0b0011011 => {
if xlen.is_32() { if !xlen.is_64() {
return Err(decode_error(code, "OP-IMM-32 only on RV64")); return Err(decode_error(code, "OP-IMM-32 only on RV64"));
} }
@ -1496,28 +1443,6 @@ impl Inst {
_ => return Err(decode_error(code, "OP funct3/funct7")), _ => return Err(decode_error(code, "OP funct3/funct7")),
} }
} }
// OP-32
0b0111011 => {
if xlen.is_32() {
return Err(decode_error(code, "OP-IMM-32 only on RV64"));
}
let (dest, src1, src2) = (code.rd(), code.rs1(), code.rs2());
match (code.funct3(), code.funct7()) {
(0b000, 0b0000000) => Inst::AddW { dest, src1, src2 },
(0b000, 0b0100000) => Inst::SubW { dest, src1, src2 },
(0b001, 0b0000000) => Inst::SllW { dest, src1, src2 },
(0b101, 0b0000000) => Inst::SrlW { dest, src1, src2 },
(0b101, 0b0100000) => Inst::SraW { dest, src1, src2 },
(0b000, 0b0000001) => Inst::MulW { dest, src1, src2 },
(0b100, 0b0000001) => Inst::DivW { dest, src1, src2 },
(0b101, 0b0000001) => Inst::DivuW { dest, src1, src2 },
(0b110, 0b0000001) => Inst::RemW { dest, src1, src2 },
(0b111, 0b0000001) => Inst::RemuW { dest, src1, src2 },
_ => return Err(decode_error(code, "OP-32 funct3/funct7")),
}
}
// MISC-MEM // MISC-MEM
0b0001111 => { 0b0001111 => {
let fm = code.extract(28..=31); let fm = code.extract(28..=31);
@ -1632,8 +1557,6 @@ impl Inst {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate std; extern crate std;
use core::sync::atomic::AtomicU32;
use core::sync::atomic::Ordering;
use std::prelude::rust_2024::*; use std::prelude::rust_2024::*;
use std::fmt::Write as _; use std::fmt::Write as _;
@ -1641,8 +1564,6 @@ mod tests {
use object::Object; use object::Object;
use object::ObjectSection; use object::ObjectSection;
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use crate::Fence; use crate::Fence;
use crate::FenceSet; use crate::FenceSet;
@ -1653,17 +1574,7 @@ mod tests {
#[test] #[test]
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")] #[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
fn exhaustive_decode_no_panic_32() { fn exhaustive_decode_no_panic() {
exhaustive_decode_no_panic(Xlen::Rv32);
}
#[test]
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
fn exhaustive_decode_no_panic_64() {
exhaustive_decode_no_panic(Xlen::Rv64);
}
fn exhaustive_decode_no_panic(xlen: Xlen) {
for i in 0..u32::MAX { for i in 0..u32::MAX {
if (i % (2 << 25)) == 0 { if (i % (2 << 25)) == 0 {
let percent = i as f32 / (u32::MAX as f32); let percent = i as f32 / (u32::MAX as f32);
@ -1671,18 +1582,25 @@ mod tests {
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done)); std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
std::io::stdout().flush().unwrap(); std::io::stdout().flush().unwrap();
} }
let _ = Inst::decode(i, xlen); let _ = Inst::decode(i, Xlen::Rv32);
} }
let _ = Inst::decode(u32::MAX, xlen); let _ = Inst::decode(u32::MAX, Xlen::Rv32);
for i in 0..u32::MAX {
if (i % (2 << 25)) == 0 {
let percent = i as f32 / (u32::MAX as f32);
let done = (100.0 * percent) as usize;
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
std::io::stdout().flush().unwrap();
}
let _ = Inst::decode(i, Xlen::Rv64);
}
let _ = Inst::decode(u32::MAX, Xlen::Rv64);
} }
#[test] #[test]
fn size_of_instruction() { fn size_of_instruction() {
assert!( assert!(size_of::<Inst>() <= 12);
size_of::<Inst>() <= 16,
"size of instruction is too large: {}",
size_of::<Inst>()
);
} }
const TEST_SECTION_NAME: &str = ".text.rvdctest"; const TEST_SECTION_NAME: &str = ".text.rvdctest";
@ -1781,14 +1699,14 @@ mod tests {
const CHUNKS: u32 = 128; const CHUNKS: u32 = 128;
const CHUNK_SIZE: u32 = u32::MAX / CHUNKS; const CHUNK_SIZE: u32 = u32::MAX / CHUNKS;
let chunks = ((SKIP_CHUNKS * CHUNK_SIZE)..u32::MAX) for (chunk_idx, start) in ((SKIP_CHUNKS * CHUNK_SIZE)..u32::MAX)
.step_by(CHUNK_SIZE as usize) .step_by(CHUNK_SIZE as usize)
.collect::<Vec<_>>(); .enumerate()
{
let chunk_idx = chunk_idx + SKIP_CHUNKS as usize;
let start_time = std::time::Instant::now(); let start_time = std::time::Instant::now();
let completed = AtomicU32::new(0);
chunks.par_iter().for_each(|&start| {
let insts = (start..=start.saturating_add(CHUNK_SIZE)) let insts = (start..=start.saturating_add(CHUNK_SIZE))
.filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?))) .filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?)))
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst)) .filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
@ -1807,26 +1725,23 @@ mod tests {
assert_eq!( assert_eq!(
insts[i].0, result_code, insts[i].0, result_code,
"failed to rountrip!\n\ "failed to rountrip!\n\
instruction `{:0>32b}` failed to rountrip\n\ instruction `{:0>32b}` failed to rountrip\n\
resulted in `{:0>32b}` instead.\n\ resulted in `{:0>32b}` instead.\n\
disassembly of original instruction: `{}`", disassembly of original instruction: `{}`",
insts[i].0, result_code, insts[i].1 insts[i].0, result_code, insts[i].1
); );
} }
let already_completed = completed.fetch_add(1, Ordering::Relaxed); let elapsed = start_time.elapsed();
let already_elapsed = start_time.elapsed(); let chunk_number = chunk_idx + 1;
let remaining = elapsed * (CHUNKS.saturating_sub(chunk_number as u32));
let remaining_chunks = CHUNKS.saturating_sub(already_completed);
let remaining =
already_elapsed / std::cmp::max(already_completed, 1) * remaining_chunks;
writeln!( writeln!(
std::io::stdout(), std::io::stdout(),
"Completed chunk {already_completed}/{CHUNKS} (estimated {remaining:?} remaining)", "Completed chunk {chunk_number}/{CHUNKS} (estimated {remaining:?} remaining)",
) )
.unwrap(); .unwrap();
}); }
} }
#[test] #[test]

View file

@ -386,33 +386,8 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].unsigned_shr(imm.as_u32()), Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].unsigned_shr(imm.as_u32()),
Inst::Srai { imm, dest, src1 } => self[dest] = self[src1].signed_shr(imm.as_u32()), Inst::Srai { imm, dest, src1 } => self[dest] = self[src1].signed_shr(imm.as_u32()),
Inst::Add { dest, src1, src2 } => self[dest] = self[src1].add(self[src2]), Inst::Add { dest, src1, src2 } => self[dest] = self[src1].add(self[src2]),
Inst::AddW { dest, src1, src2 } => {
self[dest] = XLEN::switch2(
self[src1],
self[src2],
|_, _| unreachable!(),
|a, b| (a as u32).add(b as u32) as i32 as i64 as u64,
);
}
Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]), Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]),
Inst::SubW { dest, src1, src2 } => { Inst::Sll { dest, src1, src2 } => self[dest] = self[src1].shl(self[src2].truncate32()),
self[dest] = XLEN::switch2(
self[src1],
self[src2],
|_, _| unreachable!(),
|a, b| (a as u32).sub(b as u32) as i32 as i64 as u64,
);
}
Inst::Sll { dest, src1, src2 } => {
self[dest] = self[src1].shl(self[src2].truncate32());
}
Inst::SllW { dest, src1, src2 } => {
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|a| (a as u32).shl(self[src2].truncate32()) as i32 as i64 as u64,
);
}
Inst::Slt { dest, src1, src2 } => { Inst::Slt { dest, src1, src2 } => {
self[dest] = XLEN::from_bool(self[src1].signed_lt(self[src2])); self[dest] = XLEN::from_bool(self[src1].signed_lt(self[src2]));
} }
@ -423,23 +398,9 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Srl { dest, src1, src2 } => { Inst::Srl { dest, src1, src2 } => {
self[dest] = self[src1].unsigned_shr(self[src2].truncate32()) self[dest] = self[src1].unsigned_shr(self[src2].truncate32())
} }
Inst::SrlW { dest, src1, src2 } => {
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|a| (a as u32).unsigned_shr(self[src2].truncate32()) as i32 as i64 as u64,
);
}
Inst::Sra { dest, src1, src2 } => { Inst::Sra { dest, src1, src2 } => {
self[dest] = self[src1].signed_shr(self[src2].truncate32()) self[dest] = self[src1].signed_shr(self[src2].truncate32())
} }
Inst::SraW { dest, src1, src2 } => {
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|a| (a as u32).unsigned_shr(self[src2].truncate32()) as i32 as i64 as u64,
);
}
Inst::Or { dest, src1, src2 } => self[dest] = self[src1].or(self[src2]), Inst::Or { dest, src1, src2 } => self[dest] = self[src1].or(self[src2]),
Inst::And { dest, src1, src2 } => self[dest] = self[src1].and(self[src2]), Inst::And { dest, src1, src2 } => self[dest] = self[src1].and(self[src2]),
Inst::Fence { fence: _ } => { /* dont care */ } Inst::Fence { fence: _ } => { /* dont care */ }
@ -541,40 +502,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
} }
self.reservation_set = None; self.reservation_set = None;
} }
Inst::AddiW { imm, dest, src1 } => { _ => return Err(Status::UnsupportedInst(inst)),
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|src| ((src as u32).wrapping_add(imm.as_u32())) as i32 as i64 as u64,
);
}
Inst::SlliW { imm, dest, src1 } => {
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|src| ((src as u32).shl(imm.as_u32())) as u64,
);
}
Inst::SrliW { imm, dest, src1 } => {
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|src| ((src as u32).unsigned_shr(imm.as_u32())) as u64,
);
}
Inst::SraiW { imm, dest, src1 } => {
self[dest] = XLEN::switch(
self[src1],
|_| unreachable!(),
|src| ((src as u32).signed_shr(imm.as_u32())) as u64,
);
}
Inst::MulW { dest, src1, src2 } => todo!(),
Inst::DivW { dest, src1, src2 } => todo!(),
Inst::DivuW { dest, src1, src2 } => todo!(),
Inst::RemW { dest, src1, src2 } => todo!(),
Inst::RemuW { dest, src1, src2 } => todo!(),
_ => todo!(),
} }
if !jumped { if !jumped {
@ -748,7 +676,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
} }
} }
pub trait XLen: Copy + PartialEq + Eq + Debug { pub trait XLen: Copy + PartialEq + Eq {
type Signed; type Signed;
type NextUnsigned; type NextUnsigned;
type NextSigned; type NextSigned;
@ -759,15 +687,7 @@ pub trait XLen: Copy + PartialEq + Eq + Debug {
const SIGNED_MIN: Self; const SIGNED_MIN: Self;
const MAX: Self; const MAX: Self;
const IS_64: bool = matches!(Self::XLEN, rvdc::Xlen::Rv64); fn switch<R>(self, on_32: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R;
fn switch(self, on_32: impl FnOnce(u32) -> u32, on_64: impl FnOnce(u64) -> u64) -> Self;
fn switch2(
self,
other: Self,
on_32: impl FnOnce(u32, u32) -> u32,
on_64: impl FnOnce(u64, u64) -> u64,
) -> Self;
fn from_bool(v: bool) -> Self { fn from_bool(v: bool) -> Self {
Self::from_32_z(v as u32) Self::from_32_z(v as u32)
@ -919,17 +839,9 @@ impl XLen for u32 {
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv32; const XLEN: rvdc::Xlen = rvdc::Xlen::Rv32;
fn switch(self, on_32: impl FnOnce(u32) -> u32, _: impl FnOnce(u64) -> u64) -> Self { fn switch<R>(self, on_32: impl FnOnce(u32) -> R, _: impl FnOnce(u64) -> R) -> R {
on_32(self) on_32(self)
} }
fn switch2(
self,
other: Self,
on_32: impl FnOnce(u32, u32) -> u32,
_: impl FnOnce(u64, u64) -> u64,
) -> Self {
on_32(self, other)
}
fn from_32_s(v: u32) -> Self { fn from_32_s(v: u32) -> Self {
v v
@ -951,17 +863,9 @@ impl XLen for u64 {
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64; const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64;
fn switch(self, _: impl FnOnce(u32) -> u32, on_64: impl FnOnce(u64) -> u64) -> Self { fn switch<R>(self, _: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R {
on_64(self) on_64(self)
} }
fn switch2(
self,
other: Self,
_: impl FnOnce(u32, u32) -> u32,
on_64: impl FnOnce(u64, u64) -> u64,
) -> Self {
on_64(self, other)
}
fn from_32_s(v: u32) -> Self { fn from_32_s(v: u32) -> Self {
v as i32 as i64 as u64 v as i32 as i64 as u64

View file

@ -30,7 +30,7 @@ fn check() -> eyre::Result<()> {
test_case(tmpdir.path(), &file, name, 32, "rv32ima")?; test_case(tmpdir.path(), &file, name, 32, "rv32ima")?;
test_case(tmpdir.path(), &file, name, 32, "rv32imac")?; test_case(tmpdir.path(), &file, name, 32, "rv32imac")?;
test_case(tmpdir.path(), &file, name, 64, "rv64ima")?; // test_case(tmpdir.path(), &file, name, 64, "rv64ima")?;
// test_case(tmpdir.path(), &file, name, 64, "rv64imac")?; // test_case(tmpdir.path(), &file, name, 64, "rv64imac")?;
} }
@ -70,7 +70,7 @@ fn test_case(
} }
}), }),
Box::new(|_, xreg| { Box::new(|_, xreg| {
if xreg[Reg::A7.0 as usize] == u64::MAX { if xreg[Reg::A7.0 as usize] == u32::MAX as u64 {
if xreg[Reg::A0.0 as usize] == 1 { if xreg[Reg::A0.0 as usize] == 1 {
Err(rustv32i::emu::Status::Exit { code: 0 }) Err(rustv32i::emu::Status::Exit { code: 0 })
} else { } else {
@ -101,7 +101,6 @@ fn build(tmpdir: &Path, src: &Path, size: u8, march: &str) -> eyre::Result<PathB
cmd.arg(src); cmd.arg(src);
cmd.arg("-o"); cmd.arg("-o");
cmd.arg(&out_path); cmd.arg(&out_path);
cmd.arg(format!("-DRV{size}"));
let output = cmd.output().wrap_err("failed to spawn clang")?; let output = cmd.output().wrap_err("failed to spawn clang")?;
if !output.status.success() { if !output.status.success() {

View file

@ -83,49 +83,25 @@ START_TEST
CASE sll, 2, 1, 4 CASE sll, 2, 1, 4
CASE sll, 2, 20, 2097152 CASE sll, 2, 20, 2097152
CASE sll, 2, 30, 2147483648 CASE sll, 2, 30, 2147483648
CASE sll, 2, 31, 0
CASER sll, 2, 32, 2 # error for immediate
CASE sll, 0, 10, 0 CASE sll, 0, 10, 0
CASE sll, 10, 0, 10 CASE sll, 10, 0, 10
#ifdef RV32
CASE sll, 2, 31, 0
CASE sll, -1, 31, -2147483648 CASE sll, -1, 31, -2147483648
CASER sll, -1, 32, -1 # error for immediate CASER sll, -1, 32, -1 # error for immediate
CASER sll, 2, 32, 2 # error for immediate
#elif RV64
#CASE_BOTH sllw, slliw, 2, 31, 0
#CASE_BOTH sllw, slliw, -1, 31, -2147483648
#CASER sllw, -1, 32, -1 # error for immediate
#CASER sllw, 2, 32, 2 # error for immediate
CASE sll, -1, 31, 18446744071562067968
CASER sll, 2, 63, 0 # error for immediate
CASE sll, -1, 32, 18446744069414584320 # test with immediate as well
CASER sll, -1, 63, 9223372036854775808 # error for immediate
CASER sll, -1, 64, -1 # error for immediate
CASER sll, 2, 64, 2 # error for immediate
#endif
CASE srl, 4, 1, 2 CASE srl, 4, 1, 2
CASE srl, 0, 10, 0 CASE srl, 0, 10, 0
CASE srl, 10, 0, 10 CASE srl, 10, 0, 10
CASE srl, 0b111, 2, 0b001
#ifdef RV32
CASE srl, -1, 1, 2147483647 CASE srl, -1, 1, 2147483647
CASE srl, 0b111, 2, 0b001
CASER srl, -1, 32, -1 # error for immediate CASER srl, -1, 32, -1 # error for immediate
#elif RV64
CASE srl, -1, 1, 9223372036854775807
CASE srl, -1, 32, 4294967295
CASER srl, -1, 64, -1 # error for immediate
#endif
CASER sub, 10, 5, 5 CASER sub, 10, 5, 5
CASER sub, -1, 1, -2 CASER sub, -1, 1, -2
CASER sub, 1, 2, -1 CASER sub, 1, 2, -1
CASER sub, -1, -2, 1 CASER sub, -1, -2, 1
#ifdef RV32
CASER sub, 0, 4294967295, 1 CASER sub, 0, 4294967295, 1
#elif RV64
CASER sub, 0, 18446744073709551615, 1
#endif
.macro CASE_SUB a:req, b:req .macro CASE_SUB a:req, b:req
CASER sub, \a, \b, \a - \b CASER sub, \a, \b, \a - \b
@ -139,12 +115,7 @@ START_TEST
CASE sra, -1, 1, -1 CASE sra, -1, 1, -1
CASE sra, -1, 31, -1 CASE sra, -1, 31, -1
CASE sra, 0b111, 2, 0b001 CASE sra, 0b111, 2, 0b001
#ifdef RV32
CASER sra, 10, 32, 10 # error for immediate CASER sra, 10, 32, 10 # error for immediate
#elif RV64
CASE sra, 10, 32, 0
CASER sra, 10, 64, 10 # error for immediate
#endif
# M extension # M extension
@ -152,11 +123,7 @@ START_TEST
CASER mul, 10, 0, 0 CASER mul, 10, 0, 0
CASER mul, 10, 1, 10 CASER mul, 10, 1, 10
CASER mul, -1, -1, 1 CASER mul, -1, -1, 1
#ifdef RV32
CASER mul, 25252566, 5225225, 353909638 CASER mul, 25252566, 5225225, 353909638
#elif RV64
// TODO
#endif
.macro CASE_MUL a:req, b:req .macro CASE_MUL a:req, b:req
CASER mul, \a, \b, \a * \b CASER mul, \a, \b, \a * \b
@ -166,19 +133,11 @@ START_TEST
CASER mulh 4, 4, 0 CASER mulh 4, 4, 0
CASER mulh, -1, -1, 0 CASER mulh, -1, -1, 0
#ifdef RV32
CASER mulh, 25252566, 5225225, 30722 CASER mulh, 25252566, 5225225, 30722
#elif RV64
// TODO
#endif
CASER mulhu 4, 4, 0 CASER mulhu 4, 4, 0
#ifdef RV32
CASER mulhu, -1, -1, 4294967294 CASER mulhu, -1, -1, 4294967294
CASER mulhu, 25252566, 5225225, 30722 CASER mulhu, 25252566, 5225225, 30722
#elif RV64
// TODO
#endif
# mulhsu hasn't been implemented yet. # mulhsu hasn't been implemented yet.
@ -189,33 +148,21 @@ START_TEST
CASER div, -10, 2, -5 CASER div, -10, 2, -5
CASER div, 5, 2, 2 CASER div, 5, 2, 2
CASER div, 5, -1, -5 CASER div, 5, -1, -5
#ifdef RV32
CASER div, -2147483648, -1, -1 CASER div, -2147483648, -1, -1
#elif RV64
// TODO
#endif
CASER divu, 4, 2, 2 CASER divu, 4, 2, 2
CASER divu, -1, 1, -1 CASER divu, -1, 1, -1
CASER divu, 1, 1, 1 CASER divu, 1, 1, 1
CASER divu, 1, 0, -1 CASER divu, 1, 0, -1
CASER divu, 5, 2, 2
#ifdef RV32
CASER divu, -10, 2, 2147483643 CASER divu, -10, 2, 2147483643
#elif RV64 CASER divu, 5, 2, 2
// TODO
#endif
CASER rem, 4, 2, 0 CASER rem, 4, 2, 0
CASER rem, 5, 2, 1 CASER rem, 5, 2, 1
CASER rem, 5, 0, 5 CASER rem, 5, 0, 5
CASER rem, -10, 3, -1 CASER rem, -10, 3, -1
CASER rem, 5, -1, 0 CASER rem, 5, -1, 0
#ifdef RV32
CASER rem, -2147483648, -1, 0 CASER rem, -2147483648, -1, 0
#elif RV64
// TODO
#endif
CASER remu, 4, 2, 0 CASER remu, 4, 2, 0
CASER remu, 5, 2, 1 CASER remu, 5, 2, 1