Merge pull request #3660 from alyssarosenzweig/opt/smash-less

Delete a big chunk of IR/Passes/*
This commit is contained in:
Ryan Houdek authored and GitHub committed 2024-05-24 16:23:48 -07:00
commit 9d0ff7929e
20 files changed
+223 -623

No files matched your search

-1
View File
@@ -137,7 +137,6 @@ set (SRCS
Interface/IR/IREmitter.cpp
Interface/IR/PassManager.cpp
Interface/IR/Passes/ConstProp.cpp
Interface/IR/Passes/DeadCodeElimination.cpp
Interface/IR/Passes/DeadContextStoreElimination.cpp
Interface/IR/Passes/IRDumperPass.cpp
Interface/IR/Passes/IRValidation.cpp
+4 -11
View File
@@ -79,13 +79,9 @@ void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl* ctx, bool Inli
}
InsertPass(CreateDeadStoreElimination());
InsertPass(CreatePassDeadCodeElimination());
InsertPass(CreateConstProp(InlineConstants, ctx->HostFeatures.SupportsTSOImm9, Is64BitMode()));
InsertPass(CreateDeadFlagCalculationEliminination());
InsertPass(CreateInlineCallOptimization(&ctx->CPUID));
InsertPass(CreatePassDeadCodeElimination());
InsertPass(CreateDeadFlagCalculationEliminination());
}
}
@@ -100,20 +96,17 @@ void PassManager::InsertRegisterAllocationPass() {
InsertPass(IR::CreateRegisterAllocationPass(), "RA");
}
bool PassManager::Run(IREmitter* IREmit) {
void PassManager::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::Run");
bool Changed = false;
for (const auto& Pass : Passes) {
Changed |= Pass->Run(IREmit);
Pass->Run(IREmit);
}
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
for (const auto& Pass : ValidationPasses) {
Changed |= Pass->Run(IREmit);
Pass->Run(IREmit);
}
#endif
return Changed;
}
} // namespace FEXCore::IR
+2 -2
View File
@@ -32,7 +32,7 @@ class IREmitter;
class Pass {
public:
virtual ~Pass() = default;
virtual bool Run(IREmitter* IREmit) = 0;
virtual void Run(IREmitter* IREmit) = 0;
void RegisterPassManager(PassManager* _Manager) {
Manager = _Manager;
@@ -58,7 +58,7 @@ public:
void InsertRegisterAllocationPass();
bool Run(IREmitter* IREmit);
void Run(IREmitter* IREmit);
bool HasPass(fextl::string Name) const {
return NameToPassMaping.contains(Name);
-1
View File
@@ -21,7 +21,6 @@ fextl::unique_ptr<FEXCore::IR::Pass> CreateContextLoadStoreElimination(bool Supp
fextl::unique_ptr<FEXCore::IR::Pass> CreateInlineCallOptimization(const FEXCore::CPUIDEmu* CPUID);
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination();
fextl::unique_ptr<FEXCore::IR::Pass> CreatePassDeadCodeElimination();
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass();
fextl::unique_ptr<FEXCore::IR::Pass> CreateLongDivideEliminationPass();
+74 -327
View File
@@ -200,33 +200,6 @@ static std::optional<MemExtendedAddrResult> MemExtendedAddressing(IREmitter* IRE
return std::nullopt;
}
static OrderedNodeWrapper RemoveUselessMasking(IREmitter* IREmit, OrderedNodeWrapper src, uint64_t mask) {
#if 1 // HOTFIX: We need to clear up the meaning of opsize and dest size. See #594
return src;
#else
auto IROp = IREmit->GetOpHeader(src);
if (IROp->Op == OP_AND) {
auto Op = IROp->C<IR::IROp_And>();
uint64_t imm;
if (IREmit->IsValueConstant(IROp->Args[1], &imm) && ((imm & mask) == mask)) {
return RemoveUselessMasking(IREmit, IROp->Args[0], mask);
}
} else if (IROp->Op == OP_BFE) {
auto Op = IROp->C<IR::IROp_Bfe>();
if (Op->lsb == 0) {
uint64_t imm = 1ULL << (Op->Width - 1);
imm = (imm - 1) * 2 + 1;
if ((imm & mask) == mask) {
return RemoveUselessMasking(IREmit, IROp->Args[0], mask);
}
}
}
return src;
#endif
}
static bool IsBfeAlreadyDone(IREmitter* IREmit, OrderedNodeWrapper src, uint64_t Width) {
auto IROp = IREmit->GetOpHeader(src);
if (IROp->Op == OP_BFE) {
@@ -245,16 +218,14 @@ public:
, SupportsTSOImm9 {SupportsTSOImm9}
, Is64BitMode(Is64BitMode) {}
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
bool InlineConstants;
private:
bool HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR);
void LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListView& CurrentIR);
bool ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp);
bool ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp);
bool ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR);
void HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR);
void ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp);
void ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR);
struct ConstPoolData {
OrderedNode* Node;
@@ -282,48 +253,9 @@ private:
constexpr static uint32_t CONSTANT_POOL_RANGE_LIMIT = 500;
};
bool ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) {
bool Changed = false;
// constants are pooled per block
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_CONSTANT) {
auto Op = IROp->C<IR::IROp_Constant>();
const auto NewNodeID = CurrentIR.GetID(CodeNode);
auto it = ConstPool.find(Op->Constant);
if (it != ConstPool.end()) {
const auto OldNodeID = it->second.NodeID;
if ((NewNodeID.Value - OldNodeID.Value) > CONSTANT_POOL_RANGE_LIMIT) {
// Don't reuse if the live range is beyond the heurstic range.
// Update the tracked value to this new constant.
it->second.Node = CodeNode;
it->second.NodeID = NewNodeID;
continue;
}
auto CodeIter = CurrentIR.at(CodeNode);
IREmit->ReplaceUsesWithAfter(CodeNode, it->second.Node, CodeIter);
Changed = true;
} else {
ConstPool[Op->Constant] = ConstPoolData {
.Node = CodeNode,
.NodeID = NewNodeID,
};
}
}
}
ConstPool.clear();
}
return Changed;
}
// LoadMem / StoreMem imm pooling
// If imms are close by, use address gen to generate the values instead of using a new imm
void ConstProp::LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListView& CurrentIR) {
// Constants are pooled per block. Similarly for LoadMem / StoreMem, if imms are
// close by, use address gen to generate the values instead of using a new imm.
void ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) {
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_LOADMEM || IROp->Op == OP_STOREMEM) {
@@ -351,139 +283,41 @@ void ConstProp::LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListV
AddressgenConsts[IREmit->UnwrapNode(IROp->Args[AddrIndex])] = Addr;
}
doneOp:;
} else if (IROp->Op == OP_CONSTANT) {
auto Op = IROp->C<IR::IROp_Constant>();
const auto NewNodeID = CurrentIR.GetID(CodeNode);
auto it = ConstPool.find(Op->Constant);
if (it != ConstPool.end()) {
const auto OldNodeID = it->second.NodeID;
if ((NewNodeID.Value - OldNodeID.Value) > CONSTANT_POOL_RANGE_LIMIT) {
// Don't reuse if the live range is beyond the heurstic range.
// Update the tracked value to this new constant.
it->second.Node = CodeNode;
it->second.NodeID = NewNodeID;
continue;
}
auto CodeIter = CurrentIR.at(CodeNode);
IREmit->ReplaceUsesWithAfter(CodeNode, it->second.Node, CodeIter);
} else {
ConstPool[Op->Constant] = ConstPoolData {
.Node = CodeNode,
.NodeID = NewNodeID,
};
}
}
IREmit->SetWriteCursor(CodeNode);
}
AddressgenConsts.clear();
ConstPool.clear();
}
}
bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) {
bool Changed = false;
switch (IROp->Op) {
// Generic handling
case OP_OR:
case OP_XOR:
case OP_NOT:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_UMUL:
case OP_DIV:
case OP_UDIV:
case OP_LSHR:
case OP_ASHR:
case OP_LSHL:
case OP_ROR: {
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
case OP_AND: {
// if AND's arguments are imms, they are masking
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
uint64_t imm = 0;
if (!IREmit->IsValueConstant(IROp->Args[i ^ 1], &imm)) {
continue;
}
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], imm);
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
case OP_BFE: {
auto Op = IROp->C<IR::IROp_Bfe>();
// Is this value already BFE'd?
if (IsBfeAlreadyDone(IREmit, Op->Src, Op->Width)) {
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Src));
break;
}
// Is this value already ZEXT'd?
if (Op->lsb == 0) {
// LoadMem, LoadMemTSO & LoadContext ZExt
auto source = Op->Src;
auto sourceHeader = IREmit->GetOpHeader(source);
if (Op->Width >= (sourceHeader->Size * 8) &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) {
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
break;
}
}
// BFE does implicit masking, remove any masks leading to this, if possible
uint64_t imm = 1ULL << (Op->Width - 1);
imm = (imm - 1) * 2 + 1;
imm <<= Op->lsb;
auto newArg = RemoveUselessMasking(IREmit, Op->Src, imm);
if (newArg.ID() != Op->Src.ID()) {
IREmit->ReplaceNodeArgument(CodeNode, Op->Src_Index, IREmit->UnwrapNode(newArg));
Changed = true;
}
break;
}
case OP_SBFE: {
auto Op = IROp->C<IR::IROp_Sbfe>();
// BFE does implicit masking
uint64_t imm = 1ULL << (Op->Width - 1);
imm = (imm - 1) * 2 + 1;
imm <<= Op->lsb;
auto newArg = RemoveUselessMasking(IREmit, Op->Src, imm);
if (newArg.ID() != Op->Src.ID()) {
IREmit->ReplaceNodeArgument(CodeNode, Op->Src_Index, IREmit->UnwrapNode(newArg));
Changed = true;
}
break;
}
case OP_VMOV: {
// elim from load mem
auto source = IROp->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (IROp->Size >= sourceHeader->Size &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) {
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
} else if (IROp->Size == sourceHeader->Size) {
// VMOV of same size
// XXX: This is unsafe of an optimization since in some cases we can't see through garbage data in the upper bits of a vector
// RCLSE generates VMOV instructions which are being used as a zero extension
// IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
}
break;
}
default: break;
}
return Changed;
}
// constprop + some more per instruction logic
bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) {
bool Changed = false;
void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) {
switch (IROp->Op) {
case OP_LOADMEMTSO: {
auto Op = IROp->CW<IR::IROp_LoadMemTSO>();
@@ -501,8 +335,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
Op->OffsetScale = OffsetScale;
IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset
Changed = true;
}
break;
}
@@ -523,8 +355,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
Op->OffsetScale = OffsetScale;
IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset
Changed = true;
}
break;
}
@@ -544,8 +374,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
Op->OffsetScale = OffsetScale;
IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset
Changed = true;
}
break;
}
@@ -565,8 +393,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
Op->OffsetScale = OffsetScale;
IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset
Changed = true;
}
break;
}
@@ -588,8 +414,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
Op->OffsetScale = OffsetScale;
IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset
Changed = true;
}
break;
}
@@ -607,11 +431,9 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IsConstant1 && IsConstant2 && IROp->Op == OP_ADD) {
uint64_t NewConstant = (Constant1 + Constant2) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IsConstant1 && IsConstant2 && IROp->Op == OP_SUB) {
uint64_t NewConstant = (Constant1 - Constant2) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IsConstant2 && !IsImmAddSub(Constant2) && IsImmAddSub(-Constant2)) {
// If the second argument is constant, the immediate is not ImmAddSub, but when negated is.
// So, negate the operation to negate (and inline) the constant.
@@ -632,7 +454,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
// Replace the second source with the negated constant.
IREmit->ReplaceNodeArgument(CodeNode, Op->Src2_Index, NegConstant);
Changed = true;
}
break;
}
@@ -646,7 +467,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
// This is a pattern that shows up with direction flag calculations if DF was set just before the operation.
uint64_t NewConstant = (Constant1 - (Constant2 << Op->ShiftAmount)) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
}
break;
}
@@ -657,7 +477,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
uint64_t NewConstant = (Constant1 & Constant2) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (Constant2 == 1) {
// happens from flag calcs
auto val = IREmit->GetOpHeader(IROp->Args[0]);
@@ -666,12 +485,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (val->Op == OP_SELECT && IREmit->IsValueConstant(val->Args[2], &Constant2) && IREmit->IsValueConstant(val->Args[3], &Constant3) &&
Constant2 == 1 && Constant3 == 0) {
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
Changed = true;
}
} else if (IROp->Args[0].ID() == IROp->Args[1].ID()) {
// AND with same value results in original value
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
Changed = true;
}
break;
}
@@ -682,11 +499,9 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
uint64_t NewConstant = Constant1 | Constant2;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IROp->Args[0].ID() == IROp->Args[1].ID()) {
// OR with same value results in original value
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
Changed = true;
}
break;
}
@@ -698,7 +513,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
uint64_t NewConstant = Constant1 | (Constant2 << Op->BitShift);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
}
break;
}
@@ -710,7 +524,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
uint64_t NewConstant = Constant1 | (Constant2 >> Op->BitShift);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
}
break;
}
@@ -721,12 +534,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
uint64_t NewConstant = Constant1 ^ Constant2;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IROp->Args[0].ID() == IROp->Args[1].ID()) {
// XOR with same value results to zero
IREmit->SetWriteCursor(CodeNode);
IREmit->ReplaceAllUsesWith(CodeNode, IREmit->_Constant(0));
Changed = true;
} else {
// XOR with zero results in the nonzero source
for (unsigned i = 0; i < 2; ++i) {
@@ -741,7 +552,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
IREmit->SetWriteCursor(CodeNode);
OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[1 - i]);
IREmit->ReplaceAllUsesWith(CodeNode, Arg);
Changed = true;
break;
}
}
@@ -753,7 +563,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant)) {
uint64_t NewConstant = -Constant;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
}
break;
}
@@ -766,18 +575,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
uint64_t ShiftMask = IROp->Size == 8 ? 63 : 31;
uint64_t NewConstant = (Constant1 << (Constant2 & ShiftMask)) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && Constant2 == 0) {
IREmit->SetWriteCursor(CodeNode);
OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[0]);
IREmit->ReplaceAllUsesWith(CodeNode, Arg);
Changed = true;
} else {
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[1], IROp->Size * 8 - 1);
if (newArg.ID() != IROp->Args[1].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
@@ -790,36 +591,47 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
uint64_t ShiftMask = IROp->Size == 8 ? 63 : 31;
uint64_t NewConstant = (Constant1 >> (Constant2 & ShiftMask)) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && Constant2 == 0) {
IREmit->SetWriteCursor(CodeNode);
OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[0]);
IREmit->ReplaceAllUsesWith(CodeNode, Arg);
Changed = true;
} else {
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[1], IROp->Size * 8 - 1);
if (newArg.ID() != IROp->Args[1].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
case OP_BFE: {
auto Op = IROp->C<IR::IROp_Bfe>();
uint64_t Constant;
// Is this value already BFE'd?
if (IsBfeAlreadyDone(IREmit, Op->Src, Op->Width)) {
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Src));
break;
}
// Is this value already ZEXT'd?
if (Op->lsb == 0) {
// LoadMem, LoadMemTSO & LoadContext ZExt
auto source = Op->Src;
auto sourceHeader = IREmit->GetOpHeader(source);
if (Op->Width >= (sourceHeader->Size * 8) &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) {
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
break;
}
}
if (IROp->Size <= 8 && IREmit->IsValueConstant(Op->Src, &Constant)) {
uint64_t SourceMask = Op->Width == 64 ? ~0ULL : ((1ULL << Op->Width) - 1);
SourceMask <<= Op->lsb;
uint64_t NewConstant = (Constant & SourceMask) >> Op->lsb;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IROp->Size == CurrentIR.GetOp<IROp_Header>(IROp->Args[0])->Size && Op->Width == (IROp->Size * 8) && Op->lsb == 0) {
// A BFE that extracts all bits results in original value
// XXX - This is broken for now - see https://github.com/FEX-Emu/FEX/issues/351
// IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
// Changed = true;
} else if (Op->Width == 1 && Op->lsb == 0) {
// common from flag codegen
auto val = IREmit->GetOpHeader(IROp->Args[0]);
@@ -829,7 +641,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (val->Op == OP_SELECT && IREmit->IsValueConstant(val->Args[2], &Constant2) && IREmit->IsValueConstant(val->Args[3], &Constant3) &&
Constant2 == 1 && Constant3 == 0) {
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
Changed = true;
}
}
@@ -850,8 +661,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
NewConstant >>= 64 - Op->Width;
NewConstant &= DestMask;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
}
break;
}
@@ -868,7 +677,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
NewConstant |= (ConstantSrc & SourceMask) << Op->lsb;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (SrcIsConstant && HasConsecutiveBits(ConstantSrc, Op->Width)) {
// We are trying to insert constant, if it is a bitfield of only set bits then we can orr or and it.
IREmit->SetWriteCursor(CodeNode);
@@ -878,12 +686,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (ConstantSrc & 1) {
auto orr = IREmit->_Or(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(NewConstant));
IREmit->ReplaceAllUsesWith(CodeNode, orr);
Changed = true;
} else {
// We are wanting to clear the bitfield.
auto andn = IREmit->_Andn(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(NewConstant));
IREmit->ReplaceAllUsesWith(CodeNode, andn);
Changed = true;
}
}
break;
@@ -895,27 +701,35 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
uint64_t NewConstant = (Constant1 * Constant2) & getMask(IROp);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && std::popcount(Constant2) == 1) {
if (IROp->Size == 4 || IROp->Size == 8) {
uint64_t amt = std::countr_zero(Constant2);
IREmit->SetWriteCursor(CodeNode);
auto shift = IREmit->_Lshl(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(amt));
IREmit->ReplaceAllUsesWith(CodeNode, shift);
Changed = true;
}
}
break;
}
case OP_VMOV: {
// elim from load mem
auto source = IROp->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (IROp->Size >= sourceHeader->Size &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) {
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
}
break;
}
default: break;
}
return Changed;
}
bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) {
void ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) {
InlineConstantGen.clear();
bool Changed = false;
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
switch (IROp->Op) {
@@ -935,8 +749,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
}
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
break;
}
@@ -952,10 +764,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
// constant would be in bounds after the JIT's 24/16 shift.
if (IsImmAddSub(Constant2) && IROp->Size >= 4) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
} else if (IROp->Op == OP_SUBNZCV || IROp->Op == OP_SUBWITHFLAGS || IROp->Op == OP_SUB) {
// TODO: Generalize this
@@ -964,7 +773,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Constant1 == 0) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0));
Changed = true;
}
}
}
@@ -978,7 +786,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Constant1 == 0) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0));
Changed = true;
}
}
@@ -990,7 +797,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Constant1 == 0) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0));
Changed = true;
}
}
@@ -1002,10 +808,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
@@ -1014,7 +817,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Constant1 == 0) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0));
Changed = true;
}
}
break;
@@ -1024,10 +826,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (IREmit->IsValueConstant(IROp->Args[1], &Constant1)) {
if (IsImmLogical(Constant1, IROp->Size * 8)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant1));
Changed = true;
}
}
break;
@@ -1037,10 +836,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (IREmit->IsValueConstant(IROp->Args[1], &Constant1)) {
if (IsImmAddSub(Constant1)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant1));
Changed = true;
}
}
@@ -1071,7 +867,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (IREmit->IsValueConstant(IROp->Args[0], &Constant0) && (Constant0 == 1 || Constant0 == AllOnes)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, Constant0));
}
}
@@ -1083,10 +878,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
break;
@@ -1096,12 +888,8 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
uint64_t Constant {};
if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, Constant));
Changed = true;
} else {
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
@@ -1109,7 +897,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(IR::SizeToOpSize(EO->Header.Size), EO->Offset));
Changed = true;
}
}
break;
@@ -1123,10 +910,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
if (IsImmLogical(Constant2, IROp->Size * 8)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
break;
@@ -1138,10 +922,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) {
if (IsImmMemory(Constant2, IROp->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
break;
@@ -1153,10 +934,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) {
if (IsImmMemory(Constant2, IROp->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
break;
@@ -1169,10 +947,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) {
if (IsTSOImm9(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
}
@@ -1186,10 +961,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) {
if (IsTSOImm9(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
}
@@ -1201,10 +973,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
uint64_t Constant {};
if (IREmit->IsValueConstant(Op->Direction, &Constant)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Direction));
IREmit->ReplaceNodeArgument(CodeNode, Op->Direction_Index, CreateInlineConstant(IREmit, Constant));
Changed = true;
}
break;
}
@@ -1214,10 +983,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
uint64_t Constant {};
if (IREmit->IsValueConstant(Op->Direction, &Constant)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Direction));
IREmit->ReplaceNodeArgument(CodeNode, Op->Direction_Index, CreateInlineConstant(IREmit, Constant));
Changed = true;
}
break;
}
@@ -1229,10 +995,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) {
if (IsImmMemory(Constant2, IROp->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
}
break;
@@ -1240,38 +1003,22 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR)
default: break;
}
}
return Changed;
}
bool ConstProp::Run(IREmitter* IREmit) {
void ConstProp::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::ConstProp");
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
auto OriginalWriteCursor = IREmit->GetWriteCursor();
if (HandleConstantPools(IREmit, CurrentIR)) {
Changed = true;
}
LoadMemStoreMemImmediatePooling(IREmit, CurrentIR);
HandleConstantPools(IREmit, CurrentIR);
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
if (ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp)) {
Changed = true;
}
if (ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp)) {
Changed = true;
}
ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp);
}
if (InlineConstants && ConstantInlining(IREmit, CurrentIR)) {
Changed = true;
if (InlineConstants) {
ConstantInlining(IREmit, CurrentIR);
}
IREmit->SetWriteCursor(OriginalWriteCursor);
return Changed;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool InlineConstants, bool SupportsTSOImm9, bool Is64BitMode) {
@@ -1,113 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: ir|opts
$end_info$
*/
#include "Interface/IR/IREmitter.h"
#include "Interface/IR/PassManager.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/Profiler.h>
#include <memory>
namespace FEXCore::IR {
class DeadCodeElimination final : public FEXCore::IR::Pass {
bool Run(IREmitter* IREmit) override;
private:
void markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp);
};
bool DeadCodeElimination::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::DCE");
auto CurrentIR = IREmit->ViewIR();
bool Changed = false;
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
// Reverse iteration is not yet working with the iterators
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
// We grab these nodes this way so we can iterate easily
auto CodeBegin = CurrentIR.at(BlockIROp->Begin);
auto CodeLast = CurrentIR.at(BlockIROp->Last);
while (1) {
auto [CodeNode, IROp] = CodeLast();
bool HasSideEffects = IR::HasSideEffects(IROp->Op);
switch (IROp->Op) {
case OP_SYSCALL:
case OP_INLINESYSCALL: {
FEXCore::IR::SyscallFlags Flags {};
if (IROp->Op == OP_SYSCALL) {
auto Op = IROp->C<IR::IROp_Syscall>();
Flags = Op->Flags;
} else {
auto Op = IROp->C<IR::IROp_InlineSyscall>();
Flags = Op->Flags;
}
if ((Flags & FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) == FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) {
HasSideEffects = false;
}
break;
}
case OP_ATOMICFETCHADD:
case OP_ATOMICFETCHSUB:
case OP_ATOMICFETCHAND:
case OP_ATOMICFETCHCLR:
case OP_ATOMICFETCHOR:
case OP_ATOMICFETCHXOR:
case OP_ATOMICFETCHNEG: {
// If the result of the atomic fetch is completely unused, convert it to a non-fetching atomic operation.
if (CodeNode->GetUses() == 0) {
switch (IROp->Op) {
case OP_ATOMICFETCHADD: IROp->Op = OP_ATOMICADD; break;
case OP_ATOMICFETCHSUB: IROp->Op = OP_ATOMICSUB; break;
case OP_ATOMICFETCHAND: IROp->Op = OP_ATOMICAND; break;
case OP_ATOMICFETCHCLR: IROp->Op = OP_ATOMICCLR; break;
case OP_ATOMICFETCHOR: IROp->Op = OP_ATOMICOR; break;
case OP_ATOMICFETCHXOR: IROp->Op = OP_ATOMICXOR; break;
case OP_ATOMICFETCHNEG: IROp->Op = OP_ATOMICNEG; break;
default: FEX_UNREACHABLE;
}
Changed = true;
}
break;
}
default: break;
}
// Skip over anything that has side effects
// Use count tracking can't safely remove anything with side effects
if (!HasSideEffects) {
if (CodeNode->GetUses() == 0) {
Changed = true;
IREmit->Remove(CodeNode);
}
}
if (CodeLast == CodeBegin) {
break;
}
--CodeLast;
}
}
return Changed;
}
void DeadCodeElimination::markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp) {}
fextl::unique_ptr<FEXCore::IR::Pass> CreatePassDeadCodeElimination() {
return fextl::make_unique<DeadCodeElimination>();
}
} // namespace FEXCore::IR
@@ -456,12 +456,9 @@ public:
explicit RCLSE(bool SupportsAVX_)
: SupportsAVX {SupportsAVX_} {
ClassifyContextStruct(&ClassifiedStruct, SupportsAVX);
DCE = FEXCore::IR::CreatePassDeadCodeElimination();
}
bool Run(FEXCore::IR::IREmitter* IREmit) override;
void Run(FEXCore::IR::IREmitter* IREmit) override;
private:
fextl::unique_ptr<FEXCore::IR::Pass> DCE;
ContextInfo ClassifiedStruct;
fextl::unordered_map<FEXCore::IR::NodeID, BlockInfo> OffsetToBlockMap;
@@ -473,16 +470,16 @@ private:
ContextMemberInfo* RecordAccess(ContextInfo* ClassifiedInfo, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size,
LastAccessType AccessType, FEXCore::IR::OrderedNode* Node, FEXCore::IR::OrderedNode* StoreNode = nullptr);
bool HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag);
void HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag);
// Classify context loads and stores.
bool ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset,
void ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset,
uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::NodeIterator BlockEnd);
bool ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset,
void ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset,
uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::OrderedNode* ValueNode);
// Block local Passes
bool RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit);
void RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit);
unsigned OffsetForReg(FEXCore::IR::RegisterClassType Class, unsigned Reg, unsigned Size) {
if (Class == FEXCore::IR::FPRClass) {
@@ -531,7 +528,7 @@ ContextMemberInfo* RCLSE::RecordAccess(ContextInfo* ClassifiedInfo, FEXCore::IR:
return RecordAccess(Info, RegClass, Offset, Size, AccessType, ValueNode, StoreNode);
}
bool RCLSE::ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class,
void RCLSE::ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class,
uint32_t Offset, uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::NodeIterator BlockEnd) {
auto Info = FindMemberInfo(LocalInfo, Offset, Size);
ContextMemberInfo PreviousMemberInfoCopy = *Info;
@@ -544,13 +541,11 @@ bool RCLSE::ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* Loc
// - Previous access was a store, and we are redundantly loading immediately after the store. Eliminating the store.
IREmit->ReplaceAllUsesWithRange(CodeNode, PreviousMemberInfoCopy.ValueNode, IREmit->GetIterator(IREmit->WrapNode(CodeNode)), BlockEnd);
RecordAccess(Info, Class, Offset, Size, LastAccessType::READ, PreviousMemberInfoCopy.ValueNode);
return true;
}
// TODO: Optimize the case of partial loads.
return false;
}
bool RCLSE::ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class,
void RCLSE::ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class,
uint32_t Offset, uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::OrderedNode* ValueNode) {
auto Info = FindMemberInfo(LocalInfo, Offset, Size);
ContextMemberInfo PreviousMemberInfoCopy = *Info;
@@ -563,15 +558,13 @@ bool RCLSE::ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* Lo
// Revisit when the new RA lands.
#if 0
IREmit->Remove(PreviousMemberInfoCopy.StoreNode);
return true;
#endif
}
// TODO: Optimize the case of partial stores.
return false;
}
bool RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag) {
void RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag) {
const auto FlagOffset = offsetof(FEXCore::Core::CPUState, flags[Flag]);
auto Info = FindMemberInfo(LocalInfo, FlagOffset, 1);
LastAccessType LastAccess = Info->Accessed;
@@ -582,14 +575,10 @@ bool RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInf
IREmit->SetWriteCursor(CodeNode);
IREmit->ReplaceAllUsesWith(CodeNode, LastValueNode);
RecordAccess(Info, FEXCore::IR::GPRClass, FlagOffset, 1, LastAccessType::READ, LastValueNode);
return true;
} else if (IsReadAccess(LastAccess)) {
IREmit->ReplaceAllUsesWith(CodeNode, LastValueNode);
RecordAccess(Info, FEXCore::IR::GPRClass, FlagOffset, 1, LastAccessType::READ, LastValueNode);
return true;
}
return false;
}
/**
@@ -624,11 +613,10 @@ bool RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInf
* (%%176) StoreContext %175 i128, 0x10, 0xa0
*/
bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) {
void RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) {
using namespace FEXCore;
using namespace FEXCore::IR;
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
auto OriginalWriteCursor = IREmit->GetWriteCursor();
@@ -645,19 +633,19 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_STORECONTEXT) {
auto Op = IROp->CW<IR::IROp_StoreContext>();
Changed |= ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value));
ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value));
} else if (IROp->Op == OP_STOREREGISTER) {
auto Op = IROp->CW<IR::IROp_StoreRegister>();
auto Offset = OffsetForReg(Op->Class, Op->Reg, IROp->Size);
Changed |= ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value));
ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value));
} else if (IROp->Op == OP_LOADREGISTER) {
auto Op = IROp->CW<IR::IROp_LoadRegister>();
auto Offset = OffsetForReg(Op->Class, Op->Reg, IROp->Size);
Changed |= ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, BlockEnd);
ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, BlockEnd);
} else if (IROp->Op == OP_LOADCONTEXT) {
auto Op = IROp->CW<IR::IROp_LoadContext>();
Changed |= ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, BlockEnd);
ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, BlockEnd);
} else if (IROp->Op == OP_STOREFLAG) {
const auto Op = IROp->CW<IR::IROp_StoreFlag>();
const auto FlagOffset = offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag;
@@ -668,7 +656,6 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) {
// Flags don't alias, so we can take the simple route here. Kill any flags that have been overwritten
if (LastStoreNode != nullptr) {
IREmit->Remove(LastStoreNode);
Changed = true;
}
} else if (IROp->Op == OP_INVALIDATEFLAGS) {
auto Op = IROp->CW<IR::IROp_InvalidateFlags>();
@@ -689,15 +676,14 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) {
RecordAccess(&LocalInfo, FEXCore::IR::GPRClass, FlagOffset, 1, LastAccessType::WRITE, IREmit->_Constant(0), CodeNode);
IREmit->Remove(LastStoreNode);
Changed = true;
}
}
} else if (IROp->Op == OP_LOADFLAG) {
const auto Op = IROp->CW<IR::IROp_LoadFlag>();
Changed |= HandleLoadFlag(IREmit, &LocalInfo, CodeNode, Op->Flag);
HandleLoadFlag(IREmit, &LocalInfo, CodeNode, Op->Flag);
} else if (IROp->Op == OP_LOADDF) {
Changed |= HandleLoadFlag(IREmit, &LocalInfo, CodeNode, X86State::RFLAG_DF_RAW_LOC);
HandleLoadFlag(IREmit, &LocalInfo, CodeNode, X86State::RFLAG_DF_RAW_LOC);
} else if (IROp->Op == OP_SYSCALL || IROp->Op == OP_INLINESYSCALL) {
FEXCore::IR::SyscallFlags Flags {};
if (IROp->Op == OP_SYSCALL) {
@@ -720,13 +706,11 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) {
}
IREmit->SetWriteCursor(OriginalWriteCursor);
return Changed;
}
bool RCLSE::Run(FEXCore::IR::IREmitter* IREmit) {
void RCLSE::Run(FEXCore::IR::IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::RCLSE");
return RedundantStoreLoadElimination(IREmit);
RedundantStoreLoadElimination(IREmit);
}
} // namespace
@@ -14,7 +14,6 @@ $end_info$
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/unordered_map.h>
#include <memory>
#include <stddef.h>
@@ -24,71 +23,52 @@ namespace FEXCore::IR {
constexpr int PropagationRounds = 5;
// Return a bit representing a single GPR or FPR.
static inline uint64_t RegBit(RegisterClassType Class, uint32_t Reg) {
uint32_t AdjustedReg = (Class == FPRClass) ? (32 + Reg) : Reg;
return 1UL << AdjustedReg;
}
class DeadStoreElimination final : public FEXCore::IR::Pass {
public:
explicit DeadStoreElimination() {}
bool Run(IREmitter* IREmit) override;
private:
uint64_t FPRBit(RegisterClassType Class, uint32_t Reg) const {
return (Class == FPRClass) ? (1UL << Reg) : 0;
}
void Run(IREmitter* IREmit) override;
};
struct FlagInfo {
struct ReadWriteKill {
uint64_t reads {0};
uint64_t writes {0};
uint64_t kill {0};
};
struct GPRInfo {
uint32_t reads {0};
uint32_t writes {0};
uint32_t kill {0};
};
uint32_t GPRBit(RegisterClassType Class, uint32_t Reg) {
return (Class == GPRClass) ? (1U << Reg) : 0;
}
struct FPRInfo {
uint32_t reads {0};
uint32_t writes {0};
uint32_t kill {0};
};
struct Info {
FlagInfo flag;
GPRInfo gpr;
FPRInfo fpr;
ReadWriteKill flag;
ReadWriteKill reg;
};
/**
* @brief This is a temporary pass to detect simple multiblock dead flag/gpr/fpr stores
* @brief This is a temporary pass to detect simple multiblock dead flag/reg stores
*
* First pass computes which flags/gprs/fprs are read and written per block
* First pass computes which flags/regs are read and written per block
*
* Second pass computes which flags/gprs/fprs are stored, but overwritten by the next block(s).
* It also propagates this information a few times to catch dead flags/gprs/fprs across multiple blocks.
* Second pass computes which flags/regs are stored, but overwritten by the next block(s).
* It also propagates this information a few times to catch dead flags/regs across multiple blocks.
*
* Third pass removes the dead stores.
*
*/
bool DeadStoreElimination::Run(IREmitter* IREmit) {
void DeadStoreElimination::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::DSE");
fextl::unordered_map<OrderedNode*, Info> InfoMap;
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
fextl::vector<Info> InfoMap(CurrentIR.GetSSACount());
// Pass 1
// Compute flags/gprs/fprs read/writes per block
// Compute flags/regs read/writes per block
// This is conservative and doesn't try to be smart about loads after writes
{
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
auto& BlockInfo = InfoMap[BlockNode];
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_STOREFLAG) {
@@ -107,21 +87,17 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) {
BlockInfo.flag.reads |= 1UL << X86State::RFLAG_DF_RAW_LOC;
} else if (IROp->Op == OP_STOREREGISTER) {
auto Op = IROp->C<IR::IROp_StoreRegister>();
BlockInfo.gpr.writes |= GPRBit(Op->Class, Op->Reg);
BlockInfo.fpr.writes |= FPRBit(Op->Class, Op->Reg);
BlockInfo.reg.writes |= RegBit(Op->Class, Op->Reg);
} else if (IROp->Op == OP_LOADREGISTER) {
auto Op = IROp->C<IR::IROp_LoadRegister>();
BlockInfo.gpr.reads |= GPRBit(Op->Class, Op->Reg);
BlockInfo.fpr.reads |= FPRBit(Op->Class, Op->Reg);
BlockInfo.reg.reads |= RegBit(Op->Class, Op->Reg);
}
}
}
}
// Pass 2
// Compute flags/gprs/fprs that are stored, but always ovewritten in the next blocks
// Compute flags/registers that are stored, but always ovewritten in the next blocks
// Propagate the information a few times to eliminate more
for (int i = 0; i < PropagationRounds; i++) {
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
@@ -131,43 +107,33 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) {
if (IROp->Op == OP_JUMP) {
auto Op = IROp->C<IR::IROp_Jump>();
OrderedNode* TargetNode = CurrentIR.GetNode(Op->Header.Args[0]);
auto& BlockInfo = InfoMap[BlockNode];
auto& TargetInfo = InfoMap[TargetNode];
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
auto& TargetInfo = InfoMap[Op->Header.Args[0].ID().Value];
// stores to remove are written by the next block but not read
BlockInfo.flag.kill = TargetInfo.flag.writes & ~(TargetInfo.flag.reads) & ~BlockInfo.flag.reads;
BlockInfo.gpr.kill = TargetInfo.gpr.writes & ~(TargetInfo.gpr.reads) & ~BlockInfo.gpr.reads;
BlockInfo.fpr.kill = TargetInfo.fpr.writes & ~(TargetInfo.fpr.reads) & ~BlockInfo.fpr.reads;
BlockInfo.reg.kill = TargetInfo.reg.writes & ~(TargetInfo.reg.reads) & ~BlockInfo.reg.reads;
// Flags that are written by the next block can be considered as written by this block, if not read
BlockInfo.flag.writes |= BlockInfo.flag.kill & ~BlockInfo.flag.reads;
BlockInfo.gpr.writes |= BlockInfo.gpr.kill & ~BlockInfo.gpr.reads;
BlockInfo.fpr.writes |= BlockInfo.fpr.kill & ~BlockInfo.fpr.reads;
BlockInfo.reg.writes |= BlockInfo.reg.kill & ~BlockInfo.reg.reads;
} else if (IROp->Op == OP_CONDJUMP) {
auto Op = IROp->C<IR::IROp_CondJump>();
OrderedNode* TrueTargetNode = CurrentIR.GetNode(Op->TrueBlock);
OrderedNode* FalseTargetNode = CurrentIR.GetNode(Op->FalseBlock);
auto& BlockInfo = InfoMap[BlockNode];
auto& TrueTargetInfo = InfoMap[TrueTargetNode];
auto& FalseTargetInfo = InfoMap[FalseTargetNode];
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
auto& TrueTargetInfo = InfoMap[Op->TrueBlock.ID().Value];
auto& FalseTargetInfo = InfoMap[Op->FalseBlock.ID().Value];
// stores to remove are written by the next blocks but not read
BlockInfo.flag.kill = TrueTargetInfo.flag.writes & ~(TrueTargetInfo.flag.reads) & ~BlockInfo.flag.reads;
BlockInfo.gpr.kill = TrueTargetInfo.gpr.writes & ~(TrueTargetInfo.gpr.reads) & ~BlockInfo.gpr.reads;
BlockInfo.fpr.kill = TrueTargetInfo.fpr.writes & ~(TrueTargetInfo.fpr.reads) & ~BlockInfo.fpr.reads;
BlockInfo.reg.kill = TrueTargetInfo.reg.writes & ~(TrueTargetInfo.reg.reads) & ~BlockInfo.reg.reads;
BlockInfo.flag.kill &= FalseTargetInfo.flag.writes & ~(FalseTargetInfo.flag.reads) & ~BlockInfo.flag.reads;
BlockInfo.gpr.kill &= FalseTargetInfo.gpr.writes & ~(FalseTargetInfo.gpr.reads) & ~BlockInfo.gpr.reads;
BlockInfo.fpr.kill &= FalseTargetInfo.fpr.writes & ~(FalseTargetInfo.fpr.reads) & ~BlockInfo.fpr.reads;
BlockInfo.reg.kill &= FalseTargetInfo.reg.writes & ~(FalseTargetInfo.reg.reads) & ~BlockInfo.reg.reads;
// Flags that are written by the next blocks can be considered as written by this block, if not read
BlockInfo.flag.writes |= BlockInfo.flag.kill & ~BlockInfo.flag.reads;
BlockInfo.gpr.writes |= BlockInfo.gpr.kill & ~BlockInfo.gpr.reads;
BlockInfo.fpr.writes |= BlockInfo.fpr.kill & ~BlockInfo.fpr.reads;
BlockInfo.reg.writes |= BlockInfo.reg.kill & ~BlockInfo.reg.reads;
}
}
}
@@ -176,7 +142,7 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) {
// Remove the dead stores
{
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
auto& BlockInfo = InfoMap[BlockNode];
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_STOREFLAG) {
@@ -185,22 +151,18 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) {
// If this StoreFlag is never read, remove it
if (BlockInfo.flag.kill & (1UL << Op->Flag)) {
IREmit->Remove(CodeNode);
Changed = true;
}
} else if (IROp->Op == OP_STOREREGISTER) {
auto Op = IROp->C<IR::IROp_StoreRegister>();
// If this OP_STOREREGISTER is never read, remove it
if ((BlockInfo.gpr.kill & GPRBit(Op->Class, Op->Reg)) || (BlockInfo.fpr.kill & FPRBit(Op->Class, Op->Reg))) {
if (BlockInfo.reg.kill & RegBit(Op->Class, Op->Reg)) {
IREmit->Remove(CodeNode);
Changed = true;
}
}
}
}
}
return Changed;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination() {
@@ -18,7 +18,7 @@ namespace FEXCore::IR::Debug {
class IRDumper final : public FEXCore::IR::Pass {
public:
IRDumper();
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
private:
FEX_CONFIG_OPT(DumpIR, DUMPIR);
@@ -37,7 +37,7 @@ IRDumper::IRDumper() {
}
}
bool IRDumper::Run(IREmitter* IREmit) {
void IRDumper::Run(IREmitter* IREmit) {
auto RAPass = Manager->GetPass<IR::RegisterAllocationPass>("RA");
IR::RegisterAllocationData* RA {};
if (RAPass) {
@@ -71,8 +71,6 @@ bool IRDumper::Run(IREmitter* IREmit) {
LogMan::Msg::IFmt("IR-{} 0x{:x}:\n{}\n@@@@@\n", RA ? "post" : "pre", HeaderOp->OriginalRIP, out.str());
}
}
return false;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateIRDumper() {
@@ -32,7 +32,7 @@ IRValidation::~IRValidation() {
NodeIsLive.Free();
}
bool IRValidation::Run(IREmitter* IREmit) {
void IRValidation::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::IRValidation");
bool HadError = false;
@@ -267,8 +267,6 @@ bool IRValidation::Run(IREmitter* IREmit) {
Errors.clear();
Warnings.clear();
}
return false;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateIRValidation() {
@@ -21,7 +21,7 @@ class RAValidation;
class IRValidation final : public FEXCore::IR::Pass {
public:
~IRValidation();
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
private:
@@ -23,15 +23,14 @@ class InlineCallOptimization final : public FEXCore::IR::Pass {
public:
InlineCallOptimization(const FEXCore::CPUIDEmu* CPUID)
: CPUID {CPUID} {}
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
private:
const FEXCore::CPUIDEmu* CPUID;
};
bool InlineCallOptimization::Run(IREmitter* IREmit) {
void InlineCallOptimization::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::SyscallOpt");
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
@@ -73,8 +72,6 @@ bool InlineCallOptimization::Run(IREmitter* IREmit) {
}
#endif
}
Changed = true;
}
} else if (IROp->Op == FEXCore::IR::OP_CPUID) {
auto Op = IROp->CW<IR::IROp_CPUID>();
@@ -98,7 +95,6 @@ bool InlineCallOptimization::Run(IREmitter* IREmit) {
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;
}
}
}
@@ -115,11 +111,9 @@ bool InlineCallOptimization::Run(IREmitter* IREmit) {
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<FEXCore::IR::Pass> CreateInlineCallOptimization(const FEXCore::CPUIDEmu* CPUID) {
@@ -18,7 +18,7 @@ namespace FEXCore::IR {
class LongDivideEliminationPass final : public FEXCore::IR::Pass {
public:
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
private:
bool IsZeroOp(IREmitter* IREmit, OrderedNodeWrapper Arg);
bool IsSextOp(IREmitter* IREmit, OrderedNodeWrapper Lower, OrderedNodeWrapper Upper);
@@ -48,10 +48,9 @@ bool LongDivideEliminationPass::IsSextOp(IREmitter* IREmit, OrderedNodeWrapper L
return false;
}
bool LongDivideEliminationPass::Run(IREmitter* IREmit) {
void LongDivideEliminationPass::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::LDE");
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
auto OriginalWriteCursor = IREmit->GetWriteCursor();
@@ -74,7 +73,6 @@ bool LongDivideEliminationPass::Run(IREmitter* IREmit) {
SDivOp = IREmit->_Rem(OpSize::i64Bit, 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>();
@@ -91,7 +89,6 @@ bool LongDivideEliminationPass::Run(IREmitter* IREmit) {
UDivOp = IREmit->_URem(OpSize::i64Bit, Lower, Divisor);
}
IREmit->ReplaceAllUsesWith(CodeNode, UDivOp);
Changed = true;
}
}
}
@@ -99,8 +96,6 @@ bool LongDivideEliminationPass::Run(IREmitter* IREmit) {
}
IREmit->SetWriteCursor(OriginalWriteCursor);
return Changed;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateLongDivideEliminationPass() {
@@ -102,13 +102,13 @@ private:
class RAValidation final : public FEXCore::IR::Pass {
public:
~RAValidation() {}
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
};
bool RAValidation::Run(IREmitter* IREmit) {
void RAValidation::Run(IREmitter* IREmit) {
if (!Manager->HasPass("RA")) {
return false;
return;
}
FEXCORE_PROFILE_SCOPED("PassManager::RAValidation");
@@ -210,8 +210,6 @@ bool RAValidation::Run(IREmitter* IREmit) {
Errors.clear();
}
return false;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateRAValidation() {
@@ -53,12 +53,13 @@ struct FlagInfo {
class DeadFlagCalculationEliminination final : public FEXCore::IR::Pass {
public:
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
private:
FlagInfo Classify(IROp_Header* Node);
unsigned FlagForReg(unsigned Reg);
unsigned FlagsForCondClassType(CondClassType Cond);
bool EliminateDeadCode(IREmitter* IREmit, OrderedNode* CodeNode, IROp_Header* IROp);
};
unsigned DeadFlagCalculationEliminination::FlagForReg(unsigned Reg) {
@@ -310,13 +311,72 @@ FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) {
return {.Trivial = true};
}
// General purpose dead code elimination. Returns whether flag handling should
// be skipped (because it was removed or could not possibly affect flags).
bool DeadFlagCalculationEliminination::EliminateDeadCode(IREmitter* IREmit, OrderedNode* CodeNode, IROp_Header* IROp) {
bool HasSideEffects = IR::HasSideEffects(IROp->Op);
switch (IROp->Op) {
case OP_SYSCALL:
case OP_INLINESYSCALL: {
FEXCore::IR::SyscallFlags Flags {};
if (IROp->Op == OP_SYSCALL) {
auto Op = IROp->C<IR::IROp_Syscall>();
Flags = Op->Flags;
} else {
auto Op = IROp->C<IR::IROp_InlineSyscall>();
Flags = Op->Flags;
}
if ((Flags & FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) == FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) {
HasSideEffects = false;
}
break;
}
case OP_ATOMICFETCHADD:
case OP_ATOMICFETCHSUB:
case OP_ATOMICFETCHAND:
case OP_ATOMICFETCHCLR:
case OP_ATOMICFETCHOR:
case OP_ATOMICFETCHXOR:
case OP_ATOMICFETCHNEG: {
// If the result of the atomic fetch is completely unused, convert it to a non-fetching atomic operation.
if (CodeNode->GetUses() == 0) {
switch (IROp->Op) {
case OP_ATOMICFETCHADD: IROp->Op = OP_ATOMICADD; break;
case OP_ATOMICFETCHSUB: IROp->Op = OP_ATOMICSUB; break;
case OP_ATOMICFETCHAND: IROp->Op = OP_ATOMICAND; break;
case OP_ATOMICFETCHCLR: IROp->Op = OP_ATOMICCLR; break;
case OP_ATOMICFETCHOR: IROp->Op = OP_ATOMICOR; break;
case OP_ATOMICFETCHXOR: IROp->Op = OP_ATOMICXOR; break;
case OP_ATOMICFETCHNEG: IROp->Op = OP_ATOMICNEG; break;
default: FEX_UNREACHABLE;
}
}
return true;
}
default: break;
}
// Skip over anything that has side effects
// Use count tracking can't safely remove anything with side effects
if (!HasSideEffects) {
if (CodeNode->GetUses() == 0) {
IREmit->Remove(CodeNode);
return true;
}
}
return false;
}
/**
* @brief This pass removes flag calculations that will otherwise be unused INSIDE of that block
* @brief This pass removes dead code locally.
*/
bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) {
void DeadFlagCalculationEliminination::Run(IREmitter* IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::DFE");
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
@@ -338,13 +398,7 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) {
// Optimizing flags can cause earlier flag reads to become dead but dead
// flag reads should not impede optimiation of earlier dead flag writes.
// We must DCE as we go to ensure we converge in a single iteration.
//
// TODO: This whole pass could be merged with DCE?
bool HasSideEffects = IR::HasSideEffects(IROp->Op);
if (!HasSideEffects && CodeNode->GetUses() == 0) {
Changed = true;
IREmit->Remove(CodeNode);
} else {
if (!EliminateDeadCode(IREmit, CodeNode, IROp)) {
// Optimiation algorithm: For each flag written...
//
// If the flag has a later read (per FlagsRead), remove the flag from
@@ -364,13 +418,11 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) {
bool Eliminated = false;
if ((FlagsRead & Info.Write) == 0) {
if (Info.CanEliminate && CodeNode->GetUses() == 0) {
if ((Info.CanEliminate || Info.CanReplace) && CodeNode->GetUses() == 0) {
IREmit->Remove(CodeNode);
Eliminated = true;
Changed = true;
} else if (Info.CanReplace) {
IROp->Op = Info.Replacement;
Changed = true;
}
} else {
FlagsRead &= ~Info.Write;
@@ -392,8 +444,6 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) {
--CodeLast;
}
}
return Changed;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination() {
@@ -58,7 +58,7 @@ namespace {
class ConstrainedRAPass final : public RegisterAllocationPass {
public:
bool Run(IREmitter* IREmit) override;
void Run(IREmitter* IREmit) override;
void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override;
RegisterAllocationData* GetAllocationData() override;
@@ -495,7 +495,7 @@ RegisterAllocationData::UniquePtr ConstrainedRAPass::PullAllocationData() {
return std::move(AllocData);
}
bool ConstrainedRAPass::Run(IREmitter* IREmit_) {
void ConstrainedRAPass::Run(IREmitter* IREmit_) {
FEXCORE_PROFILE_SCOPED("PassManager::RA");
IREmit = IREmit_;
@@ -708,9 +708,6 @@ bool ConstrainedRAPass::Run(IREmitter* IREmit_) {
SSAToReg.clear();
SpillSlots.clear();
NextUses.clear();
/* No point tracking this finely, RA is always one-shot */
return true;
}
fextl::unique_ptr<IR::RegisterAllocationPass> CreateRegisterAllocationPass() {
+1 -1
View File
@@ -22,7 +22,7 @@ class RegisterAllocationData;
enum class SyscallFlags : uint8_t {
DEFAULT = 0,
// Syscalldoesn't care about CPUState being serialized up to the syscall instruction.
// Means DeadCodeElimination can optimize through a syscall operation.
// Means dead code elimination can optimize through a syscall operation.
OPTIMIZETHROUGH = 1 << 0,
// Syscall only reads the passed in arguments. Doesn't read CPUState.
NOSYNCSTATEONENTRY = 1 << 1,
-1
View File
@@ -109,7 +109,6 @@ IR to IR Optimization
- [PassManager.cpp](../FEXCore/Source/Interface/IR/PassManager.cpp): Defines which passes are run, and runs them
- [PassManager.h](../FEXCore/Source/Interface/IR/PassManager.h)
- [ConstProp.cpp](../FEXCore/Source/Interface/IR/Passes/ConstProp.cpp): ConstProp, ZExt elim, addressgen coalesce, const pooling, fcmp reduction, const inlining
- [DeadCodeElimination.cpp](../FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp)
- [DeadContextStoreElimination.cpp](../FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp): Transforms ContextLoad/Store to temporaries, similar to mem2reg
- [DeadStoreElimination.cpp](../FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp): Cross block store-after-store elimination
- [IRValidation.cpp](../FEXCore/Source/Interface/IR/Passes/IRValidation.cpp): Sanity checking pass
@@ -701,8 +701,8 @@
"cset x21, ne",
"mov x22, x21",
"bfxil x4, x20, #0, #16",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -715,8 +715,8 @@
"cset x21, ne",
"mov x22, x21",
"mov w4, w20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -729,8 +729,8 @@
"cset x21, ne",
"mov x22, x21",
"mov x4, x20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -743,8 +743,8 @@
"cset x21, ne",
"mov x22, x21",
"bfxil x4, x20, #0, #16",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -757,8 +757,8 @@
"cset x21, ne",
"mov x22, x21",
"mov w4, w20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -771,8 +771,8 @@
"cset x21, ne",
"mov x22, x21",
"mov x4, x20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -837,8 +837,8 @@
"cset x21, ne",
"mov x22, x21",
"bfxil x4, x20, #0, #16",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -851,8 +851,8 @@
"cset x21, ne",
"mov x22, x21",
"mov w4, w20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -865,8 +865,8 @@
"cset x21, ne",
"mov x22, x21",
"mov x4, x20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -879,8 +879,8 @@
"cset x21, ne",
"mov x22, x21",
"bfxil x4, x20, #0, #16",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -893,8 +893,8 @@
"cset x21, ne",
"mov x22, x21",
"mov w4, w20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]
@@ -907,8 +907,8 @@
"cset x21, ne",
"mov x22, x21",
"mov x4, x20",
"mov w26, #0x1",
"mov w27, #0x0",
"mov w26, #0x1",
"lsl x20, x22, #29",
"msr nzcv, x20"
]