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