mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 15:00:17 +02:00
Adds Long Divide removal pass
If the long divides don't have anything in the upper bits then they can be optimized away. Teeworlds, SuperTuxKart, and RRootage were between 50%-55% getting removed
This commit is contained in:
1 parent
60671ee6cb
commit
520dfd7edf
5 files changed
+124
No files matched your search
+1
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
@@ -83,6 +84,8 @@ private:
|
||||
ValidationPasses.emplace_back(Pass);
|
||||
}
|
||||
#endif
|
||||
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
$info$
|
||||
tags: ir|opts
|
||||
desc: Long divide elimination pass
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/IR/PassManager.h"
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
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<IR::IROp_Sbfe>();
|
||||
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<IR::IROp_LDiv>();
|
||||
// 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<IR::IROp_LUDiv>();
|
||||
// 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{};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user