pub trait BlockExecutor {
// Required method
fn run_block(
&mut self,
ctx: &Context<'_>,
emu: &mut StandaloneEmulator<VmMemory>,
block: BlockId,
start: usize,
chain: bool,
) -> Result<Option<Executed>, EmulatorErrorKind>;
}Expand description
An alternative way to execute a block’s body.
The interpreter is always present and always correct; an executor is an optimisation that may decline any block for any reason, in which case the interpreter runs it unchanged. That is what lets a backend be partial: a JIT need only handle the shapes it handles well.
An executor runs the block’s body, not its terminator. Control flow, block parameters and call semantics stay in one implementation.
It is handed the whole emulator rather than just its memory because the
interpreter still has to run the terminator, and a terminator reads
operands — a cbranch condition, a branch’s block arguments. Those are
values the body produced, so an executor that keeps them somewhere other
than the interpreter’s value table must put them back before returning.
Required Methods§
Sourcefn run_block(
&mut self,
ctx: &Context<'_>,
emu: &mut StandaloneEmulator<VmMemory>,
block: BlockId,
start: usize,
chain: bool,
) -> Result<Option<Executed>, EmulatorErrorKind>
fn run_block( &mut self, ctx: &Context<'_>, emu: &mut StandaloneEmulator<VmMemory>, block: BlockId, start: usize, chain: bool, ) -> Result<Option<Executed>, EmulatorErrorKind>
Runs everything in block except its terminator.
Ok(None) means “not mine” and is not an error — the caller falls back
to the interpreter.
On Ok(Some(_)) every value the terminator of Executed::block reads
must be readable from emu.insn_values, exactly as if the interpreter
had run that body.
chain lets the executor run on past block into successors it also
handles, instead of handing control back after one. Deciding a branch
itself is how an executor keeps control inside its own code rather than
paying a round trip per block. The caller withholds it when something
needs to observe every block — a breakpoint is set, say — because blocks
crossed this way are never offered to the interpreter.
start is the body index to begin at. It is 0 when a block is
entered, and the instruction after an interrupting op when the
interpreter has run the block up to there and hands the rest over.
Results the interpreter already computed for the block are in
emu.insn_values; an executor may read them or decline.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".