Jit64: Fix FinalizeCarryOverflow XER[OV/SO]

FinalizeCarryOverflow didn't maintain XER[OV/SO] properly due to an
oversight. Here's the code it would generate:

0:  9c                      pushf
1:  80 65 3b fe             and    BYTE PTR [rbp+0x3b],0xfe
5:  71 04                   jno    b <jno>
7:  c6 45 3b 03             mov    BYTE PTR [rbp+0x3b],0x3
000000000000000b <jno>:
b:  9d                      popf

At first glance it seems reasonable. The host flags are carefully
preserved with PUSHF. The AND instruction clears XER[OV]. Next, an
conditional branch checks the host's overflow flag and, if needed, skips
over a MOV that sets XER[OV/SO]. Finally, host flags are restored with
POPF.

However, the AND instruction also clears the host's overflow flag. As a
result, the branch that follows it is always taken and the MOV is always
skipped. The end result is that XER[OV] is always cleared while XER[SO]
is left unchanged.

Putting POPF immediately after the AND would fix this, but we already
have GenerateOverflow doing it correctly (and without the PUSHF/POPF
shenanigans too). So let's just use that instead.
This commit is contained in:
Sintendo 2021-01-09 22:52:18 +01:00
parent 0776263c5e
commit 305cd31bd9

View file

@ -122,15 +122,7 @@ void Jit64::FinalizeCarryOverflow(bool oe, bool inv)
{
if (oe)
{
// Make sure not to lose the carry flags (not a big deal, this path is rare).
PUSHF();
// XER[OV] = 0
AND(8, PPCSTATE(xer_so_ov), Imm8(~XER_OV_MASK));
FixupBranch jno = J_CC(CC_NO);
// XER[OV/SO] = 1
MOV(8, PPCSTATE(xer_so_ov), Imm8(XER_SO_MASK | XER_OV_MASK));
SetJumpTarget(jno);
POPF();
GenerateOverflow();
}
// Do carry
FinalizeCarry(inv ? CC_NC : CC_C);