From bc16a55028611acd2d769f3ea86d6465436184ba Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 10 Apr 2021 09:20:06 -0700 Subject: [PATCH 1/3] jit: Count delay slot cycles separately. This makes it easier to count cycles per instruction, instead of ignoring the delay slot's instruction for cycle count. --- Core/MIPS/ARM/ArmCompBranch.cpp | 5 +++++ Core/MIPS/ARM/ArmCompVFPU.cpp | 4 ++-- Core/MIPS/ARM64/Arm64CompBranch.cpp | 5 +++++ Core/MIPS/ARM64/Arm64CompVFPU.cpp | 4 ++-- Core/MIPS/IR/IRCompBranch.cpp | 4 ++++ Core/MIPS/MIPSTables.cpp | 9 ++------- Core/MIPS/x86/CompBranch.cpp | 5 +++++ Core/MIPS/x86/CompVFPU.cpp | 4 ++-- 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index b894bc6ea7..bf687c045d 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -113,6 +113,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely) } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -231,6 +232,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -334,6 +336,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp); CONDITIONAL_NICE_DELAYSLOT; if (!likely && delaySlotIsNice) @@ -392,6 +395,7 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) // The behavior is undefined - the CPU may take the second branch even if the first one passes. @@ -530,6 +534,7 @@ void ArmJit::Comp_JumpReg(MIPSOpcode op) bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index d9056c8238..639230db84 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -2206,7 +2206,7 @@ namespace MIPSComp u8 dregs[4]; u8 dregs2[4]; - u32 nextOp = GetOffsetInstruction(1).encoding; + MIPSOpcode nextOp = GetOffsetInstruction(1); int vd2 = -1; int imm2 = -1; if ((nextOp >> 26) == 60 && ((nextOp >> 21) & 0x1F) == 29 && _VS == MIPS_GET_VS(nextOp)) { @@ -2246,7 +2246,7 @@ namespace MIPSComp // If the negsin setting differs between the two joint invocations, we need to flip the second one. bool negSin2 = (imm2 & 0x10) ? true : false; CompVrotShuffle(dregs2, imm2, sz, negSin1 != negSin2); - js.compilerPC += 4; + EatInstruction(nextOp); } fpr.ReleaseSpillLocksAndDiscardTemps(); diff --git a/Core/MIPS/ARM64/Arm64CompBranch.cpp b/Core/MIPS/ARM64/Arm64CompBranch.cpp index 6229e5fec8..98735a2524 100644 --- a/Core/MIPS/ARM64/Arm64CompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64CompBranch.cpp @@ -113,6 +113,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely) } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -249,6 +250,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -351,6 +353,7 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp); CONDITIONAL_NICE_DELAYSLOT; if (!likely && delaySlotIsNice) @@ -408,6 +411,7 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) // The behavior is undefined - the CPU may take the second branch even if the first one passes. @@ -545,6 +549,7 @@ void Arm64Jit::Comp_JumpReg(MIPSOpcode op) bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 1307baee29..b03d7ef3a9 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -1922,7 +1922,7 @@ namespace MIPSComp { u8 dregs[4]; u8 dregs2[4]; - u32 nextOp = GetOffsetInstruction(1).encoding; + MIPSOpcode nextOp = GetOffsetInstruction(1); int vd2 = -1; int imm2 = -1; if ((nextOp >> 26) == 60 && ((nextOp >> 21) & 0x1F) == 29 && _VS == MIPS_GET_VS(nextOp)) { @@ -1958,7 +1958,7 @@ namespace MIPSComp { // If the negsin setting differs between the two joint invocations, we need to flip the second one. bool negSin2 = (imm2 & 0x10) ? true : false; CompVrotShuffle(dregs2, imm2, sz, negSin1 != negSin2); - js.compilerPC += 4; + EatInstruction(nextOp); } fpr.ReleaseSpillLocksAndDiscardTemps(); diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index 0f8bbc4ff2..79d170031a 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -65,6 +65,7 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); // Often, div/divu are followed by a likely "break" if the divisor was zero. @@ -121,6 +122,7 @@ void IRFrontend::BranchRSZeroComp(MIPSOpcode op, IRComparison cc, bool andLink, u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); MIPSGPReg lhs = rs; @@ -241,6 +243,7 @@ void IRFrontend::BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); ir.Write(IROp::VfpuCtrlToReg, IRTEMP_LHS, VFPU_CTRL_CC); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) @@ -343,6 +346,7 @@ void IRFrontend::Comp_JumpReg(MIPSOpcode op) { bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index ca8487e423..6cdea02adc 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -991,10 +991,10 @@ int MIPSInterpret_RunUntil(u64 globalTicks) bool wasInDelaySlot = curMips->inDelaySlot; MIPSInterpret(op); + curMips->downcount -= MIPSGetInstructionCycleEstimate(op); if (curMips->inDelaySlot) { - curMips->downcount -= 1; // The reason we have to check this is the delay slot hack in Int_Syscall. if (wasInDelaySlot) { @@ -1006,7 +1006,6 @@ int MIPSInterpret_RunUntil(u64 globalTicks) } } - curMips->downcount -= 1; if (CoreTiming::GetTicks() > globalTicks) { // DEBUG_LOG(CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks()); @@ -1050,11 +1049,7 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op) // TODO: Do something that makes sense here. int MIPSGetInstructionCycleEstimate(MIPSOpcode op) { - MIPSInfo info = MIPSGetInfo(op); - if (info & DELAYSLOT) - return 2; - else - return 1; + return 1; } const char *MIPSDisasmAt(u32 compilerPC) { diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index e7de2513f9..b3ec6588af 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -365,6 +365,7 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely) } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -446,6 +447,7 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -517,6 +519,7 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp); CONDITIONAL_NICE_DELAYSLOT; if (!likely && delaySlotIsNice) @@ -557,6 +560,7 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) // The behavior is undefined - the CPU may take the second branch even if the first one passes. @@ -685,6 +689,7 @@ void Jit::Comp_JumpReg(MIPSOpcode op) bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 914bed9f49..88aa7da0b9 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -3540,7 +3540,7 @@ void Jit::Comp_VRot(MIPSOpcode op) { u8 dregs[4]; u8 dregs2[4]; - u32 nextOp = GetOffsetInstruction(1).encoding; + MIPSOpcode nextOp = GetOffsetInstruction(1); int vd2 = -1; int imm2 = -1; if ((nextOp >> 26) == 60 && ((nextOp >> 21) & 0x1F) == 29 && _VS == MIPS_GET_VS(nextOp)) { @@ -3587,7 +3587,7 @@ void Jit::Comp_VRot(MIPSOpcode op) { // If the negsin setting differs between the two joint invocations, we need to flip the second one. bool negSin2 = (imm2 & 0x10) ? true : false; CompVrotShuffle(dregs2, imm2, n, negSin1 != negSin2); - js.compilerPC += 4; + EatInstruction(nextOp); } fpr.ReleaseSpillLocks(); } From 53104639ff11453b1b1f03e483dca5cec858f2a1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 10 Apr 2021 09:22:46 -0700 Subject: [PATCH 2/3] jit: Increase the cycle cost of VFPU ops. It seems like they all take at least 2 cycles, which kinda makes sense. --- Core/MIPS/MIPSTables.cpp | 7 ++++--- Core/MIPS/MIPSTables.h | 40 ++++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 6cdea02adc..eb0ffa5276 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -521,8 +521,8 @@ const MIPSInstruction tableCop1W[64] = // 010001 10100 ..... ..... ..... xxxxxx const MIPSInstruction tableVFPU0[8] = // 011000 xxx ....... . ....... . ....... { - INSTR("vadd", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), - INSTR("vsub", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vadd", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, MIPSInfo(IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX, 2)), + INSTR("vsub", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, MIPSInfo(IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX, 2)), // TODO: Disasm is wrong. INSTR("vsbn", JITFUNC(Comp_Generic), Dis_VectorSet3, Int_Vsbn, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INVALID, INVALID, INVALID, INVALID, @@ -1049,7 +1049,8 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op) // TODO: Do something that makes sense here. int MIPSGetInstructionCycleEstimate(MIPSOpcode op) { - return 1; + MIPSInfo info = MIPSGetInfo(op); + return info.cycles; } const char *MIPSDisasmAt(u32 compilerPC) { diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h index 3987aa3e87..fd12c43a96 100644 --- a/Core/MIPS/MIPSTables.h +++ b/Core/MIPS/MIPSTables.h @@ -20,21 +20,6 @@ #include "Common/CommonTypes.h" #include "Core/MIPS/MIPS.h" -struct MIPSInfo { - MIPSInfo() { - value = 0; - } - - explicit MIPSInfo(u64 v) : value(v) { - } - - u64 operator & (const u64 &arg) const { - return value & arg; - } - - u64 value; -}; - #define CONDTYPE_MASK 0x00000007 #define CONDTYPE_EQ 0x00000001 #define CONDTYPE_NE 0x00000002 @@ -108,6 +93,28 @@ struct MIPSInfo { #define CDECL #endif +struct MIPSInfo { + MIPSInfo() { + value = 0; + cycles = 0; + } + + explicit MIPSInfo(u64 v, u16 c = 0) : value(v), cycles(c) { + if (c == 0) { + cycles = 1; + if (v & IS_VFPU) + cycles++; + } + } + + u64 operator & (const u64 &arg) const { + return value & arg; + } + + u64 value : 48; + u64 cycles : 16; +}; + typedef void (CDECL *MIPSDisFunc)(MIPSOpcode opcode, char *out); typedef void (CDECL *MIPSInterpretFunc)(MIPSOpcode opcode); @@ -125,6 +132,3 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op); int MIPSGetInstructionCycleEstimate(MIPSOpcode op); const char *MIPSGetName(MIPSOpcode op); const char *MIPSDisasmAt(u32 compilerPC); - -void FillMIPSTables(); - From c4eafcf008caddd168cbcb45f2206f56818e09f7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 10 Apr 2021 09:23:27 -0700 Subject: [PATCH 3/3] jit: Increase the cycle cost of div.s. This largely matches tests on a real PSP. --- Core/MIPS/MIPSTables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index eb0ffa5276..166f0cc969 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -452,7 +452,7 @@ const MIPSInstruction tableCop1S[64] = // 010001 10000 ..... ..... ..... xxxxxx INSTR("add.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), INSTR("sub.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), INSTR("mul.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), - INSTR("div.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), + INSTR("div.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, MIPSInfo(OUT_FD|IN_FS|IN_FT|IS_FPU, 29)), INSTR("sqrt.s", JITFUNC(Comp_FPU2op), Dis_FPU2op, Int_FPU2op, OUT_FD|IN_FS|IS_FPU), INSTR("abs.s", JITFUNC(Comp_FPU2op), Dis_FPU2op, Int_FPU2op, OUT_FD|IN_FS|IS_FPU), INSTR("mov.s", JITFUNC(Comp_FPU2op), Dis_FPU2op, Int_FPU2op, OUT_FD|IN_FS|IS_FPU),