pub unsafe trait InstructionFetcher<I, Memory>where
Self: ProgramCounter<<<<I as Instruction>::Hart as HartConfig>::Reg as Register>::Type, Memory>,
I: Instruction,{
type Peeked;
// Required methods
unsafe fn peek_instruction(
&mut self,
memory: &Memory,
) -> FetchInstructionResult<I, Self::Peeked>;
unsafe fn peeked_instruction<'a>(
&'a self,
peeked: &'a Self::Peeked,
) -> &'a I;
unsafe fn advance(&mut self, instruction_size: u8);
unsafe 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.
§Fetching
The methods that read an instruction are unsafe. Implementations may assume that the program
counter is at an instruction, so they don’t have to check it on every fetch. Fetching is
allowed when the fetcher was just created by its constructor, or when the last call that moved
the program counter was one of these:
ProgramCounter::set_pc(),ProgramCounter::set_pc_relative()orProgramCounter::failed_branch()returningOk(ControlFlow::Continue(()))ProgramCounter::try_set_pc_relative()returningtrueSelf::advance()orSelf::fetch_instruction()moving past an instruction whose execution continued with the next oneSelf::peek_instruction()orSelf::fetch_instruction()returningFetchInstructionResult::Continue
A copy or a clone of a fetcher doesn’t count as just created. It starts in the state the original was in, and the rules above apply to it from there.
Anything else may leave the program counter where there is nothing to fetch, for example a
branch that ProgramCounter::set_pc_relative() refused. set_pc(get_pc()) is the cheap way
back: at an instruction it does nothing, and otherwise it returns what stopped execution there.
§Safety
Execution relies on the following for soundness, and every implementation promises it:
- Fetching is sound whenever it is allowed, see above.
- Nothing done through a shared reference to the fetcher moves the program counter. That
includes
ProgramCounter::get_pc()andSelf::peeked_instruction(). Instructions get the fetcher by shared reference while they execute, and the next instruction is fetched after that without checking the program counter again. Self::peeked_instruction()returns the instruction thatSelf::peek_instruction()read when it returnedpeeked. It keeps returning that same instruction until the program counter moves, even after the fetcher orpeekedis moved to another place in memory. Threaded dispatch picks the handler by the variant of this instruction. The handler then reads the instruction again and takes that variant for granted.
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§
Sourceunsafe fn peek_instruction(
&mut self,
memory: &Memory,
) -> FetchInstructionResult<I, Self::Peeked>
unsafe 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.
§Safety
Fetching must be allowed, see InstructionFetcher.
Sourceunsafe fn peeked_instruction<'a>(&'a self, peeked: &'a Self::Peeked) -> &'a I
unsafe 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.
§Safety
peeked must come from Self::peek_instruction() on this fetcher, and the program
counter must not have moved since.
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 what
Instruction::size() returns for the instruction that call returned. Implementations are
free to rely on that and skip checks accordingly: one over a pre-decoded stream that is
followed by an instruction that never continues, for instance, treats the resulting
position as valid without bounds-checking it.
Instruction is a safe trait, so nothing stops size() from returning a different value
for the same instruction later. An implementation that needs the size to match what it
checked earlier has to require that itself, the way BasicEagerInstructions::decode() does.
Sourceunsafe fn fetch_instruction(
&mut self,
memory: &Memory,
) -> FetchInstructionResult<I>
unsafe 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.
§Safety
Fetching must be allowed, see InstructionFetcher.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".