mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 19:20:05 +00:00
DeadBranchElim: Fix dead block elimination
The previous algorithm would leave invalid code in the case of unreachable blocks pointing into a dead branch. It would leave the unreachable blocks branching to labels that no longer exist. The previous algorithm also left unreachable blocks in some cases (a loop following an orphaned merge block). This fix also addresses that. This code will soon be replaced with the coming CFG cleanup.
This commit is contained in:
parent
5834719fc1
commit
e3a7209330
@ -24,6 +24,7 @@ namespace opt {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kBranchTargetLabIdInIdx = 0;
|
||||
const uint32_t kBranchCondTrueLabIdInIdx = 1;
|
||||
const uint32_t kBranchCondFalseLabIdInIdx = 2;
|
||||
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
|
||||
@ -190,20 +191,57 @@ bool DeadBranchElimPass::GetSelectionBranch(ir::BasicBlock* bp,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::HasNonPhiRef(uint32_t labelId) {
|
||||
bool DeadBranchElimPass::HasNonPhiNonBackedgeRef(uint32_t labelId) {
|
||||
analysis::UseList* uses = def_use_mgr_->GetUses(labelId);
|
||||
if (uses == nullptr)
|
||||
return false;
|
||||
for (auto u : *uses)
|
||||
if (u.inst->opcode() != SpvOpPhi)
|
||||
for (auto u : *uses) {
|
||||
if (u.inst->opcode() != SpvOpPhi &&
|
||||
backedges_.find(u.inst) == backedges_.end())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::ComputeBackEdges(
|
||||
std::list<ir::BasicBlock*>& structuredOrder) {
|
||||
backedges_.clear();
|
||||
std::unordered_set<uint32_t> visited;
|
||||
// In structured order, edges to visited blocks are back edges
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
visited.insert((*bi)->id());
|
||||
auto ii = (*bi)->end();
|
||||
--ii;
|
||||
switch(ii->opcode()) {
|
||||
case SpvOpBranch: {
|
||||
const uint32_t labId = ii->GetSingleWordInOperand(
|
||||
kBranchTargetLabIdInIdx);
|
||||
if (visited.find(labId) != visited.end())
|
||||
backedges_.insert(&*ii);
|
||||
} break;
|
||||
case SpvOpBranchConditional: {
|
||||
const uint32_t tLabId = ii->GetSingleWordInOperand(
|
||||
kBranchCondTrueLabIdInIdx);
|
||||
if (visited.find(tLabId) != visited.end()) {
|
||||
backedges_.insert(&*ii);
|
||||
break;
|
||||
}
|
||||
const uint32_t fLabId = ii->GetSingleWordInOperand(
|
||||
kBranchCondFalseLabIdInIdx);
|
||||
if (visited.find(fLabId) != visited.end())
|
||||
backedges_.insert(&*ii);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
// Traverse blocks in structured order
|
||||
std::list<ir::BasicBlock*> structuredOrder;
|
||||
ComputeStructuredOrder(func, &structuredOrder);
|
||||
ComputeBackEdges(structuredOrder);
|
||||
std::unordered_set<ir::BasicBlock*> elimBlocks;
|
||||
bool modified = false;
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
@ -265,37 +303,23 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
|
||||
modified = true;
|
||||
|
||||
// Initialize live block set to the live label
|
||||
std::unordered_set<uint32_t> liveLabIds;
|
||||
liveLabIds.insert(liveLabId);
|
||||
|
||||
// Iterate to merge block adding dead blocks to elimination set
|
||||
auto dbi = bi;
|
||||
++dbi;
|
||||
uint32_t dLabId = (*dbi)->id();
|
||||
while (dLabId != mergeLabId) {
|
||||
if (liveLabIds.find(dLabId) == liveLabIds.end()) {
|
||||
if (!HasNonPhiNonBackedgeRef(dLabId)) {
|
||||
// Kill use/def for all instructions and mark block for elimination
|
||||
KillAllInsts(*dbi);
|
||||
elimBlocks.insert(*dbi);
|
||||
}
|
||||
else {
|
||||
// Mark all successors as live
|
||||
(*dbi)->ForEachSuccessorLabel([&liveLabIds](const uint32_t succId){
|
||||
liveLabIds.insert(succId);
|
||||
});
|
||||
// Mark merge and continue blocks as live
|
||||
(*dbi)->ForMergeAndContinueLabel([&liveLabIds](const uint32_t succId){
|
||||
liveLabIds.insert(succId);
|
||||
});
|
||||
}
|
||||
++dbi;
|
||||
dLabId = (*dbi)->id();
|
||||
}
|
||||
|
||||
// If merge block is unreachable, continue eliminating blocks until
|
||||
// a live block or last block is reached.
|
||||
while (!HasNonPhiRef(dLabId)) {
|
||||
while (!HasNonPhiNonBackedgeRef(dLabId)) {
|
||||
KillAllInsts(*dbi);
|
||||
elimBlocks.insert(*dbi);
|
||||
++dbi;
|
||||
|
@ -97,8 +97,11 @@ class DeadBranchElimPass : public MemPass {
|
||||
bool GetSelectionBranch(ir::BasicBlock* bp, ir::Instruction** branchInst,
|
||||
ir::Instruction** mergeInst, uint32_t *condId);
|
||||
|
||||
// Return true if |labelId| has any non-phi references
|
||||
bool HasNonPhiRef(uint32_t labelId);
|
||||
// Return true if |labelId| has any non-phi, non-backedge references
|
||||
bool HasNonPhiNonBackedgeRef(uint32_t labelId);
|
||||
|
||||
// Compute backedges for blocks in |structuredOrder|.
|
||||
void ComputeBackEdges(std::list<ir::BasicBlock*>& structuredOrder);
|
||||
|
||||
// For function |func|, look for BranchConditionals with constant condition
|
||||
// and convert to a Branch to the indicated label. Delete resulting dead
|
||||
@ -125,6 +128,9 @@ class DeadBranchElimPass : public MemPass {
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
|
||||
// All backedge branches in current function
|
||||
std::unordered_set<ir::Instruction*> backedges_;
|
||||
|
||||
// Extensions supported by this pass.
|
||||
std::unordered_set<std::string> extensions_whitelist_;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user