1#[cfg(feature = "alloc")]
4mod eager_instruction_fetcher;
5#[cfg(test)]
6mod tests;
7
8#[cfg(feature = "alloc")]
9pub use crate::basic::eager_instruction_fetcher::{
10 BasicEagerInstructionFetcher, BasicEagerInstructions,
11};
12use crate::zawrs::WrsHandler;
13use crate::zifencei::FenceIHandler;
14use crate::{
15 Address, BasicInt, ExecutableInstruction, ExecutionError, ExecutionResult,
16 FetchInstructionResult, InstructionFetcher, PackedAddress, ProgramCounter, RegisterFile,
17 Rs1Rs2OperandValues, Rs1Rs2Operands, SystemInstructionHandler, VirtualMemory,
18 VirtualMemoryError,
19};
20use ab_riscv_primitives::prelude::*;
21#[cfg(feature = "alloc")]
22use alloc::boxed::Box;
23use core::hint::cold_path;
24use core::mem;
25use core::ops::ControlFlow;
26use replace_with::replace_with_or_abort_and_return;
27
28pub const unsafe trait BasicRegister
34where
35 Self: [const] Register,
36{
37 const N: usize;
41
42 fn offset(self) -> u8;
44}
45
46const unsafe impl<Type> BasicRegister for EReg<Type>
48where
49 Self: [const] Register,
50{
51 const N: usize = 16;
52
53 #[inline(always)]
54 fn offset(self) -> u8 {
55 unsafe { mem::transmute::<Self, u8>(self) }
57 }
58}
59
60const unsafe impl<Type> BasicRegister for Reg<Type>
62where
63 Self: [const] Register,
64{
65 const N: usize = 32;
66
67 #[inline(always)]
68 fn offset(self) -> u8 {
69 unsafe { mem::transmute::<Self, u8>(self) }
71 }
72}
73
74#[derive(Debug, Clone, Copy)]
80#[repr(align(16))]
81pub struct BasicRegisters<Reg, const ZEROSTORE: bool = false>
82where
83 Reg: BasicRegister,
84{
85 regs: [Reg::Type; Reg::N],
86}
87
88impl<Reg, const ZEROSTORE: bool> Default for BasicRegisters<Reg, ZEROSTORE>
89where
90 Reg: BasicRegister,
91{
92 #[inline(always)]
93 fn default() -> Self {
94 Self {
95 regs: [Reg::Type::default(); _],
96 }
97 }
98}
99
100const impl<Reg, const ZEROSTORE: bool> RegisterFile<Reg> for BasicRegisters<Reg, ZEROSTORE>
101where
102 Reg: [const] BasicRegister,
103{
104 #[inline(always)]
105 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
106 fn read(&self, reg: Reg) -> Reg::Type {
107 if reg == Reg::ZERO && !ZEROSTORE {
108 return Reg::Type::default();
110 }
111
112 *unsafe { self.regs.get_unchecked(usize::from(reg.offset())) }
114 }
115
116 #[inline(always)]
117 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
118 fn write(&mut self, reg: Reg, value: Reg::Type) {
119 *unsafe { self.regs.get_unchecked_mut(usize::from(reg.offset())) } = value;
121 if ZEROSTORE {
122 *unsafe { self.regs.get_unchecked_mut(0) } = Reg::Type::default();
124 }
125 }
126}
127
128#[derive(Debug)]
133pub struct BasicInterpreterState<Regs, Env, Memory, IF> {
134 pub regs: Regs,
136 pub env: Env,
142 pub memory: Memory,
144 pub instruction_fetcher: IF,
146}
147
148impl<Regs, Env, Memory, IF> BasicInterpreterState<Regs, Env, Memory, IF> {
149 #[rustc_align(64)]
159 pub fn execute<I>(&mut self) -> Result<(), ExecutionError<Address<I>>>
160 where
161 Regs: RegisterFile<<I as Instruction>::Reg>,
162 I: ExecutableInstruction<Regs, Env, Memory, IF>,
163 Memory: VirtualMemory,
164 IF: InstructionFetcher<I, Memory> + ProgramCounter<Address<I>, Memory>,
165 {
166 replace_with_or_abort_and_return(
167 &mut self.instruction_fetcher,
168 #[inline(always)]
169 |mut instruction_fetcher| {
170 loop {
171 let instruction = match instruction_fetcher.fetch_instruction(&self.memory) {
172 FetchInstructionResult::Instruction(instruction) => instruction,
173 FetchInstructionResult::Continue => {
174 cold_path();
175 continue;
176 }
177 FetchInstructionResult::Break => {
178 cold_path();
179 break;
180 }
181 FetchInstructionResult::Err(error) => {
182 cold_path();
183 return (Err(error), instruction_fetcher);
184 }
185 };
186
187 let Rs1Rs2Operands { rs1, rs2 } = instruction.get_rs1_rs2_operands();
188 let rs1rs2_values = Rs1Rs2OperandValues {
189 rs1_value: self.regs.read(rs1),
190 rs2_value: self.regs.read(rs2),
191 };
192
193 let outcome = instruction.execute(
194 rs1rs2_values,
195 &mut self.regs,
196 &mut self.env,
197 &mut self.memory,
198 &mut instruction_fetcher,
199 );
200
201 let control_flow =
202 match outcome {
203 ExecutionResult::Continue { rd, value } => {
204 self.regs.write(rd, value);
205 continue;
206 }
207 ExecutionResult::ContinueNoWrite => {
208 continue;
209 }
210 ExecutionResult::Branch { offset } => instruction_fetcher
211 .set_pc_relative(&self.memory, instruction.size(), offset),
212 ExecutionResult::Jump { target } => {
213 instruction_fetcher.set_pc(&self.memory, target)
214 }
215 ExecutionResult::Break => {
216 cold_path();
217 break;
218 }
219 ExecutionResult::Err(error) => {
220 cold_path();
221 return (Err(error), instruction_fetcher);
222 }
223 };
224
225 match control_flow {
226 Ok(ControlFlow::Continue(())) => {}
227 Ok(ControlFlow::Break(())) => {
228 cold_path();
229 break;
230 }
231 Err(error) => {
232 cold_path();
233 return (Err(error), instruction_fetcher);
234 }
235 }
236 }
237
238 (Ok(()), instruction_fetcher)
239 },
240 )
241 }
242}
243
244#[derive(Debug, Copy, Clone)]
263#[repr(align(16))]
264pub struct BasicMemory<const BASE_ADDR: u64, const SIZE: usize> {
265 data: [u8; SIZE],
266}
267
268const impl<const BASE_ADDR: u64, const SIZE: usize> VirtualMemory for BasicMemory<BASE_ADDR, SIZE> {
269 #[inline(always)]
270 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
271 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
272 where
273 T: BasicInt,
274 {
275 let offset = Self::offset(address);
276
277 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
278 cold_path();
279 return Err(VirtualMemoryError::OutOfBoundsRead { address });
280 }
281
282 unsafe {
284 Ok(self
285 .data
286 .as_ptr()
287 .cast::<T>()
288 .byte_add(offset as usize)
289 .read_unaligned())
290 }
291 }
292
293 #[inline(always)]
294 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
295 unsafe fn read_unchecked<T>(&self, address: u64) -> T
296 where
297 T: BasicInt,
298 {
299 unsafe {
301 let offset = address.unchecked_sub(BASE_ADDR) as usize;
302 self.data
303 .as_ptr()
304 .cast::<T>()
305 .byte_add(offset)
306 .read_unaligned()
307 }
308 }
309
310 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
311 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
312 let offset = Self::offset(address);
313
314 if offset > self.data.len() as u64 {
315 cold_path();
316 return Err(VirtualMemoryError::OutOfBoundsRead { address });
317 }
318
319 self.data
320 .get(offset as usize..)
321 .and_then(const |data| data.get(..len as usize))
322 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })
323 }
324
325 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
326 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
327 let offset = Self::offset(address);
328
329 if offset > self.data.len() as u64 {
330 cold_path();
331 return &[];
332 }
333
334 let remaining = self.data.get(offset as usize..).unwrap_or_default();
335 remaining.get(..len as usize).unwrap_or(remaining)
336 }
337
338 #[inline(always)]
339 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
340 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
341 where
342 T: BasicInt,
343 {
344 let offset = Self::offset(address);
345
346 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
347 cold_path();
348 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
349 }
350
351 unsafe {
353 self.data
354 .as_mut_ptr()
355 .cast::<T>()
356 .byte_add(offset as usize)
357 .write_unaligned(value);
358 }
359
360 Ok(())
361 }
362
363 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
364 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
365 let offset = Self::offset(address);
366
367 if offset > self.data.len() as u64 {
368 cold_path();
369 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
370 }
371
372 let len = data.len();
373 let Some(target_data) = self
374 .data
375 .get_mut(offset as usize..)
376 .and_then(const |data| data.get_mut(..len))
377 else {
378 cold_path();
379 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
380 };
381
382 target_data.copy_from_slice(data);
383
384 Ok(())
385 }
386}
387
388impl<const BASE_ADDR: u64, const SIZE: usize> Default for BasicMemory<BASE_ADDR, SIZE> {
389 #[inline(always)]
390 fn default() -> Self {
391 Self { data: [0; _] }
392 }
393}
394
395impl<const BASE_ADDR: u64, const SIZE: usize> BasicMemory<BASE_ADDR, SIZE> {
396 #[inline(always)]
402 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
403 const fn offset(address: u64) -> u64 {
404 const {
405 assert!(
406 BASE_ADDR.checked_add(SIZE as u64).is_some(),
407 "`BasicMemory` must not wrap around the end of the address space, or an address \
408 below its base address could wrap into it"
409 );
410 }
411
412 address.wrapping_sub(BASE_ADDR)
413 }
414
415 #[cfg(feature = "alloc")]
416 pub fn new_boxed() -> Box<Self> {
417 unsafe { Box::<Self>::new_zeroed().assume_init() }
419 }
420
421 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
425 pub const fn get_mut_bytes(
426 &mut self,
427 address: u64,
428 size: usize,
429 ) -> Result<&mut [u8], VirtualMemoryError> {
430 let Some(offset) = address.checked_sub(BASE_ADDR) else {
431 cold_path();
432 return Err(VirtualMemoryError::OutOfBoundsRead { address });
433 };
434 let offset = offset as usize;
435
436 let Some(slice) = self
437 .data
438 .get_mut(offset..)
439 .and_then(const |data| data.get_mut(..size))
440 else {
441 cold_path();
442 return Err(VirtualMemoryError::OutOfBoundsRead { address });
443 };
444
445 Ok(slice)
446 }
447}
448
449#[derive(Debug, Copy, Clone)]
458pub struct BasicInstructionFetcher<I>
459where
460 I: Instruction,
461{
462 return_trap_address: Address<I>,
463 pc: Address<I>,
464}
465
466const impl<I, Memory> ProgramCounter<Address<I>, Memory> for BasicInstructionFetcher<I>
467where
468 I: [const] Instruction,
469 Memory: [const] VirtualMemory,
470{
471 #[inline(always)]
472 fn get_pc(&self) -> Address<I> {
473 self.pc
474 }
475
476 #[inline(always)]
477 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
478 unsafe fn try_set_pc_relative(&mut self, instruction_size: u8, offset: i32) -> bool {
479 let old_pc = <Self as ProgramCounter<_, Memory>>::old_pc(self, instruction_size);
480 let pc = old_pc.wrapping_add_signed(offset);
481 self.pc = pc;
484
485 pc != self.return_trap_address && pc.as_u64().is_multiple_of(u64::from(I::ALIGNMENT))
486 }
487
488 #[cold]
489 #[inline(never)]
490 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
491 unsafe fn failed_branch(
492 &mut self,
493 memory: &Memory,
494 ) -> Result<ControlFlow<()>, ExecutionError<Address<I>>> {
495 self.set_pc(memory, self.pc)
498 }
499
500 #[inline]
501 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
502 fn set_pc(
503 &mut self,
504 _memory: &Memory,
505 pc: Address<I>,
506 ) -> Result<ControlFlow<()>, ExecutionError<Address<I>>> {
507 if pc == self.return_trap_address {
508 cold_path();
509 return Ok(ControlFlow::Break(()));
510 }
511
512 if !pc.as_u64().is_multiple_of(u64::from(I::ALIGNMENT)) {
513 cold_path();
514 return Err(ExecutionError::UnalignedInstruction {
515 address: PackedAddress::new(pc),
516 });
517 }
518
519 self.pc = pc;
520
521 Ok(ControlFlow::Continue(()))
522 }
523}
524
525const impl<I, Memory> InstructionFetcher<I, Memory> for BasicInstructionFetcher<I>
526where
527 I: [const] Instruction,
528 Memory: [const] VirtualMemory,
529{
530 type Peeked = I;
531
532 #[inline(always)]
533 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
534 fn peeked_instruction<'a>(&'a self, peeked: &'a I) -> &'a I {
535 peeked
536 }
537
538 #[inline]
539 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
540 fn peek_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I> {
541 let instruction = match memory.read(self.pc.as_u64()).or_else(const |error| {
542 cold_path();
543 if let Ok(instruction) = memory.read::<u16>(self.pc.as_u64())
545 && (instruction & 0b11) != 0b11
546 {
547 return Ok(u32::from(instruction));
548 }
549 Err(error)
550 }) {
551 Ok(instruction) => instruction,
552 Err(error) => {
553 cold_path();
554 return FetchInstructionResult::Err(ExecutionError::from(error));
555 }
556 };
557
558 let Some(instruction) = I::try_decode(instruction) else {
559 cold_path();
560 return FetchInstructionResult::Err(ExecutionError::IllegalInstruction {
561 address: PackedAddress::new(self.pc),
562 });
563 };
564 FetchInstructionResult::Instruction(instruction)
565 }
566
567 #[inline(always)]
568 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
569 unsafe fn advance(&mut self, instruction_size: u8) {
570 self.pc = self.pc.wrapping_add_signed(i32::from(instruction_size));
571 }
572
573 #[inline]
574 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
575 fn fetch_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I> {
576 let result = InstructionFetcher::<I, Memory>::peek_instruction(self, memory);
577
578 if let FetchInstructionResult::Instruction(instruction) = result {
579 unsafe {
582 InstructionFetcher::<I, Memory>::advance(self, instruction.size());
583 }
584 }
585
586 result
587 }
588}
589
590impl<I> BasicInstructionFetcher<I>
591where
592 I: Instruction,
593{
594 #[inline(always)]
599 pub const fn new(return_trap_address: Address<I>, pc: Address<I>) -> Self {
600 Self {
601 return_trap_address,
602 pc,
603 }
604 }
605}
606
607#[derive(Debug, Default, Clone, Copy)]
613pub struct IllegalEcallSystemInstructionHandler;
614
615const impl<Reg, Regs, Memory, PC> SystemInstructionHandler<Reg, Regs, Memory, PC>
616 for IllegalEcallSystemInstructionHandler
617where
618 Reg: [const] Register,
619 PC: [const] ProgramCounter<Reg::Type, Memory>,
620{
621 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
622 fn handle_ecall(
623 &mut self,
624 _regs: &mut Regs,
625 _memory: &mut Memory,
626 program_counter: &mut PC,
627 ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type>> {
628 Err(ExecutionError::IllegalInstruction {
629 address: PackedAddress::new(program_counter.old_pc(size_of::<u32>() as u8)),
630 })
631 }
632}
633
634const impl WrsHandler for IllegalEcallSystemInstructionHandler {}
635
636const impl FenceIHandler for IllegalEcallSystemInstructionHandler {}