Compare all 64 bits in the relational branches

BEQ and BNE already compare the full GPR, but BLEZ, BGTZ, BLTZ and BGEZ (and
their likely/and-link variants) were emitted against the low word only. The
R5900 compares the whole 64-bit register, so any value whose upper half is
significant takes the wrong branch.

Compilers reach these opcodes through the dsll32/dsra32 sign-extension idiom,
which leaves a canonical value and hides the bug; code that keeps a genuine
64-bit quantity in the register does not.
This commit is contained in:
Sinan KARAKAYA
2026-08-17 18:29:01 +02:00
parent 5c906ddf4b
commit 20848417af
+8 -4
View File
@@ -404,12 +404,16 @@ namespace ps2recomp
case OPCODE_BNE:
case OPCODE_BNEL:
return fmt::format("GPR_U64(ctx, {}) != GPR_U64(ctx, {})", rsReg, rtReg);
// The R5900 compares the full 64-bit GPR for the relational branches, as it
// already does for BEQ/BNE above. Comparing only the low word takes the wrong
// branch whenever the upper half is significant, which happens with the
// dsll32/dsra32 sign-extension idiom compilers emit ahead of these opcodes.
case OPCODE_BLEZ:
case OPCODE_BLEZL:
return fmt::format("GPR_S32(ctx, {}) <= 0", rsReg);
return fmt::format("GPR_S64(ctx, {}) <= 0", rsReg);
case OPCODE_BGTZ:
case OPCODE_BGTZL:
return fmt::format("GPR_S32(ctx, {}) > 0", rsReg);
return fmt::format("GPR_S64(ctx, {}) > 0", rsReg);
case OPCODE_REGIMM:
switch (m_branchInst.rt)
{
@@ -417,12 +421,12 @@ namespace ps2recomp
case REGIMM_BLTZL:
case REGIMM_BLTZAL:
case REGIMM_BLTZALL:
return fmt::format("GPR_S32(ctx, {}) < 0", rsReg);
return fmt::format("GPR_S64(ctx, {}) < 0", rsReg);
case REGIMM_BGEZ:
case REGIMM_BGEZL:
case REGIMM_BGEZAL:
case REGIMM_BGEZALL:
return fmt::format("GPR_S32(ctx, {}) >= 0", rsReg);
return fmt::format("GPR_S64(ctx, {}) >= 0", rsReg);
default:
return "false";
}