diff options
| author | JosJuice <josjuice@gmail.com> | 2025-12-05 23:21:17 +0100 |
|---|---|---|
| committer | JosJuice <josjuice@gmail.com> | 2026-01-18 13:25:53 +0100 |
| commit | 711c3e1013772634dd8787c659de84aadb9de4c2 (patch) | |
| tree | 4c40a4bdd3c0b8ba8ddaf1a5b8756898ff4962a3 /Source/Core | |
| parent | 373e35ed5b3202bd91c05f6dd1f49fcee449d2a5 (diff) | |
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.
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp | 92 |
1 files 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<u32> 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(); } } |
