Skip to main content

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}