1#[cfg(all(feature = "alloc", target_pointer_width = "64"))]
11mod eager_instruction_fetcher;
12#[cfg(test)]
13mod tests;
14
15#[cfg(all(feature = "alloc", target_pointer_width = "64"))]
16pub use crate::basic::eager_instruction_fetcher::{
17 BasicEagerDecodeError, BasicEagerInstructionFetcher, BasicEagerInstructions,
18};
19use crate::zawrs::WrsHandler;
20use crate::zifencei::FenceIHandler;
21use crate::{
22 Address, BasicInt, ExecutableInstruction, ExecutionError, ExecutionResult,
23 FetchInstructionResult, InstructionFetcher, InstructionReg, PackedAddress, ProgramCounter,
24 RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands, SystemInstructionHandler, VirtualMemory,
25 VirtualMemoryError,
26};
27use ab_riscv_primitives::prelude::*;
28#[cfg(feature = "alloc")]
29use alloc::boxed::Box;
30use core::hint::{assert_unchecked, cold_path};
31use core::ops::ControlFlow;
32use replace_with::replace_with_or_abort_and_return;
33
34#[derive(Debug, Clone, Copy)]
36#[derive_const(PartialEq, Eq)]
37#[repr(transparent)]
38pub struct RegisterOffset(u8);
39
40const impl From<RegisterOffset> for usize {
41 #[inline(always)]
42 fn from(offset: RegisterOffset) -> Self {
43 unsafe {
46 assert_unchecked(usize::from(offset.0) < RegisterOffset::COUNT);
47 }
48 Self::from(offset.0)
49 }
50}
51
52impl RegisterOffset {
53 pub type const COUNT: usize = 32;
55
56 #[inline(always)]
60 pub const fn new(offset: u8) -> Option<Self> {
61 if usize::from(offset) < Self::COUNT {
62 Some(Self(offset))
63 } else {
64 None
65 }
66 }
67}
68
69pub const trait BasicRegister
71where
72 Self: [const] Register,
73{
74 fn offset(self) -> RegisterOffset;
79}
80
81const impl<Type> BasicRegister for EReg<Type>
82where
83 Self: [const] Register,
84{
85 #[inline(always)]
86 fn offset(self) -> RegisterOffset {
87 let offset = match self {
88 Self::Zero => 0,
89 Self::Ra => 1,
90 Self::Sp => 2,
91 Self::Gp => 3,
92 Self::Tp => 4,
93 Self::T0 => 5,
94 Self::T1 => 6,
95 Self::T2 => 7,
96 Self::S0 => 8,
97 Self::S1 => 9,
98 Self::A0 => 10,
99 Self::A1 => 11,
100 Self::A2 => 12,
101 Self::A3 => 13,
102 Self::A4 => 14,
103 Self::A5 => 15,
104 };
105 RegisterOffset::new(offset).expect("All offsets above are below 16; qed")
106 }
107}
108
109const impl<Type> BasicRegister for Reg<Type>
110where
111 Self: [const] Register,
112{
113 #[inline(always)]
114 fn offset(self) -> RegisterOffset {
115 let offset = match self {
116 Self::Zero => 0,
117 Self::Ra => 1,
118 Self::Sp => 2,
119 Self::Gp => 3,
120 Self::Tp => 4,
121 Self::T0 => 5,
122 Self::T1 => 6,
123 Self::T2 => 7,
124 Self::S0 => 8,
125 Self::S1 => 9,
126 Self::A0 => 10,
127 Self::A1 => 11,
128 Self::A2 => 12,
129 Self::A3 => 13,
130 Self::A4 => 14,
131 Self::A5 => 15,
132 Self::A6 => 16,
133 Self::A7 => 17,
134 Self::S2 => 18,
135 Self::S3 => 19,
136 Self::S4 => 20,
137 Self::S5 => 21,
138 Self::S6 => 22,
139 Self::S7 => 23,
140 Self::S8 => 24,
141 Self::S9 => 25,
142 Self::S10 => 26,
143 Self::S11 => 27,
144 Self::T3 => 28,
145 Self::T4 => 29,
146 Self::T5 => 30,
147 Self::T6 => 31,
148 };
149 RegisterOffset::new(offset).expect("All offsets above are below 32; qed")
150 }
151}
152
153#[derive(Debug, Clone, Copy)]
162#[repr(align(16))]
163pub struct BasicRegisters<Reg, const ZEROSTORE: bool = false>
164where
165 Reg: BasicRegister,
166{
167 regs: [Reg::Type; RegisterOffset::COUNT],
168}
169
170impl<Reg, const ZEROSTORE: bool> Default for BasicRegisters<Reg, ZEROSTORE>
171where
172 Reg: BasicRegister,
173{
174 #[inline(always)]
175 fn default() -> Self {
176 Self {
177 regs: [Reg::Type::default(); _],
178 }
179 }
180}
181
182const impl<Reg, const ZEROSTORE: bool> RegisterFile<Reg> for BasicRegisters<Reg, ZEROSTORE>
183where
184 Reg: [const] BasicRegister,
185{
186 #[inline(always)]
187 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
188 fn read(&self, reg: Reg) -> Reg::Type {
189 if reg == Reg::ZERO && !ZEROSTORE {
190 return Reg::Type::default();
192 }
193
194 *self
195 .regs
196 .get(usize::from(reg.offset()))
197 .expect("There is a register for every offset; qed")
198 }
199
200 #[inline(always)]
201 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
202 fn write(&mut self, reg: Reg, value: Reg::Type) {
203 *self
204 .regs
205 .get_mut(usize::from(reg.offset()))
206 .expect("There is a register for every offset; qed") = value;
207 if ZEROSTORE {
208 *self
209 .regs
210 .get_mut(usize::from(Reg::ZERO.offset()))
211 .expect("There is a register for every offset; qed") = Reg::Type::default();
212 }
213 }
214}
215
216#[derive(Debug)]
221pub struct BasicInterpreterState<Regs, Env, Memory, IF> {
222 pub regs: Regs,
224 pub env: Env,
230 pub memory: Memory,
232 pub instruction_fetcher: IF,
234}
235
236impl<Regs, Env, Memory, IF> BasicInterpreterState<Regs, Env, Memory, IF> {
237 #[rustc_align(64)]
255 pub fn execute<I>(&mut self) -> Result<(), ExecutionError<Address<I>>>
256 where
257 Regs: RegisterFile<InstructionReg<I>>,
258 I: ExecutableInstruction<Regs, Env, Memory, IF>,
259 Memory: VirtualMemory,
260 IF: InstructionFetcher<I, Memory> + ProgramCounter<Address<I>, Memory>,
261 {
262 replace_with_or_abort_and_return(
263 &mut self.instruction_fetcher,
264 #[inline(always)]
265 |mut instruction_fetcher| {
266 let pc = instruction_fetcher.get_pc();
268 match instruction_fetcher.set_pc(&self.memory, pc) {
269 Ok(ControlFlow::Continue(())) => {}
270 Ok(ControlFlow::Break(())) => {
271 cold_path();
272 return (Ok(()), instruction_fetcher);
273 }
274 Err(error) => {
275 cold_path();
276 return (Err(error), instruction_fetcher);
277 }
278 }
279
280 loop {
281 let fetched = unsafe { instruction_fetcher.fetch_instruction(&self.memory) };
285 let instruction = match fetched {
286 FetchInstructionResult::Instruction(instruction) => instruction,
287 FetchInstructionResult::Continue => {
288 cold_path();
289 continue;
290 }
291 FetchInstructionResult::Break => {
292 cold_path();
293 break;
294 }
295 FetchInstructionResult::Err(error) => {
296 cold_path();
297 return (Err(error), instruction_fetcher);
298 }
299 };
300
301 let Rs1Rs2Operands { rs1, rs2 } = instruction.get_rs1_rs2_operands();
302 let rs1rs2_values = Rs1Rs2OperandValues {
303 rs1_value: self.regs.read(rs1),
304 rs2_value: self.regs.read(rs2),
305 };
306
307 let outcome = instruction.execute(
308 rs1rs2_values,
309 &mut self.regs,
310 &mut self.env,
311 &mut self.memory,
312 &instruction_fetcher,
313 );
314
315 let control_flow =
316 match outcome {
317 ExecutionResult::Continue { rd, value } => {
318 self.regs.write(rd, value);
319 continue;
320 }
321 ExecutionResult::ContinueNoWrite => {
322 continue;
323 }
324 ExecutionResult::Branch { offset } => instruction_fetcher
325 .set_pc_relative(&self.memory, instruction.size(), offset),
326 ExecutionResult::Jump { target } => {
327 instruction_fetcher.set_pc(&self.memory, target)
328 }
329 ExecutionResult::Break => {
330 cold_path();
331 break;
332 }
333 ExecutionResult::Err(error) => {
334 cold_path();
335 return (Err(error), instruction_fetcher);
336 }
337 };
338
339 match control_flow {
340 Ok(ControlFlow::Continue(())) => {}
341 Ok(ControlFlow::Break(())) => {
342 cold_path();
343 break;
344 }
345 Err(error) => {
346 cold_path();
347 return (Err(error), instruction_fetcher);
348 }
349 }
350 }
351
352 (Ok(()), instruction_fetcher)
353 },
354 )
355 }
356}
357
358const NON_WRAPPING_MEMORY<const BASE_ADDR: u64, const SIZE: usize>: usize = {
360 assert!(
361 BASE_ADDR.checked_add(SIZE as u64).is_some(),
362 "`BasicMemory` must not wrap around the end of the address space, or an address below its \
363 base address could wrap into it"
364 );
365 0
366};
367
368#[derive(Debug, Copy, Clone)]
387#[repr(align(16))]
388pub struct BasicMemory<const BASE_ADDR: u64, const SIZE: usize> {
389 data: [u8; SIZE],
390}
391
392const impl<const BASE_ADDR: u64, const SIZE: usize> VirtualMemory for BasicMemory<BASE_ADDR, SIZE>
393where
394 [(); NON_WRAPPING_MEMORY::<BASE_ADDR, SIZE>]:,
395{
396 #[inline(always)]
397 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
398 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
399 where
400 T: BasicInt,
401 {
402 let offset = Self::offset(address);
403
404 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
405 cold_path();
406 return Err(VirtualMemoryError::OutOfBoundsRead { address });
407 }
408
409 unsafe {
411 Ok(self
412 .data
413 .as_ptr()
414 .cast::<T>()
415 .byte_add(offset as usize)
416 .read_unaligned())
417 }
418 }
419
420 #[inline(always)]
421 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
422 unsafe fn read_unchecked<T>(&self, address: u64) -> T
423 where
424 T: BasicInt,
425 {
426 unsafe {
428 let offset = address.unchecked_sub(BASE_ADDR) as usize;
429 self.data
430 .as_ptr()
431 .cast::<T>()
432 .byte_add(offset)
433 .read_unaligned()
434 }
435 }
436
437 #[inline(always)]
438 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
439 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
440 let result = self.read_slice_inner(address, len);
441 if let Ok(bytes) = result {
442 unsafe {
446 assert_unchecked(bytes.len() == len as usize);
447 }
448 }
449 result
450 }
451
452 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
453 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
454 let offset = Self::offset(address);
455
456 if offset > self.data.len() as u64 {
457 cold_path();
458 return &[];
459 }
460
461 let remaining = self.data.get(offset as usize..).unwrap_or_default();
462 remaining.get(..len as usize).unwrap_or(remaining)
463 }
464
465 #[inline(always)]
466 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
467 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
468 where
469 T: BasicInt,
470 {
471 let offset = Self::offset(address);
472
473 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
474 cold_path();
475 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
476 }
477
478 unsafe {
480 self.data
481 .as_mut_ptr()
482 .cast::<T>()
483 .byte_add(offset as usize)
484 .write_unaligned(value);
485 }
486
487 Ok(())
488 }
489
490 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
491 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
492 let offset = Self::offset(address);
493
494 if offset > self.data.len() as u64 {
495 cold_path();
496 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
497 }
498
499 let len = data.len();
500 let Some(target_data) = self
501 .data
502 .get_mut(offset as usize..)
503 .and_then(const |data| data.get_mut(..len))
504 else {
505 cold_path();
506 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
507 };
508
509 target_data.copy_from_slice(data);
510
511 Ok(())
512 }
513}
514
515impl<const BASE_ADDR: u64, const SIZE: usize> Default for BasicMemory<BASE_ADDR, SIZE>
516where
517 [(); NON_WRAPPING_MEMORY::<BASE_ADDR, SIZE>]:,
518{
519 #[inline(always)]
520 fn default() -> Self {
521 Self { data: [0; _] }
522 }
523}
524
525impl<const BASE_ADDR: u64, const SIZE: usize> BasicMemory<BASE_ADDR, SIZE>
526where
527 [(); NON_WRAPPING_MEMORY::<BASE_ADDR, SIZE>]:,
528{
529 #[inline(always)]
535 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
536 const fn offset(address: u64) -> u64 {
537 address.wrapping_sub(BASE_ADDR)
538 }
539
540 #[inline]
542 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
543 const fn read_slice_inner(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
544 let offset = Self::offset(address);
545
546 if offset > self.data.len() as u64 {
547 cold_path();
548 return Err(VirtualMemoryError::OutOfBoundsRead { address });
549 }
550
551 self.data
552 .get(offset as usize..)
553 .and_then(const |data| data.get(..len as usize))
554 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })
555 }
556
557 #[cfg(feature = "alloc")]
558 pub fn new_boxed() -> Box<Self> {
559 unsafe { Box::<Self>::new_zeroed().assume_init() }
561 }
562
563 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
567 pub const fn get_mut_bytes(
568 &mut self,
569 address: u64,
570 size: usize,
571 ) -> Result<&mut [u8], VirtualMemoryError> {
572 let Some(offset) = address.checked_sub(BASE_ADDR) else {
573 cold_path();
574 return Err(VirtualMemoryError::OutOfBoundsRead { address });
575 };
576 let offset = offset as usize;
577
578 let Some(slice) = self
579 .data
580 .get_mut(offset..)
581 .and_then(const |data| data.get_mut(..size))
582 else {
583 cold_path();
584 return Err(VirtualMemoryError::OutOfBoundsRead { address });
585 };
586
587 Ok(slice)
588 }
589}
590
591#[derive(Debug, Copy, Clone)]
603pub struct BasicInstructionFetcher<I>
604where
605 I: Instruction,
606{
607 return_trap_address: Address<I>,
608 pc: Address<I>,
609}
610
611const impl<I, Memory> ProgramCounter<Address<I>, Memory> for BasicInstructionFetcher<I>
612where
613 I: [const] Instruction,
614 Memory: [const] VirtualMemory,
615{
616 #[inline(always)]
617 fn get_pc(&self) -> Address<I> {
618 self.pc
619 }
620
621 #[inline(always)]
622 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
623 unsafe fn try_set_pc_relative(&mut self, instruction_size: u8, offset: i32) -> bool {
624 let old_pc = <Self as ProgramCounter<_, Memory>>::old_pc(self, instruction_size);
625 let pc = old_pc.wrapping_add_signed(offset);
626 self.pc = pc;
629
630 pc != self.return_trap_address && pc.as_u64().is_multiple_of(u64::from(I::ALIGNMENT))
631 }
632
633 #[cold]
634 #[inline(never)]
635 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
636 unsafe fn failed_branch(
637 &mut self,
638 memory: &Memory,
639 ) -> Result<ControlFlow<()>, ExecutionError<Address<I>>> {
640 self.set_pc(memory, self.pc)
643 }
644
645 #[inline]
646 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
647 fn set_pc(
648 &mut self,
649 _memory: &Memory,
650 pc: Address<I>,
651 ) -> Result<ControlFlow<()>, ExecutionError<Address<I>>> {
652 if pc == self.return_trap_address {
653 cold_path();
654 self.pc = pc;
656 return Ok(ControlFlow::Break(()));
657 }
658
659 if !pc.as_u64().is_multiple_of(u64::from(I::ALIGNMENT)) {
660 cold_path();
661 return Err(ExecutionError::UnalignedInstruction {
662 address: PackedAddress::new(pc),
663 });
664 }
665
666 self.pc = pc;
667
668 Ok(ControlFlow::Continue(()))
669 }
670}
671
672const unsafe impl<I, Memory> InstructionFetcher<I, Memory> for BasicInstructionFetcher<I>
677where
678 I: [const] Instruction,
679 Memory: [const] VirtualMemory,
680{
681 type Peeked = I;
682
683 #[inline(always)]
684 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
685 unsafe fn peeked_instruction<'a>(&'a self, peeked: &'a I) -> &'a I {
686 peeked
687 }
688
689 #[inline]
690 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
691 unsafe fn peek_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I> {
692 let instruction = match memory.read(self.pc.as_u64()).or_else(const |error| {
693 cold_path();
694 if let Ok(instruction) = memory.read::<u16>(self.pc.as_u64())
696 && (instruction & 0b11) != 0b11
697 {
698 return Ok(u32::from(instruction));
699 }
700 Err(error)
701 }) {
702 Ok(instruction) => instruction,
703 Err(error) => {
704 cold_path();
705 return FetchInstructionResult::Err(ExecutionError::from(error));
706 }
707 };
708
709 let Some(instruction) = I::try_decode(instruction) else {
710 cold_path();
711 return FetchInstructionResult::Err(ExecutionError::IllegalInstruction {
712 address: PackedAddress::new(self.pc),
713 });
714 };
715 FetchInstructionResult::Instruction(instruction)
716 }
717
718 #[inline(always)]
719 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
720 unsafe fn advance(&mut self, instruction_size: u8) {
721 self.pc = self.pc.wrapping_add_signed(i32::from(instruction_size));
722 }
723
724 #[inline]
725 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
726 unsafe fn fetch_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I> {
727 let result = unsafe { InstructionFetcher::<I, Memory>::peek_instruction(self, memory) };
729
730 if let FetchInstructionResult::Instruction(instruction) = result {
731 unsafe {
734 InstructionFetcher::<I, Memory>::advance(self, instruction.size());
735 }
736 }
737
738 result
739 }
740}
741
742impl<I> BasicInstructionFetcher<I>
743where
744 I: Instruction,
745{
746 #[inline(always)]
751 pub const fn new(return_trap_address: Address<I>, pc: Address<I>) -> Self {
752 Self {
753 return_trap_address,
754 pc,
755 }
756 }
757}
758
759#[derive(Debug, Default, Clone, Copy)]
765pub struct IllegalEcallSystemInstructionHandler;
766
767const impl<Reg, Regs, Memory, PC> SystemInstructionHandler<Reg, Regs, Memory, PC>
768 for IllegalEcallSystemInstructionHandler
769where
770 Reg: [const] Register,
771 PC: [const] ProgramCounter<Reg::Type, Memory>,
772{
773 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
774 fn handle_ecall(
775 &mut self,
776 _regs: &mut Regs,
777 _memory: &mut Memory,
778 program_counter: &PC,
779 ) -> ExecutionResult<Reg> {
780 ExecutionResult::Err(ExecutionError::IllegalInstruction {
781 address: PackedAddress::new(program_counter.old_pc(size_of::<u32>() as u8)),
782 })
783 }
784}
785
786const impl WrsHandler for IllegalEcallSystemInstructionHandler {}
787
788const impl FenceIHandler for IllegalEcallSystemInstructionHandler {}