From 728d57718a5ce47fc8ebaa8250a43d33585c63d1 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 24 Oct 2026 19:17:00 -0700 Subject: [PATCH 1/3] unittests/ASM: Include local directory of asm file --- unittests/32Bit_ASM/CMakeLists.txt | 5 ++++- unittests/ASM/CMakeLists.txt | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/unittests/32Bit_ASM/CMakeLists.txt b/unittests/32Bit_ASM/CMakeLists.txt index abad50eef3..adc7e36bce 100644 --- a/unittests/32Bit_ASM/CMakeLists.txt +++ b/unittests/32Bit_ASM/CMakeLists.txt @@ -37,7 +37,10 @@ foreach(ASM_SRC ${ASM_SOURCES}) add_custom_command(OUTPUT ${OUTPUT_NAME} DEPENDS "${TMP_FILE}" - COMMAND "nasm" ARGS "${TMP_FILE}" "-o" "${OUTPUT_NAME}") + COMMAND "nasm" ARGS + "-i" "${CMAKE_SOURCE_DIR}/${ASM_DIR}/" + "${TMP_FILE}" + "-o" "${OUTPUT_NAME}") add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME} DEPENDS "${ASM_SRC}" diff --git a/unittests/ASM/CMakeLists.txt b/unittests/ASM/CMakeLists.txt index 9aa6b637c8..40d9c259b6 100644 --- a/unittests/ASM/CMakeLists.txt +++ b/unittests/ASM/CMakeLists.txt @@ -37,7 +37,10 @@ foreach(ASM_SRC ${ASM_SOURCES}) add_custom_command(OUTPUT ${OUTPUT_NAME} DEPENDS "${TMP_FILE}" - COMMAND "nasm" ARGS "-i" "${CMAKE_SOURCE_DIR}/unittests/ASM/Includes/" "${TMP_FILE}" "-o" "${OUTPUT_NAME}") + COMMAND "nasm" ARGS + "-i" "${CMAKE_SOURCE_DIR}/unittests/ASM/Includes/" + "-i" "${CMAKE_SOURCE_DIR}/${ASM_DIR}/" + "${TMP_FILE}" "-o" "${OUTPUT_NAME}") add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME} DEPENDS "${ASM_SRC}" From 7efd1d10fcb4425e771afa9168a6c462f9e7e255 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 23 Oct 2026 19:42:57 -0700 Subject: [PATCH 2/3] unittests/ASM: Adds an x87 FIP unittest Hammer all the instructions and readback FIP to ensure it is set. --- unittests/32Bit_ASM/FEX_bugs/x87_FIP.asm | 11 + unittests/32Bit_ASM/FEX_bugs/x87_FIP.mac | 459 ++++++++++++++++++ .../32Bit_ASM/FEX_bugs/x87_FIP_reduced.asm | 11 + 3 files changed, 481 insertions(+) create mode 100644 unittests/32Bit_ASM/FEX_bugs/x87_FIP.asm create mode 100644 unittests/32Bit_ASM/FEX_bugs/x87_FIP.mac create mode 100644 unittests/32Bit_ASM/FEX_bugs/x87_FIP_reduced.asm diff --git a/unittests/32Bit_ASM/FEX_bugs/x87_FIP.asm b/unittests/32Bit_ASM/FEX_bugs/x87_FIP.asm new file mode 100644 index 0000000000..6767754477 --- /dev/null +++ b/unittests/32Bit_ASM/FEX_bugs/x87_FIP.asm @@ -0,0 +1,11 @@ +%ifdef CONFIG +{ + "RegData": { + "RDX": "0" + }, + "Env": { "FEX_JITFEATURES" : "enablex87fip", "FEX_X87REDUCEDPRECISION" : "0" }, + "Mode": "32BIT" +} +%endif + +%include "x87_FIP.mac" diff --git a/unittests/32Bit_ASM/FEX_bugs/x87_FIP.mac b/unittests/32Bit_ASM/FEX_bugs/x87_FIP.mac new file mode 100644 index 0000000000..ecc09c8f47 --- /dev/null +++ b/unittests/32Bit_ASM/FEX_bugs/x87_FIP.mac @@ -0,0 +1,459 @@ +; FEX-Emu had a bug where FIP wasn't being calculated for ALU operations. +; Not all x87 instructions update FIP, but run through the suite just in case. +; Which will catch any instructions accidentally setting FIP, but shouldn't be. +; FDP isn't calculated here, but keep it around in-case we care in the future. + +; Split/Second (SteamID 297860) had a SecuROM implementation that used this for some form of anti-tamper/cheat. + +; Follow the formula: +; - finit +; - +; - get_result , +; Or: +; - +; - basic_op + +%macro get_result 2 + mov eax, 0x41424344 + mov ebx, 0 + + fsave [rel .data] + ; Load FIP. + mov eax, [rel .data + 0xc] + ; Load FDP + ; XXX: Not validated in this test. + ; mov ebx, [rel .data + 0x14] + + mov dword [ecx + 0], eax + mov dword [ecx + 4], ebx + cmp eax, %1 + jne %%.bad_end + cmp ebx, %2 + jne %%.bad_end + + add ecx, 8 + ; Reset the state. + finit + jmp %%.good + %%.bad_end: + mov edx, 1 + ud2 + %%.good: +%endmacro + +%macro basic_op 1 + lea esi, [rel %%.local_op] + %%.local_op: + %1 + get_result esi, 0 +%endmacro + +lea ecx, [rel .data_results] +mov edx, 0 + +basic_op f2xm1 +basic_op fabs +basic_op fadd +fldz +basic_op fadd dword [rel .data_tmp] +fldz +basic_op fadd qword [rel .data_tmp] +fldz +basic_op faddp +fldz +basic_op fiadd word [rel .data_tmp] +fldz +basic_op fiadd dword [rel .data_tmp] +basic_op fbld [rel .data_tmp] +fldz +basic_op fbstp [rel .data_tmp] +fldz +basic_op fchs +fnclex +get_result 0, 0 + +fldz +fldz +basic_op fcmovb + +fldz +fldz +basic_op fcmovbe + +fldz +fldz +basic_op fcmove + +fldz +fldz +basic_op fcmovnb + +fldz +fldz +basic_op fcmovnbe + +fldz +fldz +basic_op fcmovne + +fldz +fldz +basic_op fcmovnu + +fldz +fldz +basic_op fcmovu + +fldz +fldz +basic_op fcom + +fldz +basic_op fcom dword [rel .data_tmp] + +fldz +basic_op fcom qword [rel .data_tmp] + +fldz +fldz +basic_op fcomp + +fldz +basic_op fcomp dword [rel .data_tmp] + +fldz +basic_op fcomp qword [rel .data_tmp] + +fldz +fldz +basic_op fcompp + +fldz +fldz +basic_op fcomip + +fld1 +basic_op fcos + +basic_op fdecstp + +fld1 +fld1 +basic_op fdiv + +fld1 +fst dword [rel .data_tmp] +basic_op fdiv dword [rel .data_tmp] + +fld1 +fst qword [rel .data_tmp] +basic_op fdiv qword [rel .data_tmp] + +fld1 +fld1 +basic_op fdivp + +fld1 +mov word [rel .data_tmp], 1 +basic_op fidiv word [rel .data_tmp] + +fld1 +mov dword [rel .data_tmp], 1 +basic_op fidiv dword [rel .data_tmp] + +fld1 +fld1 +basic_op fdivr + +fld1 +basic_op fst dword [rel .data_tmp] + +fld1 +fst dword [rel .data_tmp] +basic_op fdivr qword [rel .data_tmp] + +fld1 +fld1 +basic_op fdivrp + +fld1 +mov word [rel .data_tmp], 1 +basic_op fidivr word [rel .data_tmp] + +fld1 +mov dword [rel .data_tmp], 1 +basic_op fidivr dword [rel .data_tmp] + +fldz +basic_op ffree + +fldz +basic_op ffreep + +fldz +basic_op ficom word [rel .data_tmp] + +fldz +basic_op ficom dword [rel .data_tmp] + +fldz +basic_op ficomp word [rel .data_tmp] + +fldz +basic_op ficomp dword [rel .data_tmp] + +basic_op fild word [rel .data_tmp] + +basic_op fild dword [rel .data_tmp] + +basic_op fild qword [rel .data_tmp] + +fldz +basic_op fincstp + +; Sets FIP to zero, not the instruction location. +fninit +get_result 0, 0 + +fldz +basic_op fist word [rel .data_tmp] + +fldz +basic_op fist dword [rel .data_tmp] + +fldz +basic_op fistp word [rel .data_tmp] + +fldz +basic_op fistp dword [rel .data_tmp] + +fldz +basic_op fistp qword [rel .data_tmp] + +fldz +basic_op fisttp word [rel .data_tmp] + +fldz +basic_op fisttp dword [rel .data_tmp] + +fldz +basic_op fisttp qword [rel .data_tmp] + +fldz +basic_op fld + +basic_op fld dword [rel .data_tmp] + +basic_op fld qword [rel .data_tmp] + +basic_op fld tword [rel .data_tmp] + +basic_op fld1 + +; Load Control word doesn't set FIP. +mov word [rel .data_tmp], 0 +fldcw [rel .data_tmp] +get_result 0, 0 + +; LoadEnv doesn't set FIP. +mov word [rel .data_tmp], 0 +fldenv [rel .data_tmp] +get_result 0, 0 + +basic_op fldl2e + +basic_op fldl2t + +basic_op fldlg2 + +basic_op fldln2 + +basic_op fldpi + +basic_op fldz + +basic_op fmul + +fldz +fldz +basic_op fmul dword [rel .data_tmp] + +fldz +fldz +basic_op fmul qword [rel .data_tmp] + +fldz +fldz +basic_op fmulp + +fldz +basic_op fimul word [rel .data_tmp] + +fldz +basic_op fimul dword [rel .data_tmp] + +basic_op fnop + +fldz +fldz +basic_op fpatan + +fld1 +fld1 +basic_op fprem + +fld1 +fld1 +basic_op fprem1 + +fldz +basic_op fptan + +fldz +basic_op frndint + +fldz +fldz +basic_op fscale + +fldz +basic_op fsin + +fldz +basic_op fsincos + +fld1 +basic_op fsqrt + +fldz +fldz +basic_op fst + +fldz +basic_op fst dword [rel .data_tmp] + +fldz +basic_op fst qword [rel .data_tmp] + +fldz +basic_op fstp + +fldz +basic_op fstp dword [rel .data_tmp] + +fldz +basic_op fstp qword [rel .data_tmp] + +fldz +basic_op fstp tword [rel .data_tmp] + +; Storing control word doesn't set FIP +fnstcw [rel .data_tmp] +get_result 0, 0 + +; Neither does storing the environment +fnstenv [rel .data_tmp] +get_result 0, 0 + +; Nor storing the status word +fnstsw ax +get_result 0, 0 + +fnstsw [rel .data_tmp] +get_result 0, 0 + +fldz +fldz +basic_op fsub + +fldz +basic_op fsub dword [rel .data_tmp] + +fldz +basic_op fsub qword [rel .data_tmp] + +fldz +fldz +basic_op fsubp + +fldz +basic_op fisub word [rel .data_tmp] + +fldz +basic_op fisub dword [rel .data_tmp] + +fldz +fldz +basic_op fsubr + +fldz +basic_op fsubr dword [rel .data_tmp] + +fldz +basic_op fsubr qword [rel .data_tmp] + +fldz +fldz +basic_op fsubrp + +fldz +basic_op fisubr word [rel .data_tmp] + +fldz +basic_op fisubr dword [rel .data_tmp] + +fldz +fldz +basic_op ftst + +fldz +fldz +basic_op fucom + +fldz +fldz +basic_op fucomp + +fldz +fldz +basic_op fucompp + +fldz +fldz +basic_op fucomi + +; fwait doesn't set FIP +fwait +get_result 0, 0 + +fldz +basic_op fxam + +fldz +fldz +basic_op fxch + +fldz +basic_op fxtract + +fldz +fldz +basic_op fyl2x + +fldz +fldz +basic_op fyl2xp1 + +; rdx == 0 here means success. +hlt + +align 4096 +.data: +times 64 dq 0x5152535455565758 + +align 32 +.data_tmp: +times 64 dq 0 + +.data_results: +times 512 dd 0x91929394 diff --git a/unittests/32Bit_ASM/FEX_bugs/x87_FIP_reduced.asm b/unittests/32Bit_ASM/FEX_bugs/x87_FIP_reduced.asm new file mode 100644 index 0000000000..725c9ff7c5 --- /dev/null +++ b/unittests/32Bit_ASM/FEX_bugs/x87_FIP_reduced.asm @@ -0,0 +1,11 @@ +%ifdef CONFIG +{ + "RegData": { + "RDX": "0" + }, + "Env": { "FEX_JITFEATURES" : "enablex87fip", "FEX_X87REDUCEDPRECISION" : "1" }, + "Mode": "32BIT" +} +%endif + +%include "x87_FIP.mac" From 983aa475c142d4cb247d4715e067e26a611090eb Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 24 Oct 2026 19:18:22 -0700 Subject: [PATCH 3/3] OpcodeDispatcher/x87: Implement support for FIP Only enabled with `FEX_JITFEATURES=enablex87fip`. This way most games don't pay the cost. For #6054 --- .../Source/Interface/Config/Config.json.in | 13 ++++++++ FEXCore/Source/Interface/Context/Context.h | 1 + FEXCore/Source/Interface/Core/CPUID.cpp | 5 ++- FEXCore/Source/Interface/Core/CPUID.h | 1 + .../Source/Interface/Core/OpcodeDispatcher.h | 7 ++++ .../Interface/Core/OpcodeDispatcher/X87.cpp | 32 +++++++++++++++++++ .../Core/OpcodeDispatcher/X87F64.cpp | 18 +++++++++++ .../Interface/Core/X86Tables/X87Tables.cpp | 4 +-- 8 files changed, 78 insertions(+), 3 deletions(-) diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 264349a615..dffc656328 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -42,6 +42,19 @@ "Enable expensive validation when loading code caches" ] }, + "JITFeatures": { + "Type": "strenum", + "Default": "FEXCore::Config::JITFeatures::OFF", + "AffectsCodeGen": "true", + "Enums": { + "ENABLEX87FIP": "enablex87fip" + }, + "Desc": [ + "Allows controlling JIT features in the unified location", + "\toff: Default JIT features.", + "\tenablex86fip: Turns on the x87 FIP emulation feature" + ] + }, "HostFeatures": { "Type": "strenum", "Default": "FEXCore::Config::HostFeatures::OFF", diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index 9f427da200..9b91055bd0 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -371,6 +371,7 @@ class ContextImpl final : public FEXCore::Context::Context, public CPU::SharedCo FEX_CONFIG_OPT(SoftwareRNG, SOFTWARERNG); FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS); FEX_CONFIG_OPT(MonoHacks, MONOHACKS); + FEX_CONFIG_OPT(JITFeatures, JITFEATURES); } Config; bool SoftwareRNGEnabled() const { diff --git a/FEXCore/Source/Interface/Core/CPUID.cpp b/FEXCore/Source/Interface/Core/CPUID.cpp index b4d125d15b..6d97080c8e 100644 --- a/FEXCore/Source/Interface/Core/CPUID.cpp +++ b/FEXCore/Source/Interface/Core/CPUID.cpp @@ -697,6 +697,9 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const { const uint32_t SupportsWFXT = CTX->HostFeatures.SupportsWFXT; const uint32_t SupportsRAND = CTX->HostFeatures.SupportsRAND || CTX->SoftwareRNGEnabled(); + // When the X87 FIP feature is enabled, we update the state pointers on every instruction. + // We don't even update them on exception so this isn't quite true the other way anyway. + const uint32_t X87DataPtrExcept = (CTX->Config.JITFeatures & FEXCore::Config::JITFeatures::ENABLEX87FIP) == 0; // Number of subfunctions // TODO: For now, subfunction 1 only exposes AVX-VNNI so we make it conditional @@ -708,7 +711,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const { (SupportsAVX() << 3) | // BMI1 (0 << 4) | // Intel Hardware Lock Elison (SupportsAVX() << 5) | // AVX2 support - (1 << 6) | // FPU data pointer updated only on exception + (X87DataPtrExcept << 6) | // FPU data pointer updated only on exception (1 << 7) | // SMEP support (SupportsAVX() << 8) | // BMI2 (SupportsEnhancedREPMOVS << 9) | // Enhanced REP MOVSB/STOSB diff --git a/FEXCore/Source/Interface/Core/CPUID.h b/FEXCore/Source/Interface/Core/CPUID.h index f7f32571bd..688eb9429c 100644 --- a/FEXCore/Source/Interface/Core/CPUID.h +++ b/FEXCore/Source/Interface/Core/CPUID.h @@ -119,6 +119,7 @@ class CPUIDEmu final { bool Hybrid {}; uint32_t Cores {}; FEX_CONFIG_OPT(HideHypervisorBit, HIDEHYPERVISORBIT); + FEX_CONFIG_OPT(JITFeatures, JITFEATURES); // XFEATURE_ENABLED_MASK // Mask that configures what features are enabled on the CPU. diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 75d1360b3d..46e5df57f0 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -756,6 +756,7 @@ class OpDispatchBuilder final : public IREmitter { void X87LDENV(OpcodeArgs); void X87ModifySTP(OpcodeArgs, bool Inc); void X87OpHelper(OpcodeArgs, FEXCore::IR::IROps IROp, bool ZeroC2); + void X87NOP(OpcodeArgs); enum class FCOMIFlags { FLAGS_X87, @@ -2497,6 +2498,12 @@ class OpDispatchBuilder final : public IREmitter { void SetX87FTW(Ref FTW); Ref GetX87FTW_Helper(); void SetX87Top(Ref Value); + void StoreX87FIP(FEXCore::X86Tables::DecodedOp Op) { + if (CTX->Config.JITFeatures & FEXCore::Config::JITFeatures::ENABLEX87FIP) { + // Only store FIP if the feature is enabled. + _StoreContextGPR(OpSize::i64Bit, GetRelocatedPC(Op, -Op->InstSize), offsetof(FEXCore::Core::CPUState, FIP)); + } + } void ChgStateX87_MMX() override { LOGMAN_THROW_A_FMT(MMXState == MMXState_X87, "Expected state to be x87"); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp index e6ae973fb2..7cb516d00d 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp @@ -67,11 +67,13 @@ void OpDispatchBuilder::FLD(OpcodeArgs, IR::OpSize Width) { ConvertedData = _F80CVTTo(Data, Width); } _PushStack(ConvertedData, Data, Width); + StoreX87FIP(Op); } // Float LoaD operation with memory operand void OpDispatchBuilder::FLDFromStack(OpcodeArgs) { _CopyPushStack(Op->OP & 7); + StoreX87FIP(Op); } void OpDispatchBuilder::FBLD(OpcodeArgs) { @@ -79,18 +81,21 @@ void OpDispatchBuilder::FBLD(OpcodeArgs) { Ref Data = LoadSourceFPR_WithOpSize(Op, Op->Src[0], OpSize::f80Bit, Op->Flags); Ref ConvertedData = _F80BCDLoad(Data); _PushStack(ConvertedData, Invalid(), OpSize::iInvalid); + StoreX87FIP(Op); } void OpDispatchBuilder::FBSTP(OpcodeArgs) { Ref converted = _F80BCDStore(_ReadStackValue(0)); StoreResultFPR_WithOpSize(Op, Op->Dest, converted, OpSize::f80Bit, OpSize::i8Bit); _PopStackDestroy(); + StoreX87FIP(Op); } void OpDispatchBuilder::FLD_Const(OpcodeArgs, NamedVectorConstant K) { // Update TOP Ref Data = LoadAndCacheNamedVectorConstant(OpSize::i128Bit, K); _PushStack(Data, Data, OpSize::f80Bit); + StoreX87FIP(Op); } void OpDispatchBuilder::FILD(OpcodeArgs) { @@ -122,6 +127,7 @@ void OpDispatchBuilder::FILD(OpcodeArgs) { Ref ConvertedData = _VLoadTwoGPRs(shifted, upper); _PushStack(ConvertedData, Invalid(), OpSize::iInvalid); + StoreX87FIP(Op); } void OpDispatchBuilder::FST(OpcodeArgs, IR::OpSize Width) { @@ -135,6 +141,7 @@ void OpDispatchBuilder::FST(OpcodeArgs, IR::OpSize Width) { if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FSTToStack(OpcodeArgs) { @@ -144,6 +151,7 @@ void OpDispatchBuilder::FSTToStack(OpcodeArgs) { if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } // Store integer to memory (possibly with truncation) @@ -183,6 +191,7 @@ void OpDispatchBuilder::FIST(OpcodeArgs, bool Truncate) { if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FADD(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispatchBuilder::OpResult ResInST0) { @@ -197,6 +206,7 @@ void OpDispatchBuilder::FADD(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispa if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -213,6 +223,7 @@ void OpDispatchBuilder::FADD(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispa // top of stack is at offset zero _F80AddValue(0, Arg); + StoreX87FIP(Op); } void OpDispatchBuilder::FMUL(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispatchBuilder::OpResult ResInST0) { @@ -227,6 +238,7 @@ void OpDispatchBuilder::FMUL(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispa if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -247,6 +259,7 @@ void OpDispatchBuilder::FMUL(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispa if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FDIV(OpcodeArgs, IR::OpSize Width, bool Integer, bool Reverse, OpDispatchBuilder::OpResult ResInST0) { @@ -264,6 +277,7 @@ void OpDispatchBuilder::FDIV(OpcodeArgs, IR::OpSize Width, bool Integer, bool Re if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -288,6 +302,7 @@ void OpDispatchBuilder::FDIV(OpcodeArgs, IR::OpSize Width, bool Integer, bool Re if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FSUB(OpcodeArgs, IR::OpSize Width, bool Integer, bool Reverse, OpDispatchBuilder::OpResult ResInST0) { @@ -305,6 +320,7 @@ void OpDispatchBuilder::FSUB(OpcodeArgs, IR::OpSize Width, bool Integer, bool Re if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -329,6 +345,7 @@ void OpDispatchBuilder::FSUB(OpcodeArgs, IR::OpSize Width, bool Integer, bool Re if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } Ref OpDispatchBuilder::GetX87FTW_Helper() { @@ -627,6 +644,7 @@ void OpDispatchBuilder::FXCH(OpcodeArgs) { _F80StackXchange(Offset); } SetRFLAG(Constant(0)); + StoreX87FIP(Op); } void OpDispatchBuilder::X87FYL2X(OpcodeArgs, bool IsFYL2XP1) { @@ -635,6 +653,7 @@ void OpDispatchBuilder::X87FYL2X(OpcodeArgs, bool IsFYL2XP1) { } else { _F80FYL2XStack(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FCOMI(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispatchBuilder::FCOMIFlags WhichFlags, bool PopTwice) { @@ -699,6 +718,7 @@ void OpDispatchBuilder::FCOMI(OpcodeArgs, IR::OpSize Width, bool Integer, OpDisp } else if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FTST(OpcodeArgs) { @@ -718,6 +738,7 @@ void OpDispatchBuilder::FTST(OpcodeArgs) { // Set Invalid Operation flag when unordered (NaN comparison) // The x87 exception flags are sticky. Preserve earlier result SetRFLAG(_Or(OpSize::i32Bit, GetRFLAG(FEXCore::X86State::X87FLAG_IE_LOC), HostFlag_Unordered)); + StoreX87FIP(Op); } void OpDispatchBuilder::X87OpHelper(OpcodeArgs, FEXCore::IR::IROps IROp, bool ZeroC2) { @@ -725,6 +746,7 @@ void OpDispatchBuilder::X87OpHelper(OpcodeArgs, FEXCore::IR::IROps IROp, bool Ze if (ZeroC2) { SetRFLAG(Constant(0)); } + StoreX87FIP(Op); } void OpDispatchBuilder::X87ModifySTP(OpcodeArgs, bool Inc) { @@ -736,6 +758,7 @@ void OpDispatchBuilder::X87ModifySTP(OpcodeArgs, bool Inc) { // C1 set to 0 SetRFLAG(Constant(0)); + StoreX87FIP(Op); } // Operations dealing with loading and storing environment pieces @@ -824,11 +847,13 @@ void OpDispatchBuilder::FNINIT(OpcodeArgs) { void OpDispatchBuilder::X87FFREE(OpcodeArgs) { _InvalidateStack(Op->OP & 7); + StoreX87FIP(Op); } void OpDispatchBuilder::X87EMMS(OpcodeArgs) { // Tags all get set to 0b11 _InvalidateStack(0xff); + StoreX87FIP(Op); } void OpDispatchBuilder::X87FCMOV(OpcodeArgs) { @@ -867,6 +892,7 @@ void OpDispatchBuilder::X87FCMOV(OpcodeArgs) { Ref VecCond = _VDupFromGPR(OpSize::i128Bit, OpSize::i64Bit, SelectCC0All1(CC)); _F80VBSLStack(OpSize::i128Bit, VecCond, Op->OP & 7, 0); + StoreX87FIP(Op); } void OpDispatchBuilder::X87FXAM(OpcodeArgs) { @@ -971,6 +997,7 @@ void OpDispatchBuilder::X87FXAM(OpcodeArgs) { SetRFLAG(C0); SetRFLAG(C2); SetRFLAG(C3); + StoreX87FIP(Op); } void OpDispatchBuilder::X87FXTRACT(OpcodeArgs) { @@ -981,6 +1008,11 @@ void OpDispatchBuilder::X87FXTRACT(OpcodeArgs) { auto Sig = _F80XTRACT_SIG(Top); _PushStack(Exp, Invalid(), OpSize::iInvalid); _PushStack(Sig, Invalid(), OpSize::iInvalid); + StoreX87FIP(Op); +} + +void OpDispatchBuilder::X87NOP(OpcodeArgs) { + StoreX87FIP(Op); } } // namespace FEXCore::IR diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87F64.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87F64.cpp index 33fba69015..28c06146c6 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87F64.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87F64.cpp @@ -68,6 +68,7 @@ void OpDispatchBuilder::FLDF64(OpcodeArgs, IR::OpSize Width) { ConvertedData = _F80CVT(OpSize::i64Bit, Data); } _PushStack(ConvertedData, Data, Width); + StoreX87FIP(Op); } void OpDispatchBuilder::FBLDF64(OpcodeArgs) { @@ -76,6 +77,7 @@ void OpDispatchBuilder::FBLDF64(OpcodeArgs) { Ref ConvertedData = _F80BCDLoad(Data); ConvertedData = _F80CVT(OpSize::i64Bit, ConvertedData); _PushStack(ConvertedData, Invalid(), OpSize::iInvalid); + StoreX87FIP(Op); } void OpDispatchBuilder::FBSTPF64(OpcodeArgs) { @@ -83,11 +85,13 @@ void OpDispatchBuilder::FBSTPF64(OpcodeArgs) { converted = _F80BCDStore(converted); StoreResultFPR_WithOpSize(Op, Op->Dest, converted, OpSize::f80Bit, OpSize::i8Bit); _PopStackDestroy(); + StoreX87FIP(Op); } void OpDispatchBuilder::FLDF64_Const(OpcodeArgs, uint64_t Num) { auto Data = _VCastFromGPR(OpSize::i64Bit, OpSize::i64Bit, Constant(Num)); _PushStack(Data, Data, OpSize::i64Bit); + StoreX87FIP(Op); } void OpDispatchBuilder::FILDF64(OpcodeArgs) { @@ -100,6 +104,7 @@ void OpDispatchBuilder::FILDF64(OpcodeArgs) { } auto ConvertedData = _Float_FromGPR_S(OpSize::i64Bit, ReadWidth == OpSize::i32Bit ? OpSize::i32Bit : OpSize::i64Bit, Data); _PushStack(ConvertedData, Invalid(), OpSize::iInvalid); + StoreX87FIP(Op); } void OpDispatchBuilder::FISTF64(OpcodeArgs, bool Truncate) { @@ -128,6 +133,7 @@ void OpDispatchBuilder::FISTF64(OpcodeArgs, bool Truncate) { if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FADDF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispatchBuilder::OpResult ResInST0) { @@ -142,6 +148,7 @@ void OpDispatchBuilder::FADDF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDi if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -165,6 +172,7 @@ void OpDispatchBuilder::FADDF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDi // top of stack is at offset zero _F80AddValue(0, arg); + StoreX87FIP(Op); } // FIXME: following is very similar to FADDF64 @@ -180,6 +188,7 @@ void OpDispatchBuilder::FMULF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDi if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -207,6 +216,7 @@ void OpDispatchBuilder::FMULF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDi if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FDIVF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool Reverse, OpDispatchBuilder::OpResult ResInST0) { @@ -231,6 +241,7 @@ void OpDispatchBuilder::FDIVF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); return; } @@ -264,6 +275,7 @@ void OpDispatchBuilder::FDIVF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FSUBF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool Reverse, OpDispatchBuilder::OpResult ResInST0) { @@ -288,6 +300,8 @@ void OpDispatchBuilder::FSUBF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); + return; } @@ -321,6 +335,7 @@ void OpDispatchBuilder::FSUBF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::FTSTF64(OpcodeArgs) { @@ -330,6 +345,7 @@ void OpDispatchBuilder::FTSTF64(OpcodeArgs) { // Now we do our comparison. _F80StackTest(0); ConvertNZCVToX87(); + StoreX87FIP(Op); } void OpDispatchBuilder::FCOMIF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispatchBuilder::FCOMIFlags WhichFlags, bool PopTwice) { @@ -376,6 +392,7 @@ void OpDispatchBuilder::FCOMIF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpD } else if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) { _PopStackDestroy(); } + StoreX87FIP(Op); } void OpDispatchBuilder::X87FXTRACTF64(OpcodeArgs) { @@ -429,5 +446,6 @@ void OpDispatchBuilder::X87FXTRACTF64(OpcodeArgs) { _PopStackDestroy(); _PushStack(Exp, Invalid(), OpSize::iInvalid); _PushStack(Sig, Invalid(), OpSize::iInvalid); + StoreX87FIP(Op); } } // namespace FEXCore::IR diff --git a/FEXCore/Source/Interface/Core/X86Tables/X87Tables.cpp b/FEXCore/Source/Interface/Core/X86Tables/X87Tables.cpp index 5d2dff353e..1f52c6ffae 100644 --- a/FEXCore/Source/Interface/Core/X86Tables/X87Tables.cpp +++ b/FEXCore/Source/Interface/Core/X86Tables/X87Tables.cpp @@ -69,7 +69,7 @@ constexpr std::array X87F64OpTable = {{ {OPD(0xD9, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDFromStack>}, {OPD(0xD9, 0xC8), 8, &OpDispatchBuilder::FXCH}, - {OPD(0xD9, 0xD0), 1, &OpDispatchBuilder::NOPOp}, // FNOP + {OPD(0xD9, 0xD0), 1, &OpDispatchBuilder::X87NOP}, // FNOP // D1 = Invalid // D8 = Invalid {OPD(0xD9, 0xE0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80STACKCHANGESIGN, false>}, @@ -341,7 +341,7 @@ constexpr std::array X87F80OpTable = {{ {OPD(0xD9, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDFromStack>}, {OPD(0xD9, 0xC8), 8, &OpDispatchBuilder::FXCH}, - {OPD(0xD9, 0xD0), 1, &OpDispatchBuilder::NOPOp}, // FNOP + {OPD(0xD9, 0xD0), 1, &OpDispatchBuilder::X87NOP}, // FNOP // D1 = Invalid // D8 = Invalid {OPD(0xD9, 0xE0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80STACKCHANGESIGN, false>},