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