From de3fed609339ec89ff93122d304598f3e4495685 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 30 May 2021 21:57:31 +0200 Subject: [PATCH] Jits: Fix accidentally setting GT in CR when clearing EQ https://bugs.dolphin-emu.org/issues/12526 --- Source/Core/Core/PowerPC/Jit64/Jit.h | 1 + .../PowerPC/Jit64/Jit_SystemRegisters.cpp | 33 ++++++++--------- Source/Core/Core/PowerPC/JitArm64/Jit.h | 1 + .../JitArm64/JitArm64_SystemRegisters.cpp | 35 +++++++++---------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.h b/Source/Core/Core/PowerPC/Jit64/Jit.h index 358e7a8ea6..029cef0974 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.h +++ b/Source/Core/Core/PowerPC/Jit64/Jit.h @@ -116,6 +116,7 @@ public: void SetCRFieldBit(int field, int bit, Gen::X64Reg in); void ClearCRFieldBit(int field, int bit); void SetCRFieldBit(int field, int bit); + void FixGTBeforeSettingCRFieldBit(Gen::X64Reg reg); // Generates a branch that will check if a given bit of a CR register part // is set or not. diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp index 99c323398f..8044986492 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -54,16 +54,8 @@ void Jit64::SetCRFieldBit(int field, int bit, X64Reg in) MOV(64, R(RSCRATCH2), CROffset(field)); MOVZX(32, 8, in, R(in)); - // Gross but necessary; if the input is totally zero and we set SO or LT, - // or even just add the (1<<32), GT will suddenly end up set without us - // intending to. This can break actual games, so fix it up. if (bit != PowerPC::CR_GT_BIT) - { - TEST(64, R(RSCRATCH2), R(RSCRATCH2)); - FixupBranch dont_clear_gt = J_CC(CC_NZ); - BTS(64, R(RSCRATCH2), Imm8(63)); - SetJumpTarget(dont_clear_gt); - } + FixGTBeforeSettingCRFieldBit(RSCRATCH2); switch (bit) { @@ -107,7 +99,10 @@ void Jit64::ClearCRFieldBit(int field, int bit) break; case PowerPC::CR_EQ_BIT: - OR(64, CROffset(field), Imm8(1)); + MOV(64, R(RSCRATCH), CROffset(field)); + FixGTBeforeSettingCRFieldBit(RSCRATCH); + OR(64, R(RSCRATCH), Imm8(1)); + MOV(64, CROffset(field), R(RSCRATCH)); break; case PowerPC::CR_GT_BIT: @@ -126,12 +121,7 @@ void Jit64::SetCRFieldBit(int field, int bit) { MOV(64, R(RSCRATCH), CROffset(field)); if (bit != PowerPC::CR_GT_BIT) - { - TEST(64, R(RSCRATCH), R(RSCRATCH)); - FixupBranch dont_clear_gt = J_CC(CC_NZ); - BTS(64, R(RSCRATCH), Imm8(63)); - SetJumpTarget(dont_clear_gt); - } + FixGTBeforeSettingCRFieldBit(RSCRATCH); switch (bit) { @@ -157,6 +147,17 @@ void Jit64::SetCRFieldBit(int field, int bit) MOV(64, CROffset(field), R(RSCRATCH)); } +void Jit64::FixGTBeforeSettingCRFieldBit(Gen::X64Reg reg) +{ + // Gross but necessary; if the input is totally zero and we set SO or LT, + // or even just add the (1<<32), GT will suddenly end up set without us + // intending to. This can break actual games, so fix it up. + TEST(64, R(reg), R(reg)); + FixupBranch dont_clear_gt = J_CC(CC_NZ); + BTS(64, R(reg), Imm8(63)); + SetJumpTarget(dont_clear_gt); +} + FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set) { switch (bit) diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.h b/Source/Core/Core/PowerPC/JitArm64/Jit.h index 93263704fa..c16521b240 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.h +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.h @@ -255,6 +255,7 @@ protected: void FakeLKExit(u32 exit_address_after_return); void WriteBLRExit(Arm64Gen::ARM64Reg dest); + void FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg); Arm64Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set); void ComputeRC0(Arm64Gen::ARM64Reg reg); diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp index 074f4d3c42..33cf6d51b7 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp @@ -36,6 +36,19 @@ FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set) } } +void JitArm64::FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg) +{ + // Gross but necessary; if the input is totally zero and we set SO or LT, + // or even just add the (1<<32), GT will suddenly end up set without us + // intending to. This can break actual games, so fix it up. + ARM64Reg WA = gpr.GetReg(); + ARM64Reg XA = EncodeRegTo64(WA); + ORR(XA, reg, 64 - 63, 0, true); // XB | 1<<63 + CMP(reg, ARM64Reg::ZR); + CSEL(reg, reg, XA, CC_NEQ); + gpr.Unlock(WA); +} + void JitArm64::mtmsr(UGeckoInstruction inst) { INSTRUCTION_START @@ -432,6 +445,7 @@ void JitArm64::crXXX(UGeckoInstruction inst) break; case PowerPC::CR_EQ_BIT: + FixGTBeforeSettingCRFieldBit(XA); ORR(XA, XA, 0, 0, true); // XA | 1<<0 break; @@ -457,14 +471,7 @@ void JitArm64::crXXX(UGeckoInstruction inst) ARM64Reg XA = gpr.CR(field); if (bit != PowerPC::CR_GT_BIT) - { - ARM64Reg WB = gpr.GetReg(); - ARM64Reg XB = EncodeRegTo64(WB); - ORR(XB, XA, 64 - 63, 0, true); // XA | 1<<63 - CMP(XA, ARM64Reg::ZR); - CSEL(XA, XA, XB, CC_NEQ); - gpr.Unlock(WB); - } + FixGTBeforeSettingCRFieldBit(XA); switch (bit) { @@ -569,18 +576,8 @@ void JitArm64::crXXX(UGeckoInstruction inst) gpr.BindCRToRegister(field, true); XB = gpr.CR(field); - // Gross but necessary; if the input is totally zero and we set SO or LT, - // or even just add the (1<<32), GT will suddenly end up set without us - // intending to. This can break actual games, so fix it up. if (bit != PowerPC::CR_GT_BIT) - { - ARM64Reg WC = gpr.GetReg(); - ARM64Reg XC = EncodeRegTo64(WC); - ORR(XC, XB, 64 - 63, 0, true); // XB | 1<<63 - CMP(XB, ARM64Reg::ZR); - CSEL(XB, XB, XC, CC_NEQ); - gpr.Unlock(WC); - } + FixGTBeforeSettingCRFieldBit(XB); switch (bit) {