diff --git a/src/riscv/lib/src/machine_state.rs b/src/riscv/lib/src/machine_state.rs index ca13b98df9e55b1bb343986ae3cde90920e85fe5..4e93f29090d42433691f1c604fc9a02f3d38767c 100644 --- a/src/riscv/lib/src/machine_state.rs +++ b/src/riscv/lib/src/machine_state.rs @@ -26,7 +26,7 @@ use crate::{ csregisters::CSRegister, hart_state::{HartState, HartStateLayout}, }, - parser::{instruction::Instr, parse}, + parser::{instruction::Instr, is_compressed}, program::Program, range_utils::{bound_saturating_sub, less_than_bound, unwrap_bound}, state_backend::{self as backend}, @@ -354,32 +354,34 @@ impl InstructionCache { } } - /// Cache an instruction at the physical address. + /// Cache a compressed instruction at the physical address, returning the parsed instruction. + #[inline] + pub fn cache_compressed(&mut self, phys_addr: Address, raw: u16) -> Instr + where + M: ManagerReadWrite, + { + let instr = parse_compressed_instruction(raw); + + self.cache_inner(phys_addr, Unparsed(raw as u32), instr); + + instr + } + + /// Cache an uncompressed instruction at the physical address, returning the parsed instruction. /// - /// Both the parsed instruction and raw bytes should be supplied. + /// If the instruction crossed page boundaries, it will still be returned, but not cached. #[inline(never)] - pub fn cache_instr(&mut self, phys_addr: Address, raw: u32, instr: Instr) + pub fn cache_uncompressed(&mut self, phys_addr: Address, raw: u32) -> Instr where M: ManagerReadWrite, { - let unparsed = Unparsed(raw); - debug_assert!( - (instr, unparsed) == unparsed.into(), - "The raw bytes {raw:#x} do not match the given instruction {instr:?}" - ); + let instr = parse_uncompressed_instruction(raw); - if !cacheable(phys_addr, raw) { - return; + if cacheable_uncompressed(phys_addr) { + self.cache_inner(phys_addr, Unparsed(raw), instr); } + instr + } + + #[inline] + fn cache_inner(&mut self, phys_addr: Address, unparsed: Unparsed, instr: Instr) + where + M: ManagerReadWrite, + { let fence_counter = self.fence_counter.read(); let cached = ICL::entry_mut(&mut self.entries, phys_addr); @@ -386,10 +403,10 @@ impl InstructionCache { /// An uncompressed instruction is not cacheable, if it crosses /// page boundaries. #[inline] -const fn cacheable(phys_addr: Address, raw: u32) -> bool { +const fn cacheable_uncompressed(phys_addr: Address) -> bool { const END_OF_PAGE: Address = PAGE_SIZE - 2; - is_compressed(raw as u16) || phys_addr % PAGE_SIZE != END_OF_PAGE + phys_addr % PAGE_SIZE != END_OF_PAGE } #[cfg(test)] @@ -440,21 +457,16 @@ mod tests { let compressed = Instr::CLi(CIBTypeArgs { rd_rs1: a0, imm: 1 }); let uncompressed_bytes = 0x00533423; - let uncompressed = Instr::Sd(SBTypeArgs { - rs1: t1, - rs2: t0, - imm: 8, - }); let phys_addr = PAGE_SIZE - 2; // Compressed instruction can be cached - state.cache_instr(phys_addr, compressed_bytes, compressed); + state.cache_compressed(phys_addr, compressed_bytes); assert_eq!(Some(compressed), state.fetch_instr(phys_addr)); // Caching an uncompressed instruction across page boundary should fail - the old // uncompressed instruction is still there. - state.cache_instr(phys_addr, uncompressed_bytes, uncompressed); + state.cache_uncompressed(phys_addr, uncompressed_bytes); assert_eq!(Some(compressed), state.fetch_instr(phys_addr)); }); @@ -486,10 +498,10 @@ mod tests { TestInstructionCacheLayout ); - state.cache_instr(phys_addr_compressed, compressed_bytes, compressed); + state.cache_compressed(phys_addr_compressed, compressed_bytes); assert_eq!(Some(compressed), state.fetch_instr(phys_addr_compressed)); - state.cache_instr(phys_addr_uncompressed, uncompressed_bytes, uncompressed); + state.cache_uncompressed(phys_addr_uncompressed, uncompressed_bytes); assert_eq!( Some(uncompressed), state.fetch_instr(phys_addr_uncompressed) diff --git a/src/riscv/lib/src/parser.rs b/src/riscv/lib/src/parser.rs index a3d83deafd0a3bbcc9c4291b23a085926a78c6b2..6606ab4b6909583ed9f6fe19ba2a9815c63347cb 100644 --- a/src/riscv/lib/src/parser.rs +++ b/src/riscv/lib/src/parser.rs @@ -482,8 +482,9 @@ const RS2_3_U5: u5 = u5::new(0b11); const FM_0: u32 = 0b0; const FM_8: u32 = 0b1000; +/// Parse an uncompressed instruction from a u32. #[inline] -const fn parse_uncompressed_instruction(instr: u32) -> Instr { +pub const fn parse_uncompressed_instruction(instr: u32) -> Instr { use Instr::*; match opcode(instr) { // R-type instructions @@ -1237,8 +1238,9 @@ const fn parse_compressed_instruction_inner(instr: u16) -> Instr { } } +/// Parse a compressed instruction from a u16. #[inline(always)] -fn parse_compressed_instruction(bytes: u16) -> Instr { +pub fn parse_compressed_instruction(bytes: u16) -> Instr { COMPRESSED_JUMP_TABLE[bytes as usize] }