From e33f38afa6ded708f0774b6735542ed0144ec8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Oct 2026 02:50:59 +0200 Subject: [PATCH] ARM64 IR JIT: Look up the block for a jump to a register at the jump Each jr and jalr now does the dispatcher's lookup itself, so each has its own indirect branch, which the CPU predicts much better than the one in the dispatcher that every one of them shared. Out of downcount or with no block yet, it goes to the dispatcher as before. Cycles over 10 emulated seconds: -2.3% in ATV, -1.4% Tekken 6 and Ridge Racer, -1.2% Daxter, -1.1% Killzone, and -0.3% to -0.8% in the rest. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/MIPS/ARM64/Arm64IRCompBranch.cpp | 18 +++++++++++++++++- Core/MIPS/ARM64/Arm64IRJit.cpp | 15 +++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/ARM64/Arm64IRCompBranch.cpp b/Core/MIPS/ARM64/Arm64IRCompBranch.cpp index c7bb4ae110..223bb4e61a 100644 --- a/Core/MIPS/ARM64/Arm64IRCompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64IRCompBranch.cpp @@ -51,7 +51,23 @@ void Arm64JitBackend::CompIR_Exit(IRInst inst) { exitReg = regs_.MapGPR(inst.src1); FlushAll(); MOV(SCRATCH1, exitReg); - B(dispatcherPCInSCRATCH1_); + // The dispatcher's lookup, here: each of these then has its own indirect branch, which the + // CPU predicts far better than one shared by every jr. Anything unusual goes the long way. + MovToPC(SCRATCH1); + { + // Out of the downcount, or no block yet: the dispatcher, with the PC stored. + FixupBranch inTime = TBZ(DOWNCOUNTREG, 31); + B(hooks_.dispatcher); + SetJumpTarget(inTime); + ANDI2R(SCRATCH1, SCRATCH1, Memory::BLOCK_SHADOW_ENTRY_MASK); + MOVP2R(SCRATCH2_64, Memory::blockShadow); + LDR(SCRATCH1, SCRATCH2_64, SCRATCH1_64); + FixupBranch found = CBNZ(SCRATCH1); + B(dispatcherNoCheck_); + SetJumpTarget(found); + ADD(SCRATCH1_64, JITBASEREG, SCRATCH1_64); + BR(SCRATCH1_64); + } break; case IROp::ExitToPC: diff --git a/Core/MIPS/ARM64/Arm64IRJit.cpp b/Core/MIPS/ARM64/Arm64IRJit.cpp index 471010b0d9..772f067829 100644 --- a/Core/MIPS/ARM64/Arm64IRJit.cpp +++ b/Core/MIPS/ARM64/Arm64IRJit.cpp @@ -75,8 +75,19 @@ bool Arm64JitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num) { // An accurate dot product can flush several registers around its call. size_t sizeEstimate = 0; const IRInst *estimateInsts = irBlockCache->GetBlockInstructionPtr(*block); - for (int i = 0; i < block->GetNumIRInstructions(); ++i) - sizeEstimate += estimateInsts[i].op == IROp::Vec4VfpuDot || estimateInsts[i].op == IROp::Vec4VfpuMultiDot ? 96 : 32; + for (int i = 0; i < block->GetNumIRInstructions(); ++i) { + switch (estimateInsts[i].op) { + case IROp::Vec4VfpuDot: + case IROp::Vec4VfpuMultiDot: + // The jump to a register looks up the block itself. + case IROp::ExitToReg: + sizeEstimate += 96; + break; + default: + sizeEstimate += 32; + break; + } + } BeginWrite(std::min(GetSpaceLeft(), sizeEstimate)); u32 startPC = block->GetOriginalStart();