Skip to main content

ab_riscv_interpreter/
lib.rs

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