[DYNAREC] Fix the issue where source register values get overwritten. (#4032)

In the implementations of `addsd/subsd/mulsd/divsd` when BOX64_DYNAREC_FASTNAN
is enabled, the source register values are being overwritten. This violates the
x86-64 instruction specification, which prohibits modification of source
registers. The resulting loss of source register data triggers subsequent
runtime errors.

I referenced the LA64 and ARM64 implementations and used temporary floating-point
registers to store intermediate values.

SNAN-to-QNAN conversion handling may also need to be added here. I will address
this in a separate follow-up patch.
This commit is contained in:
Zhijin Zeng authored and GitHub committed 2026-07-13 09:55:38 +02:00
1 parent a96396985e
commit 0ccd7513b4
1 file changed
+32 -24
+32 -24
View File
@@ -205,42 +205,46 @@ uintptr_t dynarec64_F20F(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int
nextop = F8; nextop = F8;
GETGXSD(v0); GETGXSD(v0);
GETEXSD(v1, 0); GETEXSD(v1, 0);
d0 = fpu_get_scratch(dyn);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FMVD(d0, v1);
FEQD(x3, v1, v1); FEQD(x3, v1, v1);
FEQD(x4, v0, v0); FEQD(x4, v0, v0);
AND(x5, x3, x4); AND(x5, x3, x4);
BEQZ(x5, 4 + 4 * 4); BEQZ(x5, 4 + 4 * 4);
} }
FADDD(v1, v1, v0); FADDD(d0, v1, v0);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FEQD(x5, v1, v1); FEQD(x5, d0, d0);
BNEZ(x5, 4 + 4); BNEZ(x5, 4 + 4 * 3);
FNEGD(v1, v1); FNEGD(d0, d0);
BNEZ(x4, 4 + 4); BNEZ(x4, 4 + 4);
FMVD(v1, v0); FMVD(d0, v0);
} }
FMVD(v0, v1); FMVD(v0, d0);
break; break;
case 0x59: case 0x59:
INST_NAME("MULSD Gx, Ex"); INST_NAME("MULSD Gx, Ex");
nextop = F8; nextop = F8;
GETGXSD(v0); GETGXSD(v0);
GETEXSD(v1, 0); GETEXSD(v1, 0);
d0 = fpu_get_scratch(dyn);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FMVD(d0, v1);
FEQD(x3, v1, v1); FEQD(x3, v1, v1);
FEQD(x4, v0, v0); FEQD(x4, v0, v0);
AND(x5, x3, x4); AND(x5, x3, x4);
BEQZ(x5, 4 + 4 * 4); BEQZ(x5, 4 + 4 * 4);
} }
FMULD(v1, v1, v0); FMULD(d0, v1, v0);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FEQD(x5, v1, v1); FEQD(x5, d0, d0);
BNEZ(x5, 4 + 4); BNEZ(x5, 4 + 4 * 3);
FNEGD(v1, v1); FNEGD(d0, d0);
BNEZ(x4, 4 + 4); BNEZ(x4, 4 + 4);
FMVD(v1, v0); FMVD(d0, v0);
} }
FMVD(v0, v1); FMVD(v0, d0);
break; break;
case 0x5A: case 0x5A:
INST_NAME("CVTSD2SS Gx, Ex"); INST_NAME("CVTSD2SS Gx, Ex");
@@ -260,21 +264,23 @@ uintptr_t dynarec64_F20F(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int
nextop = F8; nextop = F8;
GETGXSD(v0); GETGXSD(v0);
GETEXSD(v1, 0); GETEXSD(v1, 0);
d0 = fpu_get_scratch(dyn);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FMVD(d0, v1);
FEQD(x3, v1, v1); FEQD(x3, v1, v1);
FEQD(x4, v0, v0); FEQD(x4, v0, v0);
AND(x5, x3, x4); AND(x5, x3, x4);
BEQZ(x5, 4 + 4 * 4); BEQZ(x5, 4 + 4 * 4);
} }
FSUBD(v1, v0, v1); FSUBD(d0, v0, v1);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FEQD(x5, v1, v1); FEQD(x5, d0, d0);
BNEZ(x5, 4 + 4); BNEZ(x5, 4 + 4 * 3);
FNEGD(v1, v1); FNEGD(d0, d0);
BNEZ(x4, 4 + 4); BNEZ(x4, 4 + 4);
FMVD(v1, v0); FMVD(d0, v0);
} }
FMVD(v0, v1); FMVD(v0, d0);
break; break;
case 0x5D: case 0x5D:
INST_NAME("MINSD Gx, Ex"); INST_NAME("MINSD Gx, Ex");
@@ -296,21 +302,23 @@ uintptr_t dynarec64_F20F(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int
nextop = F8; nextop = F8;
GETGXSD(v0); GETGXSD(v0);
GETEXSD(v1, 0); GETEXSD(v1, 0);
d0 = fpu_get_scratch(dyn);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FMVD(d0, v1);
FEQD(x3, v1, v1); FEQD(x3, v1, v1);
FEQD(x4, v0, v0); FEQD(x4, v0, v0);
AND(x5, x3, x4); AND(x5, x3, x4);
BEQZ(x5, 4 + 4 * 4); BEQZ(x5, 4 + 4 * 4);
} }
FDIVD(v1, v0, v1); FDIVD(d0, v0, v1);
if (!BOX64ENV(dynarec_fastnan)) { if (!BOX64ENV(dynarec_fastnan)) {
FEQD(x5, v1, v1); FEQD(x5, d0, d0);
BNEZ(x5, 4 + 4); BNEZ(x5, 4 + 4 * 3);
FNEGD(v1, v1); FNEGD(d0, d0);
BNEZ(x4, 4 + 4); BNEZ(x4, 4 + 4);
FMVD(v1, v0); FMVD(d0, v0);
} }
FMVD(v0, v1); FMVD(v0, d0);
break; break;
case 0x5F: case 0x5F:
INST_NAME("MAXSD Gx, Ex"); INST_NAME("MAXSD Gx, Ex");