ab_riscv_interpreter/lib.rs
1//! Composable and generic RISC-V interpreter.
2//!
3//! This interpreter is designed to work with abstractions from [`ab-riscv-primitives`] crate and is
4//! similarly composable with a powerful macro system and trait abstractions over handling of
5//! memory, syscalls, etc.
6//!
7//! [`ab-riscv-primitives`]: ab_riscv_primitives
8//!
9//! The immediate needs dictate the current set of available instructions and extensions. Consider
10//! contributing if you need something not yet available.
11//!
12//! `ab-riscv-act4-runner` crate in the repository contains a complementary RISC-V Architectural
13//! Certification Tests runner for <https://github.com/riscv-non-isa/riscv-arch-test> that ensures
14//! correct implementation.
15//!
16//! Does not require a standard library (`no_std`) or an allocator, never panics, almost 100% of the
17//! API abstractions are usable in const, including several extensions beyond base ISA.
18//!
19//! ## Supported ISA variants and extensions
20//!
21//! ISA variants:
22//! * RV32I (version 2.1)
23//! * RV32E (version 2.0)
24//! * RV64I (version 2.1)
25//! * RV64E (version 2.0)
26//!
27//! Extensions:
28//! * A (version 2.1)
29//! * M (version 2.0)
30//! * B (version 1.0.0)
31//! * Zaamo (version 1.0.0)
32//! * Zabha (version 1.0.0)
33//! * Zacas (version 1.0.0)
34//! * (experimental) Zalasr (version 1.0.0)
35//! * Zalrsc (version 1.0.0)
36//! * Zawrs (version 1.0.0)
37//! * Zba (version 1.0.0)
38//! * Zbb (version 1.0.0)
39//! * Zbc (version 1.0.0)
40//! * Zbkb (version 1.0.1)
41//! * Zbkc (version 1.0.1)
42//! * Zbkx (version 1.0.1)
43//! * Zbs (version 1.0.0)
44//! * Zca (version 1.0.0)
45//! * Zcb (version 1.0.0)
46//! * (experimental) Zcmp (version 1.0.0)
47//! * Zicond (version 2.0)
48//! * Zicsr (version 2.0)
49//! * Zkn (version 1.0.1)
50//! * Zknd (version 1.0.1)
51//! * Zkne (version 1.0.1)
52//! * Zknh (version 1.0.1)
53//! * Zkr (version 1.0.1)
54//! * Zvbb (version 1.0.0)
55//! * Zvbc (version 1.0.0)
56//! * ZveXx (version 1.0.0), where `X` is anything allowed by the specification like Zve32x or
57//! Zve64x
58//! * Zvkb (version 1.0.0)
59//! * Zvl*b (version 1.0.0), where `*` is anything allowed by the specification like Zvl128b or
60//! Zvl512b
61//!
62//! All extensions except experimental pass all relevant RISC-V Architectural Certification Tests
63//! (ACTs) using the ACT4 framework.
64//!
65//! Any permutation of compatible extensions is supported.
66//!
67//! Experimental extensions may not have ACT4 tests yet and are not guaranteed to work correctly.
68//!
69//! ## Design choices
70//!
71//! This crate was designed with a blockchain use case in mind, though it is in no way tied to any
72//! particular blockchain and is completely general purpose. As a result, the implementation is
73//! designed to be precise and non-ambiguous.
74//!
75//! A few key points:
76//! * anything "reserved" in the specification is considered to be illegal
77//! * anything "optional" in the specification is considered to be illegal
78//! * anything "implementation-defined" in the specification is selected to be the most natural and
79//! deterministic
80//! * type system is used to make the majority of invalid invariants impossible to represent in code
81//! and/or decode
82//!
83//! Examples:
84//! * `vma`/`vta` are always undisturbed in vector extensions
85//! * Zve64x extension instructions are purposefully restricted to what it is required to be capable
86//! of, although it would be cheaper to support the fuller feature set only required by V
87//! extension
88
89#![expect(incomplete_features, reason = "generic_const_*")]
90#![feature(
91 adt_const_params,
92 const_closures,
93 const_cmp,
94 const_convert,
95 const_default,
96 const_destruct,
97 const_index,
98 const_ops,
99 const_option_ops,
100 const_result_trait_fn,
101 const_trait_impl,
102 const_try,
103 generic_const_args,
104 generic_const_items,
105 inherent_associated_types,
106 integer_widen_truncate,
107 macroless_generic_const_args,
108 min_generic_const_args,
109 signed_bigint_helpers,
110 widening_mul
111)]
112#![cfg_attr(test, feature(const_block_items, try_blocks))]
113#![cfg_attr(
114 not(any(
115 all(target_arch = "riscv32", target_feature = "zbkx"),
116 all(target_arch = "riscv64", target_feature = "zbkx")
117 )),
118 feature(portable_simd)
119)]
120#![cfg_attr(
121 any(
122 all(
123 target_arch = "riscv32",
124 any(
125 target_feature = "zbb",
126 target_feature = "zbc",
127 target_feature = "zbkb",
128 target_feature = "zbkx",
129 target_feature = "zknd",
130 target_feature = "zkne",
131 target_feature = "zknh"
132 ),
133 not(miri)
134 ),
135 all(
136 target_arch = "riscv64",
137 any(
138 target_feature = "zbb",
139 target_feature = "zbc",
140 target_feature = "zbkx",
141 target_feature = "zknd",
142 target_feature = "zkne",
143 target_feature = "zknh"
144 ),
145 not(miri)
146 )
147 ),
148 feature(riscv_ext_intrinsics)
149)]
150#![no_std]
151
152pub mod basic;
153pub mod prelude;
154mod private;
155pub mod rv32;
156pub mod rv64;
157pub mod v;
158pub mod zawrs;
159pub mod zicond;
160pub mod zicsr;
161pub mod zkr;
162pub mod zvbb;
163pub mod zvbc;
164
165#[cfg(feature = "alloc")]
166extern crate alloc;
167
168use crate::private::BasicIntSealed;
169use ab_riscv_primitives::prelude::*;
170#[cfg(feature = "alloc")]
171use alloc::boxed::Box;
172use core::fmt;
173use core::marker::Destruct;
174use core::ops::{ControlFlow, Sub};
175
176type RegisterType<I> = <<I as Instruction>::Reg as Register>::Type;
177type Address<I> = RegisterType<I>;
178
179/// A GPR (General Purpose Register) file abstraction
180pub const trait RegisterFile<Reg>
181where
182 Reg: [const] Register,
183{
184 /// Read register value
185 fn read(&self, reg: Reg) -> Reg::Type;
186
187 /// Write register value
188 fn write(&mut self, reg: Reg, value: Reg::Type);
189}
190
191/// Errors for [`VirtualMemory`]
192#[derive(Debug, thiserror::Error)]
193pub enum VirtualMemoryError {
194 /// Out-of-bounds read
195 #[error("Out-of-bounds read at address {address}")]
196 OutOfBoundsRead {
197 /// Address of the out-of-bounds read
198 address: u64,
199 },
200 /// Out-of-bounds write
201 #[error("Out-of-bounds write at address {address}")]
202 OutOfBoundsWrite {
203 /// Address of the out-of-bounds write
204 address: u64,
205 },
206}
207
208/// Basic integer types that can be read and written to/from memory freely
209pub trait BasicInt: Sized + Copy + BasicIntSealed + 'static {}
210
211impl BasicIntSealed for u8 {}
212impl BasicIntSealed for u16 {}
213impl BasicIntSealed for u32 {}
214impl BasicIntSealed for u64 {}
215impl BasicIntSealed for i8 {}
216impl BasicIntSealed for i16 {}
217impl BasicIntSealed for i32 {}
218impl BasicIntSealed for i64 {}
219
220impl BasicInt for u8 {}
221impl BasicInt for u16 {}
222impl BasicInt for u32 {}
223impl BasicInt for u64 {}
224impl BasicInt for i8 {}
225impl BasicInt for i16 {}
226impl BasicInt for i32 {}
227impl BasicInt for i64 {}
228
229/// Virtual memory interface
230pub const trait VirtualMemory {
231 /// Read a value from memory at the specified address
232 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
233 where
234 T: BasicInt;
235
236 /// Unchecked read a value from memory at the specified address.
237 ///
238 /// # Safety
239 /// The address and value must be in-bounds.
240 unsafe fn read_unchecked<T>(&self, address: u64) -> T
241 where
242 T: BasicInt;
243
244 /// Read a contiguous byte slice from memory
245 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError>;
246
247 /// Read as many contiguous bytes as possible starting at `address`, up to `len` bytes total.
248 ///
249 /// Can return an empty slice in cases like when the address is out of bounds.
250 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8];
251
252 /// Write a value to memory at the specified address
253 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
254 where
255 T: BasicInt;
256
257 /// Write a contiguous byte slice to memory
258 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError>;
259}
260
261#[cfg(feature = "alloc")]
262impl<M> VirtualMemory for Box<M>
263where
264 M: VirtualMemory,
265{
266 #[inline(always)]
267 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
268 where
269 T: BasicInt,
270 {
271 self.as_ref().read(address)
272 }
273
274 #[inline(always)]
275 unsafe fn read_unchecked<T>(&self, address: u64) -> T
276 where
277 T: BasicInt,
278 {
279 // SAFETY: Guaranteed by the caller
280 unsafe { self.as_ref().read_unchecked(address) }
281 }
282
283 #[inline(always)]
284 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
285 self.as_ref().read_slice(address, len)
286 }
287
288 #[inline(always)]
289 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
290 self.as_ref().read_slice_up_to(address, len)
291 }
292
293 #[inline(always)]
294 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
295 where
296 T: BasicInt,
297 {
298 self.as_mut().write(address, value)
299 }
300
301 #[inline(always)]
302 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
303 self.as_mut().write_slice(address, data)
304 }
305}
306
307/// Placeholder for custom errors in [`ExecutionError`]
308#[derive(Debug, Copy, Clone)]
309pub struct CustomErrorPlaceholder;
310
311impl fmt::Display for CustomErrorPlaceholder {
312 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 Ok(())
314 }
315}
316
317/// Program counter errors
318#[derive(Debug, thiserror::Error)]
319pub enum ProgramCounterError<Address, CustomError = CustomErrorPlaceholder> {
320 /// Unaligned instruction
321 #[error("Unaligned instruction at address {address}")]
322 UnalignedInstruction {
323 /// Address of the unaligned instruction fetch
324 address: Address,
325 },
326 /// Memory access error
327 #[error("Memory access error: {0}")]
328 MemoryAccess(#[from] VirtualMemoryError),
329 /// Custom error
330 #[error("Custom error: {0}")]
331 Custom(CustomError),
332}
333
334/// Generic program counter
335pub const trait ProgramCounter<Address, Memory, CustomError = CustomErrorPlaceholder> {
336 /// Get the current value of the program counter
337 fn get_pc(&self) -> Address;
338
339 /// Get the previous value of the program counter before executing an `instruction`.
340 ///
341 /// This is usually called from under instruction execution when the program counter is already
342 /// advanced during instruction fetching. As such, `pc - instruction_size` is expected to never
343 /// underflow.
344 #[inline(always)]
345 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
346 fn old_pc(&self, instruction_size: u8) -> Address
347 where
348 Address: [const] From<u8> + [const] Sub<Output = Address>,
349 {
350 // TODO: Wrapping subtraction would be nice, but causes a lot of additional generic bounds
351 // that are bad for ergonomics
352 self.get_pc() - Address::from(instruction_size)
353 }
354
355 /// Set the current value of the program counter
356 fn set_pc(
357 &mut self,
358 memory: &Memory,
359 pc: Address,
360 ) -> Result<ControlFlow<()>, ProgramCounterError<Address, CustomError>>;
361}
362
363/// Execution errors
364#[derive(Debug, thiserror::Error)]
365pub enum ExecutionError<Address, CustomError = CustomErrorPlaceholder> {
366 /// Program counter error
367 #[error("Program counter error: {0}")]
368 ProgramCounter(ProgramCounterError<Address, CustomError>),
369 /// Memory access error
370 #[error("Memory access error: {0}")]
371 MemoryAccess(VirtualMemoryError),
372 /// Unsupported `ecall` instruction
373 #[error("Unsupported `ecall` instruction at address {address:#x}")]
374 EcallUnsupported {
375 /// Address of the unsupported instruction
376 address: Address,
377 },
378 /// Unimplemented/illegal instruction
379 #[error("Unimplemented/illegal instruction at address {address:#x}")]
380 IllegalInstruction {
381 /// Address of the `unimp` instruction
382 address: Address,
383 },
384 /// Invalid instruction
385 #[error("Invalid instruction at address {address:#x}: {instruction:#010x}")]
386 InvalidInstruction {
387 /// Address of the invalid instruction
388 address: Address,
389 /// Instruction that caused the error
390 instruction: u32,
391 },
392 /// CSR error
393 #[error("CSR error: {0}")]
394 CsrError(CsrError<CustomError>),
395 /// Custom error
396 #[error("Custom error: {0}")]
397 Custom(CustomError),
398}
399
400const impl<Address, CustomError> From<ProgramCounterError<Address, CustomError>>
401 for ExecutionError<Address, CustomError>
402{
403 #[inline(always)]
404 fn from(value: ProgramCounterError<Address, CustomError>) -> Self {
405 Self::ProgramCounter(value)
406 }
407}
408
409const impl<Address, CustomError> From<VirtualMemoryError> for ExecutionError<Address, CustomError> {
410 #[inline(always)]
411 fn from(value: VirtualMemoryError) -> Self {
412 Self::MemoryAccess(value)
413 }
414}
415
416const impl<Address, CustomError> From<CsrError<CustomError>>
417 for ExecutionError<Address, CustomError>
418{
419 #[inline(always)]
420 fn from(value: CsrError<CustomError>) -> Self {
421 Self::CsrError(value)
422 }
423}
424
425/// Result of [`InstructionFetcher::fetch_instruction()`] call
426#[derive(Debug, Copy, Clone)]
427pub enum FetchInstructionResult<Instruction> {
428 /// Instruction fetched successfully
429 Instruction(Instruction),
430 /// Control flow instruction encountered
431 ControlFlow(ControlFlow<()>),
432}
433
434/// Generic instruction fetcher
435pub const trait InstructionFetcher<I, Memory, CustomError = CustomErrorPlaceholder>
436where
437 Self: ProgramCounter<Address<I>, Memory, CustomError>,
438 I: Instruction,
439{
440 /// Fetch a single instruction at a specified address and advance the program counter on
441 /// successful fetch
442 fn fetch_instruction(
443 &mut self,
444 memory: &Memory,
445 ) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, CustomError>>;
446}
447
448/// CSR error
449#[derive(Debug, thiserror::Error)]
450pub enum CsrError<CustomError = CustomErrorPlaceholder> {
451 /// Read only CSR
452 #[error("Read only CSR {csr_index:#x}")]
453 ReadOnly {
454 /// Index of CSR where write was attempted
455 csr_index: u16,
456 },
457 /// Illegal read access
458 #[error("Illegal read access to CSR {csr_index:#x}")]
459 IllegalRead {
460 /// Index of the accessed CSR
461 csr_index: u16,
462 },
463 /// Illegal write access
464 #[error("Illegal write access to CSR {csr_index:#x}")]
465 IllegalWrite {
466 /// Index of the accessed CSR
467 csr_index: u16,
468 },
469 /// Unknown CSR
470 #[error("Unknown CSR {csr_index:#x}")]
471 Unknown {
472 /// Index of the accessed CSR
473 csr_index: u16,
474 },
475 /// Insufficient privilege level
476 #[error(
477 "Insufficient privilege level for CSR {csr_index:#x}: required {required:?}, \
478 current {current:?}"
479 )]
480 InsufficientPrivilege {
481 /// Index of the accessed CSR
482 csr_index: u16,
483 /// Required privilege level
484 required: PrivilegeLevel,
485 /// Current privilege level
486 current: PrivilegeLevel,
487 },
488 /// Custom error
489 #[error("Custom error: {0}")]
490 Custom(CustomError),
491}
492
493/// CSRs (Control and Status Registers)
494pub const trait Csrs<Reg, CustomError = CustomErrorPlaceholder>
495where
496 Reg: [const] Register,
497 CustomError: [const] Destruct,
498{
499 /// Current privilege level
500 #[inline(always)]
501 fn privilege_level(&self) -> PrivilegeLevel {
502 PrivilegeLevel::Machine
503 }
504
505 /// Reads register value
506 fn read_csr(&self, csr_index: u16) -> Result<Reg::Type, CsrError<CustomError>>;
507
508 /// Writes register value
509 fn write_csr(&mut self, csr_index: u16, value: Reg::Type) -> Result<(), CsrError<CustomError>>;
510}
511
512/// Custom handler for system instructions `ecall` and `ebreak`
513pub const trait SystemInstructionHandler<
514 Reg,
515 Regs,
516 Memory,
517 PC,
518 CustomError = CustomErrorPlaceholder,
519> where
520 Reg: Register,
521{
522 // TODO: Figure out the correct API for this method
523 /// Handle a `fence` instruction
524 #[inline(always)]
525 fn handle_fence(&mut self, pred: u8, succ: u8) {
526 let _: u8 = pred;
527 let _: u8 = succ;
528 // NOP by default
529 }
530
531 // TODO: Figure out the correct API for this method
532 /// Handle a `fence.tso` instruction
533 #[inline(always)]
534 fn handle_fence_tso(&mut self) {
535 // NOP by default
536 }
537
538 /// Handle an `ecall` instruction
539 fn handle_ecall(
540 &mut self,
541 regs: &mut Regs,
542 memory: &mut Memory,
543 program_counter: &mut PC,
544 ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, CustomError>>;
545
546 /// Handle an `ebreak` instruction.
547 ///
548 /// NOTE: the program counter here is the current value, meaning it is already incremented past
549 /// the instruction itself.
550 #[inline(always)]
551 fn handle_ebreak(&mut self, regs: &mut Regs, memory: &mut Memory, pc: Reg::Type) {
552 // These are for cleaner trait API without leading `_` on arguments
553 let _: &Regs = regs;
554 let _: &mut Memory = memory;
555 let _: Reg::Type = pc;
556 // NOP by default
557 }
558}
559
560/// `rs1`/`rs2` instruction operands
561#[derive(Debug, Default, Copy, Clone)]
562pub struct Rs1Rs2Operands<Reg> {
563 /// `rs1` operand.
564 ///
565 /// Zero register if `rs1` was missing in the original instruction definition.
566 pub rs1: Reg,
567 /// `rs2` operand.
568 ///
569 /// Zero register if `rs2` was missing in the original instruction definition.
570 pub rs2: Reg,
571}
572
573/// `rs1`/`rs2` instruction operands
574#[derive(Debug, Default, Copy, Clone)]
575pub struct Rs1Rs2OperandValues<RegType> {
576 /// `rs1` operand value.
577 ///
578 /// Zero if `rs1` was missing in the original instruction definition.
579 pub rs1_value: RegType,
580 /// `rs2` operand value.
581 ///
582 /// Zero if `rs2` was missing in the original instruction definition.
583 pub rs2_value: RegType,
584}
585
586/// `rs1`/`rs2` instruction operands
587pub const trait ExecutableInstructionOperands
588where
589 Self: Instruction,
590{
591 /// `rs1`/`rs2` instruction operands.
592 ///
593 /// Returns zero register for `rs1`/`rs2` that were missing in the original instruction
594 /// definition.
595 fn get_rs1_rs2_operands(self) -> Rs1Rs2Operands<Self::Reg>;
596}
597
598pub const trait ExecutableInstructionCsr<ExtState, CustomError = CustomErrorPlaceholder>
599where
600 Self: Instruction,
601{
602 /// Prepare CSR read.
603 ///
604 /// This method is called on each extension one by one with the `raw_value` (contents of the
605 /// corresponding CSR register) and initially zero-initialized `output_value`. In return value
606 /// every extension can accept (`Ok(true)`), ignore (`Ok(false)`) or reject (`Err(CsrError)`)
607 /// read request. For accepted reads the extension must update `output_value` accordingly, which
608 /// will be the value used by the `Zicsr` extension handler.
609 ///
610 /// Some extensions will just copy `raw_value` to output value, others will copy only some bits
611 /// or zero some bits of the `raw_value`, as required by the specification.
612 ///
613 /// `will_write` indicates whether the CSR instruction performing this read will also perform a
614 /// write immediately afterward (as part of the same instruction). It is always `true` for
615 /// `csrrw{,i}`, and `true` for `csrrs{,i}`/`csrrc{,i}` unless their `rs1`/`zimm` operand is
616 /// zero (in which case they are a pure read with no write). Some CSRs (e.g. `Zkr`'s `seed`)
617 /// are only legal to access through a genuine read-write instruction and must reject the read
618 /// when `will_write` is `false`.
619 ///
620 /// If no extension returns `Ok(true)`, the read operation is implicitly rejected as illegal
621 /// access.
622 #[inline(always)]
623 fn prepare_csr_read(
624 ext_state: &ExtState,
625 csr_index: u16,
626 will_write: bool,
627 raw_value: RegisterType<Self>,
628 output_value: &mut RegisterType<Self>,
629 ) -> Result<bool, CsrError<CustomError>> {
630 // These are for cleaner trait API without leading `_` on arguments
631 let _: &ExtState = ext_state;
632 let _: u16 = csr_index;
633 let _: bool = will_write;
634 let _: RegisterType<Self> = raw_value;
635 let _: &mut RegisterType<Self> = output_value;
636 // The default implementation is to not allow anything
637 Ok(false)
638 }
639
640 /// Prepare CSR write.
641 ///
642 /// This method is called on each extension one by one with `write_value` being prepared by the
643 /// `Zicsr` extension handler. In return value every extension can accept (`Ok(true)`), ignore
644 /// (`Ok(false)`) or reject (`Err(CsrError)`) write request. For accepted writes the extension
645 /// must update `output_value` accordingly, which will be written to the corresponding CSR
646 /// register.
647 ///
648 /// Some extensions will just copy `write_value` to output value, others will copy some bits or
649 /// zero some bits of the `write_value`, as required by the specification.
650 ///
651 /// If no extension returns `Ok(true)`, the write operation is implicitly rejected as illegal
652 /// access.
653 #[inline(always)]
654 fn prepare_csr_write(
655 ext_state: &mut ExtState,
656 csr_index: u16,
657 write_value: RegisterType<Self>,
658 output_value: &mut RegisterType<Self>,
659 ) -> Result<bool, CsrError<CustomError>> {
660 // These are for cleaner trait API without leading `_` on arguments
661 let _: &mut ExtState = ext_state;
662 let _: u16 = csr_index;
663 let _: RegisterType<Self> = write_value;
664 let _: &mut RegisterType<Self> = output_value;
665 // The default implementation is to not allow anything
666 Ok(false)
667 }
668}
669
670/// Type alias for the result returned by [`ExecutableInstruction::execute`]
671pub type ExecutableInstructionResult<T, I, CustomError> = Result<
672 ControlFlow<
673 T,
674 (
675 <I as Instruction>::Reg,
676 <<I as Instruction>::Reg as Register>::Type,
677 ),
678 >,
679 ExecutionError<Address<I>, CustomError>,
680>;
681
682/// Trait for executable instructions
683pub const trait ExecutableInstruction<
684 Regs,
685 ExtState,
686 Memory,
687 PC,
688 InstructionHandler,
689 CustomError = CustomErrorPlaceholder,
690> where
691 Self: ExecutableInstructionOperands + ExecutableInstructionCsr<ExtState, CustomError>,
692{
693 /// Execute instruction.
694 ///
695 /// Instructions might place additional constraints on `ExtState` to require additional
696 /// registers or other resources. If no such constraint is used, `()` can be used as a
697 /// placeholder.
698 ///
699 /// On success `Ok(ControlFlow::Continue((rd, rd_value)))` is returned, which will be written
700 /// into the register file. In most cases this is the only register that needs to be written. If
701 /// no value needs to be written, `Ok(ControlFlow::Continue(Default::default()))` should be
702 /// returned, which corresponds to `Ok(ControlFlow::Continue(Reg::ZERO, 0))` and is no-op.
703 fn execute(
704 self,
705 rs1rs2_values: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
706 regs: &mut Regs,
707 ext_state: &mut ExtState,
708 memory: &mut Memory,
709 program_counter: &mut PC,
710 system_instruction_handler: &mut InstructionHandler,
711 ) -> ExecutableInstructionResult<(), Self, CustomError>;
712}