immediate improvements
Some checks are pending
Rust / build (push) Waiting to run

This commit is contained in:
nora 2025-03-25 20:33:05 +01:00
parent 92dc3cb349
commit 78aa1e8d75
4 changed files with 202 additions and 116 deletions

View file

@ -1,3 +1,8 @@
## 2.0.0
- BREAKING CHANGE: Make `Inst` `#[non_exhaustive]`
- BREAKING CHANGE: Change immediate fields in `Inst` to `Imm`
## 0.1.1 ## 0.1.1
- Add `Fence::is_tso` - Add `Fence::is_tso`

View file

@ -4,7 +4,9 @@ The main function is [`Inst::decode`], which will decode an instruction into the
The [`core::fmt::Display`] impl of [`Inst`] provides disassembly functionality The [`core::fmt::Display`] impl of [`Inst`] provides disassembly functionality
(note that the precise output of that implementation is not considered stable). (note that the precise output of that implementation is not considered stable).
# Register size support # XLEN (Register size) support
RISC-V calls the parameter of the instruction size `XLEN`, and this crate refers to it as such.
This crate currenly only supports RV32 instructions. This crate currenly only supports RV32 instructions.
RV64 instructions that are the same between versions will still be decoded successfully, but the user RV64 instructions that are the same between versions will still be decoded successfully, but the user
@ -13,7 +15,7 @@ has to be careful around sign-extended immediates to preserve the correct value
RV64-specific instructions are not yet implemented, but will be in the future. RV64-specific instructions are not yet implemented, but will be in the future.
The immediates will also be switched to `u64` in the future to allow for easier usage of RV64. The immediates will also be switched to `u64` in the future to allow for easier usage of RV64.
RV128 is not intended to be supported. RV128 is currently not intended to be supported.
# Extension support # Extension support
@ -34,7 +36,7 @@ More extensions may be implemented in the future.
```rust ```rust
// addi sp, sp, -0x20 (compressed) // addi sp, sp, -0x20 (compressed)
let x = 0x1101_u32; let x = 0x1101_u32;
let expected = rvdc::Inst::Addi { imm: (-0x20_i32) as u32, 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).unwrap(); let (inst, is_compressed) = rvdc::Inst::decode(x).unwrap();
assert_eq!(inst, expected); assert_eq!(inst, expected);
@ -45,7 +47,7 @@ assert_eq!(format!("{inst}"), "addi sp, sp, -32")
```rust ```rust
// auipc t1, 0xa // auipc t1, 0xa
let x = 0x0000a317; let x = 0x0000a317;
let expected = rvdc::Inst::Auipc { uimm: 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).unwrap(); let (inst, is_compressed) = rvdc::Inst::decode(x).unwrap();
assert_eq!(inst, expected); assert_eq!(inst, expected);

View file

