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//! ### Instruction implementations are assembled, not compiled in place
90//!
91//! Instruction implementations are not compiled where they are written. `build.rs` calls
92//! `ab_riscv_macros::process_instruction_macros()`, which parses the sources marked with the
93//! `#[instruction]` and `#[instruction_execution]` attribute macros and writes a generated
94//! execution implementation per instruction set into `OUT_DIR`.
95//!
96//! Macros have documentation about how they work, but the important thing is that `match`
97//! expressions are parsed and re-assembled as needed instead of being compiled in place.
98//!
99//! Two things follow from this, and both constrain how instruction implementations must be written:
100//! * the same arm is expanded into more than one crate, so a `crate::`-relative path inside an arm
101//! resolves against whichever crate it landed in and is avoided. Shared code is reached through
102//! helper modules re-exported from [`prelude`] - like `rv64_zbb_helpers` and similar - so that an
103//! arm can name them unqualified wherever it ends up
104//! * because each arm is kept separately and names exactly the operands its instruction uses, the
105//! same arms can be emitted as something other than one large `match`. Emitting them as
106//! standalone per-instruction functions, dispatched by tail call, is what allows an interpreter
107//! to avoid decoding operands that the instruction about to run does not use, and to stop paying
108//! for extensions that are compiled in but never executed
109
110#![expect(incomplete_features, reason = "generic_const_*, explicit_tail_calls")]
111#![feature(
112 adt_const_params,
113 const_block_items,
114 const_closures,
115 const_cmp,
116 const_convert,
117 const_default,
118 const_destruct,
119 const_index,
120 const_iter,
121 const_ops,
122 const_option_ops,
123 const_result_trait_fn,
124 const_trait_impl,
125 const_try,
126 explicit_tail_calls,
127 fn_align,
128 generic_const_args,
129 generic_const_items,
130 impl_restriction,
131 inherent_associated_types,
132 integer_widen_truncate,
133 macroless_generic_const_args,
134 min_generic_const_args,
135 signed_bigint_helpers,
136 try_trait_v2,
137 widening_mul
138)]
139#![cfg_attr(test, feature(try_blocks))]
140#![cfg_attr(
141 not(any(
142 all(target_arch = "riscv32", target_feature = "zbkx"),
143 all(target_arch = "riscv64", target_feature = "zbkx")
144 )),
145 feature(portable_simd)
146)]
147#![cfg_attr(
148 any(
149 all(
150 target_arch = "riscv32",
151 any(
152 target_feature = "zbb",
153 target_feature = "zbc",
154 target_feature = "zbkb",
155 target_feature = "zbkx",
156 target_feature = "zknd",
157 target_feature = "zkne",
158 target_feature = "zknh"
159 ),
160 not(miri)
161 ),
162 all(
163 target_arch = "riscv64",
164 any(
165 target_feature = "zbb",
166 target_feature = "zbc",
167 target_feature = "zbkx",
168 target_feature = "zknd",
169 target_feature = "zkne",
170 target_feature = "zknh"
171 ),
172 not(miri)
173 )
174 ),
175 feature(riscv_ext_intrinsics)
176)]
177#![cfg_attr(
178 test,
179 expect(
180 clippy::rest_pattern_accessible_field,
181 reason = "Too verbose for tests"
182 )
183)]
184#![no_std]
185
186pub mod basic;
187mod const_utils;
188pub mod prelude;
189pub mod rv32;
190pub mod rv64;
191#[cfg(test)]
192mod tests;
193pub mod v;
194pub mod zawrs;
195pub mod zicond;
196pub mod zicsr;
197pub mod zkr;
198pub mod zvbb;
199pub mod zvbc;
200
201#[cfg(feature = "alloc")]
202extern crate alloc;
203
204use ab_riscv_primitives::prelude::*;
205#[cfg(feature = "alloc")]
206use alloc::boxed::Box;
207use core::fmt;
208use core::hint::cold_path;
209use core::marker::{Destruct, PhantomData};
210#[cfg(all(target_arch = "x86_64", any(not(miri), target_feature = "avx")))]
211use core::mem;
212use core::ops::{ControlFlow, FromResidual, Sub};
213
214type RegisterType<I> = <<I as Instruction>::Reg as Register>::Type;
215type Address<I> = RegisterType<I>;
216
217/// A GPR (General Purpose Register) file abstraction
218pub const trait RegisterFile<Reg>
219where
220 Reg: [const] Register,
221{
222 /// Read register value
223 fn read(&self, reg: Reg) -> Reg::Type;
224
225 /// Write register value
226 fn write(&mut self, reg: Reg, value: Reg::Type);
227}
228
229/// Errors for [`VirtualMemory`]
230#[derive(Debug, thiserror::Error)]
231pub enum VirtualMemoryError {
232 /// Out-of-bounds read
233 #[error("Out-of-bounds read at address {address}")]
234 OutOfBoundsRead {
235 /// Address of the out-of-bounds read
236 address: u64,
237 },
238 /// Out-of-bounds write
239 #[error("Out-of-bounds write at address {address}")]
240 OutOfBoundsWrite {
241 /// Address of the out-of-bounds write
242 address: u64,
243 },
244}
245
246/// Basic integer types that can be read and written to/from memory freely
247pub impl(self) trait BasicInt: Sized + Copy + 'static {}
248
249impl BasicInt for u8 {}
250impl BasicInt for u16 {}
251impl BasicInt for u32 {}
252impl BasicInt for u64 {}
253impl BasicInt for i8 {}
254impl BasicInt for i16 {}
255impl BasicInt for i32 {}
256impl BasicInt for i64 {}
257
258/// Virtual memory interface
259pub const trait VirtualMemory {
260 /// Read a value from memory at the specified address
261 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
262 where
263 T: BasicInt;
264
265 /// Unchecked read a value from memory at the specified address.
266 ///
267 /// # Safety
268 /// The address and value must be in-bounds.
269 unsafe fn read_unchecked<T>(&self, address: u64) -> T
270 where
271 T: BasicInt;
272
273 /// Read a contiguous byte slice from memory
274 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError>;
275
276 /// Read as many contiguous bytes as possible starting at `address`, up to `len` bytes total.
277 ///
278 /// Can return an empty slice in cases like when the address is out of bounds.
279 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8];
280
281 /// Write a value to memory at the specified address
282 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
283 where
284 T: BasicInt;
285
286 /// Write a contiguous byte slice to memory
287 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError>;
288}
289
290#[cfg(feature = "alloc")]
291impl<M> VirtualMemory for Box<M>
292where
293 M: VirtualMemory,
294{
295 #[inline(always)]
296 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
297 where
298 T: BasicInt,
299 {
300 self.as_ref().read(address)
301 }
302
303 #[inline(always)]
304 unsafe fn read_unchecked<T>(&self, address: u64) -> T
305 where
306 T: BasicInt,
307 {
308 // SAFETY: Guaranteed by the caller
309 unsafe { self.as_ref().read_unchecked(address) }
310 }
311
312 #[inline(always)]
313 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
314 self.as_ref().read_slice(address, len)
315 }
316
317 #[inline(always)]
318 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
319 self.as_ref().read_slice_up_to(address, len)
320 }
321
322 #[inline(always)]
323 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
324 where
325 T: BasicInt,
326 {
327 self.as_mut().write(address, value)
328 }
329
330 #[inline(always)]
331 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
332 self.as_mut().write_slice(address, data)
333 }
334}
335
336/// Generic program counter
337pub const trait ProgramCounter<Address, Memory>
338where
339 Address: Copy,
340{
341 /// Get the current value of the program counter
342 fn get_pc(&self) -> Address;
343
344 /// Get the previous value of the program counter before executing an `instruction`.
345 ///
346 /// This is usually called from under instruction execution when the program counter is already
347 /// advanced during instruction fetching. As such, `pc - instruction_size` is expected to never
348 /// underflow.
349 #[inline(always)]
350 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
351 fn old_pc(&self, instruction_size: u8) -> Address
352 where
353 Address: [const] From<u8> + [const] Sub<Output = Address>,
354 {
355 // TODO: Wrapping subtraction would be nice, but causes a lot of additional generic bounds
356 // that are bad for ergonomics
357 self.get_pc() - Address::from(instruction_size)
358 }
359
360 /// Apply [`ExecutionResult::Branch`], continuing `offset` bytes from the instruction being
361 /// executed.
362 ///
363 /// This is [`Self::try_set_pc_relative()`] followed, if it refused the target, by
364 /// [`Self::failed_branch()`], and exists for callers that have nothing better to do with a
365 /// refused target than ask what was wrong with it right there.
366 #[inline(always)]
367 fn set_pc_relative(
368 &mut self,
369 memory: &Memory,
370 instruction_size: u8,
371 offset: i32,
372 ) -> Result<ControlFlow<()>, ExecutionError<Address>>
373 where
374 Self: [const] ProgramCounter<Address, Memory>,
375 {
376 // SAFETY: A refused target is handed straight to `failed_branch()` below, before anything
377 // else can observe the program counter
378 if unsafe { self.try_set_pc_relative(instruction_size, offset) } {
379 return Ok(ControlFlow::Continue(()));
380 }
381
382 cold_path();
383
384 // SAFETY: `try_set_pc_relative()` has just refused the target
385 unsafe { self.failed_branch(memory) }
386 }
387
388 /// Move `offset` bytes from the instruction being executed for targets this can resolve.
389 ///
390 /// A simple implementation can resolve the offset against the program counter and validate it
391 /// the way [`Self::set_pc()`] does. Implementation that keeps the program counter as a position
392 /// in an already decoded instruction stream can move within that stream directly, which avoids
393 /// converting an address back into a position.
394 ///
395 /// Returns `true` when the program counter now points at the target, and `false` when the
396 /// target is not somewhere it may point at.
397 ///
398 /// Separate from [`Self::failed_branch()`] for the sake of threaded dispatch, where working out
399 /// what exactly is wrong with a target is code that no branch or jump ever runs, and inlining
400 /// it into the handler of every one of them puts it between the parts of the interpreter that
401 /// do run frequently. Split this way, a handler can hand a refused target to a cold
402 /// continuation with a tail call, which is the one call that costs it nothing, since nothing it
403 /// holds has to survive one.
404 ///
405 /// # Safety
406 /// When this returns `false`, the program counter is left holding the refused target, which is
407 /// not a position to fetch from - it is what [`Self::failed_branch()`] reads to say what was
408 /// wrong with it. That call must come next before anything else observes the program counter.
409 unsafe fn try_set_pc_relative(&mut self, instruction_size: u8, offset: i32) -> bool;
410
411 /// Say what is wrong with the target [`Self::try_set_pc_relative()`] refused.
412 ///
413 /// Implementations are expected to be `#[cold]` and `#[inline(never)]`: this is the half of
414 /// [`Self::set_pc_relative()`] that a program only reaches on its way out.
415 ///
416 /// # Safety
417 /// Must be called right after [`Self::try_set_pc_relative()`] returned `false`, with nothing in
418 /// between having observed the program counter.
419 unsafe fn failed_branch(
420 &mut self,
421 memory: &Memory,
422 ) -> Result<ControlFlow<()>, ExecutionError<Address>>;
423
424 /// Set the current value of the program counter
425 fn set_pc(
426 &mut self,
427 memory: &Memory,
428 pc: Address,
429 ) -> Result<ControlFlow<()>, ExecutionError<Address>>;
430}
431
432/// Address wrapper for [`ExecutionError`] with an alignment of 4 rather than its natural one.
433///
434/// This is needed for [`ExecutionResult`] that contains [`ExecutionError`] to fit within 16 bytes
435/// or two native registers on 64-bit platforms.
436#[derive(Copy, Clone, Eq, PartialEq)]
437#[repr(C, packed(4))]
438pub struct PackedAddress<Address>(Address);
439
440impl<Address> PackedAddress<Address>
441where
442 Address: Copy,
443{
444 /// Create a new instance
445 #[inline(always)]
446 pub const fn new(address: Address) -> Self {
447 Self(address)
448 }
449
450 /// Read the address back
451 #[inline(always)]
452 pub const fn get(self) -> Address {
453 self.0
454 }
455}
456
457const impl<Address> From<Address> for PackedAddress<Address>
458where
459 Address: Copy + [const] Destruct,
460{
461 #[inline(always)]
462 fn from(address: Address) -> Self {
463 Self(address)
464 }
465}
466
467impl<Address> fmt::Debug for PackedAddress<Address>
468where
469 Address: fmt::Debug + Copy,
470{
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472 let address = self.0;
473 fmt::Debug::fmt(&address, f)
474 }
475}
476
477impl<Address> fmt::Display for PackedAddress<Address>
478where
479 Address: fmt::Display + Copy,
480{
481 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 let address = self.0;
483 fmt::Display::fmt(&address, f)
484 }
485}
486
487impl<Address> fmt::LowerHex for PackedAddress<Address>
488where
489 Address: fmt::LowerHex + Copy,
490{
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 let address = self.0;
493 fmt::LowerHex::fmt(&address, f)
494 }
495}
496
497/// Execution errors.
498///
499/// The variants of [`VirtualMemoryError`] and [`CsrError`] are inlined
500/// here rather than nested. Nesting cost an extra discriminant per level, which made this type 24
501/// bytes; flattened it is 16, which is what lets `Result<_, ExecutionError>` be returned in
502/// registers instead of through a hidden out-pointer. `From` implementations for all three are
503/// provided, so `?` on them keeps working unchanged.
504#[derive(Debug, thiserror::Error)]
505pub enum ExecutionError<Address>
506where
507 Address: Copy,
508{
509 /// Unaligned instruction
510 #[error("Unaligned instruction at address {address}")]
511 UnalignedInstruction {
512 /// Address of the unaligned instruction fetch
513 address: PackedAddress<Address>,
514 },
515 /// Out-of-bounds read
516 #[error("Out-of-bounds read at address {address}")]
517 OutOfBoundsRead {
518 /// Address of the out-of-bounds read
519 address: PackedAddress<u64>,
520 },
521 /// Out-of-bounds write
522 #[error("Out-of-bounds write at address {address}")]
523 OutOfBoundsWrite {
524 /// Address of the out-of-bounds write
525 address: PackedAddress<u64>,
526 },
527 /// Misaligned read from an instruction that requires natural alignment (e.g. `lr`), unlike
528 /// ordinary loads
529 #[error("Misaligned read at address {address}")]
530 MisalignedRead {
531 /// Address of the misaligned read
532 address: PackedAddress<u64>,
533 },
534 /// Misaligned write from an instruction that requires natural alignment (e.g. `sc`), unlike
535 /// ordinary stores
536 #[error("Misaligned write at address {address}")]
537 MisalignedWrite {
538 /// Address of the misaligned write
539 address: PackedAddress<u64>,
540 },
541 /// Misaligned atomic access (e.g. AMO) whose accessed bytes are not all within the same
542 /// misaligned atomicity granule, unlike an ordinary misaligned read or write
543 #[error("Misaligned atomic access at address {address}")]
544 MisalignedAtomic {
545 /// Address of the misaligned atomic access
546 address: PackedAddress<u64>,
547 },
548 /// Unsupported `ecall` instruction
549 #[error("Unsupported `ecall` instruction at address {address:#x}")]
550 EcallUnsupported {
551 /// Address of the unsupported instruction
552 address: PackedAddress<Address>,
553 },
554 /// Unimplemented/illegal instruction
555 #[error("Unimplemented/illegal instruction at address {address:#x}")]
556 IllegalInstruction {
557 /// Address of the `unimp` instruction
558 address: PackedAddress<Address>,
559 },
560 /// Read only CSR
561 #[error("Read only CSR {csr_index:#x}")]
562 CsrReadOnly {
563 /// Index of CSR where write was attempted
564 csr_index: u16,
565 },
566 /// Illegal read access
567 #[error("Illegal read access to CSR {csr_index:#x}")]
568 CsrIllegalRead {
569 /// Index of the accessed CSR
570 csr_index: u16,
571 },
572 /// Illegal write access
573 #[error("Illegal write access to CSR {csr_index:#x}")]
574 CsrIllegalWrite {
575 /// Index of the accessed CSR
576 csr_index: u16,
577 },
578 /// Unknown CSR
579 #[error("Unknown CSR {csr_index:#x}")]
580 CsrUnknown {
581 /// Index of the accessed CSR
582 csr_index: u16,
583 },
584 /// Insufficient privilege level
585 #[error(
586 "Insufficient privilege level for CSR {csr_index:#x}: required {required:?}, \
587 current {current:?}"
588 )]
589 CsrInsufficientPrivilege {
590 /// Index of the accessed CSR
591 csr_index: u16,
592 /// Required privilege level
593 required: PrivilegeLevel,
594 /// Current privilege level
595 current: PrivilegeLevel,
596 },
597 /// Threaded execution is not supported on this platform.
598 ///
599 /// See [`OpaqueThreadedExecutionResult::platform_supported()`] for what makes a platform
600 /// unsupported and why the answer is a run-time one.
601 #[error("Threaded execution is not supported on this platform")]
602 UnsupportedPlatform,
603 /// Custom error
604 #[error("Custom error: {0:?}")]
605 Custom([u8; 8]),
606}
607
608/// Where execution continues after an instruction, or why it could not.
609///
610/// Instructions describe control flow rather than performing it: they say where execution goes next
611/// instead of moving the program counter themselves. This keeps instruction bodies independent of
612/// how the program counter is represented, which is what allows the same bodies to drive both an
613/// interpreter loop that owns a program counter and one that carries it in a register.
614#[derive(Debug)]
615pub enum ExecutionResult<Reg>
616where
617 Reg: Register,
618{
619 /// Write the register and continue with the instruction that follows this one
620 Continue {
621 /// Register to write
622 rd: Reg,
623 /// Value to write into it
624 value: Reg::Type,
625 },
626 /// Continue with the instruction that follows this one without writing to `rd` register
627 ContinueNoWrite,
628 /// Continue `offset` bytes away from the address of *this* instruction.
629 ///
630 /// Keeping it relative rather than resolving it against the program counter here means a
631 /// pre-decoded interpreter can reach the target by moving within the decoded stream instead of
632 /// converting an address back into a position.
633 Branch {
634 /// Signed byte offset from this instruction
635 offset: i32,
636 },
637 /// Continue at an absolute guest address, as `jalr`-style jumps produce
638 Jump {
639 /// Address to continue at
640 target: Reg::Type,
641 },
642 /// Stop execution
643 Break,
644 /// Execution failed
645 Err(ExecutionError<Reg::Type>),
646}
647
648const {
649 // Ensure result type retains its size
650 assert!(size_of::<ExecutionResult<Reg<u64>>>() <= 16);
651 assert!(size_of::<ExecutionResult<Reg<u32>>>() <= 16);
652}
653
654const impl<Reg> From<ExecutionError<Reg::Type>> for ExecutionResult<Reg>
655where
656 Reg: Register,
657{
658 #[inline(always)]
659 fn from(error: ExecutionError<Reg::Type>) -> Self {
660 cold_path();
661 Self::Err(error)
662 }
663}
664
665const impl<Reg> FromResidual<Result<!, ExecutionError<Reg::Type>>> for ExecutionResult<Reg>
666where
667 Reg: Register,
668{
669 #[inline(always)]
670 fn from_residual(residual: Result<!, ExecutionError<Reg::Type>>) -> Self {
671 match residual {
672 Ok(never) => match never {},
673 Err(error) => {
674 cold_path();
675 Self::Err(error)
676 }
677 }
678 }
679}
680
681const impl<Reg> FromResidual<Result<!, VirtualMemoryError>> for ExecutionResult<Reg>
682where
683 Reg: Register,
684{
685 #[inline(always)]
686 fn from_residual(residual: Result<!, VirtualMemoryError>) -> Self {
687 match residual {
688 Ok(never) => match never {},
689 Err(error) => {
690 cold_path();
691 Self::Err(ExecutionError::from(error))
692 }
693 }
694 }
695}
696
697const impl<Reg> FromResidual<Result<!, CsrError>> for ExecutionResult<Reg>
698where
699 Reg: Register,
700{
701 #[inline(always)]
702 fn from_residual(residual: Result<!, CsrError>) -> Self {
703 match residual {
704 Ok(never) => match never {},
705 Err(error) => {
706 cold_path();
707 Self::Err(ExecutionError::from(error))
708 }
709 }
710 }
711}
712
713const impl<Address> From<VirtualMemoryError> for ExecutionError<Address>
714where
715 Address: Copy,
716{
717 #[inline(always)]
718 fn from(value: VirtualMemoryError) -> Self {
719 match value {
720 VirtualMemoryError::OutOfBoundsRead { address } => Self::OutOfBoundsRead {
721 address: PackedAddress::new(address),
722 },
723 VirtualMemoryError::OutOfBoundsWrite { address } => Self::OutOfBoundsWrite {
724 address: PackedAddress::new(address),
725 },
726 }
727 }
728}
729
730const impl<Address> From<CsrError> for ExecutionError<Address>
731where
732 Address: Copy,
733{
734 #[inline(always)]
735 fn from(value: CsrError) -> Self {
736 match value {
737 CsrError::ReadOnly { csr_index } => Self::CsrReadOnly { csr_index },
738 CsrError::IllegalRead { csr_index } => Self::CsrIllegalRead { csr_index },
739 CsrError::IllegalWrite { csr_index } => Self::CsrIllegalWrite { csr_index },
740 CsrError::Unknown { csr_index } => Self::CsrUnknown { csr_index },
741 CsrError::InsufficientPrivilege {
742 csr_index,
743 required,
744 current,
745 } => Self::CsrInsufficientPrivilege {
746 csr_index,
747 required,
748 current,
749 },
750 CsrError::Custom(error) => Self::Custom(error),
751 }
752 }
753}
754
755/// Result of [`InstructionFetcher::fetch_instruction()`] call
756#[derive(Debug)]
757pub enum FetchInstructionResult<I>
758where
759 I: Instruction,
760{
761 /// Instruction to execute
762 Instruction(I),
763 /// Nothing to execute here, carry on from wherever the program counter now points
764 Continue,
765 /// Stop execution
766 Break,
767 /// Fetching failed
768 Err(ExecutionError<Address<I>>),
769}
770
771/// Generic instruction fetcher.
772///
773/// # Performance considerations
774/// In threaded dispatch, the instruction fetcher is moved through the handler chain by value, so it
775/// should have 16 bytes size (next instruction pointer + pointer to extra state) and no drop glue.
776/// A fetcher that owns something is dropped by whichever handler ends execution. Every handler that
777/// can fail is a candidate for that, which forces a stack frame, callee-saved register spills, and
778/// a reload into the hot path of every load, store, branch and jump. A fetcher that only borrows
779/// what it walks (`Copy`, or at least `!needs_drop`) keeps them all frameless and fast.
780pub const trait InstructionFetcher<I, Memory>
781where
782 Self: ProgramCounter<Address<I>, Memory>,
783 I: Instruction,
784{
785 /// Read the instruction at the current position, leaving the program counter on it.
786 ///
787 /// [`Self::advance()`] is what moves past it, and the two are separate because of what deriving
788 /// the size from the instruction is costly for threaded dispatch: it makes the address of the
789 /// *next* instruction depend on decoding the current one. In threaded dispatch caller already
790 /// knows which variant it is holding and advances by a constant instead, allowing the next load
791 /// to be issued immediately.
792 fn peek_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I>;
793
794 /// Move the program counter past an instruction of `instruction_size` bytes that
795 /// [`Self::peek_instruction()`] has just returned.
796 ///
797 /// # Safety
798 /// Must be called exactly once after a successful [`Self::peek_instruction()`], with the size
799 /// of the instruction that call returned. Implementations are free to rely on that and skip
800 /// checks accordingly: one over a pre-decoded stream that is known to end with a jump, for
801 /// instance, treats the resulting position as valid without bounds-checking it.
802 unsafe fn advance(&mut self, instruction_size: u8);
803
804 /// Fetch a single instruction at a specified address and advance the program counter on
805 /// successful fetch.
806 ///
807 /// This is [`Self::peek_instruction()`] followed by [`Self::advance()`] and exists for callers
808 /// that do not know what they are about to fetch, which is every caller that dispatches
809 /// through a `match` rather than through per-variant handlers.
810 fn fetch_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I>;
811}
812
813/// CSR error
814#[derive(Debug, thiserror::Error)]
815pub enum CsrError {
816 /// Read only CSR
817 #[error("Read only CSR {csr_index:#x}")]
818 ReadOnly {
819 /// Index of CSR where write was attempted
820 csr_index: u16,
821 },
822 /// Illegal read access
823 #[error("Illegal read access to CSR {csr_index:#x}")]
824 IllegalRead {
825 /// Index of the accessed CSR
826 csr_index: u16,
827 },
828 /// Illegal write access
829 #[error("Illegal write access to CSR {csr_index:#x}")]
830 IllegalWrite {
831 /// Index of the accessed CSR
832 csr_index: u16,
833 },
834 /// Unknown CSR
835 #[error("Unknown CSR {csr_index:#x}")]
836 Unknown {
837 /// Index of the accessed CSR
838 csr_index: u16,
839 },
840 /// Insufficient privilege level
841 #[error(
842 "Insufficient privilege level for CSR {csr_index:#x}: required {required:?}, \
843 current {current:?}"
844 )]
845 InsufficientPrivilege {
846 /// Index of the accessed CSR
847 csr_index: u16,
848 /// Required privilege level
849 required: PrivilegeLevel,
850 /// Current privilege level
851 current: PrivilegeLevel,
852 },
853 /// Custom error
854 #[error("Custom error: {0:?}")]
855 Custom([u8; 8]),
856}
857
858/// CSRs (Control and Status Registers)
859pub const trait Csrs<Reg>
860where
861 Reg: [const] Register,
862{
863 /// Current privilege level
864 #[inline(always)]
865 fn privilege_level(&self) -> PrivilegeLevel {
866 PrivilegeLevel::Machine
867 }
868
869 /// Reads register value
870 fn read_csr(&self, csr_index: u16) -> Result<Reg::Type, CsrError>;
871
872 /// Writes register value
873 fn write_csr(&mut self, csr_index: u16, value: Reg::Type) -> Result<(), CsrError>;
874}
875
876// Convenience for threaded execution
877const impl<Reg, T> Csrs<Reg> for &mut T
878where
879 Reg: [const] Register,
880 T: [const] Csrs<Reg>,
881{
882 #[inline(always)]
883 fn privilege_level(&self) -> PrivilegeLevel {
884 T::privilege_level(self)
885 }
886
887 #[inline(always)]
888 fn read_csr(&self, csr_index: u16) -> Result<Reg::Type, CsrError> {
889 T::read_csr(self, csr_index)
890 }
891
892 #[inline(always)]
893 fn write_csr(&mut self, csr_index: u16, value: Reg::Type) -> Result<(), CsrError> {
894 T::write_csr(self, csr_index, value)
895 }
896}
897
898/// Custom handler for system instructions `ecall` and `ebreak`
899pub const trait SystemInstructionHandler<Reg, Regs, Memory, PC>
900where
901 Reg: Register,
902{
903 // TODO: Figure out the correct API for this method
904 /// Handle a `fence` instruction
905 #[inline(always)]
906 fn handle_fence(&mut self, pred: u8, succ: u8) {
907 let _: u8 = pred;
908 let _: u8 = succ;
909 // NOP by default
910 }
911
912 // TODO: Figure out the correct API for this method
913 /// Handle a `fence.tso` instruction
914 #[inline(always)]
915 fn handle_fence_tso(&mut self) {
916 // NOP by default
917 }
918
919 /// Handle an `ecall` instruction
920 fn handle_ecall(
921 &mut self,
922 regs: &mut Regs,
923 memory: &mut Memory,
924 program_counter: &mut PC,
925 ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type>>;
926
927 /// Handle an `ebreak` instruction.
928 ///
929 /// NOTE: the program counter here is the current value, meaning it is already incremented past
930 /// the instruction itself.
931 #[inline(always)]
932 fn handle_ebreak(&mut self, regs: &mut Regs, memory: &mut Memory, pc: Reg::Type) {
933 // These are for cleaner trait API without leading `_` on arguments
934 let _: &Regs = regs;
935 let _: &mut Memory = memory;
936 let _: Reg::Type = pc;
937 // NOP by default
938 }
939}
940
941// Convenience for threaded execution
942const impl<Reg, Regs, Memory, PC, T> SystemInstructionHandler<Reg, Regs, Memory, PC> for &mut T
943where
944 Reg: [const] Register,
945 T: [const] SystemInstructionHandler<Reg, Regs, Memory, PC>,
946{
947 #[inline(always)]
948 fn handle_fence(&mut self, pred: u8, succ: u8) {
949 T::handle_fence(self, pred, succ);
950 }
951
952 #[inline(always)]
953 fn handle_fence_tso(&mut self) {
954 T::handle_fence_tso(self);
955 }
956
957 #[inline(always)]
958 fn handle_ecall(
959 &mut self,
960 regs: &mut Regs,
961 memory: &mut Memory,
962 program_counter: &mut PC,
963 ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type>> {
964 T::handle_ecall(self, regs, memory, program_counter)
965 }
966
967 #[inline(always)]
968 fn handle_ebreak(&mut self, regs: &mut Regs, memory: &mut Memory, pc: Reg::Type) {
969 T::handle_ebreak(self, regs, memory, pc);
970 }
971}
972
973/// `rs1`/`rs2` instruction operands
974#[derive(Debug, Default, Copy, Clone)]
975pub struct Rs1Rs2Operands<Reg> {
976 /// `rs1` operand.
977 ///
978 /// Zero register if `rs1` was missing in the original instruction definition.
979 pub rs1: Reg,
980 /// `rs2` operand.
981 ///
982 /// Zero register if `rs2` was missing in the original instruction definition.
983 pub rs2: Reg,
984}
985
986/// `rs1`/`rs2` instruction operands
987#[derive(Debug, Default, Copy, Clone)]
988pub struct Rs1Rs2OperandValues<RegType> {
989 /// `rs1` operand value.
990 ///
991 /// Zero if `rs1` was missing in the original instruction definition.
992 pub rs1_value: RegType,
993 /// `rs2` operand value.
994 ///
995 /// Zero if `rs2` was missing in the original instruction definition.
996 pub rs2_value: RegType,
997}
998
999/// `rs1`/`rs2` instruction operands
1000pub const trait ExecutableInstructionOperands
1001where
1002 Self: Instruction,
1003{
1004 /// `rs1`/`rs2` instruction operands.
1005 ///
1006 /// Returns zero register for `rs1`/`rs2` that were missing in the original instruction
1007 /// definition.
1008 fn get_rs1_rs2_operands(self) -> Rs1Rs2Operands<Self::Reg>;
1009}
1010
1011pub const trait ExecutableInstructionCsr<Env>
1012where
1013 Self: Instruction,
1014{
1015 /// Prepare CSR read.
1016 ///
1017 /// This method is called on each extension one by one with the `raw_value` (contents of the
1018 /// corresponding CSR register) and initially zero-initialized `output_value`. In return value
1019 /// every extension can accept (`Ok(true)`), ignore (`Ok(false)`) or reject (`Err(CsrError)`)
1020 /// read request. For accepted reads the extension must update `output_value` accordingly, which
1021 /// will be the value used by the `Zicsr` extension handler.
1022 ///
1023 /// Some extensions will just copy `raw_value` to output value, others will copy only some bits
1024 /// or zero some bits of the `raw_value`, as required by the specification.
1025 ///
1026 /// `will_write` indicates whether the CSR instruction performing this read will also perform a
1027 /// write immediately afterward (as part of the same instruction). It is always `true` for
1028 /// `csrrw{,i}`, and `true` for `csrrs{,i}`/`csrrc{,i}` unless their `rs1`/`zimm` operand is
1029 /// zero (in which case they are a pure read with no write). Some CSRs (e.g. `Zkr`'s `seed`)
1030 /// are only legal to access through a genuine read-write instruction and must reject the read
1031 /// when `will_write` is `false`.
1032 ///
1033 /// If no extension returns `Ok(true)`, the read operation is implicitly rejected as illegal
1034 /// access.
1035 #[inline(always)]
1036 fn prepare_csr_read(
1037 env: &Env,
1038 csr_index: u16,
1039 will_write: bool,
1040 raw_value: RegisterType<Self>,
1041 output_value: &mut RegisterType<Self>,
1042 ) -> Result<bool, CsrError> {
1043 // These are for cleaner trait API without leading `_` on arguments
1044 let _: &Env = env;
1045 let _: u16 = csr_index;
1046 let _: bool = will_write;
1047 let _: RegisterType<Self> = raw_value;
1048 let _: &mut RegisterType<Self> = output_value;
1049 // The default implementation is to not allow anything
1050 Ok(false)
1051 }
1052
1053 /// Prepare CSR write.
1054 ///
1055 /// This method is called on each extension one by one with `write_value` being prepared by the
1056 /// `Zicsr` extension handler. In return value every extension can accept (`Ok(true)`), ignore
1057 /// (`Ok(false)`) or reject (`Err(CsrError)`) write request. For accepted writes the extension
1058 /// must update `output_value` accordingly, which will be written to the corresponding CSR
1059 /// register.
1060 ///
1061 /// Some extensions will just copy `write_value` to output value, others will copy some bits or
1062 /// zero some bits of the `write_value`, as required by the specification.
1063 ///
1064 /// If no extension returns `Ok(true)`, the write operation is implicitly rejected as illegal
1065 /// access.
1066 #[inline(always)]
1067 fn prepare_csr_write(
1068 env: &mut Env,
1069 csr_index: u16,
1070 write_value: RegisterType<Self>,
1071 output_value: &mut RegisterType<Self>,
1072 ) -> Result<bool, CsrError> {
1073 // These are for cleaner trait API without leading `_` on arguments
1074 let _: &mut Env = env;
1075 let _: u16 = csr_index;
1076 let _: RegisterType<Self> = write_value;
1077 let _: &mut RegisterType<Self> = output_value;
1078 // The default implementation is to not allow anything
1079 Ok(false)
1080 }
1081}
1082
1083/// Trait for executable instructions
1084pub const trait ExecutableInstruction<Regs, Env, Memory, PC>
1085where
1086 Self: ExecutableInstructionOperands + ExecutableInstructionCsr<Env>,
1087{
1088 /// Execute instruction.
1089 ///
1090 /// Instructions might place additional constraints on `Env` to require additional registers,
1091 /// handlers (like [`SystemInstructionHandler`]) or other resources. If no such constraint is
1092 /// used, `()` can be used as a placeholder.
1093 ///
1094 /// On success `ExecutionResult::Continue { rd: rd, value: rd_value }` is returned, which will
1095 /// be written into the register file. In most cases this is the only register that needs to
1096 /// be written. If no value needs to be written, `ExecutionResult::ContinueNoWrite` should be
1097 /// returned, which skips the register file write entirely.
1098 fn execute(
1099 self,
1100 rs1rs2_values: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
1101 regs: &mut Regs,
1102 env: &mut Env,
1103 memory: &mut Memory,
1104 program_counter: &mut PC,
1105 ) -> ExecutionResult<Self::Reg>;
1106}
1107
1108/// Outcome of [`ThreadedExecutableInstruction::execute_threaded()`].
1109///
1110/// Unlike [`ExecutionResult`], this is produced exactly once, by the handler that stops the chain
1111/// and is not on the per-instruction path.
1112///
1113/// For performance reasons everything inside has to fit into what the platform returns in
1114/// registers, in the shape of [`OpaqueThreadedExecutionResult`]. A caller that needs its fetcher
1115/// after execution keeps its own copy and sets the program counter to the correct value after
1116/// execution manually.
1117#[derive(Debug)]
1118pub struct ThreadedExecutionResult<I>
1119where
1120 I: Instruction,
1121{
1122 /// Program counter as of the moment execution stopped
1123 pub program_counter: Address<I>,
1124 /// Why execution stopped
1125 pub outcome: Result<(), ExecutionError<Address<I>>>,
1126}
1127
1128impl<I> ThreadedExecutionResult<I>
1129where
1130 I: Instruction,
1131{
1132 /// Execution stopped gracefully
1133 #[inline(always)]
1134 pub const fn stopped(program_counter: Address<I>) -> Self {
1135 Self {
1136 program_counter,
1137 outcome: Ok(()),
1138 }
1139 }
1140
1141 /// Execution failed
1142 #[inline(always)]
1143 pub const fn failed(program_counter: Address<I>, error: ExecutionError<Address<I>>) -> Self {
1144 cold_path();
1145 Self {
1146 program_counter,
1147 outcome: Err(error),
1148 }
1149 }
1150}
1151
1152cfg_select! {
1153 all(target_arch = "x86_64", any(not(miri), target_feature = "avx")) => {
1154 /// x86-64 System V only returns an aggregate larger than two eightbytes in registers when
1155 /// it is a single vector, hence a 256-bit one (a pair of 128-bit vectors classifies as
1156 /// memory). Moving one of those needs AVX, which is what makes this the only shape here
1157 /// with a run-time platform requirement.
1158 type OpaqueLanes = core::arch::x86_64::__m256i;
1159 }
1160 all(target_arch = "aarch64", any(not(miri), target_feature = "neon")) => {
1161 /// AArch64 returns an aggregate larger than 16 bytes in registers only as a homogeneous
1162 /// aggregate of up to four members, hence three `u64`.
1163 // TODO: `[f64; 3]` is used temporarily due to compiler bug:
1164 // https://github.com/rust-lang/rust/issues/161382
1165 type OpaqueLanes = [f64; 3];
1166 }
1167 _ => {
1168 type OpaqueLanes = [u64; 3];
1169 }
1170}
1171
1172/// [`ThreadedExecutionResult`] in the shape tail-called handlers return it in.
1173///
1174/// Threaded handler internally returns this rather than [`ThreadedExecutionResult`] so that the
1175/// outcome travels in registers instead of through a hidden out-pointer where possible for
1176/// performance reasons (primarily on x86-64 due to a limited number of usable GPRs in the ABI).
1177///
1178/// On x86-64 the lanes are a 256-bit vector, so producing one of these executes AVX instructions -
1179/// see [`Self::platform_supported()`] and [`Self::new()`].
1180#[derive(Debug, Copy, Clone)]
1181#[repr(transparent)]
1182pub struct OpaqueThreadedExecutionResult<I> {
1183 lanes: OpaqueLanes,
1184 phantom: PhantomData<I>,
1185}
1186
1187impl<I> OpaqueThreadedExecutionResult<I>
1188where
1189 I: Instruction,
1190{
1191 const TAG_STOPPED: u64 = 0;
1192 const TAG_UNALIGNED_INSTRUCTION: u64 = 1;
1193 const TAG_OUT_OF_BOUNDS_READ: u64 = 2;
1194 const TAG_OUT_OF_BOUNDS_WRITE: u64 = 3;
1195 const TAG_MISALIGNED_READ: u64 = 4;
1196 const TAG_MISALIGNED_WRITE: u64 = 5;
1197 const TAG_MISALIGNED_ATOMIC: u64 = 6;
1198 const TAG_ECALL_UNSUPPORTED: u64 = 7;
1199 const TAG_ILLEGAL_INSTRUCTION: u64 = 8;
1200 const TAG_CSR_READ_ONLY: u64 = 9;
1201 const TAG_CSR_ILLEGAL_READ: u64 = 10;
1202 const TAG_CSR_ILLEGAL_WRITE: u64 = 11;
1203 const TAG_CSR_UNKNOWN: u64 = 12;
1204 const TAG_CSR_INSUFFICIENT_PRIVILEGE: u64 = 13;
1205 const TAG_UNSUPPORTED_PLATFORM: u64 = 14;
1206 const TAG_CUSTOM: u64 = 15;
1207
1208 /// Whether this platform can carry an outcome the way [`Self::new()`] does.
1209 ///
1210 /// It cannot on an x86-64 CPU without AVX, where the lanes are a 256-bit vector: Rust targets
1211 /// x86-64-v1 by default, so a runtime check is necessary.
1212 ///
1213 /// This is called once within [`ThreadedExecutableInstruction::execute_threaded()`].
1214 #[inline(always)]
1215 #[must_use]
1216 pub fn platform_supported() -> bool {
1217 cfg_select! {
1218 all(target_arch = "x86_64", not(target_feature = "avx")) => {
1219 cpufeatures::new!(cpuid_avx, "avx");
1220
1221 cpuid_avx::get()
1222 }
1223 _ => true,
1224 }
1225 }
1226
1227 /// Serialize an outcome into the shape handlers return.
1228 ///
1229 /// # Safety
1230 /// [`Self::platform_supported()`] must return `true`.
1231 #[inline(always)]
1232 pub unsafe fn new(result: ThreadedExecutionResult<I>) -> Self {
1233 let program_counter = result.program_counter.as_u64();
1234
1235 let (tag, payload) = match result.outcome {
1236 Ok(()) => (Self::TAG_STOPPED, 0),
1237 Err(error) => match error {
1238 ExecutionError::UnalignedInstruction { address } => {
1239 (Self::TAG_UNALIGNED_INSTRUCTION, address.get().as_u64())
1240 }
1241 ExecutionError::OutOfBoundsRead { address } => {
1242 (Self::TAG_OUT_OF_BOUNDS_READ, address.get())
1243 }
1244 ExecutionError::OutOfBoundsWrite { address } => {
1245 (Self::TAG_OUT_OF_BOUNDS_WRITE, address.get())
1246 }
1247 ExecutionError::MisalignedRead { address } => {
1248 (Self::TAG_MISALIGNED_READ, address.get())
1249 }
1250 ExecutionError::MisalignedWrite { address } => {
1251 (Self::TAG_MISALIGNED_WRITE, address.get())
1252 }
1253 ExecutionError::MisalignedAtomic { address } => {
1254 (Self::TAG_MISALIGNED_ATOMIC, address.get())
1255 }
1256 ExecutionError::EcallUnsupported { address } => {
1257 (Self::TAG_ECALL_UNSUPPORTED, address.get().as_u64())
1258 }
1259 ExecutionError::IllegalInstruction { address } => {
1260 (Self::TAG_ILLEGAL_INSTRUCTION, address.get().as_u64())
1261 }
1262 ExecutionError::CsrReadOnly { csr_index } => {
1263 (Self::TAG_CSR_READ_ONLY, u64::from(csr_index))
1264 }
1265 ExecutionError::CsrIllegalRead { csr_index } => {
1266 (Self::TAG_CSR_ILLEGAL_READ, u64::from(csr_index))
1267 }
1268 ExecutionError::CsrIllegalWrite { csr_index } => {
1269 (Self::TAG_CSR_ILLEGAL_WRITE, u64::from(csr_index))
1270 }
1271 ExecutionError::CsrUnknown { csr_index } => {
1272 (Self::TAG_CSR_UNKNOWN, u64::from(csr_index))
1273 }
1274 ExecutionError::CsrInsufficientPrivilege {
1275 csr_index,
1276 required,
1277 current,
1278 } => (
1279 Self::TAG_CSR_INSUFFICIENT_PRIVILEGE,
1280 u64::from(csr_index)
1281 | (u64::from(required.to_bits()) << 16)
1282 | (u64::from(current.to_bits()) << 24),
1283 ),
1284 ExecutionError::Custom(error) => (Self::TAG_CUSTOM, u64::from_le_bytes(error)),
1285 ExecutionError::UnsupportedPlatform => (Self::TAG_UNSUPPORTED_PLATFORM, 0),
1286 },
1287 };
1288
1289 Self {
1290 lanes: cfg_select! {
1291 all(target_arch = "x86_64", any(not(miri), target_feature = "avx")) => {
1292 // SAFETY: Method contract guarantees that `Self::platform_supported()` was
1293 // called, which ensures that AVX is supported
1294 unsafe {
1295 core::arch::x86_64::_mm256_setr_epi64x(
1296 program_counter.cast_signed(),
1297 tag.cast_signed(),
1298 payload.cast_signed(),
1299 0,
1300 )
1301 }
1302 }
1303 all(target_arch = "aarch64", any(not(miri), target_feature = "neon")) => {
1304 [program_counter, tag, payload].map(f64::from_bits)
1305 }
1306 _ => [program_counter, tag, payload],
1307 },
1308 phantom: PhantomData,
1309 }
1310 }
1311
1312 /// Deserialize what [`Self::new()`] produced.
1313 ///
1314 /// The lanes only ever come from there, in this very crate, which is what makes the
1315 /// unknown-tag arm unreachable.
1316 #[inline(always)]
1317 pub fn into_result(self) -> ThreadedExecutionResult<I> {
1318 let [program_counter, tag, payload] =
1319 cfg_select! {
1320 all(target_arch = "x86_64", any(not(miri), target_feature = "avx")) => {
1321 {
1322 // SAFETY: Same size, alignment is larger than necessary
1323 let [program_counter, tag, payload, _] = unsafe {
1324 mem::transmute::<core::arch::x86_64::__m256i, [u64; 4]>(self.lanes)
1325 };
1326
1327 [program_counter, tag, payload]
1328 }
1329 }
1330 all(target_arch = "aarch64", any(not(miri), target_feature = "neon")) => {
1331 self.lanes.map(f64::to_bits)
1332 }
1333 _ => self.lanes,
1334 };
1335
1336 let program_counter = Address::<I>::truncate_from_u64(program_counter);
1337 let csr_index = payload as u16;
1338
1339 let error = match tag {
1340 Self::TAG_STOPPED => {
1341 return ThreadedExecutionResult::stopped(program_counter);
1342 }
1343 Self::TAG_UNALIGNED_INSTRUCTION => ExecutionError::UnalignedInstruction {
1344 address: PackedAddress::new(Address::<I>::truncate_from_u64(payload)),
1345 },
1346 Self::TAG_OUT_OF_BOUNDS_READ => ExecutionError::OutOfBoundsRead {
1347 address: PackedAddress::new(payload),
1348 },
1349 Self::TAG_OUT_OF_BOUNDS_WRITE => ExecutionError::OutOfBoundsWrite {
1350 address: PackedAddress::new(payload),
1351 },
1352 Self::TAG_MISALIGNED_READ => ExecutionError::MisalignedRead {
1353 address: PackedAddress::new(payload),
1354 },
1355 Self::TAG_MISALIGNED_WRITE => ExecutionError::MisalignedWrite {
1356 address: PackedAddress::new(payload),
1357 },
1358 Self::TAG_MISALIGNED_ATOMIC => ExecutionError::MisalignedAtomic {
1359 address: PackedAddress::new(payload),
1360 },
1361 Self::TAG_ECALL_UNSUPPORTED => ExecutionError::EcallUnsupported {
1362 address: PackedAddress::new(Address::<I>::truncate_from_u64(payload)),
1363 },
1364 Self::TAG_ILLEGAL_INSTRUCTION => ExecutionError::IllegalInstruction {
1365 address: PackedAddress::new(Address::<I>::truncate_from_u64(payload)),
1366 },
1367 Self::TAG_CSR_READ_ONLY => ExecutionError::CsrReadOnly { csr_index },
1368 Self::TAG_CSR_ILLEGAL_READ => ExecutionError::CsrIllegalRead { csr_index },
1369 Self::TAG_CSR_ILLEGAL_WRITE => ExecutionError::CsrIllegalWrite { csr_index },
1370 Self::TAG_CSR_UNKNOWN => ExecutionError::CsrUnknown { csr_index },
1371 Self::TAG_CSR_INSUFFICIENT_PRIVILEGE => {
1372 // SAFETY: `::new()` constructor created this value with `to_bits()`
1373 let required =
1374 unsafe { PrivilegeLevel::from_bits((payload >> 16) as u8).unwrap_unchecked() };
1375 // SAFETY: `::new()` constructor created this value with `to_bits()`
1376 let current =
1377 unsafe { PrivilegeLevel::from_bits((payload >> 24) as u8).unwrap_unchecked() };
1378
1379 ExecutionError::CsrInsufficientPrivilege {
1380 csr_index,
1381 required,
1382 current,
1383 }
1384 }
1385 Self::TAG_UNSUPPORTED_PLATFORM => ExecutionError::UnsupportedPlatform,
1386 Self::TAG_CUSTOM => ExecutionError::Custom(payload.to_le_bytes()),
1387 _ => {
1388 unreachable!("Lanes are only ever produced by `new()`; qed");
1389 }
1390 };
1391
1392 ThreadedExecutionResult::failed(program_counter, error)
1393 }
1394}
1395
1396/// Tail-call-threaded counterpart of [`ExecutableInstruction`].
1397///
1398/// [`ExecutableInstruction::execute()`] describes what a single instruction does and leaves both
1399/// fetching and control flow to a driver loop. This trait instead runs the whole program: it is
1400/// generated as one handler function per instruction variant, each of which executes its own
1401/// instruction and then tail-calls (`become`) the handler of the next one, so there is no loop and
1402/// no return until execution stops. Each handler touches only the operands its own instruction
1403/// names, which is what the shared loop cannot do.
1404///
1405/// Instruction implementations are unaffected: the handlers are assembled from the very same
1406/// `match` arms that [`ExecutableInstruction::execute()`] are assembled from, so both paths execute
1407/// identical logic and a caller picks between them purely on the trade between code size
1408/// (`execute`) and throughput (`execute_threaded`).
1409///
1410/// This trait is deliberately not `const`, unlike [`ExecutableInstruction`]: dispatch goes through
1411/// a table of function pointers, and calls through a function pointer are not allowed in
1412/// `const fn`.
1413pub trait ThreadedExecutableInstruction<Regs, Env, Memory, PC>
1414where
1415 Self: ExecutableInstruction<Regs, Env, Memory, PC>,
1416 PC: InstructionFetcher<Self, Memory>,
1417{
1418 /// Execute instructions starting at the instruction fetcher's current position and continue
1419 /// until execution stops or fails.
1420 ///
1421 /// The instruction fetcher is taken by value rather than behind a reference so that it stays in
1422 /// registers across the whole handler chain.
1423 ///
1424 /// `env` is taken by value for the same reason and one more: an owned value can be a zero-sized
1425 /// type, which occupies no argument register at all, whereas a `&mut` occupies one whether
1426 /// there is anything behind it or not. Most configurations have a stateless environment, which
1427 /// thus vanishes, allowing more registers for other arguments. A configuration that does have a
1428 /// state passes a `&mut` to it (which is an owned value too and for which there are blanket
1429 /// implementations for convenience).
1430 fn execute_threaded(
1431 instruction_fetcher: PC,
1432 regs: &mut Regs,
1433 env: Env,
1434 memory: &mut Memory,
1435 ) -> ThreadedExecutionResult<Self>;
1436}