mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-07-11 01:24:22 +02:00
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.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user