Skip to main content

ab_riscv_interpreter/
lib.rs

1#![feature(const_convert, const_trait_impl, control_flow_ok, widening_mul)]
2#![expect(incomplete_features, reason = "generic_const_exprs")]
3// TODO: This feature is not actually used in this crate, but is added as a workaround for
4//  https://github.com/rust-lang/rust/issues/141492
5#![feature(generic_const_exprs)]
6#![no_std]
7
8pub mod rv64;
9
10use ab_riscv_primitives::instructions::Instruction;
11use ab_riscv_primitives::registers::general_purpose::Register;
12use core::marker::PhantomData;
13use core::ops::ControlFlow;
14
15type Address<I> = <<I as Instruction>::Reg as Register>::Type;
16
17/// Errors for [`VirtualMemory`]
18#[derive(Debug, thiserror::Error)]
19pub enum VirtualMemoryError {
20    /// Out-of-bounds read
21    #[error("Out-of-bounds read at address {address}")]
22    OutOfBoundsRead {
23        /// Address of the out-of-bounds read
24        address: u64,
25    },
26    /// Out-of-bounds write
27    #[error("Out-of-bounds write at address {address}")]
28    OutOfBoundsWrite {
29        /// Address of the out-of-bounds write
30        address: u64,
31    },
32}
33
34mod private {
35    pub trait Sealed {}
36
37    impl Sealed for u8 {}
38    impl Sealed for u16 {}
39    impl Sealed for u32 {}
40    impl Sealed for u64 {}
41    impl Sealed for i8 {}
42    impl Sealed for i16 {}
43    impl Sealed for i32 {}
44    impl Sealed for i64 {}
45}
46
47/// Basic integer types that can be read and written to/from memory freely
48pub trait BasicInt: Sized + Copy + private::Sealed {}
49
50impl BasicInt for u8 {}
51impl BasicInt for u16 {}
52impl BasicInt for u32 {}
53impl BasicInt for u64 {}
54impl BasicInt for i8 {}
55impl BasicInt for i16 {}
56impl BasicInt for i32 {}
57impl BasicInt for i64 {}
58
59/// Virtual memory interface
60pub trait VirtualMemory {
61    /// Read a value from memory at the specified address
62    fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
63    where
64        T: BasicInt;
65
66    /// Unchecked read a value from memory at the specified address.
67    ///
68    /// # Safety
69    /// The address and value must be in-bounds.
70    unsafe fn read_unchecked<T>(&self, address: u64) -> T
71    where
72        T: BasicInt;
73
74    /// Write a value to memory at the specified address
75    fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
76    where
77        T: BasicInt;
78}
79
80/// Program counter errors
81#[derive(Debug, thiserror::Error)]
82pub enum ProgramCounterError<Address, Custom> {
83    /// Unaligned instruction
84    #[error("Unaligned instruction at address {address}")]
85    UnalignedInstruction {
86        /// Address of the unaligned instruction fetch
87        address: Address,
88    },
89    /// Memory access error
90    #[error("Memory access error: {0}")]
91    MemoryAccess(#[from] VirtualMemoryError),
92    /// Custom error
93    #[error("Custom error: {0}")]
94    Custom(Custom),
95}
96
97/// Generic program counter
98pub trait ProgramCounter<Address, Memory, CustomError> {
99    /// Get the current value of the program counter
100    fn get_pc(&self) -> Address;
101
102    /// Set the current value of the program counter
103    fn set_pc(
104        &mut self,
105        memory: &mut Memory,
106        pc: Address,
107    ) -> Result<ControlFlow<()>, ProgramCounterError<Address, CustomError>>;
108}
109
110/// Execution errors
111#[derive(Debug, thiserror::Error)]
112pub enum ExecutionError<Address, I, Custom> {
113    /// Unaligned instruction fetch
114    #[error("Unaligned instruction fetch at address {address}")]
115    UnalignedInstructionFetch {
116        /// Address of the unaligned instruction fetch
117        address: Address,
118    },
119    /// Program counter error
120    #[error("Program counter error: {0}")]
121    ProgramCounter(#[from] ProgramCounterError<Address, Custom>),
122    /// Memory access error
123    #[error("Memory access error: {0}")]
124    MemoryAccess(#[from] VirtualMemoryError),
125    /// Unsupported instruction
126    #[error("Unsupported instruction at address {address:#x}: {instruction}")]
127    UnsupportedInstruction {
128        /// Address of the unsupported instruction
129        address: Address,
130        /// Instruction that caused the error
131        instruction: I,
132    },
133    /// Unimplemented/illegal instruction
134    #[error("Unimplemented/illegal instruction at address {address:#x}")]
135    UnimpInstruction {
136        /// Address of the `unimp` instruction
137        address: Address,
138    },
139    /// Invalid instruction
140    #[error("Invalid instruction at address {address:#x}: {instruction:#010x}")]
141    InvalidInstruction {
142        /// Address of the invalid instruction
143        address: Address,
144        /// Instruction that caused the error
145        instruction: u32,
146    },
147    /// Custom error
148    #[error("Custom error: {0}")]
149    Custom(Custom),
150}
151
152impl<Address, BI, Custom> ExecutionError<Address, BI, Custom> {
153    /// Map instruction type
154    #[inline]
155    pub fn map_instruction<I>(self, map: fn(BI) -> I) -> ExecutionError<Address, I, Custom> {
156        match self {
157            Self::UnalignedInstructionFetch { address } => {
158                ExecutionError::UnalignedInstructionFetch { address }
159            }
160            Self::MemoryAccess(error) => ExecutionError::MemoryAccess(error),
161            Self::ProgramCounter(error) => ExecutionError::ProgramCounter(error),
162            Self::UnsupportedInstruction {
163                address,
164                instruction,
165            } => ExecutionError::UnsupportedInstruction {
166                address,
167                instruction: map(instruction),
168            },
169            Self::UnimpInstruction { address } => ExecutionError::UnimpInstruction { address },
170            Self::InvalidInstruction {
171                address,
172                instruction,
173            } => ExecutionError::InvalidInstruction {
174                address,
175                instruction,
176            },
177            Self::Custom(error) => ExecutionError::Custom(error),
178        }
179    }
180}
181
182/// Result of [`InstructionFetcher::fetch_instruction()`] call
183#[derive(Debug, Copy, Clone)]
184pub enum FetchInstructionResult<Instruction> {
185    /// Instruction fetched successfully
186    Instruction(Instruction),
187    /// Control flow instruction encountered
188    ControlFlow(ControlFlow<()>),
189}
190
191/// Generic instruction fetcher
192pub trait InstructionFetcher<I, Memory, CustomError>
193where
194    Self: ProgramCounter<Address<I>, Memory, CustomError>,
195    I: Instruction,
196{
197    /// Fetch a single instruction at a specified address and advance the program counter
198    fn fetch_instruction(
199        &mut self,
200        memory: &mut Memory,
201    ) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, I, CustomError>>;
202}
203
204/// Basic instruction fetcher implementation
205#[derive(Debug, Copy, Clone)]
206pub struct BasicInstructionFetcher<I, CustomError>
207where
208    I: Instruction,
209{
210    return_trap_address: Address<I>,
211    pc: Address<I>,
212    _phantom: PhantomData<CustomError>,
213}
214
215impl<I, Memory, CustomError> ProgramCounter<Address<I>, Memory, CustomError>
216    for BasicInstructionFetcher<I, CustomError>
217where
218    I: Instruction,
219    Memory: VirtualMemory,
220{
221    #[inline(always)]
222    fn get_pc(&self) -> Address<I> {
223        self.pc
224    }
225
226    #[inline]
227    fn set_pc(
228        &mut self,
229        memory: &mut Memory,
230        pc: Address<I>,
231    ) -> Result<ControlFlow<()>, ProgramCounterError<Address<I>, CustomError>> {
232        if pc == self.return_trap_address {
233            return Ok(ControlFlow::Break(()));
234        }
235
236        if !pc.into().is_multiple_of(u64::from(I::alignment())) {
237            return Err(ProgramCounterError::UnalignedInstruction { address: pc });
238        }
239
240        memory.read::<u32>(pc.into())?;
241
242        self.pc = pc;
243
244        Ok(ControlFlow::Continue(()))
245    }
246}
247
248impl<I, Memory, CustomError> InstructionFetcher<I, Memory, CustomError>
249    for BasicInstructionFetcher<I, CustomError>
250where
251    I: Instruction,
252    Memory: VirtualMemory,
253{
254    #[inline]
255    fn fetch_instruction(
256        &mut self,
257        memory: &mut Memory,
258    ) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, I, CustomError>> {
259        // SAFETY: Constructor guarantees that the last instruction is a jump, which means going
260        // through `Self::set_pc()` method that does bound check. Otherwise, advancing forward by
261        // one instruction can't result in out-of-bounds access.
262        let instruction = unsafe { memory.read_unchecked(self.pc.into()) };
263        // SAFETY: All instructions are valid, according to the constructor contract
264        let instruction = unsafe { I::try_decode(instruction).unwrap_unchecked() };
265        self.pc += instruction.size().into();
266
267        Ok(FetchInstructionResult::Instruction(instruction))
268    }
269}
270
271impl<I, CustomError> BasicInstructionFetcher<I, CustomError>
272where
273    I: Instruction,
274{
275    /// Create a new instance.
276    ///
277    /// `return_trap_address` is the address at which the interpreter will stop execution
278    /// (gracefully).
279    ///
280    /// # Safety
281    /// The program counter must be valid and aligned, the instructions processed must be valid and
282    /// end with a jump instruction.
283    #[inline(always)]
284    pub unsafe fn new(return_trap_address: Address<I>, pc: Address<I>) -> Self {
285        Self {
286            return_trap_address,
287            pc,
288            _phantom: PhantomData,
289        }
290    }
291}
292
293/// Trait for executable instructions
294pub trait ExecutableInstruction<State, CustomError>
295where
296    Self: Instruction,
297{
298    /// Execute instruction
299    fn execute(
300        self,
301        state: &mut State,
302    ) -> Result<ControlFlow<()>, ExecutionError<Address<Self>, Self, CustomError>>;
303}