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