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