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