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.

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 unsafe fn fetcher( &self, pc: <<I as Instruction>::Reg as Register>::Type, ) -> BasicEagerInstructionFetcher<'_, I>

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

§Safety

pc must be 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, with base_addr and instructions being what that call received.

Source

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

Decode instructions and create a new instance holding the result.

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, so it only has to fail when executed (unimp is the canonical choice).

§Safety

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

  • The instructions must end with an unconditional jump, so that execution can’t fall through past the end of the decoded stream. Instruction fetching does not bounds-check the position, only ProgramCounter::set_pc() and ProgramCounter::try_set_pc_relative() do, which means the last instruction must be one that goes through them.
  • return_trap_address must not fall inside the instructions. Instruction fetching does not compare against the return trap, so an address inside them would stop execution when jumped to, but not when reached by falling through.
  • base_addr must be a multiple of Instruction::ALIGNMENT, since it is the address of the first decoded instruction, and every position within the decoded stream is resolved relative to it.
  • base_addr + instructions.len() must not overflow the address space, which is what makes the address of every decoded instruction representable.
  • The memory the program executes with must contain these very instructions at base_addr, and the program must not modify them (there is no Zifencei support here). The decoded stream is a snapshot taken here, and it is what execution walks, so writes into the code region are not reflected in what is executed.

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.