Skip to main content

BasicEagerInstructions

Struct BasicEagerInstructions 

Source
pub struct BasicEagerInstructions<I>
where I: Instruction,
{ /* private fields */ }
Expand description

Instructions decoded upfront, which BasicEagerInstructionFetcher walks.

Decoding a program once instead of on every fetch is what makes this faster than BasicInstructionFetcher, at the cost of holding the whole decoded program in memory and of not seeing writes the program makes to the memory it was decoded from.

The decoded stream has one slot per Instruction::ALIGNMENT bytes of guest code, which is the granularity at which an instruction of this instruction set can start, and what makes an address a position within the stream and back. With compressed instructions that is a halfword, so the second half of a 32-bit instruction gets a slot of its own, holding whatever those bytes decode to, which is only ever reached by jumping into the middle of an instruction. Without them, no address in the middle of an instruction is aligned in the first place, so there is nothing to hold a slot for and the stream is half the size.

The decoded stream is followed by one more slot holding the fallback instruction, which is where execution ends up when it falls through the end of the decoded stream.

Ownership of the allocation lives here rather than in the fetcher because the fetcher is moved through tail-called instruction handlers by value. A destructor on it would make every handler that can fail (every load, store, branch and jump) responsible for dropping it on the way out, which costs a stack frame, callee-saved register spills and a reload in the hot path of each of them, even though the failing path is never taken.

Implementations§

Source§

impl<I> BasicEagerInstructions<I>
where I: Instruction,

Source

pub fn fetcher( &self, pc: <<<I as Instruction>::Hart as HartConfig>::Reg as Register>::Type, ) -> Option<BasicEagerInstructionFetcher<'_, I>>

Create a fetcher positioned at the instruction that guest address pc corresponds to.

Returns None unless pc is the address of one of the instructions Self::decode() was given, meaning it is within base_addr..base_addr + instructions.len() and is a multiple of Instruction::ALIGNMENT.

Source

pub unsafe fn decode( instructions: &[u8], fallback: I, return_trap_address: <<<I as Instruction>::Hart as HartConfig>::Reg as Register>::Type, base_addr: <<<I as Instruction>::Hart as HartConfig>::Reg as Register>::Type, ) -> Result<Self, BasicEagerDecodeError>

Decode instructions and create a new instance holding the result.

Execution runs the frozen set of instructions decoded here, before execution starts.

base_addr is the guest address of the first instruction and return_trap_address is the address at which the interpreter will stop execution (gracefully).

Every Instruction::ALIGNMENT bytes of guest code own a slot of the decoded stream, including, where instructions may be compressed, the second half of a 32-bit instruction, which is only ever reached by jumping into the middle of one. Such a slot may or may not decode into a valid instruction on its own, and fallback is what is stored when it doesn’t.

Execution moves past an instruction by its Instruction::size(), so an instruction whose size is zero, isn’t a multiple of Instruction::ALIGNMENT or is larger than the guest code left is stored as fallback too.

fallback is also stored right after the last slot, where execution ends up when it falls through the end of the decoded stream.

Returns an error in these cases:

  • BasicEagerDecodeError::UnalignedBaseAddress when base_addr is not a multiple of Instruction::ALIGNMENT. It is the address of the first decoded instruction, and every position within the decoded stream is resolved relative to it.
  • BasicEagerDecodeError::CodeOverflowsAddressSpace when base_addr + instructions.len() is larger than the largest guest address, which is u32::MAX for a 32-bit guest. This keeps the address of every decoded instruction representable, and the address right after the code too.
  • BasicEagerDecodeError::ReturnTrapInsideCode when return_trap_address is within base_addr..base_addr + instructions.len(). Relative jumps and branches, and falling through, reach a decoded instruction without comparing its address against the return trap. Only an absolute jump would stop at a trap there. The address right after the code is accepted. Every jump or branch to it stops at the trap, and falling through the end of the code runs fallback, wherever the trap is.
  • BasicEagerDecodeError::ReturnTrapTooFar when return_trap_address is too far from base_addr, as explained below.

Execution that stops at the return trap leaves the fetcher where the trap would be in the decoded stream, and ProgramCounter::get_pc() turns that position back into the trap address. The position is the trap’s distance from base_addr in guest bytes, multiplied by the number of decoded stream bytes per guest byte. That product must fit into isize. The distance is the trap address minus base_addr, computed in 64 bits and read as a signed number. For a 64-bit guest, that is the shorter way around its address space, so a trap near the top of it is close to code near the bottom. For a 32-bit guest, whose addresses are zero-extended first, it is the plain difference.

§Safety

Execution of the resulting instruction stream skips the checks that BasicInstructionFetcher does, which is where the performance comes from. Both of the following must hold:

  • fallback must never continue to the next instruction when executed (unimp, which always fails, is the canonical choice). Instruction fetching does not bounds-check the position, only ProgramCounter::set_pc() and ProgramCounter::try_set_pc_relative() do, and falling through the end of the decoded stream lands on fallback, which must not fall through any further.
  • Instruction::size() must return the same value every time for the same instruction. Decoding checks the size of each instruction once, and execution moves past the instruction by whatever size it returns later, without checking it again.

Trait Implementations§

Source§

impl<I> Debug for BasicEagerInstructions<I>
where I: Instruction,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<I> Drop for BasicEagerInstructions<I>
where I: Instruction,

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<I> !Send for BasicEagerInstructions<I>

§

impl<I> !Sync for BasicEagerInstructions<I>

§

impl<I> Freeze for BasicEagerInstructions<I>
where NonNull<BasicEagerInstructionFetcherState<I>>: Freeze,

§

impl<I> RefUnwindSafe for BasicEagerInstructions<I>
where NonNull<BasicEagerInstructionFetcherState<I>>: RefUnwindSafe,

§

impl<I> Unpin for BasicEagerInstructions<I>
where NonNull<BasicEagerInstructionFetcherState<I>>: Unpin,

§

impl<I> UnsafeUnpin for BasicEagerInstructions<I>
where NonNull<BasicEagerInstructionFetcherState<I>>: UnsafeUnpin,

§

impl<I> UnwindSafe for BasicEagerInstructions<I>
where NonNull<BasicEagerInstructionFetcherState<I>>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.