Merge pull request #4811 from pmatos/fix/SPEC_GCC-C-execute-ieee-fp-cmp-8l

Fix IEEE 754 unordered comparison detection in x87 floating-point operations
This commit is contained in:
Ryan Houdek authored and GitHub committed 2025-09-01 14:39:59 -07:00
commit 4a71edf7bc
8 files changed
+531 -222

No files matched your search

+5 -1
View File
@@ -294,7 +294,11 @@ struct FEX_PACKED X80SoftFloat {
FCMP(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs, bool* eq, bool* lt, bool* nan) {
*eq = extF80_eq(state, lhs, rhs);
*lt = extF80_lt(state, lhs, rhs);
*nan = IsNan(lhs) || IsNan(rhs);
// Use IEEE 754 semantics: unordered if neither <, =, nor > is true
// This is more reliable than custom NaN detection
bool gt = !(*eq) && !(*lt) && extF80_le(state, rhs, lhs);
*nan = !(*eq) && !(*lt) && !gt;
}
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSCALE(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
@@ -680,6 +680,9 @@ void OpDispatchBuilder::FCOMI(OpcodeArgs, IR::OpSize Width, bool Integer, OpDisp
SetRFLAG<FEXCore::X86State::RFLAG_PF_RAW_LOC>(PF);
}
// Set Invalid Operation flag when unordered (NaN comparison)
SetRFLAG<FEXCore::X86State::X87FLAG_IE_LOC>(HostFlag_Unordered);
if (PopTwice) {
_PopStackDestroy();
_PopStackDestroy();
@@ -701,6 +704,9 @@ void OpDispatchBuilder::FTST(OpcodeArgs) {
SetRFLAG<FEXCore::X86State::X87FLAG_C1_LOC>(Constant(0));
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(HostFlag_Unordered);
SetRFLAG<FEXCore::X86State::X87FLAG_C3_LOC>(HostFlag_ZF);
// Set Invalid Operation flag when unordered (NaN comparison)
SetRFLAG<FEXCore::X86State::X87FLAG_IE_LOC>(HostFlag_Unordered);
}
void OpDispatchBuilder::X87OpHelper(OpcodeArgs, FEXCore::IR::IROps IROp, bool ZeroC2) {
@@ -0,0 +1,73 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0xcafecafe"
},
"Mode": "32BIT"
}
%endif
; IsNan() couldn't detect negative NaNs (sign bit set in exponent field).
; This caused __builtin_isunordered() to return wrong values.
mov esp, 0xe000_1000
; Test 1: __builtin_isunordered(1.0, 2.0) should return 0
; Pattern: fucomip + setp + test for 0
fld1
lea edx, [two]
fld tword [edx]
fucomip st1
setp al
movzx eax, al
test eax, eax
jnz test_failed ; If not 0, test failed (should be ordered)
; Test 2: __builtin_isunordered(1.0, NaN) should return 1
fld1
lea edx, [qnan]
fld tword [edx]
fucomip st1
setp al
movzx eax, al
test eax, eax
jz test_failed ; If 0, test failed (should be unordered)
; Test 3: __builtin_isunordered(NaN, 1.0) should return 1
lea edx, [qnan]
fld tword [edx]
fld1
fucomip st1
setp al
movzx eax, al
test eax, eax
jz test_failed ; If 0, test failed (should be unordered)
; Test 4: __builtin_isunordered(2.0, 2.0) should return 0 (equal case)
lea edx, [two]
fld tword [edx]
lea edx, [two]
fld tword [edx]
fucomip st1
setp al
movzx eax, al
test eax, eax
jnz test_failed ; If not 0, test failed (should be ordered)
; All tests passed
mov eax, 0xcafecafe
hlt
test_failed:
; Test failed
mov eax, 0xdeadbeef
hlt
align 8
two:
dt 2.0
align 8
qnan:
dq 0xC000000000000000 ; Quiet NaN with only quiet bit set (no bottom 62 bits) - this breaks IsNan
dw 0x7FFF ; Standard NaN exponent (0x7FFF)
@@ -0,0 +1,72 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0x0000000cafecafe"
}
}
%endif
; IsNan() couldn't detect negative NaNs (sign bit set in exponent field).
; This caused __builtin_isunordered() to return wrong values.
mov rsp, 0xe000_1000
; Test 1: __builtin_isunordered(1.0, 2.0) should return 0
; Pattern: fucomip + setp + test for 0
fld1
lea rdx, [rel two]
fld tword [rdx]
fucomip st1
setp al
movzx eax, al
test eax, eax
jnz test_failed ; If not 0, test failed (should be ordered)
; Test 2: __builtin_isunordered(1.0, NaN) should return 1
fld1
lea rdx, [rel qnan]
fld tword [rdx]
fucomip st1
setp al
movzx eax, al
test eax, eax
jz test_failed ; If 0, test failed (should be unordered)
; Test 3: __builtin_isunordered(NaN, 1.0) should return 1
lea rdx, [rel qnan]
fld tword [rdx]
fld1
fucomip st1
setp al
movzx eax, al
test eax, eax
jz test_failed ; If 0, test failed (should be unordered)
; Test 4: __builtin_isunordered(2.0, 2.0) should return 0 (equal case)
lea rdx, [rel two]
fld tword [rdx]
lea rdx, [rel two]
fld tword [rdx]
fucomip st1
setp al
movzx eax, al
test eax, eax
jnz test_failed ; If not 0, test failed (should be ordered)
; All tests passed
mov rax, 0xcafecafe
hlt
test_failed:
; Test failed
mov rax, 0xdeadbeef
hlt
align 8
two:
dt 2.0
align 8
qnan:
dq 0xC000000000000000 ; Quiet NaN with only quiet bit set (no bottom 62 bits) - this breaks IsNan
dw 0x7FFF ; Standard NaN exponent (0x7FFF)
@@ -14,7 +14,7 @@
"Instructions": {
"Block1": {
"x86InstructionCount": 70,
"ExpectedInstructionCount": 415,
"ExpectedInstructionCount": 416,
"x86Insts": [
"sub esp,0x2c",
"mov ecx,dword [esp + 0x34]",
@@ -475,6 +475,7 @@
"rmif x21, #63, #nzCv",
"rmif x22, #62, #nZcv",
"eor w26, w20, #0x1",
"strb w20, [x28, #1008]",
"ldrb w20, [x28, #1019]",
"sub w20, w20, #0x3 (3)",
"and w20, w20, #0x7",
@@ -507,7 +508,7 @@
},
"Block2": {
"x86InstructionCount": 37,
"ExpectedInstructionCount": 213,
"ExpectedInstructionCount": 217,
"x86Insts": [
"sub esp,0x1c",
"mov edx,dword [esp + 0x20]",
@@ -585,10 +586,11 @@
"ubfx x22, x20, #0, #1",
"ubfx x20, x20, #2, #1",
"orr w21, w21, w20",
"orr w20, w22, w20",
"orr w22, w22, w20",
"eor x21, x21, #0x1",
"rmif x21, #63, #nzCv",
"rmif x20, #62, #nZcv",
"rmif x22, #62, #nZcv",
"strb w20, [x28, #1008]",
"csetm x20, ls",
"dup v4.2d, x20",
"bsl v4.16b, v3.16b, v2.16b",
@@ -625,10 +627,11 @@
"ubfx x22, x20, #0, #1",
"ubfx x20, x20, #2, #1",
"orr w21, w21, w20",
"orr w20, w22, w20",
"orr w22, w22, w20",
"eor x21, x21, #0x1",
"rmif x21, #63, #nzCv",
"rmif x20, #62, #nZcv",
"rmif x22, #62, #nZcv",
"strb w20, [x28, #1008]",
"csetm x20, ls",
"dup v5.2d, x20",
"bsl v5.16b, v3.16b, v2.16b",
@@ -665,10 +668,11 @@
"ubfx x22, x20, #0, #1",
"ubfx x20, x20, #2, #1",
"orr w21, w21, w20",
"orr w20, w22, w20",
"orr w22, w22, w20",
"eor x21, x21, #0x1",
"rmif x21, #63, #nzCv",
"rmif x20, #62, #nZcv",
"rmif x22, #62, #nZcv",
"strb w20, [x28, #1008]",
"csetm x20, ls",
"dup v6.2d, x20",
"bsl v6.16b, v3.16b, v2.16b",
@@ -737,6 +741,7 @@
"rmif x21, #63, #nzCv",
"rmif x22, #62, #nZcv",
"eor w26, w20, #0x1",
"strb w20, [x28, #1008]",
"ldrb w20, [x28, #1019]",
"sub w20, w20, #0x2 (2)",
"and w20, w20, #0x7",
@@ -1202,7 +1207,7 @@
},
"Block5": {
"x86InstructionCount": 49,
"ExpectedInstructionCount": 299,
"ExpectedInstructionCount": 300,
"x86Insts": [
"fld dword [esp + 0x80]",
"fsub dword [esp + 0x7c]",
@@ -1534,6 +1539,7 @@
"rmif x21, #63, #nzCv",
"rmif x22, #62, #nZcv",
"eor w26, w20, #0x1",
"strb w20, [x28, #1008]",
"cset x20, hi",
"strb w20, [x8, #48]",
"ldrb w4, [x8, #48]",
@@ -1558,7 +1564,7 @@
},
"Block6": {
"x86InstructionCount": 39,
"ExpectedInstructionCount": 295,
"ExpectedInstructionCount": 296,
"x86Insts": [
"push ebp",
"push edi",
@@ -1864,6 +1870,7 @@
"rmif x21, #63, #nzCv",
"rmif x22, #62, #nZcv",
"eor w26, w20, #0x1",
"strb w20, [x28, #1008]",
"ldrb w20, [x28, #1019]",
"sub w20, w20, #0x4 (4)",
"and w20, w20, #0x7",
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,3 @@
pr72867.c.gcc-target-test-32.n500.gcc-target-32
pr72867.c.gcc-target-test-32.n1.gcc-target-32
sse2-mmx-pextrw.c.gcc-target-test-32.n500.gcc-target-32
sse2-mmx-pextrw.c.gcc-target-test-32.n1.gcc-target-32