From 711c3e1013772634dd8787c659de84aadb9de4c2 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 5 Dec 2025 23:21:17 +0100 Subject: [PATCH] Jit64: Improve cmpXX imm handling In the past, the register cache would contain either a host register or an immediate (or neither) for a given guest register. Jit64::cmpXX would roll with whichever one it got. But now that PR 12134 is merged, the register cache can contain a host register at the same time as an immediate. This gives us an optimization opportunity in Jit64::cmpXX. Some paths are more efficient when we use an immediate and some paths are more efficient when we don't, so to generate the best code, we should carefully choose if we want an immediate or not rather than leaving it to the register cache. --- .../Core/Core/PowerPC/Jit64/Jit_Integer.cpp | 92 ++++++++++++------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp index 21db558bdb..64ecd29d75 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp @@ -568,39 +568,38 @@ void Jit64::cmpXX(UGeckoInstruction inst) u32 crf = inst.CRFD; bool merge_branch = CheckMergedBranch(crf); - bool signedCompare; - RCOpArg comparand; + bool signedCompare = false; + std::optional imm_comparand; switch (inst.OPCD) { // cmp / cmpl case 31: signedCompare = (inst.SUBOP10 == 0); - comparand = signedCompare ? gpr.Use(b, RCMode::Read) : gpr.Bind(b, RCMode::Read); - RegCache::Realize(comparand); + if (gpr.IsImm(b)) + imm_comparand = gpr.Imm32(b); break; // cmpli case 10: signedCompare = false; - comparand = RCOpArg::Imm32((u32)inst.UIMM); + imm_comparand = u32(inst.UIMM); break; // cmpi case 11: signedCompare = true; - comparand = RCOpArg::Imm32((u32)(s32)(s16)inst.UIMM); + imm_comparand = u32(s32(s16(inst.UIMM))); break; default: - signedCompare = false; // silence compiler warning PanicAlertFmt("cmpXX"); } - if (gpr.IsImm(a) && comparand.IsImm()) + if (gpr.IsImm(a) && imm_comparand.has_value()) { // Both registers contain immediate values, so we can pre-compile the compare result - s64 compareResult = signedCompare ? (s64)gpr.SImm32(a) - (s64)comparand.SImm32() : - (u64)gpr.Imm32(a) - (u64)comparand.Imm32(); + s64 compareResult = signedCompare ? s64(gpr.SImm32(a)) - s64(s32(imm_comparand.value())) : + u64(gpr.Imm32(a)) - u64(imm_comparand.value()); if (compareResult == (s32)compareResult) { MOV(64, PPCSTATE_CR(crf), Imm32((u32)compareResult)); @@ -612,15 +611,12 @@ void Jit64::cmpXX(UGeckoInstruction inst) } if (merge_branch) - { - RegCache::Unlock(comparand); DoMergedBranchImmediate(compareResult); - } return; } - if (!gpr.IsImm(a) && !signedCompare && comparand.IsImm() && comparand.Imm32() == 0) + if (!gpr.IsImm(a) && !signedCompare && imm_comparand == u32(0)) { RCX64Reg Ra = gpr.Bind(a, RCMode::Read); RegCache::Realize(Ra); @@ -629,12 +625,38 @@ void Jit64::cmpXX(UGeckoInstruction inst) if (merge_branch) { TEST(64, Ra, Ra); - RegCache::Unlock(comparand, Ra); + RegCache::Unlock(Ra); DoMergedBranchCondition(); } return; } + const bool comparand_needs_reg = + !signedCompare && imm_comparand.has_value() && (imm_comparand.value() & 0x80000000U) != 0; + + RCOpArg Rb; + OpArg comparand; + if (inst.OPCD != 31) + { + // The comparand is from the instruction encoding, so we can't get it from the register cache. + comparand = Imm32(imm_comparand.value()); + } + else if (signedCompare && imm_comparand.has_value()) + { + // Asking the register cache for the comparand might give us a register, but we prefer an imm. + comparand = Imm32(imm_comparand.value()); + } + else if (imm_comparand != u32(0)) + { + // Ask the register cache for the comparand. We prefer getting a register, + // but may also accept imm or memory depending on the circumstances. + Rb = signedCompare ? + gpr.Use(b, RCMode::Read) : + (comparand_needs_reg ? gpr.Bind(b, RCMode::Read) : gpr.BindOrImm(b, RCMode::Read)); + RegCache::Realize(Rb); + comparand = Rb; + } + const X64Reg input = RSCRATCH; if (gpr.IsImm(a)) { @@ -653,25 +675,7 @@ void Jit64::cmpXX(UGeckoInstruction inst) MOVZX(64, 32, input, Ra); } - if (comparand.IsImm()) - { - // sign extension will ruin this, so store it in a register - if (!signedCompare && (comparand.Imm32() & 0x80000000U) != 0) - { - MOV(32, R(RSCRATCH2), comparand); - comparand = RCOpArg::R(RSCRATCH2); - } - } - else - { - if (signedCompare) - { - MOVSX(64, 32, RSCRATCH2, comparand); - comparand = RCOpArg::R(RSCRATCH2); - } - } - - if (comparand.IsImm() && comparand.Imm32() == 0) + if (imm_comparand == u32(0)) { MOV(64, PPCSTATE_CR(crf), R(input)); // Place the comparison next to the branch for macro-op fusion @@ -680,13 +684,31 @@ void Jit64::cmpXX(UGeckoInstruction inst) } else { + if (comparand.IsImm()) + { + if (comparand_needs_reg) + { + // sign extension will ruin this, so store it in a register + MOV(32, R(RSCRATCH2), comparand); + comparand = R(RSCRATCH2); + } + } + else + { + if (signedCompare) + { + MOVSX(64, 32, RSCRATCH2, comparand); + comparand = R(RSCRATCH2); + } + } + SUB(64, R(input), comparand); MOV(64, PPCSTATE_CR(crf), R(input)); } if (merge_branch) { - RegCache::Unlock(comparand); + RegCache::Unlock(Rb); DoMergedBranchCondition(); } }