pub trait InstructionFetcher<I, Memory>{
type Peeked;
// Required methods
fn peek_instruction(
&mut self,
memory: &Memory,
) -> FetchInstructionResult<I, Self::Peeked>;
fn peeked_instruction<'a>(&'a self, peeked: &'a Self::Peeked) -> &'a I;
unsafe fn advance(&mut self, instruction_size: u8);
fn fetch_instruction(
&mut self,
memory: &Memory,
) -> FetchInstructionResult<I>;
}Expand description
Generic instruction fetcher.
§Performance considerations
In threaded dispatch, the instruction fetcher is moved through the handler chain by value, so it
should have 16 bytes size (next instruction pointer + pointer to extra state) and no drop glue.
A fetcher that owns something is dropped by whichever handler ends execution. Every handler that
can fail is a candidate for that, which forces a stack frame, callee-saved register spills, and
a reload into the hot path of every load, store, branch and jump. A fetcher that only borrows
what it walks (Copy, or at least !needs_drop) keeps them all frameless and fast.
Required Associated Types§
Sourcetype Peeked
type Peeked
What Self::peek_instruction() returns an instruction as, which is whatever
Self::peeked_instruction() needs besides the fetcher to get back to the instruction.
A fetcher that decodes on the fly has nothing but the instruction itself to hand over,
while one that walks a decoded stream still has the instruction where it was decoded, so it
hands over nothing at all. The difference matters to threaded dispatch, which passes this
to the handler by value: an instruction takes a register, and each operand then costs a
shift and a mask to extract from it, while () takes no register at all, and the handler
loads each operand it uses from the decoded stream at a constant offset, one instruction
each.
Required Methods§
Sourcefn peek_instruction(
&mut self,
memory: &Memory,
) -> FetchInstructionResult<I, Self::Peeked>
fn peek_instruction( &mut self, memory: &Memory, ) -> FetchInstructionResult<I, Self::Peeked>
Read the instruction at the current position, leaving the program counter on it.
Self::advance() is what moves past it, and the two are separate because of what deriving
the size from the instruction is costly for threaded dispatch: it makes the address of the
next instruction depend on decoding the current one. In threaded dispatch caller already
knows which variant it is holding and advances by a constant instead, allowing the next load
to be issued immediately.
Sourcefn peeked_instruction<'a>(&'a self, peeked: &'a Self::Peeked) -> &'a I
fn peeked_instruction<'a>(&'a self, peeked: &'a Self::Peeked) -> &'a I
The instruction that Self::peek_instruction() returned peeked for.
Only meaningful until the program counter moves, since peeked may be nothing more than a
promise that the instruction is still at the current position.
A reference rather than a copy on purpose, even though callers destructure it right away: destructuring through a reference into a decoded stream loads each field on its own, while a copy is loaded whole and its fields are then extracted from a register with a shift and a mask each.
Sourceunsafe fn advance(&mut self, instruction_size: u8)
unsafe fn advance(&mut self, instruction_size: u8)
Move the program counter past an instruction of instruction_size bytes that
Self::peek_instruction() has just returned.
§Safety
Must be called exactly once after a successful Self::peek_instruction(), with the size
of the instruction that call returned. Implementations are free to rely on that and skip
checks accordingly: one over a pre-decoded stream that is known to end with a jump, for
instance, treats the resulting position as valid without bounds-checking it.
Sourcefn fetch_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I>
fn fetch_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I>
Fetch a single instruction at a specified address and advance the program counter on successful fetch.
This is effectively Self::peek_instruction() followed by Self::peeked_instruction()
and Self::advance(). It exists for callers that do not know what they are about to
fetch, which is every caller that dispatches through a match rather than through
per-variant handlers.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".