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,
impl<I> BasicEagerInstructions<I>where
I: Instruction,
Sourcepub unsafe fn fetcher(
&self,
pc: <<I as Instruction>::Reg as Register>::Type,
) -> BasicEagerInstructionFetcher<'_, I>
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.
Sourcepub 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
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()andProgramCounter::try_set_pc_relative()do, which means the last instruction must be one that goes through them. return_trap_addressmust 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_addrmust be a multiple ofInstruction::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 noZifenceisupport 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.