diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index 553f65f6c..07f46dc5c 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -39,6 +39,12 @@ namespace FEXCore::Config { case FEXCore::Config::CONFIG_SMC_CHECKS: CTX->Config.SMCChecks = Config != 0; break; + case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS: + CTX->Config.ABILocalFlags = Config != 0; + break; + case FEXCore::Config::CONFIG_ABI_NO_PF: + CTX->Config.ABINoPF = Config != 0; + break; default: LogMan::Msg::A("Unknown configuration option"); } } @@ -92,6 +98,12 @@ namespace FEXCore::Config { case FEXCore::Config::CONFIG_SMC_CHECKS: return CTX->Config.SMCChecks; break; + case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS: + return CTX->Config.ABILocalFlags; + break; + case FEXCore::Config::CONFIG_ABI_NO_PF: + return CTX->Config.ABINoPF; + break; default: LogMan::Msg::A("Unknown configuration option"); } diff --git a/External/FEXCore/Source/Interface/Context/Context.h b/External/FEXCore/Source/Interface/Context/Context.h index bbbc482bb..677e0958e 100644 --- a/External/FEXCore/Source/Interface/Context/Context.h +++ b/External/FEXCore/Source/Interface/Context/Context.h @@ -62,8 +62,11 @@ namespace FEXCore::Context { uint64_t EmulatedCPUCores{1}; bool TSOEnabled {true}; bool SMCChecks {false}; + bool ABILocalFlags {false}; + bool ABINoPF {false}; std::string DumpIR; + } Config; FEXCore::Memory::MemMapper MemoryMapper; diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp index afd6057b6..ccba0cb14 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp @@ -488,6 +488,7 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { case IR::OP_DUMMY: case IR::OP_BEGINBLOCK: case IR::OP_ENDBLOCK: + case IR::OP_INVALIDATEFLAGS: break; case IR::OP_FENCE: { auto Op = IROp->C(); diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp index 10250282b..c6d8fadae 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp @@ -126,6 +126,7 @@ void JITCore::RegisterMiscHandlers() { REGISTER_OP(PRINT, Unhandled); REGISTER_OP(GETROUNDINGMODE, GetRoundingMode); REGISTER_OP(SETROUNDINGMODE, SetRoundingMode); + REGISTER_OP(INVALIDATEFLAGS, NoOp); #undef REGISTER_OP } } diff --git a/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp b/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp index 6b68ab7aa..04ebdf664 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp @@ -142,6 +142,7 @@ void JITCore::RegisterMiscHandlers() { REGISTER_OP(PRINT, Print); REGISTER_OP(GETROUNDINGMODE, GetRoundingMode); REGISTER_OP(SETROUNDINGMODE, SetRoundingMode); + REGISTER_OP(INVALIDATEFLAGS, NoOp); #undef REGISTER_OP } } diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 03a0bf9b1..79a767627 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -144,6 +144,11 @@ void OpDispatchBuilder::NOPOp(OpcodeArgs) { void OpDispatchBuilder::RETOp(OpcodeArgs) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; + // ABI Optimization: Flags don't survive calls or rets + if (CTX->Config.ABILocalFlags) { + _InvalidateFlags(); + } + auto Constant = _Constant(GPRSize); auto OldSP = _LoadContext(GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]), GPRClass); @@ -622,6 +627,12 @@ void OpDispatchBuilder::CALLOp(OpcodeArgs) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; BlockSetRIP = true; + + // ABI Optimization: Flags don't survive calls or rets + if (CTX->Config.ABILocalFlags) { + _InvalidateFlags(); + } + auto ConstantPC = _Constant(Op->PC + Op->InstSize); OrderedNode *JMPPCOffset = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1); @@ -4721,7 +4732,7 @@ void OpDispatchBuilder::GenerateFlags_ADC(FEXCore::X86Tables::DecodedOp Op, Orde } // PF - { + if (!CTX->Config.ABINoPF) { auto PopCountOp = _Popcount(_And(Res, _Constant(0xFF))); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -4788,7 +4799,7 @@ void OpDispatchBuilder::GenerateFlags_SBB(FEXCore::X86Tables::DecodedOp Op, Orde } // PF - { + if (!CTX->Config.ABINoPF) { auto PopCountOp = _Popcount(_And(Res, _Constant(0xFF))); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -4854,7 +4865,7 @@ void OpDispatchBuilder::GenerateFlags_SUB(FEXCore::X86Tables::DecodedOp Op, Orde } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -4909,7 +4920,7 @@ void OpDispatchBuilder::GenerateFlags_ADD(FEXCore::X86Tables::DecodedOp Op, Orde } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5019,7 +5030,7 @@ void OpDispatchBuilder::GenerateFlags_Logical(FEXCore::X86Tables::DecodedOp Op, } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5056,7 +5067,7 @@ void OpDispatchBuilder::GenerateFlags_ShiftLeft(FEXCore::X86Tables::DecodedOp Op } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5102,7 +5113,7 @@ void OpDispatchBuilder::GenerateFlags_ShiftRight(FEXCore::X86Tables::DecodedOp O } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5148,7 +5159,7 @@ void OpDispatchBuilder::GenerateFlags_SignShiftRight(FEXCore::X86Tables::Decoded } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5194,7 +5205,7 @@ void OpDispatchBuilder::GenerateFlags_ShiftLeftImmediate(FEXCore::X86Tables::Dec } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5241,7 +5252,7 @@ void OpDispatchBuilder::GenerateFlags_SignShiftRightImmediate(FEXCore::X86Tables } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); @@ -5290,7 +5301,7 @@ void OpDispatchBuilder::GenerateFlags_ShiftRightImmediate(FEXCore::X86Tables::De } // PF - { + if (!CTX->Config.ABINoPF) { auto EightBitMask = _Constant(0xFF); auto PopCountOp = _Popcount(_And(Res, EightBitMask)); auto XorOp = _Xor(PopCountOp, _Constant(1)); diff --git a/External/FEXCore/Source/Interface/IR/IR.json b/External/FEXCore/Source/Interface/IR/IR.json index 358473b50..4bdbbf7bf 100644 --- a/External/FEXCore/Source/Interface/IR/IR.json +++ b/External/FEXCore/Source/Interface/IR/IR.json @@ -89,6 +89,11 @@ ] }, + "InvalidateFlags": { + "OpClass": "Misc", + "HasSideEffects": true + }, + "EndBlock": { "HasSideEffects": true, "OpClass": "Misc", diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp index 54a233ea5..29f8fedd3 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp @@ -42,6 +42,9 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { auto Op = IROp->CW(); FlagMap[BlockNode].writes |= 1 << Op->Flag; } + else if (IROp->Op == OP_INVALIDATEFLAGS) { + FlagMap[BlockNode].writes = -1; + } else if (IROp->Op == OP_LOADFLAG) { auto Op = IROp->CW(); FlagMap[BlockNode].reads |= 1 << Op->Flag; diff --git a/External/FEXCore/include/FEXCore/Config/Config.h b/External/FEXCore/include/FEXCore/Config/Config.h index 07381f5c0..ed7cc1937 100644 --- a/External/FEXCore/include/FEXCore/Config/Config.h +++ b/External/FEXCore/include/FEXCore/Config/Config.h @@ -18,7 +18,9 @@ namespace FEXCore::Config { CONFIG_EMULATED_CPU_CORES, CONFIG_TSO_ENABLED, CONFIG_SMC_CHECKS, - CONFIG_DUMPIR + CONFIG_ABI_LOCAL_FLAGS, + CONFIG_ABI_NO_PF, + CONFIG_DUMPIR, }; enum ConfigCore { diff --git a/Source/Common/ArgumentLoader.cpp b/Source/Common/ArgumentLoader.cpp index 2254209fb..6550ff436 100644 --- a/Source/Common/ArgumentLoader.cpp +++ b/Source/Common/ArgumentLoader.cpp @@ -48,29 +48,23 @@ namespace FEX::ArgLoader { .dest("MaxInst") .help("Maximum number of instructions to stick in a block") .set_default(~0U); - CPUGroup.add_option("-m", "--multiblock") - .dest("Multiblock") - .action("store_true") - .help("Enable Multiblock code compilation"); - CPUGroup.add_option("--no-multiblock") - .dest("Multiblock") - .action("store_false") - .help("Enable Multiblock code compilation"); - CPUGroup.add_option("-G", "--gdb") - .dest("GdbServer") - .action("store_true") - .help("Enables the GDB server"); + CPUGroup.add_option("-m", "--multiblock") + .dest("Multiblock") + .action("store_true") + .help("Enable Multiblock code compilation"); + CPUGroup.add_option("--no-multiblock") + .dest("Multiblock") + .action("store_false") + .help("Enable Multiblock code compilation"); + CPUGroup.add_option("-G", "--gdb") + .dest("GdbServer") + .action("store_true") + .help("Enables the GDB server"); - CPUGroup.add_option("-T", "--Threads") - .dest("Threads") - .help("Number of physical hardware threads to tell the process we have") - .set_default(1); - - CPUGroup.add_option("--disable-tso") - .dest("TSOEnabled") - .action("store_false") - .help("Disables TSO IR ops. Highly likely to break any threaded application") - .set_default(true); + CPUGroup.add_option("-T", "--Threads") + .dest("Threads") + .help("Number of physical hardware threads to tell the process we have") + .set_default(1); CPUGroup.add_option("--smc-full-checks") .dest("SMCChecks") @@ -78,6 +72,24 @@ namespace FEX::ArgLoader { .help("Checks code for modification before execution. Slow.") .set_default(false); + CPUGroup.add_option("--unsafe-no-tso") + .dest("TSOEnabled") + .action("store_false") + .help("Disables TSO IR ops. Highly likely to break any threaded application") + .set_default(true); + + CPUGroup.add_option("--unsafe-local-flags") + .dest("AbiLocalFlags") + .action("store_true") + .help("Assumes flags are not used across calls. Hand-written asm could violate this assumption") + .set_default(false); + + CPUGroup.add_option("--unsafe-no-pf") + .dest("AbiNoPF") + .action("store_true") + .help("Does not calculate the parity flag on integer operations") + .set_default(false); + Parser.add_option_group(CPUGroup); } { @@ -208,6 +220,14 @@ namespace FEX::ArgLoader { bool SMCChecks = Options.get("SMCChecks"); Config::Add("SMCChecks", std::to_string(SMCChecks)); } + if (Options.is_set_by_user("AbiLocalFlags")) { + bool AbiLocalFlags = Options.get("AbiLocalFlags"); + Config::Add("AbiLocalFlags", std::to_string(AbiLocalFlags)); + } + if (Options.is_set_by_user("AbiNoPF")) { + bool AbiNoPF = Options.get("AbiNoPF"); + Config::Add("AbiNoPF", std::to_string(AbiNoPF)); + } } { diff --git a/Source/Tests/ELFLoader.cpp b/Source/Tests/ELFLoader.cpp index 824e0aecb..8c9b690ce 100644 --- a/Source/Tests/ELFLoader.cpp +++ b/Source/Tests/ELFLoader.cpp @@ -111,6 +111,8 @@ int main(int argc, char **argv, char **const envp) { FEX::Config::Value DumpIR{"DumpIR", "no"}; FEX::Config::Value TSOEnabledConfig{"TSOEnabled", true}; FEX::Config::Value SMCChecksConfig{"SMCChecks", false}; + FEX::Config::Value ABILocalFlags{"ABILocalFlags", false}; + FEX::Config::Value AbiNoPF{"AbiNoPF", false}; ::SilentLog = SilentLog(); @@ -156,6 +158,8 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_EMULATED_CPU_CORES, ThreadsConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_TSO_ENABLED, TSOEnabledConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_SMC_CHECKS, SMCChecksConfig()); + FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS, ABILocalFlags()); + FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ABI_NO_PF, AbiNoPF()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_DUMPIR, DumpIR()); FEXCore::Context::SetCustomCPUBackendFactory(CTX, VMFactory::CPUCreationFactory); diff --git a/Source/Tests/TestHarnessRunner.cpp b/Source/Tests/TestHarnessRunner.cpp index 2f1d060c1..72f4fc966 100644 --- a/Source/Tests/TestHarnessRunner.cpp +++ b/Source/Tests/TestHarnessRunner.cpp @@ -60,7 +60,10 @@ int main(int argc, char **argv, char **const envp) { FEX::Config::Value BlockSizeConfig{"MaxInst", 1}; FEX::Config::Value SingleStepConfig{"SingleStep", false}; FEX::Config::Value MultiblockConfig{"Multiblock", false}; + FEX::Config::Value TSOEnabledConfig{"TSOEnabled", true}; FEX::Config::Value SMCChecksConfig{"SMCChecks", false}; + FEX::Config::Value ABILocalFlags{"ABILocalFlags", false}; + FEX::Config::Value AbiNoPF{"AbiNoPF", false}; auto Args = FEX::ArgLoader::Get(); @@ -78,7 +81,10 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_SINGLESTEP, SingleStepConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_MAXBLOCKINST, BlockSizeConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode()); + FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_TSO_ENABLED, TSOEnabledConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_SMC_CHECKS, SMCChecksConfig()); + FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS, ABILocalFlags()); + FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ABI_NO_PF, AbiNoPF()); FEXCore::Context::SetCustomCPUBackendFactory(CTX, HostCPUFactory::HostCPUCreationFactory); FEXCore::Context::AddGuestMemoryRegion(CTX, SHM);