CPU: Pass the MIPSState explicitly where it was already at hand

The JIT backends' calls out of generated code (the MIPS and IR interpreter
fallbacks, and the bad address report) now take the state the backend was
built for instead of reading currentMIPS. Likewise the interpreter's tick
check, the IR dispatcher's Advance, and a couple of debugger helpers.

MIPSState::InitRegisters() no longer sets currentMIPS; the boot paths set it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-20 09:47:48 +02:00
1 parent 2be8d57f8c
commit 5a9cbf007c
20 files changed
+121 -31

No files matched your search

+73
View File
@@ -107,6 +107,14 @@ void XEmitter::ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32
ABI_RestoreStack(3 * 4);
}
void XEmitter::ABI_CallFunctionCP(const void *func, u32 param1, void *param2) {
ABI_AlignStack(2 * 4);
PUSH(32, ImmPtr(param2));
PUSH(32, Imm32(param1));
CALL(func);
ABI_RestoreStack(2 * 4);
}
void XEmitter::ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3) {
ABI_AlignStack(3 * 4);
PUSH(32, ImmPtr(param3));
@@ -150,6 +158,14 @@ void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::Op
ABI_RestoreStack(3 * 4);
}
void XEmitter::ABI_CallFunctionPP(const void *func, void *param1, void *param2) {
ABI_AlignStack(2 * 4);
PUSH(32, ImmPtr(param2));
PUSH(32, ImmPtr(param1));
CALL(func);
ABI_RestoreStack(2 * 4);
}
void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) {
ABI_AlignStack(3 * 4);
PUSH(32, Imm32(param3));
@@ -196,6 +212,17 @@ void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32
ABI_RestoreStack(3 * 4);
}
void XEmitter::ABI_CallFunctionACCP(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3, void *param4)
{
ABI_AlignStack(4 * 4);
PUSH(32, ImmPtr(param4));
PUSH(32, Imm32(param3));
PUSH(32, Imm32(param2));
PUSH(32, arg1);
CALL(func);
ABI_RestoreStack(4 * 4);
}
void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1)
{
ABI_AlignStack(1 * 4);
@@ -348,6 +375,20 @@ void XEmitter::ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32
}
}
void XEmitter::ABI_CallFunctionCP(const void *func, u32 param1, void *param2) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(64, R(ABI_PARAM2), ImmPtr(param2));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
@@ -424,6 +465,20 @@ void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::Op
}
}
void XEmitter::ABI_CallFunctionPP(const void *func, void *param1, void *param2) {
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
MOV(64, R(ABI_PARAM2), ImmPtr(param2));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) {
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
MOV(64, R(ABI_PARAM2), ImmPtr(param2));
@@ -511,6 +566,24 @@ void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32
}
}
void XEmitter::ABI_CallFunctionACCP(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3, void *param4)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(32, R(ABI_PARAM1), arg1);
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(32, R(ABI_PARAM3), Imm32(param3));
MOV(64, R(ABI_PARAM4), ImmPtr(param4));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
+3
View File
@@ -1412,14 +1412,17 @@ public:
void ABI_CallFunctionC(const void *func, u32 param1);
void ABI_CallFunctionCC(const void *func, u32 param1, u32 param2);
void ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32 param3);
void ABI_CallFunctionCP(const void *func, u32 param1, void *param2);
void ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3);
void ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2, u32 param3, void *param4);
void ABI_CallFunctionP(const void *func, void *param1);
void ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2);
void ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3);
void ABI_CallFunctionPP(const void *func, void *param1, void *param2);
void ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3);
void ABI_CallFunctionAC(const void *func, const Gen::OpArg &arg1, u32 param2);
void ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3);
void ABI_CallFunctionACCP(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3, void *param4);
void ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1);
void ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2);
+1
View File
@@ -415,6 +415,7 @@ void ArmJitBackend::CompIR_ValidateAddress(IRInst inst) {
// Otherwise would need a thunk to restore regs.
MOVI2R(R1, alignment);
MOVI2R(R2, isWrite ? 1 : 0);
MOVP2R(R3, mips_);
QuickCallFunction(R12, &ReportBadAddress);
B(dispatcherCheckCoreState_);
+2
View File
@@ -237,6 +237,7 @@ void ArmJitBackend::CompIR_Generic(IRInst inst) {
// The 64-bit argument goes in R0:R1.
MOVI2R(R0, (u32)value);
MOVI2R(R1, (u32)(value >> 32));
MOVP2R(R2, mips_);
QuickCallFunction(R12, &DoIRInst);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
@@ -261,6 +262,7 @@ void ArmJitBackend::CompIR_Interpret(IRInst inst) {
QuickCallFunction(R12, &NotifyMIPSInterpret);
}
MOVI2R(R0, inst.constant);
MOVP2R(R1, mips_);
QuickCallFunction(R12, &MIPSInterpretTrampoline);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
+1
View File
@@ -447,6 +447,7 @@ void Arm64JitBackend::CompIR_ValidateAddress(IRInst inst) {
MOV(W0, SCRATCH2);
MOVI2R(W1, alignment);
MOVI2R(W2, isWrite ? 1 : 0);
MOVP2R(X3, mips_);
QuickCallFunction(SCRATCH2, &ReportBadAddress);
B(dispatcherCheckCoreState_);
+3 -1
View File
@@ -272,6 +272,7 @@ void Arm64JitBackend::CompIR_Generic(IRInst inst) {
SaveStaticRegisters();
WriteDebugProfilerStatus(IRProfilerStatus::IR_INTERPRET);
MOVI2R(X0, value);
MOVP2R(X1, mips_);
QuickCallFunction(SCRATCH2_64, &DoIRInst);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
@@ -304,7 +305,8 @@ void Arm64JitBackend::CompIR_Interpret(IRInst inst) {
MOVP2R(X0, MIPSGetName(op));
QuickCallFunction(SCRATCH2_64, &NotifyMIPSInterpret);
}
MOVI2R(X0, inst.constant);
MOVI2R(W0, inst.constant);
MOVP2R(X1, mips_);
QuickCallFunction(SCRATCH2_64, &MIPSInterpretTrampoline);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
+1 -1
View File
@@ -312,7 +312,7 @@ void IRJit::RunBlocks() {
MIPSState *mips = mips_;
while (true) {
RestoreHostRoundingMode();
CoreTiming::Advance(currentMIPS);
CoreTiming::Advance(mips);
if (coreState != 0) {
break;
}
+7 -6
View File
@@ -147,31 +147,31 @@ void IRNativeBackend::NotifyMIPSInterpret(const char *name) {
debugSeenNotCompiled[name]++;
}
void IRNativeBackend::DoMIPSInst(uint32_t value) {
void IRNativeBackend::DoMIPSInst(uint32_t value, MIPSState *mips) {
MIPSOpcode op;
memcpy(&op, &value, sizeof(op));
if constexpr (enableDebugStats)
debugSeenNotCompiled[MIPSGetName(op)]++;
MIPSInterpret(currentMIPS, op);
MIPSInterpret(mips, op);
}
// This is called from IR->JIT implementation to fall back to the IR interpreter for missing ops.
// Not fast.
uint32_t IRNativeBackend::DoIRInst(uint64_t value) {
uint32_t IRNativeBackend::DoIRInst(uint64_t value, MIPSState *mips) {
IRInst inst[2]{};
memcpy(&inst[0], &value, sizeof(value));
if constexpr (enableDebugStats)
debugSeenNotCompiledIR[(uint8_t)inst[0].op]++;
// Doesn't really matter what value it returns as PC.
inst[1].op = IROp::ExitToPC;
return IRInterpret(currentMIPS, &inst[0]);
return IRInterpret(mips, &inst[0]);
}
int IRNativeBackend::ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite) {
int IRNativeBackend::ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite, MIPSState *mips) {
const auto toss = [&](MemoryExceptionType t) {
Core_MemoryException(addr, alignment, currentMIPS->pc, t);
Core_MemoryException(addr, alignment, mips->pc, t);
return coreState != CORE_RUNNING_CPU ? 1 : 0;
};
@@ -514,6 +514,7 @@ void IRNativeJit::Init(IRNativeBackend &backend) {
backend_ = &backend;
backend_->SetProfiling(profiling_);
debugInterface_.Init(backend_);
backend_->SetMIPSState(mips_);
backend_->GenerateFixedCode(mips_);
// Wanted this to be a reference, but vtbls get in the way. Shouldn't change.
+8 -3
View File
@@ -89,6 +89,9 @@ public:
bool CodeInRange(const u8 *ptr) const;
int OffsetFromCodePtr(const u8 *ptr);
void SetMIPSState(MIPSState *mips) {
mips_ = mips;
}
virtual void GenerateFixedCode(MIPSState *mipsState) = 0;
virtual bool CompileBlock(IRBlockCache *irBlockCache, int block_num) = 0;
virtual void ClearAllBlocks() = 0;
@@ -171,12 +174,12 @@ protected:
static void NotifyMIPSInterpret(const char *name);
// Callback to log AND perform a base interpreter op. Alternative to NotifyMIPSInterpret().
static void DoMIPSInst(uint32_t op);
static void DoMIPSInst(uint32_t op, MIPSState *mips);
// Callback to log AND perform an IR interpreter inst. Returns 0 or a PC to jump to.
static uint32_t DoIRInst(uint64_t inst);
static uint32_t DoIRInst(uint64_t inst, MIPSState *mips);
static int ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite);
static int ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite, MIPSState *mips);
void AddLinkableExit(int block_num, uint32_t pc, int exitStartOffset, int exitLen);
void EraseAllLinks(int block_num);
@@ -185,6 +188,8 @@ protected:
IRNativeHooks hooks_;
IRBlockCache &blocks_;
// The CPU the generated code runs, for the calls out of it.
MIPSState *mips_ = nullptr;
std::vector<IRNativeBlock> nativeBlocks_;
std::unordered_multimap<uint32_t, int> linksTo_;
bool profiling_ = false;
+2
View File
@@ -249,6 +249,7 @@ void LoongArch64JitBackend::CompIR_Generic(IRInst inst) {
LI(R4, value);
SaveStaticRegisters();
WriteDebugProfilerStatus(IRProfilerStatus::IR_INTERPRET);
LI(R5, (uintptr_t)mips_);
QuickCallFunction(&DoIRInst, SCRATCH2);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
@@ -279,6 +280,7 @@ void LoongArch64JitBackend::CompIR_Interpret(IRInst inst) {
QuickCallFunction(&NotifyMIPSInterpret, SCRATCH2);
}
LI(R4, (int32_t)inst.constant);
LI(R5, (uintptr_t)mips_);
QuickCallFunction((const u8 *)&MIPSInterpretTrampoline, SCRATCH2);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
-1
View File
@@ -364,7 +364,6 @@ void MIPSState::InitRegisters() {
fpcond = 0;
fcr31 = 0;
debugCount = 0;
currentMIPS = this;
inDelaySlot = false;
llBit = 0;
nextPC = 0;
+2 -2
View File
@@ -1569,7 +1569,7 @@ namespace MIPSAnalyst {
switch (opInfo & CONDTYPE_MASK) {
case CONDTYPE_EQ:
if (opInfo & IN_FPUFLAG) { // fpu branch
info.conditionMet = currentMIPS->fpcond == 0;
info.conditionMet = cpu->GetFPCond() == 0;
} else {
info.conditionMet = (rt == rs);
if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) { // always true
@@ -1579,7 +1579,7 @@ namespace MIPSAnalyst {
break;
case CONDTYPE_NE:
if (opInfo & IN_FPUFLAG) { // fpu branch
info.conditionMet = currentMIPS->fpcond != 0;
info.conditionMet = cpu->GetFPCond() != 0;
} else {
info.conditionMet = (rt != rs);
if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) { // always true
+3 -3
View File
@@ -1372,8 +1372,8 @@ void MIPSInterpret(MIPSState *mips, MIPSOpcode op) {
}
// See the declaration comment in MIPSTables.h.
void CDECL MIPSInterpretTrampoline(MIPSOpcode op) {
MIPSInterpret(currentMIPS, op);
void CDECL MIPSInterpretTrampoline(u32 op, MIPSState *mips) {
MIPSInterpret(mips, MIPSOpcode(op));
}
// This is the slow, feature-rich runloop for the interpreter.
@@ -1434,7 +1434,7 @@ static void RunUntilDowncountZeroWithChecks(MIPSState *mips, u64 globalTicks) {
}
}
if (CoreTiming::GetTicks(currentMIPS) > globalTicks)
if (CoreTiming::GetTicks(mips) > globalTicks)
return;
}
}
+3 -7
View File
@@ -135,13 +135,9 @@ void MIPSInterpret(MIPSState *mips, MIPSOpcode op); //only for those rare ones
int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks);
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op);
// 1-arg (MIPSOpcode only) trampoline around MIPSInterpret(currentMIPS, op), for JIT backends
// (x86/ARM/ARM64/RiscV/LoongArch64 Comp_Generic / CompIR_Interpret) that bake a raw function
// pointer directly into generated machine code as their interpreter fallback - that codegen
// only arranges a single MIPSOpcode argument before the call, so it can't target a real
// MIPSInterpretFunc (now 2-arg) without also being taught to pass an explicit MIPSState*.
// TODO: teach each JIT's codegen to pass mips explicitly, then delete this.
void CDECL MIPSInterpretTrampoline(MIPSOpcode op);
// MIPSInterpret for the JIT backends' interpreter fallback (CompIR_Interpret), which calls it from
// generated code, with the opcode as a plain u32.
void CDECL MIPSInterpretTrampoline(u32 op, MIPSState *mips);
int MIPSGetInstructionCycleEstimate(MIPSOpcode op);
int MIPSGetMemoryAccessSize(MIPSOpcode op);
+1 -1
View File
@@ -324,7 +324,7 @@ void WriteMatrix(MIPSState *mips, const float *rd, MatrixSize size, int reg) {
default: side = 0;
}
if (currentMIPS->VfpuWriteMask() != 0) {
if (mips->VfpuWriteMask() != 0) {
ERROR_LOG_REPORT(Log::CPU, "Write mask used with vfpu matrix instruction.");
}
+5 -2
View File
@@ -252,7 +252,9 @@ void RiscVJitBackend::CompIR_Generic(IRInst inst) {
LI(X10, value, SCRATCH2);
SaveStaticRegisters();
WriteDebugProfilerStatus(IRProfilerStatus::IR_INTERPRET);
QuickCallFunction(&DoIRInst, SCRATCH2);
// X11 is SCRATCH2, so the call takes RA as its temp.
LI(X11, (uintptr_t)mips_);
QuickCallFunction(&DoIRInst);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
@@ -282,7 +284,8 @@ void RiscVJitBackend::CompIR_Interpret(IRInst inst) {
QuickCallFunction(&NotifyMIPSInterpret, SCRATCH2);
}
LI(X10, (int32_t)inst.constant);
QuickCallFunction((const u8 *)&MIPSInterpretTrampoline, SCRATCH2);
LI(X11, (uintptr_t)mips_);
QuickCallFunction((const u8 *)&MIPSInterpretTrampoline);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
}
+1 -1
View File
@@ -446,7 +446,7 @@ void X64JitBackend::CompIR_ValidateAddress(IRInst inst) {
// Ignores the return value, always returns to the dispatcher.
// Otherwise would need a thunk to restore regs.
ABI_CallFunctionACC((const void *)&ReportBadAddress, R(SCRATCH1), alignment, isWrite);
ABI_CallFunctionACCP((const void *)&ReportBadAddress, R(SCRATCH1), alignment, isWrite, mips_);
JMP(dispatcherCheckCoreState_, true);
for (FixupBranch &b : validJumps)
+3 -3
View File
@@ -233,9 +233,9 @@ void X64JitBackend::CompIR_Generic(IRInst inst) {
SaveStaticRegisters();
WriteDebugProfilerStatus(IRProfilerStatus::IR_INTERPRET);
#if PPSSPP_ARCH(AMD64)
ABI_CallFunctionP((const void *)&DoIRInst, (void *)value);
ABI_CallFunctionPP((const void *)&DoIRInst, (void *)value, mips_);
#else
ABI_CallFunctionCC((const void *)&DoIRInst, (u32)(value & 0xFFFFFFFF), (u32)(value >> 32));
ABI_CallFunctionCCP((const void *)&DoIRInst, (u32)(value & 0xFFFFFFFF), (u32)(value >> 32), mips_);
#endif
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
@@ -259,7 +259,7 @@ void X64JitBackend::CompIR_Interpret(IRInst inst) {
if (DebugStatsEnabled()) {
ABI_CallFunctionP((const void *)&NotifyMIPSInterpret, (void *)MIPSGetName(op));
}
ABI_CallFunctionC((const void *)&MIPSInterpretTrampoline, inst.constant);
ABI_CallFunctionCP((const void *)&MIPSInterpretTrampoline, inst.constant, mips_);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
}
+1
View File
@@ -548,6 +548,7 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
LoadSymbolsIfSupported();
LoadGameSymbolsIfEnabled();
currentMIPS = &mipsr4k;
mipsr4k.Reset();
CoreTiming::Init(&mipsr4k);
+1
View File
@@ -240,6 +240,7 @@ bool InitMinimalPSP() {
return false;
}
currentMIPS = &mipsr4k;
mipsr4k.Reset();
CoreTiming::Init(&mipsr4k);
HLEInit();