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:
Ryan Houdek committed 2021-04-24 23:10:59 -07:00
1 parent 60671ee6cb
commit 520dfd7edf
5 files changed
+124

No files matched your search

+1
View File
@@ -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
+7
View File
@@ -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));
+3
View File
@@ -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);
};
}
+1
View File
@@ -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{};
}
}