Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/backend/llvm/register_state.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "backend/llvm/emitter.h"

#include "cpu/cpu.h"

#include <llvm/ADT/SmallVector.h>
Expand Down Expand Up @@ -138,6 +139,43 @@ void FunctionEmitter::scanRegionLeaders() {
region_leaders_[i] = true;
if (i + 1u < source_.block_count && term.kind != DOLIR_TERM_FALLTHROUGH)
region_leaders_[i + 1u] = true;
// A block that writes MSR can return to the dispatcher and resume at the
// NEXT instruction, so that instruction has to be enterable. Terminators
// are already covered above; this catches the fallthrough case, which is
// mtmsr.
//
// Keyed on an MSR write rather than on MAY_EXIT generally. MAY_EXIT is
// carried by a large number of instructions -- the broader rule fired
// 160,525 times on Colosseum -- and every extra leader fragments a region,
// which costs optimisation. Measured: the broad rule cost plain llvm
// 5-16% on the three titles that never needed it, while only a handful of
// addresses are ever actually re-entered.
//
// Without this, re-entry lands on a non-leader address, the entry dispatch
// has no case for it, and the runtime interprets from there until it
// reaches the next leader. Colosseum calls OSRestoreInterrupts roughly
// every 139 guest cycles, and its last two instructions sit exactly in that
// gap: 166M interpreted instructions, 92% of all dispatches in that arm.
// The C backend never pays this because it labels every address.
//
// REQUIRES the runtime to deliver pending external interrupts at the
// post-mtmsr boundary. emitStateWrite side-exits to guest_pc+4 when MSR[EE]
// goes 0->1; before this rule the address was not enterable, so the runtime
// interpreted from there and delivered the interrupt as a side effect of
// that detour. Removing the detour without the runtime half leaves the
// guest spinning with EE set on an interrupt that never arrives -- Colosseum
// advances 19 frames in 20s instead of 1069. native_exc is the tell: 312
// while hung, 25,009 once delivery is restored.
if (i + 1u < source_.block_count) {
const DolIRBlock &body = source_.blocks[i];
for (u32 n = 0; n < body.instruction_count; n++) {
if (dolir_state_mask_test(body.instructions[n].state_defs,
DOLIR_STATE_MSR)) {
region_leaders_[i + 1u] = true;
break;
}
}
}
u32 count = term.kind == DOLIR_TERM_COND_BRANCH ? 2u
: term.kind == DOLIR_TERM_BRANCH ? 1u
: term.kind == DOLIR_TERM_INDIRECT ? 2u
Expand Down
Loading