From ff24f64b2a0a3f82bb34080096e7d99bc1b8f556 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 23 Sep 2023 20:34:57 -0700 Subject: [PATCH] PassManager: Optimize out CPUID and XGetBV calls If we const-prop the required functions and leafs then we can directly encode the CPUID information rather than jumping out of the JIT. In testing almost all CPUID executions const-prop which function is getting called. Worst case that I found was only 85% const-prop rate. This isn't quite 100% optimal since we need to call the RCLSE and Constprop passes after we optimize these, which would remove some redundant moves. Sadly there seems to be a bug in the constprop pass that starts crashing applications if that is done. Easily enough tested by running Half-Life 2 and it immediately hitting SIGILL. Even without this optimization, this is stil a significant savings since we aren't jumping out of the JIT anymore for these optimized CPUIDs. --- .../Interface/Core/OpcodeDispatcher.cpp | 4 +- FEXCore/Source/Interface/IR/PassManager.cpp | 2 +- FEXCore/Source/Interface/IR/Passes.h | 6 ++- .../IR/Passes/SyscallOptimization.cpp | 52 +++++++++++++++++-- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index fd602711b..02237b64c 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -1758,7 +1758,9 @@ void OpDispatchBuilder::MOVOffsetOp(OpcodeArgs) { } void OpDispatchBuilder::CPUIDOp(OpcodeArgs) { - OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1); + const auto GPRSize = CTX->GetGPRSize(); + + OrderedNode *Src = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], GPRSize, Op->Flags, -1); OrderedNode *Leaf = LoadGPRRegister(X86State::REG_RCX); auto Res = _CPUID(Src, Leaf); diff --git a/FEXCore/Source/Interface/IR/PassManager.cpp b/FEXCore/Source/Interface/IR/PassManager.cpp index b29689446..154d8393c 100644 --- a/FEXCore/Source/Interface/IR/PassManager.cpp +++ b/FEXCore/Source/Interface/IR/PassManager.cpp @@ -84,7 +84,7 @@ void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool Inli ////// InsertPass(CreateDeadFlagCalculationEliminination()); - InsertPass(CreateSyscallOptimization()); + InsertPass(CreateSyscallOptimization(&ctx->CPUID)); InsertPass(CreatePassDeadCodeElimination()); } diff --git a/FEXCore/Source/Interface/IR/Passes.h b/FEXCore/Source/Interface/IR/Passes.h index 4a5b2c17d..7524e5bd7 100644 --- a/FEXCore/Source/Interface/IR/Passes.h +++ b/FEXCore/Source/Interface/IR/Passes.h @@ -3,6 +3,10 @@ #include +namespace FEXCore { + class CPUIDEmu; +} + namespace FEXCore::Utils { class IntrusivePooledAllocator; } @@ -14,7 +18,7 @@ class RegisterAllocationData; fextl::unique_ptr CreateConstProp(bool InlineConstants, bool SupportsTSOImm9); fextl::unique_ptr CreateContextLoadStoreElimination(bool SupportsAVX); -fextl::unique_ptr CreateSyscallOptimization(); +fextl::unique_ptr CreateSyscallOptimization(const FEXCore::CPUIDEmu* CPUID); fextl::unique_ptr CreateDeadFlagCalculationEliminination(); fextl::unique_ptr CreateDeadStoreElimination(bool SupportsAVX); fextl::unique_ptr CreatePassDeadCodeElimination(); diff --git a/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp b/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp index 6df270970..144077cf2 100644 --- a/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp +++ b/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp @@ -6,6 +6,7 @@ desc: Removes unused arguments if known syscall number $end_info$ */ +#include "Interface/Core/CPUID.h" #include "Interface/IR/PassManager.h" #include @@ -21,7 +22,11 @@ namespace FEXCore::IR { class SyscallOptimization final : public FEXCore::IR::Pass { public: + SyscallOptimization(const FEXCore::CPUIDEmu* CPUID) + : CPUID {CPUID} {} bool Run(IREmitter *IREmit) override; +private: + const FEXCore::CPUIDEmu* CPUID; }; bool SyscallOptimization::Run(IREmitter *IREmit) { @@ -78,13 +83,54 @@ bool SyscallOptimization::Run(IREmitter *IREmit) { Changed = true; } } - } + else if (IROp->Op == FEXCore::IR::OP_CPUID) { + auto Op = IROp->CW(); + uint64_t ConstantFunction{}, ConstantLeaf{}; + bool IsConstantFunction = IREmit->IsValueConstant(Op->Function, &ConstantFunction); + bool IsConstantLeaf = IREmit->IsValueConstant(Op->Leaf, &ConstantLeaf); + // If the CPUID function is constant then we can try and optimize. + if (IsConstantFunction) { // && ConstantFunction != 1) { + // Check if it supports constant data reporting for this function. + const auto SupportsConstant = CPUID->DoesFunctionReportConstantData(ConstantFunction); + if (SupportsConstant.SupportsConstantFunction == CPUIDEmu::SupportsConstant::CONSTANT) { + // If the CPUID needs a constant leaf to be optimized then this can't work if we didn't const-prop the leaf register. + if (!(SupportsConstant.NeedsLeaf == CPUIDEmu::NeedsLeafConstant::NEEDSLEAFCONSTANT && !IsConstantLeaf)) { + // Calculate the constant data and replace all uses. + // DCE will remove the CPUID IR operation. + const auto ConstantCPUIDResult = CPUID->RunFunction(ConstantFunction, ConstantLeaf); + uint64_t ResultsLower = (static_cast(ConstantCPUIDResult.ebx) << 32) | ConstantCPUIDResult.eax; + uint64_t ResultsUpper = (static_cast(ConstantCPUIDResult.edx) << 32) | ConstantCPUIDResult.ecx; + IREmit->SetWriteCursor(CodeNode); + auto ElementPair = IREmit->_CreateElementPair(IR::OpSize::i128Bit, IREmit->_Constant(ResultsLower), IREmit->_Constant(ResultsUpper)); + // Replace all CPUID uses with this inline one + IREmit->ReplaceAllUsesWith(CodeNode, ElementPair); + Changed = true; + } + } + } + } + + else if (IROp->Op == FEXCore::IR::OP_XGETBV) { + auto Op = IROp->CW(); + + uint64_t ConstantFunction{}; + if (IREmit->IsValueConstant(Op->Function, &ConstantFunction) && + CPUID->DoesXCRFunctionReportConstantData(ConstantFunction)) { + const auto ConstantXCRResult = CPUID->RunXCRFunction(ConstantFunction); + IREmit->SetWriteCursor(CodeNode); + auto ElementPair = IREmit->_CreateElementPair(IR::OpSize::i64Bit, IREmit->_Constant(ConstantXCRResult.eax), IREmit->_Constant(ConstantXCRResult.edx)); + // Replace all xgetbv uses with this inline one + IREmit->ReplaceAllUsesWith(CodeNode, ElementPair); + Changed = true; + } + } + } return Changed; } -fextl::unique_ptr CreateSyscallOptimization() { - return fextl::make_unique(); +fextl::unique_ptr CreateSyscallOptimization(const FEXCore::CPUIDEmu* CPUID) { + return fextl::make_unique(CPUID); } }