@ -102,6 +102,65 @@ impl Display for Reg {
} }
} }
/// An immediate in an instruction.
/// This represents the real value that will be put in the register,
/// so sign extension has been performed if necessary, and for instructions
/// like `lui` the value will have been shifted.
///
/// This type is XLEN-agnostic, use the XLEN-specific accessors to get the correct value.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Imm(u32);
impl Imm {
/// The immediate `0`.
/// Useful as a shortcut for `Imm::new_u32(0)` and for patterns.
pub const ZERO: Self = Self::new_u32(0);
/// Create a new immediate from the (if necessary) sign-extended value.
pub const fn new_i32(value: i32) -> Self {
Self(value as u32)
}
/// Create a new immediate from the (if necessary) zero-extended value.
pub const fn new_u32(value: u32) -> Self {
Self(value)
}
/// Get the `u32` (RV32) value of the immediate.
pub const fn as_u32(self) -> u32 {
self.0
}
/// Get the `i32` (RV32) value of the immediate.
pub const fn as_i32(self) -> i32 {
self.0 as i32
}
}
impl From<i32> for Imm {
fn from(value: i32) -> Self {
Self::new_i32(value)
}
}
impl From<u32> for Imm {
fn from(value: u32) -> Self {
Self::new_u32(value)
}
}
impl From<Imm> for u32 {
fn from(value: Imm) -> Self {
value.as_u32()
}
}
impl From<Imm> for i32 {
fn from(value: Imm) -> Self {
value.as_i32()
}
}
/// A RISC-V instruction. /// A RISC-V instruction.
/// ///
/// Every variant is a different instruction, with immediates as `u32`. /// Every variant is a different instruction, with immediates as `u32`.
@ -112,66 +171,67 @@ impl Display for Reg {
#[derive(Clone, Copy, PartialEq, Eq, Hash)] #[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[rustfmt::skip] #[rustfmt::skip]
#[expect(missing_docs)] // enum variant fields #[expect(missing_docs)] // enum variant fields
#[non_exhaustive]
pub enum Inst { pub enum Inst {
/// Load Upper Immediate /// Load Upper Immediate
Lui { uimm: u32, dest: Reg }, Lui { uimm: Imm, dest: Reg },
/// Add Upper Immediate to PC /// Add Upper Immediate to PC
Auipc { uimm: u32, dest: Reg }, Auipc { uimm: Imm, dest: Reg },
/// Jump And Link /// Jump And Link
Jal { offset: u32, dest: Reg }, Jal { offset: Imm, dest: Reg },
/// Jump And Link Register (indirect) /// Jump And Link Register (indirect)
Jalr { offset: u32, base: Reg, dest: Reg }, Jalr { offset: Imm, base: Reg, dest: Reg },
/// Branch Equal /// Branch Equal
Beq { offset: u32, src1: Reg, src2: Reg }, Beq { offset: Imm, src1: Reg, src2: Reg },
/// Branch Not Equal /// Branch Not Equal
Bne { offset: u32, src1: Reg, src2: Reg }, Bne { offset: Imm, src1: Reg, src2: Reg },
/// Branch Less Than (signed) /// Branch Less Than (signed)
Blt { offset: u32, src1: Reg, src2: Reg }, Blt { offset: Imm, src1: Reg, src2: Reg },
/// Branch Greater or Equal (signed) /// Branch Greater or Equal (signed)
Bge { offset: u32, src1: Reg, src2: Reg }, Bge { offset: Imm, src1: Reg, src2: Reg },
/// Branch Less Than Unsigned /// Branch Less Than Unsigned
Bltu { offset: u32, src1: Reg, src2: Reg }, Bltu { offset: Imm, src1: Reg, src2: Reg },
/// Branch Greater or Equal Unsigned /// Branch Greater or Equal Unsigned
Bgeu { offset: u32, src1: Reg, src2: Reg }, Bgeu { offset: Imm, src1: Reg, src2: Reg },
/// Load Byte (sign-ext) /// Load Byte (sign-ext)
Lb { offset: u32, dest: Reg, base: Reg }, Lb { offset: Imm, dest: Reg, base: Reg },
/// Load Unsigned Byte (zero-ext) /// Load Unsigned Byte (zero-ext)
Lbu { offset: u32, dest: Reg, base: Reg }, Lbu { offset: Imm, dest: Reg, base: Reg },
/// Load Half (sign-ext) /// Load Half (sign-ext)
Lh { offset: u32, dest: Reg, base: Reg }, Lh { offset: Imm, dest: Reg, base: Reg },
/// Load Unsigned Half (zero-ext) /// Load Unsigned Half (zero-ext)
Lhu { offset: u32, dest: Reg, base: Reg }, Lhu { offset: Imm, dest: Reg, base: Reg },
/// Load Word /// Load Word
Lw { offset: u32, dest: Reg, base: Reg }, Lw { offset: Imm, dest: Reg, base: Reg },
/// Store Byte /// Store Byte
Sb { offset: u32, src: Reg, base: Reg }, Sb { offset: Imm, src: Reg, base: Reg },
/// Store Half /// Store Half
Sh { offset: u32, src: Reg, base: Reg }, Sh { offset: Imm, src: Reg, base: Reg },
/// Store Word /// Store Word
Sw { offset: u32, src: Reg, base: Reg }, Sw { offset: Imm, src: Reg, base: Reg },
/// Add Immediate /// Add Immediate
Addi { imm: u32, dest: Reg, src1: Reg }, Addi { imm: Imm, dest: Reg, src1: Reg },
/// Set Less Than Immediate (signed) /// Set Less Than Immediate (signed)
Slti { imm: u32, dest: Reg, src1: Reg }, Slti { imm: Imm, dest: Reg, src1: Reg },
/// Set Less Than Immediate Unsigned /// Set Less Than Immediate Unsigned
Sltiu { imm: u32, dest: Reg, src1: Reg }, Sltiu { imm: Imm, dest: Reg, src1: Reg },
/// XOR Immediate /// XOR Immediate
Xori { imm: u32, dest: Reg, src1: Reg }, Xori { imm: Imm, dest: Reg, src1: Reg },
/// OR Immediate /// OR Immediate
Ori { imm: u32, dest: Reg, src1: Reg }, Ori { imm: Imm, dest: Reg, src1: Reg },
/// AND Immediate /// AND Immediate
Andi { imm: u32, dest: Reg, src1: Reg }, Andi { imm: Imm, dest: Reg, src1: Reg },
/// Shift Left Logical Immediate /// Shift Left Logical Immediate
Slli { imm: u32, dest: Reg, src1: Reg }, Slli { imm: Imm, dest: Reg, src1: Reg },
/// Shift Right Logical Immediate (unsigned) /// Shift Right Logical Immediate (unsigned)
Srli { imm: u32, dest: Reg, src1: Reg }, Srli { imm: Imm, dest: Reg, src1: Reg },
/// Shift Right Arithmetic Immediate (signed) /// Shift Right Arithmetic Immediate (signed)
Srai { imm: u32, dest: Reg, src1: Reg }, Srai { imm: Imm, dest: Reg, src1: Reg },
/// Add /// Add
Add { dest: Reg, src1: Reg, src2: Reg }, Add { dest: Reg, src1: Reg, src2: Reg },
@ -385,91 +445,103 @@ impl Debug for Inst {
impl Display for Inst { impl Display for Inst {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {
Inst::Lui { uimm, dest } => write!(f, "lui {dest}, {}", uimm >> 12), Inst::Lui { uimm, dest } => write!(f, "lui {dest}, {}", uimm.as_u32() >> 12),
Inst::Auipc { uimm, dest } => write!(f, "auipc {dest}, {}", uimm >> 12), Inst::Auipc { uimm, dest } => write!(f, "auipc {dest}, {}", uimm.as_u32() >> 12),
Inst::Jal { offset, dest } => { Inst::Jal { offset, dest } => {
if dest.0 == 0 { if dest.0 == 0 {
write!(f, "j {}", offset as i32) write!(f, "j {}", offset.as_i32())
} else { } else {
write!(f, "jal {dest}, {}", offset as i32) write!(f, "jal {dest}, {}", offset.as_i32())
} }
} }
Inst::Jalr { offset, base, dest } => { Inst::Jalr { offset, base, dest } => {
if dest == Reg::ZERO && offset == 0 && base == Reg::RA { if dest == Reg::ZERO && offset.as_u32() == 0 && base == Reg::RA {
write!(f, "ret") write!(f, "ret")
} else { } else {
write!(f, "jalr {dest}, {}({base})", offset as i32) write!(f, "jalr {dest}, {}({base})", offset.as_i32())
} }
} }
Inst::Beq { offset, src1, src2 } => write!(f, "beq {src1}, {src2}, {}", offset as i32), Inst::Beq { offset, src1, src2 } => {
Inst::Bne { offset, src1, src2 } => write!(f, "bne {src1}, {src2}, {}", offset as i32), write!(f, "beq {src1}, {src2}, {}", offset.as_i32())
Inst::Blt { offset, src1, src2 } => write!(f, "blt {src1}, {src2}, {}", offset as i32), }
Inst::Bge { offset, src1, src2 } => write!(f, "bge {src1}, {src2}, {}", offset as i32), Inst::Bne { offset, src1, src2 } => {
write!(f, "bne {src1}, {src2}, {}", offset.as_i32())
}
Inst::Blt { offset, src1, src2 } => {
write!(f, "blt {src1}, {src2}, {}", offset.as_i32())
}
Inst::Bge { offset, src1, src2 } => {
write!(f, "bge {src1}, {src2}, {}", offset.as_i32())
}
Inst::Bltu { offset, src1, src2 } => { Inst::Bltu { offset, src1, src2 } => {
write!(f, "bltu {src1}, {src2}, {}", offset as i32) write!(f, "bltu {src1}, {src2}, {}", offset.as_i32())
} }
Inst::Bgeu { offset, src1, src2 } => { Inst::Bgeu { offset, src1, src2 } => {
write!(f, "bgeu {src1}, {src2}, {}", offset as i32) write!(f, "bgeu {src1}, {src2}, {}", offset.as_i32())
} }
Inst::Lb { offset, dest, base } => write!(f, "lb {dest}, {}({base})", offset as i32), Inst::Lb { offset, dest, base } => write!(f, "lb {dest}, {}({base})", offset.as_i32()),
Inst::Lbu { offset, dest, base } => write!(f, "lbu {dest}, {}({base})", offset as i32), Inst::Lbu { offset, dest, base } => {
Inst::Lh { offset, dest, base } => write!(f, "lh {dest}, {}({base})", offset as i32), write!(f, "lbu {dest}, {}({base})", offset.as_i32())
Inst::Lhu { offset, dest, base } => write!(f, "lhu {dest}, {}({base})", offset as i32), }
Inst::Lw { offset, dest, base } => write!(f, "lw {dest}, {}({base})", offset as i32), Inst::Lh { offset, dest, base } => write!(f, "lh {dest}, {}({base})", offset.as_i32()),
Inst::Sb { offset, src, base } => write!(f, "sb {src}, {}({base})", offset as i32), Inst::Lhu { offset, dest, base } => {
Inst::Sh { offset, src, base } => write!(f, "sh {src}, {}({base})", offset as i32), write!(f, "lhu {dest}, {}({base})", offset.as_i32())
Inst::Sw { offset, src, base } => write!(f, "sw {src}, {}({base})", offset as i32), }
Inst::Lw { offset, dest, base } => write!(f, "lw {dest}, {}({base})", offset.as_i32()),
Inst::Sb { offset, src, base } => write!(f, "sb {src}, {}({base})", offset.as_i32()),
Inst::Sh { offset, src, base } => write!(f, "sh {src}, {}({base})", offset.as_i32()),
Inst::Sw { offset, src, base } => write!(f, "sw {src}, {}({base})", offset.as_i32()),
Inst::Addi { imm, dest, src1 } => { Inst::Addi { imm, dest, src1 } => {
if dest.0 == 0 && src1.0 == 0 && imm == 0 { if dest.0 == 0 && src1.0 == 0 && imm.as_u32() == 0 {
write!(f, "nop") write!(f, "nop")
} else if src1.0 == 0 { } else if src1.0 == 0 {
write!(f, "li {dest}, {}", imm as i32) write!(f, "li {dest}, {}", imm.as_i32())
} else if imm == 0 { } else if imm.as_u32() == 0 {
write!(f, "mv {dest}, {src1}") write!(f, "mv {dest}, {src1}")
} else { } else {
write!(f, "addi {dest}, {src1}, {}", imm as i32) write!(f, "addi {dest}, {src1}, {}", imm.as_i32())
} }
} }
Inst::Slti { Inst::Slti {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "slti {dest}, {rs1}, {}", imm as i32), } => write!(f, "slti {dest}, {rs1}, {}", imm.as_i32()),
Inst::Sltiu { Inst::Sltiu {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "sltiu {dest}, {rs1}, {}", imm as i32), } => write!(f, "sltiu {dest}, {rs1}, {}", imm.as_i32()),
Inst::Andi { Inst::Andi {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "andi {dest}, {rs1}, {}", imm as i32), } => write!(f, "andi {dest}, {rs1}, {}", imm.as_i32()),
Inst::Ori { Inst::Ori {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "ori {dest}, {rs1}, {}", imm as i32), } => write!(f, "ori {dest}, {rs1}, {}", imm.as_i32()),
Inst::Xori { Inst::Xori {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "xori {dest}, {rs1}, {}", imm as i32), } => write!(f, "xori {dest}, {rs1}, {}", imm.as_i32()),
Inst::Slli { Inst::Slli {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "slli {dest}, {rs1}, {}", imm as i32), } => write!(f, "slli {dest}, {rs1}, {}", imm.as_i32()),
Inst::Srli { Inst::Srli {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "srli {dest}, {rs1}, {}", imm as i32), } => write!(f, "srli {dest}, {rs1}, {}", imm.as_i32()),
Inst::Srai { Inst::Srai {
imm, imm,
dest, dest,
src1: rs1, src1: rs1,
} => write!(f, "srai {dest}, {rs1}, {}", imm as i32), } => write!(f, "srai {dest}, {rs1}, {}", imm.as_i32()),
Inst::Add { dest, src1, src2 } => { Inst::Add { dest, src1, src2 } => {
write!(f, "add {dest}, {src1}, {src2}") write!(f, "add {dest}, {src1}, {src2}")
} }
@ -604,15 +676,15 @@ 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)]) -> u32 { fn immediate_u(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
let mut imm = 0; let mut imm = 0;
for (from, to) in mappings { for (from, to) in mappings {
let value = self.extract(from.clone()); let value = self.extract(from.clone());
imm |= value << to; imm |= value << to;
} }
imm Imm::new_u32(imm)
} }
fn immediate_s(self, mappings: &[(RangeInclusive<u32>, u32)]) -> u32 { 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;
for (from, to) in mappings { for (from, to) in mappings {
@ -621,7 +693,7 @@ impl InstCode {
let this_size = from.end() - from.start() + 1; let this_size = from.end() - from.start() + 1;
size = size.max(*to + this_size); size = size.max(*to + this_size);
} }
sign_extend(imm, size) Imm::new_i32(sign_extend(imm, size) as i32)
} }
fn opcode(self) -> u32 { fn opcode(self) -> u32 {
@ -645,19 +717,19 @@ impl InstCode {
fn rd(self) -> Reg { fn rd(self) -> Reg {
Reg(self.extract(7..=11) as u8) Reg(self.extract(7..=11) as u8)
} }
fn imm_i(self) -> u32 { fn imm_i(self) -> Imm {
self.immediate_s(&[(20..=31, 0)]) self.immediate_s(&[(20..=31, 0)])
} }
fn imm_s(self) -> u32 { fn imm_s(self) -> Imm {
self.immediate_s(&[(25..=31, 5), (7..=11, 0)]) self.immediate_s(&[(25..=31, 5), (7..=11, 0)])
} }
fn imm_b(self) -> u32 { fn imm_b(self) -> Imm {
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) -> u32 { fn imm_u(self) -> Imm {
self.immediate_u(&[(12..=31, 12)]) self.immediate_u(&[(12..=31, 12)])
} }
fn imm_j(self) -> u32 { 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)])
} }
} }
@ -670,15 +742,15 @@ impl InstCodeC {
let end_span = u16::BITS - (range.end() + 1); let end_span = u16::BITS - (range.end() + 1);
((self.0 << (end_span)) >> (end_span + range.start())) as u32 ((self.0 << (end_span)) >> (end_span + range.start())) as u32
} }
fn immediate_u(self, mappings: &[(RangeInclusive<u32>, u32)]) -> u32 { fn immediate_u(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
let mut imm = 0; let mut imm = 0;
for (from, to) in mappings { for (from, to) in mappings {
let value = self.extract(from.clone()); let value = self.extract(from.clone());
imm |= value << to; imm |= value << to;
} }
imm Imm::new_u32(imm)
} }
fn immediate_s(self, mappings: &[(RangeInclusive<u32>, u32)]) -> u32 { 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;
for (from, to) in mappings { for (from, to) in mappings {
@ -688,7 +760,7 @@ impl InstCodeC {
let this_size = from.end() - from.start() + 1; let this_size = from.end() - from.start() + 1;
size = size.max(*to + this_size); size = size.max(*to + this_size);
} }
sign_extend(imm, size) Imm::new_i32(sign_extend(imm, size) as i32)
} }
fn quadrant(self) -> u16 { fn quadrant(self) -> u16 {
self.0 & 0b11 self.0 & 0b11
@ -790,7 +862,7 @@ impl Inst {
/// ```rust /// ```rust
/// // Compressed addi sp, sp, -0x20 /// // Compressed addi sp, sp, -0x20
/// let x = 0x1101_u16; /// let x = 0x1101_u16;
/// let expected = rvdc::Inst::Addi { imm: (-0x20_i32) as u32, 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).unwrap(); /// let inst = rvdc::Inst::decode_compressed(x).unwrap();
/// assert_eq!(inst, expected); /// assert_eq!(inst, expected);
@ -807,7 +879,7 @@ impl Inst {
0b000 => { 0b000 => {
let imm = let imm =
code.immediate_u(&[(5..=5, 3), (6..=6, 2), (7..=10, 6), (11..=12, 4)]); code.immediate_u(&[(5..=5, 3), (6..=6, 2), (7..=10, 6), (11..=12, 4)]);
if imm == 0 { if imm.as_u32() == 0 {
return Err(decode_error(code, "uimm=0 for C.ADDISPN is reserved")); return Err(decode_error(code, "uimm=0 for C.ADDISPN is reserved"));
} }
Inst::Addi { Inst::Addi {
@ -959,7 +1031,7 @@ impl Inst {
// C.LUI -> lui \rd, \imm // C.LUI -> lui \rd, \imm
_ => { _ => {
let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]); let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]);
if uimm == 0 { if uimm.as_u32() == 0 {
return Err(decode_error(code, "imm")); return Err(decode_error(code, "imm"));
} }
Inst::Lui { Inst::Lui {
@ -1032,7 +1104,7 @@ impl Inst {
return Err(decode_error(code, "rs1")); return Err(decode_error(code, "rs1"));
} }
Inst::Jalr { Inst::Jalr {
offset: 0, offset: Imm::ZERO,
base: rd_rs1, base: rd_rs1,
dest: Reg::ZERO, dest: Reg::ZERO,
} }
@ -1047,7 +1119,7 @@ impl Inst {
(1, 0, 0) => Inst::Ebreak, (1, 0, 0) => Inst::Ebreak,
// C.JALR -> jalr ra, 0(\rs1) // C.JALR -> jalr ra, 0(\rs1)
(1, _, 0) if rd_rs1.0 != 0 => Inst::Jalr { (1, _, 0) if rd_rs1.0 != 0 => Inst::Jalr {
offset: 0, offset: Imm::ZERO,
base: rd_rs1, base: rd_rs1,
dest: Reg::RA, dest: Reg::RA,
}, },
@ -1227,12 +1299,12 @@ impl Inst {
} }
0b101 => match code.funct7() { 0b101 => match code.funct7() {
0b0000000 => Inst::Srli { 0b0000000 => Inst::Srli {
imm: 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 { 0b0100000 => Inst::Srai {
imm: code.rs2_imm(), imm: Imm::new_u32(code.rs2_imm()),
dest: code.rd(), dest: code.rd(),
src1: code.rs1(), src1: code.rs1(),
}, },
@ -1309,7 +1381,7 @@ impl Inst {
if code.rs1().0 != 0 { if code.rs1().0 != 0 {
return Err(decode_error(code, "rs1")); return Err(decode_error(code, "rs1"));
} }
match code.imm_i() { match code.imm_i().as_u32() {
0b000000000000 => Inst::Ecall, 0b000000000000 => Inst::Ecall,
0b000000000001 => Inst::Ebreak, 0b000000000001 => Inst::Ebreak,
_ => return Err(decode_error(code, "imm")), _ => return Err(decode_error(code, "imm")),
@ -1390,6 +1462,7 @@ mod tests {
use crate::Fence; use crate::Fence;
use crate::FenceSet; use crate::FenceSet;
use crate::Imm;
use crate::Inst; use crate::Inst;
use crate::Reg; use crate::Reg;
@ -1467,7 +1540,7 @@ mod tests {
dest: Reg::ZERO, .. dest: Reg::ZERO, ..
} => false, } => false,
// This does roundtrip, but only through C.MV, not C.ADDI // This does roundtrip, but only through C.MV, not C.ADDI
Inst::Addi { imm: 0, .. } => false, Inst::Addi { imm: Imm::ZERO, .. } => false,
// This does rountrip, but not through C.ADDI // This does rountrip, but not through C.ADDI
Inst::Addi { Inst::Addi {
dest: Reg::SP, dest: Reg::SP,
@ -1478,15 +1551,15 @@ mod tests {
Inst::Slli { Inst::Slli {
dest: Reg::ZERO, .. dest: Reg::ZERO, ..
} }
| Inst::Slli { imm: 0, .. } | Inst::Slli { imm: Imm::ZERO, .. }
| Inst::Srli { | Inst::Srli {
dest: Reg::ZERO, .. dest: Reg::ZERO, ..
} }
| Inst::Srli { imm: 0, .. } | Inst::Srli { imm: Imm::ZERO, .. }
| Inst::Srai { | Inst::Srai {
dest: Reg::ZERO, .. dest: Reg::ZERO, ..
} }
| Inst::Srai { imm: 0, .. } => false, | Inst::Srai { imm: Imm::ZERO, .. } => false,
// HINT // HINT
Inst::Lui { Inst::Lui {
dest: Reg::ZERO, .. dest: Reg::ZERO, ..

View file

@ -1,4 +1,4 @@
use rvdc::{AmoOp, DecodeError, Inst, IsCompressed, Reg}; use rvdc::{AmoOp, DecodeError, Imm, Inst, IsCompressed, Reg};
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
io::Write, io::Write,
@ -66,6 +66,7 @@ impl Memory {
#[derive(Debug)] #[derive(Debug)]
pub enum Status { pub enum Status {
Trap(&'static str), Trap(&'static str),
UnsupportedInst(rvdc::Inst),
IllegalInstruction(DecodeError), IllegalInstruction(DecodeError),
InvalidMemoryAccess(usize), InvalidMemoryAccess(usize),
UnalignedPc(u32), UnalignedPc(u32),
@ -237,17 +238,17 @@ impl<XLEN: XLen> Emulator<XLEN> {
let mut jumped = false; let mut jumped = false;
match inst { match inst {
Inst::Lui { uimm, dest } => self[dest] = XLEN::from_32_z(uimm), Inst::Lui { uimm, dest } => self[dest] = XLEN::from_imm(uimm),
Inst::Auipc { uimm, dest } => self[dest] = self.pc.add(XLEN::from_32_z(uimm)), Inst::Auipc { uimm, dest } => self[dest] = self.pc.add(XLEN::from_imm(uimm)),
Inst::Jal { offset, dest } => { Inst::Jal { offset, dest } => {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self[dest] = next_pc; self[dest] = next_pc;
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
Inst::Jalr { offset, base, dest } => { Inst::Jalr { offset, base, dest } => {
let target = self[base] let target = self[base]
.add(XLEN::from_32_s(offset)) .add(XLEN::from_imm(offset))
.and(XLEN::from_32_s(!1)); .and(XLEN::from_32_s(!1));
self[dest] = next_pc; self[dest] = next_pc;
self.set_pc(target)?; self.set_pc(target)?;
@ -256,7 +257,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Beq { offset, src1, src2 } => { Inst::Beq { offset, src1, src2 } => {
let take = self[src1] == self[src2]; let take = self[src1] == self[src2];
if take { if take {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
@ -264,7 +265,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Bne { offset, src1, src2 } => { Inst::Bne { offset, src1, src2 } => {
let take = self[src1] != self[src2]; let take = self[src1] != self[src2];
if take { if take {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
@ -272,7 +273,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Blt { offset, src1, src2 } => { Inst::Blt { offset, src1, src2 } => {
let take = self[src1].signed_lt(self[src2]); let take = self[src1].signed_lt(self[src2]);
if take { if take {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
@ -280,7 +281,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Bge { offset, src1, src2 } => { Inst::Bge { offset, src1, src2 } => {
let take = self[src1].signed_ge(self[src2]); let take = self[src1].signed_ge(self[src2]);
if take { if take {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
@ -288,7 +289,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Bltu { offset, src1, src2 } => { Inst::Bltu { offset, src1, src2 } => {
let take = self[src1].unsigned_lt(self[src2]); let take = self[src1].unsigned_lt(self[src2]);
if take { if take {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
@ -296,66 +297,66 @@ impl<XLEN: XLen> Emulator<XLEN> {
Inst::Bgeu { offset, src1, src2 } => { Inst::Bgeu { offset, src1, src2 } => {
let take = self[src1].unsigned_ge(self[src2]); let take = self[src1].unsigned_ge(self[src2]);
if take { if take {
let target = self.pc.add(XLEN::from_32_s(offset)); let target = self.pc.add(XLEN::from_imm(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
} }
Inst::Lb { offset, dest, base } => { Inst::Lb { offset, dest, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self[dest] = XLEN::from_8_s(self.mem.load_u8(addr)?); self[dest] = XLEN::from_8_s(self.mem.load_u8(addr)?);
} }
Inst::Lbu { offset, dest, base } => { Inst::Lbu { offset, dest, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self[dest] = XLEN::from_8_z(self.mem.load_u8(addr)?); self[dest] = XLEN::from_8_z(self.mem.load_u8(addr)?);
} }
Inst::Lh { offset, dest, base } => { Inst::Lh { offset, dest, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self[dest] = XLEN::from_16_s(self.mem.load_u16(addr)?); self[dest] = XLEN::from_16_s(self.mem.load_u16(addr)?);
} }
Inst::Lhu { offset, dest, base } => { Inst::Lhu { offset, dest, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self[dest] = XLEN::from_16_z(self.mem.load_u16(addr)?); self[dest] = XLEN::from_16_z(self.mem.load_u16(addr)?);
} }
Inst::Lw { offset, dest, base } => { Inst::Lw { offset, dest, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self[dest] = XLEN::from_32_s(self.mem.load_u32(addr)?); self[dest] = XLEN::from_32_s(self.mem.load_u32(addr)?);
} }
Inst::Sb { offset, src, base } => { Inst::Sb { offset, src, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self.mem.store_u8(addr, self[src].truncate8())?; self.mem.store_u8(addr, self[src].truncate8())?;
} }
Inst::Sh { offset, src, base } => { Inst::Sh { offset, src, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self.mem.store_u16(addr, self[src].truncate16())?; self.mem.store_u16(addr, self[src].truncate16())?;
} }
Inst::Sw { offset, src, base } => { Inst::Sw { offset, src, base } => {
let addr = self[base].add(XLEN::from_32_s(offset)); let addr = self[base].add(XLEN::from_imm(offset));
self.mem.store_u32(addr, self[src].truncate32())?; self.mem.store_u32(addr, self[src].truncate32())?;
} }
Inst::Addi { imm, dest, src1 } => { Inst::Addi { imm, dest, src1 } => {
self[dest] = self[src1].add(XLEN::from_32_s(imm)); self[dest] = self[src1].add(XLEN::from_imm(imm));
} }
Inst::Slti { imm, dest, src1 } => { Inst::Slti { imm, dest, src1 } => {
let result = self[src1].signed_lt(XLEN::from_32_s(imm)); let result = self[src1].signed_lt(XLEN::from_imm(imm));
self[dest] = XLEN::from_bool(result); self[dest] = XLEN::from_bool(result);
} }
Inst::Sltiu { imm, dest, src1 } => { Inst::Sltiu { imm, dest, src1 } => {
let result = self[src1].unsigned_lt(XLEN::from_32_s(imm)); let result = self[src1].unsigned_lt(XLEN::from_imm(imm));
self[dest] = XLEN::from_bool(result); self[dest] = XLEN::from_bool(result);
} }
Inst::Andi { imm, dest, src1 } => { Inst::Andi { imm, dest, src1 } => {
self[dest] = self[src1].and(XLEN::from_32_s(imm)); self[dest] = self[src1].and(XLEN::from_imm(imm));
} }
Inst::Ori { imm, dest, src1 } => { Inst::Ori { imm, dest, src1 } => {
self[dest] = self[src1].or(XLEN::from_32_s(imm)); self[dest] = self[src1].or(XLEN::from_imm(imm));
} }
Inst::Xori { imm, dest, src1 } => { Inst::Xori { imm, dest, src1 } => {
self[dest] = self[src1].xor(XLEN::from_32_s(imm)); self[dest] = self[src1].xor(XLEN::from_imm(imm));
} }
Inst::Slli { imm, dest, src1 } => self[dest] = self[src1].shl(imm), Inst::Slli { imm, dest, src1 } => self[dest] = self[src1].shl(imm.as_u32()),
Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].unsigned_shr(imm), 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), 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::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]), Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]),
Inst::Sll { dest, src1, src2 } => self[dest] = self[src1].shl(self[src2].truncate32()), Inst::Sll { dest, src1, src2 } => self[dest] = self[src1].shl(self[src2].truncate32()),
@ -473,6 +474,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
} }
self.reservation_set = None; self.reservation_set = None;
} }
_ => return Err(Status::UnsupportedInst(inst)),
} }
if !jumped { if !jumped {
@ -666,6 +668,7 @@ pub trait XLen: Copy + PartialEq + Eq {
} }
fn from_32_z(v: u32) -> Self; fn from_32_z(v: u32) -> Self;
fn from_32_s(v: u32) -> Self; fn from_32_s(v: u32) -> Self;
fn from_imm(v: Imm) -> Self;
fn truncate8(self) -> u8 { fn truncate8(self) -> u8 {
self.truncate32() as u8 self.truncate32() as u8
@ -715,6 +718,9 @@ impl XLen for u32 {
fn from_32_z(v: u32) -> Self { fn from_32_z(v: u32) -> Self {
v v
} }
fn from_imm(v: Imm) -> Self {
v.as_u32()
}
fn as_usize(self) -> usize { fn as_usize(self) -> usize {
self as usize self as usize
} }