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}