mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-17 16:30:30 +02:00
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) <noreply@anthropic.com>
183 lines
4.7 KiB
C++
183 lines
4.7 KiB
C++
// Copyright (c) 2023- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include "ppsspp_config.h"
|
|
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
|
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
|
|
|
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
|
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
|
|
|
// This file contains compilation for exits.
|
|
//
|
|
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
|
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
|
|
|
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
|
#define CONDITIONAL_DISABLE {}
|
|
#define DISABLE { CompIR_Generic(inst); return; }
|
|
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
|
|
|
namespace MIPSComp {
|
|
|
|
using namespace Arm64Gen;
|
|
using namespace Arm64IRJitConstants;
|
|
|
|
void Arm64JitBackend::CompIR_Exit(IRInst inst) {
|
|
CONDITIONAL_DISABLE;
|
|
|
|
ARM64Reg exitReg = INVALID_REG;
|
|
switch (inst.op) {
|
|
case IROp::ExitToConst:
|
|
FlushAll();
|
|
WriteConstExit(inst.constant);
|
|
break;
|
|
|
|
case IROp::ExitToReg:
|
|
exitReg = regs_.MapGPR(inst.src1);
|
|
FlushAll();
|
|
MOV(SCRATCH1, exitReg);
|
|
// 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:
|
|
FlushAll();
|
|
B(dispatcherCheckCoreState_);
|
|
break;
|
|
|
|
default:
|
|
INVALIDOP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Arm64JitBackend::CompIR_ExitIf(IRInst inst) {
|
|
CONDITIONAL_DISABLE;
|
|
|
|
ARM64Reg lhs = INVALID_REG;
|
|
ARM64Reg rhs = INVALID_REG;
|
|
FixupBranch fixup;
|
|
switch (inst.op) {
|
|
case IROp::ExitToConstIfEq:
|
|
case IROp::ExitToConstIfNeq:
|
|
if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {
|
|
lhs = regs_.MapGPR(inst.src2);
|
|
FlushAll();
|
|
|
|
if (inst.op == IROp::ExitToConstIfEq)
|
|
fixup = CBNZ(lhs);
|
|
else if (inst.op == IROp::ExitToConstIfNeq)
|
|
fixup = CBZ(lhs);
|
|
else
|
|
_assert_(false);
|
|
} else if (regs_.IsGPRImm(inst.src2) && regs_.GetGPRImm(inst.src2) == 0) {
|
|
lhs = regs_.MapGPR(inst.src1);
|
|
FlushAll();
|
|
|
|
if (inst.op == IROp::ExitToConstIfEq)
|
|
fixup = CBNZ(lhs);
|
|
else if (inst.op == IROp::ExitToConstIfNeq)
|
|
fixup = CBZ(lhs);
|
|
else
|
|
_assert_(false);
|
|
} else {
|
|
regs_.Map(inst);
|
|
lhs = regs_.R(inst.src1);
|
|
rhs = regs_.R(inst.src2);
|
|
FlushAll();
|
|
|
|
CMP(lhs, rhs);
|
|
if (inst.op == IROp::ExitToConstIfEq)
|
|
fixup = B(CC_NEQ);
|
|
else if (inst.op == IROp::ExitToConstIfNeq)
|
|
fixup = B(CC_EQ);
|
|
else
|
|
_assert_(false);
|
|
}
|
|
|
|
WriteConstExit(inst.constant);
|
|
SetJumpTarget(fixup);
|
|
break;
|
|
|
|
case IROp::ExitToConstIfGtZ:
|
|
lhs = regs_.MapGPR(inst.src1);
|
|
FlushAll();
|
|
CMP(lhs, 0);
|
|
fixup = B(CC_LE);
|
|
WriteConstExit(inst.constant);
|
|
SetJumpTarget(fixup);
|
|
break;
|
|
|
|
case IROp::ExitToConstIfGeZ:
|
|
// In other words, exit if sign bit is 0.
|
|
lhs = regs_.MapGPR(inst.src1);
|
|
FlushAll();
|
|
fixup = TBNZ(lhs, 31);
|
|
WriteConstExit(inst.constant);
|
|
SetJumpTarget(fixup);
|
|
break;
|
|
|
|
case IROp::ExitToConstIfLtZ:
|
|
// In other words, exit if sign bit is 1.
|
|
lhs = regs_.MapGPR(inst.src1);
|
|
FlushAll();
|
|
fixup = TBZ(lhs, 31);
|
|
WriteConstExit(inst.constant);
|
|
SetJumpTarget(fixup);
|
|
break;
|
|
|
|
case IROp::ExitToConstIfLeZ:
|
|
lhs = regs_.MapGPR(inst.src1);
|
|
FlushAll();
|
|
CMP(lhs, 0);
|
|
fixup = B(CC_GT);
|
|
WriteConstExit(inst.constant);
|
|
SetJumpTarget(fixup);
|
|
break;
|
|
|
|
case IROp::ExitToConstIfFpTrue:
|
|
case IROp::ExitToConstIfFpFalse:
|
|
// Note: not used.
|
|
DISABLE;
|
|
break;
|
|
|
|
default:
|
|
INVALIDOP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace MIPSComp
|
|
|
|
#endif
|