diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index 1a2145418..25f101082 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -114,6 +114,7 @@ set (SRCS Interface/IR/Passes/DeadContextStoreElimination.cpp Interface/IR/Passes/IRCompaction.cpp Interface/IR/Passes/IRValidation.cpp + Interface/IR/Passes/LongDivideRemovalPass.cpp Interface/IR/Passes/ValueDominanceValidation.cpp Interface/IR/Passes/PhiValidation.cpp Interface/IR/Passes/RedundantFlagCalculationElimination.cpp diff --git a/External/FEXCore/Source/Interface/IR/PassManager.cpp b/External/FEXCore/Source/Interface/IR/PassManager.cpp index 84a4bad84..e14603531 100644 --- a/External/FEXCore/Source/Interface/IR/PassManager.cpp +++ b/External/FEXCore/Source/Interface/IR/PassManager.cpp @@ -19,6 +19,13 @@ void PassManager::AddDefaultPasses(bool InlineConstants, bool StaticRegisterAllo if (!DisablePasses()) { InsertPass(CreateContextLoadStoreElimination()); + + if (Is64BitMode()) { + // This needs to run after RCLSE + // This only matters for 64-bit code since these instructions don't exist in 32-bit + InsertPass(CreateLongDivideEliminationPass()); + } + InsertPass(CreateDeadStoreElimination()); InsertPass(CreatePassDeadCodeElimination()); InsertPass(CreateConstProp(InlineConstants)); diff --git a/External/FEXCore/Source/Interface/IR/PassManager.h b/External/FEXCore/Source/Interface/IR/PassManager.h index 9a720e0fb..43fde1d93 100644 --- a/External/FEXCore/Source/Interface/IR/PassManager.h +++ b/External/FEXCore/Source/Interface/IR/PassManager.h @@ -6,6 +6,7 @@ $end_info$ #pragma once +#include #include #include @@ -83,6 +84,8 @@ private: ValidationPasses.emplace_back(Pass); } #endif + + FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); }; } diff --git a/External/FEXCore/Source/Interface/IR/Passes.h b/External/FEXCore/Source/Interface/IR/Passes.h index a5ab2a63d..5f3d75fb6 100644 --- a/External/FEXCore/Source/Interface/IR/Passes.h +++ b/External/FEXCore/Source/Interface/IR/Passes.h @@ -14,6 +14,7 @@ FEXCore::IR::Pass* CreatePassDeadCodeElimination(); FEXCore::IR::Pass* CreateIRCompaction(); FEXCore::IR::RegisterAllocationPass* CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass, bool OptimizeSRA); FEXCore::IR::Pass* CreateStaticRegisterAllocationPass(); +FEXCore::IR::Pass* CreateLongDivideEliminationPass(); namespace Validation { FEXCore::IR::Pass* CreateIRValidation(); diff --git a/External/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp b/External/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp new file mode 100644 index 000000000..bab87d45d --- /dev/null +++ b/External/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp @@ -0,0 +1,112 @@ +/* +$info$ +tags: ir|opts +desc: Long divide elimination pass +$end_info$ +*/ + +#include "Interface/IR/PassManager.h" +#include + +namespace FEXCore::IR { + +class LongDivideEliminationPass final : public FEXCore::IR::Pass { +public: + bool Run(IREmitter *IREmit) override; +private: + bool IsZeroOp(IREmitter *IREmit, OrderedNodeWrapper Arg); + bool IsSextOp(IREmitter *IREmit, OrderedNodeWrapper Lower, OrderedNodeWrapper Upper); +}; + +bool LongDivideEliminationPass::IsZeroOp(IREmitter *IREmit, OrderedNodeWrapper Arg) { + auto IROp = IREmit->GetOpHeader(Arg); + uint64_t Value; + + // XOR based zero + if (IROp->Op == OP_XOR) { + return IROp->Args[0] == IROp->Args[1]; + } + else if (IREmit->IsValueConstant(Arg, &Value)) { + // Zero constant based zero op + return Value == 0; + } + return false; +} + +bool LongDivideEliminationPass::IsSextOp(IREmitter *IREmit, OrderedNodeWrapper Lower, OrderedNodeWrapper Upper) { + // We need to check if the upper source is a sext of the lower source + auto UpperIROp = IREmit->GetOpHeader(Upper); + if (UpperIROp->Op == OP_SBFE) { + auto Op = UpperIROp->C(); + if (Op->Width == 1 && Op->lsb == 63) { + // CQO: OrderedNode *Upper = _Sbfe(1, Size * 8 - 1, Src); + // If the lower is the upper in this case then it can be optimized + return Op->Header.Args[0] == Lower; + } + } + return false; +} + +bool LongDivideEliminationPass::Run(IREmitter *IREmit) { + bool Changed = false; + auto CurrentIR = IREmit->ViewIR(); + auto OriginalWriteCursor = IREmit->GetWriteCursor(); + + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { + if (IROp->Size == 8) { + if (IROp->Op == OP_LDIV || + IROp->Op == OP_LREM) { + auto Op = IROp->C(); + // Check upper Op to see if it came from a CQO + // CQO: OrderedNode *Upper = _Sbfe(1, Size * 8 - 1, Src); + // If it does then it we only need a 64bit SDIV + if (IsSextOp(IREmit, Op->Lower, Op->Upper)) { + IREmit->SetWriteCursor(CodeNode); + OrderedNode *Lower = CurrentIR.GetNode(Op->Lower); + OrderedNode *Divisor = CurrentIR.GetNode(Op->Divisor); + OrderedNode *SDivOp{}; + if (IROp->Op == OP_LDIV) { + SDivOp = IREmit->_Div(Lower, Divisor); + } + else { + SDivOp = IREmit->_Rem(Lower, Divisor); + } + IREmit->ReplaceAllUsesWith(CodeNode, SDivOp); + Changed = true; + } + } + else if (IROp->Op == OP_LUDIV || + IROp->Op == OP_LUREM) { + auto Op = IROp->C(); + // Check upper Op to see if it came from a xor zeroing op + // XOR: Result = _Xor(Dest, Src); + // If it does then it we only need a 64bit UDIV + if (IsZeroOp(IREmit, Op->Upper)) { + IREmit->SetWriteCursor(CodeNode); + OrderedNode *Lower = CurrentIR.GetNode(Op->Lower); + OrderedNode *Divisor = CurrentIR.GetNode(Op->Divisor); + OrderedNode *UDivOp{}; + if (IROp->Op == OP_LUDIV) { + UDivOp = IREmit->_UDiv(Lower, Divisor); + } + else { + UDivOp = IREmit->_URem(Lower, Divisor); + } + IREmit->ReplaceAllUsesWith(CodeNode, UDivOp); + Changed = true; + } + } + } + } + } + + IREmit->SetWriteCursor(OriginalWriteCursor); + + return Changed; +} + +FEXCore::IR::Pass* CreateLongDivideEliminationPass() { + return new LongDivideEliminationPass{}; +} +}