Expand description
A cheap cleanup round for freshly lifted code.
SLEIGH expresses an instruction’s semantics through unique (temporary)
space: an intermediate result is stored to a temporary and immediately loaded
back. Lifting add eax, ebx produces around forty QCode operations, roughly
half of which are this round trip:
i32 %tmpe = %eax_3 ^ %ebx_3;
store($temp1:4, 0 <- %tmpe);
i32 %tmp10 = load($temp1:4, 0); // reloads what was just storedPlain dead-code elimination cannot touch this — the store has a user, and the load has users — so the interpreter re-executes the whole round trip on every pass over the block. Forwarding the stored value to the load removes both.
This is deliberately the limited version of store-to-load forwarding, not
mem2reg: it is block-local, needs no alias
analysis, and is linear in the size of the block, so it can run on every
lifted block without reintroducing the quadratic cost that a whole-function
pass would.
§Why this is sound
Only temporary spaces are forwarded. A SLEIGH unique is scratch private to one instruction’s semantics: it is written before it is read and does not outlive the block, so no other block, and no guest-visible memory access, can observe it. Registers and RAM are left alone, since a call, a fault handler, or another block legitimately observes those.
Two further restrictions keep it honest:
- A load is forwarded only when it matches the last store to that space and has the same width. A narrower or wider access is left alone rather than guessed at.
- A store through a non-constant pointer clears what is known about that space, since it may land anywhere in it.
Structs§
- Cleanup
- What one cleanup round changed.
Functions§
- forward_
temp_ stores - Forwards temporary-space stores to the loads that read them back, in
block_idonly.