mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 21:00:17 +02:00
Merge pull request #3660 from alyssarosenzweig/opt/smash-less
Delete a big chunk of IR/Passes/*
This commit is contained in:
20 files changed
+223
-623
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
|
||||
Reference in new issue
Block a user