From ee1725684ab6b861aee1769a9e0f90c09d3e7e0e Mon Sep 17 00:00:00 2001 From: Paulo Matos Date: Wed, 30 Jul 2025 08:11:13 +0200 Subject: [PATCH 1/4] Implementation of FCNLEX to clear IE flag --- .../Interface/Core/OpcodeDispatcher.cpp | 4 +-- .../Source/Interface/Core/OpcodeDispatcher.h | 1 + .../Interface/Core/OpcodeDispatcher/X87.cpp | 5 ++++ unittests/32Bit_ASM/X87/DB_E2.asm | 26 +++++++++++++++++++ unittests/ASM/X87/DB_E2.asm | 25 ++++++++++++++++++ unittests/ASM/X87_F64/DB_E2_F64.asm | 26 +++++++++++++++++++ 6 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 unittests/32Bit_ASM/X87/DB_E2.asm create mode 100644 unittests/ASM/X87/DB_E2.asm create mode 100644 unittests/ASM/X87_F64/DB_E2_F64.asm diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 54bf8a0d3..c6396f809 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -5545,7 +5545,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) { {OPD(0xDB, 0xD0), 8, &OpDispatchBuilder::X87FCMOV}, {OPD(0xDB, 0xD8), 8, &OpDispatchBuilder::X87FCMOV}, // E0 = Invalid - {OPD(0xDB, 0xE2), 1, &OpDispatchBuilder::NOPOp}, // FNCLEX + {OPD(0xDB, 0xE2), 1, &OpDispatchBuilder::FNCLEX}, {OPD(0xDB, 0xE3), 1, &OpDispatchBuilder::FNINIT}, // E4 = Invalid {OPD(0xDB, 0xE8), 8, @@ -5804,7 +5804,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) { {OPD(0xDB, 0xD0), 8, &OpDispatchBuilder::X87FCMOV}, {OPD(0xDB, 0xD8), 8, &OpDispatchBuilder::X87FCMOV}, // E0 = Invalid - {OPD(0xDB, 0xE2), 1, &OpDispatchBuilder::NOPOp}, // FNCLEX + {OPD(0xDB, 0xE2), 1, &OpDispatchBuilder::FNCLEX}, {OPD(0xDB, 0xE3), 1, &OpDispatchBuilder::FNINIT}, // E4 = Invalid {OPD(0xDB, 0xE8), 8, diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 5dc4f089a..d6ddb6fd6 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -735,6 +735,7 @@ public: void FADD(OpcodeArgs, IR::OpSize Width, bool Integer, OpResult ResInST0); void FDIV(OpcodeArgs, IR::OpSize Width, bool Integer, bool Reverse, OpResult ResInST0); void FMUL(OpcodeArgs, IR::OpSize Width, bool Integer, OpResult ResInST0); + void FNCLEX(OpcodeArgs); void FNINIT(OpcodeArgs); void FSUB(OpcodeArgs, IR::OpSize Width, bool Integer, bool Reverse, OpResult ResInST0); void FTST(OpcodeArgs); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp index be2ab5ea2..048f78f0b 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp @@ -759,6 +759,11 @@ void OpDispatchBuilder::X87FNSTSW(OpcodeArgs) { StoreResult(GPRClass, Op, StatusWord, OpSize::iInvalid); } +void OpDispatchBuilder::FNCLEX(OpcodeArgs) { + // Clear the exception flag bit + SetRFLAG(_Constant(0)); +} + void OpDispatchBuilder::FNINIT(OpcodeArgs) { auto Zero = _Constant(0); diff --git a/unittests/32Bit_ASM/X87/DB_E2.asm b/unittests/32Bit_ASM/X87/DB_E2.asm new file mode 100644 index 000000000..040fa2187 --- /dev/null +++ b/unittests/32Bit_ASM/X87/DB_E2.asm @@ -0,0 +1,26 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0", + "RBX": "1" + }, + "Mode": "32BIT" +} +%endif + +finit ; IOC is 0 +fldz +fld1 +fdiv st0, st1 ; IOC is 1 + +fnstsw ax +and eax, 1 +mov ebx, eax ; save IOC to RBX + +; Clear +fnclex + +fnstsw ax +and eax, 1 + +hlt diff --git a/unittests/ASM/X87/DB_E2.asm b/unittests/ASM/X87/DB_E2.asm new file mode 100644 index 000000000..b4481157c --- /dev/null +++ b/unittests/ASM/X87/DB_E2.asm @@ -0,0 +1,25 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0", + "RBX": "1" + } +} +%endif + +finit ; IOC is 0 +fldz +fld1 +fdiv st0, st1 ; IOC is 1 + +fnstsw ax +and rax, 1 +mov rbx, rax ; save IOC to RBX + +; Clear +fnclex + +fnstsw ax +and rax, 1 + +hlt diff --git a/unittests/ASM/X87_F64/DB_E2_F64.asm b/unittests/ASM/X87_F64/DB_E2_F64.asm new file mode 100644 index 000000000..cfe4ccee7 --- /dev/null +++ b/unittests/ASM/X87_F64/DB_E2_F64.asm @@ -0,0 +1,26 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0", + "RBX": "1" + }, + "Env": { "FEX_X87REDUCEDPRECISION" : "1" } +} +%endif + +finit ; IOC is 0 +fldz +fld1 +fdiv st0, st1 ; IOC is 1 + +fnstsw ax +and rax, 1 +mov rbx, rax ; save IOC to RBX + +; Clear +fnclex + +fnstsw ax +and rax, 1 + +hlt From b13162c5bfe2d5df51bd7fce450719a49228d77a Mon Sep 17 00:00:00 2001 From: Paulo Matos Date: Wed, 30 Jul 2025 10:44:57 +0200 Subject: [PATCH 2/4] asm_tests: Division by zero does not set IE flag --- unittests/32Bit_ASM/X87/D8_F0_2.asm | 18 ++++++++++++++++++ unittests/ASM/X87/D8_F0_2.asm | 17 +++++++++++++++++ unittests/ASM/X87_F64/D8_F0_2_F64.asm | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 unittests/32Bit_ASM/X87/D8_F0_2.asm create mode 100644 unittests/ASM/X87/D8_F0_2.asm create mode 100644 unittests/ASM/X87_F64/D8_F0_2_F64.asm diff --git a/unittests/32Bit_ASM/X87/D8_F0_2.asm b/unittests/32Bit_ASM/X87/D8_F0_2.asm new file mode 100644 index 000000000..af32cd1ce --- /dev/null +++ b/unittests/32Bit_ASM/X87/D8_F0_2.asm @@ -0,0 +1,18 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0" + }, + "Mode": "32BIT" +} +%endif + +; Tests that a division by zero does not set the IE flag +finit +fldz +fld1 +fdiv st0, st1 + +fnstsw ax +and eax, 1 +hlt diff --git a/unittests/ASM/X87/D8_F0_2.asm b/unittests/ASM/X87/D8_F0_2.asm new file mode 100644 index 000000000..5c10fbbee --- /dev/null +++ b/unittests/ASM/X87/D8_F0_2.asm @@ -0,0 +1,17 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0" + } +} +%endif + +; Tests that a division by zero does not set the IE flag +finit +fldz +fld1 +fdiv st0, st1 + +fnstsw ax +and rax, 1 +hlt diff --git a/unittests/ASM/X87_F64/D8_F0_2_F64.asm b/unittests/ASM/X87_F64/D8_F0_2_F64.asm new file mode 100644 index 000000000..318ebe82a --- /dev/null +++ b/unittests/ASM/X87_F64/D8_F0_2_F64.asm @@ -0,0 +1,18 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0" + }, + "Env": { "FEX_X87REDUCEDPRECISION" : "1" } +} +%endif + +; Tests that a division by zero does not set the IE flag +finit +fldz +fld1 +fdiv st0, st1 + +fnstsw ax +and rax, 1 +hlt From 93c3eb064d1cae48cba4922e1f6a94c633cb1806 Mon Sep 17 00:00:00 2001 From: Paulo Matos Date: Wed, 30 Jul 2025 10:46:53 +0200 Subject: [PATCH 3/4] asm_tests: Implementation of FCNLEX to clear IE flag --- unittests/32Bit_ASM/X87/DB_E2.asm | 2 +- unittests/ASM/X87/DB_E2.asm | 2 +- unittests/ASM/X87_F64/DB_E2_F64.asm | 26 -------------------------- 3 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 unittests/ASM/X87_F64/DB_E2_F64.asm diff --git a/unittests/32Bit_ASM/X87/DB_E2.asm b/unittests/32Bit_ASM/X87/DB_E2.asm index 040fa2187..ae1810110 100644 --- a/unittests/32Bit_ASM/X87/DB_E2.asm +++ b/unittests/32Bit_ASM/X87/DB_E2.asm @@ -10,7 +10,7 @@ finit ; IOC is 0 fldz -fld1 +fldz fdiv st0, st1 ; IOC is 1 fnstsw ax diff --git a/unittests/ASM/X87/DB_E2.asm b/unittests/ASM/X87/DB_E2.asm index b4481157c..0975410fe 100644 --- a/unittests/ASM/X87/DB_E2.asm +++ b/unittests/ASM/X87/DB_E2.asm @@ -9,7 +9,7 @@ finit ; IOC is 0 fldz -fld1 +fldz fdiv st0, st1 ; IOC is 1 fnstsw ax diff --git a/unittests/ASM/X87_F64/DB_E2_F64.asm b/unittests/ASM/X87_F64/DB_E2_F64.asm deleted file mode 100644 index cfe4ccee7..000000000 --- a/unittests/ASM/X87_F64/DB_E2_F64.asm +++ /dev/null @@ -1,26 +0,0 @@ -%ifdef CONFIG -{ - "RegData": { - "RAX": "0", - "RBX": "1" - }, - "Env": { "FEX_X87REDUCEDPRECISION" : "1" } -} -%endif - -finit ; IOC is 0 -fldz -fld1 -fdiv st0, st1 ; IOC is 1 - -fnstsw ax -and rax, 1 -mov rbx, rax ; save IOC to RBX - -; Clear -fnclex - -fnstsw ax -and rax, 1 - -hlt From c95f9f5e6a80b296d7d21c4a3a0c18f3ae603cd0 Mon Sep 17 00:00:00 2001 From: Paulo Matos Date: Wed, 30 Jul 2025 10:47:54 +0200 Subject: [PATCH 4/4] instcountci: Implementation of FCNLEX to clear IE flag --- unittests/InstructionCountCI/FlagM/x87.json | 6 ++++-- unittests/InstructionCountCI/FlagM/x87_f64.json | 6 ++++-- unittests/InstructionCountCI/x87.json | 6 ++++-- unittests/InstructionCountCI/x87_f64.json | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/unittests/InstructionCountCI/FlagM/x87.json b/unittests/InstructionCountCI/FlagM/x87.json index 369f5abae..6d377fcaa 100644 --- a/unittests/InstructionCountCI/FlagM/x87.json +++ b/unittests/InstructionCountCI/FlagM/x87.json @@ -4964,11 +4964,13 @@ ] }, "fnclex": { - "ExpectedInstructionCount": 0, + "ExpectedInstructionCount": 1, "Comment": [ "0xdb 11b 0xe2 /4" ], - "ExpectedArm64ASM": [] + "ExpectedArm64ASM": [ + "strb wzr, [x28, #1008]" + ] }, "fninit": { "ExpectedInstructionCount": 8, diff --git a/unittests/InstructionCountCI/FlagM/x87_f64.json b/unittests/InstructionCountCI/FlagM/x87_f64.json index 545582584..1903cba50 100644 --- a/unittests/InstructionCountCI/FlagM/x87_f64.json +++ b/unittests/InstructionCountCI/FlagM/x87_f64.json @@ -4293,11 +4293,13 @@ ] }, "fnclex": { - "ExpectedInstructionCount": 0, + "ExpectedInstructionCount": 1, "Comment": [ "0xdb 11b 0xe2 /4" ], - "ExpectedArm64ASM": [] + "ExpectedArm64ASM": [ + "strb wzr, [x28, #1008]" + ] }, "fninit": { "ExpectedInstructionCount": 16, diff --git a/unittests/InstructionCountCI/x87.json b/unittests/InstructionCountCI/x87.json index 5b4555b06..fcb2e4341 100644 --- a/unittests/InstructionCountCI/x87.json +++ b/unittests/InstructionCountCI/x87.json @@ -4963,11 +4963,13 @@ ] }, "fnclex": { - "ExpectedInstructionCount": 0, + "ExpectedInstructionCount": 1, "Comment": [ "0xdb 11b 0xe2 /4" ], - "ExpectedArm64ASM": [] + "ExpectedArm64ASM": [ + "strb wzr, [x28, #1008]" + ] }, "fninit": { "ExpectedInstructionCount": 8, diff --git a/unittests/InstructionCountCI/x87_f64.json b/unittests/InstructionCountCI/x87_f64.json index 1a5a11734..37070704e 100644 --- a/unittests/InstructionCountCI/x87_f64.json +++ b/unittests/InstructionCountCI/x87_f64.json @@ -4314,11 +4314,13 @@ ] }, "fnclex": { - "ExpectedInstructionCount": 0, + "ExpectedInstructionCount": 1, "Comment": [ "0xdb 11b 0xe2 /4" ], - "ExpectedArm64ASM": [] + "ExpectedArm64ASM": [ + "strb wzr, [x28, #1008]" + ] }, "fninit": { "ExpectedInstructionCount": 16,