mirror of
https://github.com/RPCS3/llvm.git
synced 2025-02-03 17:24:24 +00:00
[LowerSwitch] Fix a bug when LowerSwitch deletes the default block
Summary: LowerSwitch crashed with the attached test case after deleting the default block. This happened because the current implementation of deleting dead blocks is wrong. After the default block being deleted, it contains no instruction or terminator, and it should no be traversed anymore. However, since the iterator is advanced before processSwitchInst() function is executed, the block advanced to could be deleted inside processSwitchInst(). The deleted block would then be visited next and crash dyn_cast<SwitchInst>(Cur->getTerminator()) because Cur->getTerminator() returns a nullptr. This patch fixes this problem by recording dead default blocks into a list, and delete them after all processSwitchInst() has been done. It still possible to visit dead default blocks and waste time process them. But it is a compile time issue, and I plan to have another patch to add support to skip dead blocks. Reviewers: kariddi, resistor, hans, reames Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D11852 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@244642 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
2f3b47dd61
commit
a0180252f1
@ -78,7 +78,7 @@ namespace {
|
|||||||
typedef std::vector<CaseRange> CaseVector;
|
typedef std::vector<CaseRange> CaseVector;
|
||||||
typedef std::vector<CaseRange>::iterator CaseItr;
|
typedef std::vector<CaseRange>::iterator CaseItr;
|
||||||
private:
|
private:
|
||||||
void processSwitchInst(SwitchInst *SI);
|
void processSwitchInst(SwitchInst *SI, SmallVectorImpl<BasicBlock*> &DeleteList);
|
||||||
|
|
||||||
BasicBlock *switchConvert(CaseItr Begin, CaseItr End,
|
BasicBlock *switchConvert(CaseItr Begin, CaseItr End,
|
||||||
ConstantInt *LowerBound, ConstantInt *UpperBound,
|
ConstantInt *LowerBound, ConstantInt *UpperBound,
|
||||||
@ -116,16 +116,21 @@ FunctionPass *llvm::createLowerSwitchPass() {
|
|||||||
|
|
||||||
bool LowerSwitch::runOnFunction(Function &F) {
|
bool LowerSwitch::runOnFunction(Function &F) {
|
||||||
bool Changed = false;
|
bool Changed = false;
|
||||||
|
SmallVector<BasicBlock*, 8> DeleteList;
|
||||||
|
|
||||||
for (Function::iterator I = F.begin(), E = F.end(); I != E; ) {
|
for (Function::iterator I = F.begin(), E = F.end(); I != E; ) {
|
||||||
BasicBlock *Cur = I++; // Advance over block so we don't traverse new blocks
|
BasicBlock *Cur = I++; // Advance over block so we don't traverse new blocks
|
||||||
|
|
||||||
if (SwitchInst *SI = dyn_cast<SwitchInst>(Cur->getTerminator())) {
|
if (SwitchInst *SI = dyn_cast<SwitchInst>(Cur->getTerminator())) {
|
||||||
Changed = true;
|
Changed = true;
|
||||||
processSwitchInst(SI);
|
processSwitchInst(SI, DeleteList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (BasicBlock* BB: DeleteList) {
|
||||||
|
DeleteDeadBlock(BB);
|
||||||
|
}
|
||||||
|
|
||||||
return Changed;
|
return Changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +402,7 @@ unsigned LowerSwitch::Clusterify(CaseVector& Cases, SwitchInst *SI) {
|
|||||||
// processSwitchInst - Replace the specified switch instruction with a sequence
|
// processSwitchInst - Replace the specified switch instruction with a sequence
|
||||||
// of chained if-then insts in a balanced binary search.
|
// of chained if-then insts in a balanced binary search.
|
||||||
//
|
//
|
||||||
void LowerSwitch::processSwitchInst(SwitchInst *SI) {
|
void LowerSwitch::processSwitchInst(SwitchInst *SI, SmallVectorImpl<BasicBlock*> &DeleteList) {
|
||||||
BasicBlock *CurBlock = SI->getParent();
|
BasicBlock *CurBlock = SI->getParent();
|
||||||
BasicBlock *OrigBlock = CurBlock;
|
BasicBlock *OrigBlock = CurBlock;
|
||||||
Function *F = CurBlock->getParent();
|
Function *F = CurBlock->getParent();
|
||||||
@ -518,7 +523,7 @@ void LowerSwitch::processSwitchInst(SwitchInst *SI) {
|
|||||||
BasicBlock *OldDefault = SI->getDefaultDest();
|
BasicBlock *OldDefault = SI->getDefaultDest();
|
||||||
CurBlock->getInstList().erase(SI);
|
CurBlock->getInstList().erase(SI);
|
||||||
|
|
||||||
// If the Default block has no more predecessors just remove it.
|
// If the Default block has no more predecessors just add it to DeleteList.
|
||||||
if (pred_begin(OldDefault) == pred_end(OldDefault))
|
if (pred_begin(OldDefault) == pred_end(OldDefault))
|
||||||
DeleteDeadBlock(OldDefault);
|
DeleteList.push_back(OldDefault);
|
||||||
}
|
}
|
||||||
|
27
test/Transforms/LowerSwitch/delete-default-block-crash.ll
Normal file
27
test/Transforms/LowerSwitch/delete-default-block-crash.ll
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
; RUN: opt < %s -lowerswitch -disable-output
|
||||||
|
|
||||||
|
; This test verify -lowerswitch does not crash after deleting the default block.
|
||||||
|
|
||||||
|
declare i32 @f(i32)
|
||||||
|
|
||||||
|
define i32 @unreachable(i32 %x) {
|
||||||
|
|
||||||
|
entry:
|
||||||
|
switch i32 %x, label %unreachable [
|
||||||
|
i32 5, label %a
|
||||||
|
i32 6, label %a
|
||||||
|
i32 7, label %a
|
||||||
|
i32 10, label %b
|
||||||
|
i32 20, label %b
|
||||||
|
i32 30, label %b
|
||||||
|
i32 40, label %b
|
||||||
|
]
|
||||||
|
unreachable:
|
||||||
|
unreachable
|
||||||
|
a:
|
||||||
|
%0 = call i32 @f(i32 0)
|
||||||
|
ret i32 %0
|
||||||
|
b:
|
||||||
|
%1 = call i32 @f(i32 1)
|
||||||
|
ret i32 %1
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user