[WinEH] Simplify unreachable catchpads

Summary:
At least for CoreCLR, a catchpad which immediately executes an
`unreachable` instruction indicates that the exception can never have a
matching type, and so such catchpads can be removed, and so can their
catchswitches if the catchswitch becomes empty.

Reviewers: rnk, andrew.w.kaylor, majnemer

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D15846

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@256809 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Joseph Tremoulet 2016-01-05 02:37:41 +00:00
parent 7f4aa70ca5
commit d28c1bc303
4 changed files with 181 additions and 13 deletions

View File

@ -3966,6 +3966,8 @@ public:
/// point to the added handler.
void addHandler(BasicBlock *Dest);
void removeHandler(handler_iterator HI);
unsigned getNumSuccessors() const { return getNumOperands() - 1; }
BasicBlock *getSuccessor(unsigned Idx) const {
assert(Idx < getNumSuccessors() &&

View File

@ -934,6 +934,17 @@ void CatchSwitchInst::addHandler(BasicBlock *Handler) {
getOperandList()[OpNo] = Handler;
}
void CatchSwitchInst::removeHandler(handler_iterator HI) {
// Move all subsequent handlers up one.
Use *EndDst = op_end() - 1;
for (Use *CurDst = HI.getCurrent(); CurDst != EndDst; ++CurDst)
*CurDst = *(CurDst + 1);
// Null out the last handler use.
*EndDst = nullptr;
setNumHungOffUseOperands(getNumOperands() - 1);
}
BasicBlock *CatchSwitchInst::getSuccessorV(unsigned idx) const {
return getSuccessor(idx);
}

View File

@ -20,6 +20,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
@ -3448,18 +3449,26 @@ bool SimplifyCFGOpt::SimplifyUnreachable(UnreachableInst *UI) {
if (isa<CallInst>(BBI) && !isa<DbgInfoIntrinsic>(BBI)) break;
if (BBI->mayHaveSideEffects()) {
if (StoreInst *SI = dyn_cast<StoreInst>(BBI)) {
if (auto *SI = dyn_cast<StoreInst>(BBI)) {
if (SI->isVolatile())
break;
} else if (LoadInst *LI = dyn_cast<LoadInst>(BBI)) {
} else if (auto *LI = dyn_cast<LoadInst>(BBI)) {
if (LI->isVolatile())
break;
} else if (AtomicRMWInst *RMWI = dyn_cast<AtomicRMWInst>(BBI)) {
} else if (auto *RMWI = dyn_cast<AtomicRMWInst>(BBI)) {
if (RMWI->isVolatile())
break;
} else if (AtomicCmpXchgInst *CXI = dyn_cast<AtomicCmpXchgInst>(BBI)) {
} else if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(BBI)) {
if (CXI->isVolatile())
break;
} else if (isa<CatchPadInst>(BBI)) {
// A catchpad may invoke exception object constructors and such, which
// in some languages can be arbitrary code, so be conservative by
// default.
// For CoreCLR, it just involves a type test, so can be removed.
if (classifyEHPersonality(BB->getParent()->getPersonalityFn()) !=
EHPersonality::CoreCLR)
break;
} else if (!isa<FenceInst>(BBI) && !isa<VAArgInst>(BBI) &&
!isa<LandingPadInst>(BBI)) {
break;
@ -3485,7 +3494,7 @@ bool SimplifyCFGOpt::SimplifyUnreachable(UnreachableInst *UI) {
for (unsigned i = 0, e = Preds.size(); i != e; ++i) {
TerminatorInst *TI = Preds[i]->getTerminator();
IRBuilder<> Builder(TI);
if (BranchInst *BI = dyn_cast<BranchInst>(TI)) {
if (auto *BI = dyn_cast<BranchInst>(TI)) {
if (BI->isUnconditional()) {
if (BI->getSuccessor(0) == BB) {
new UnreachableInst(TI->getContext(), TI);
@ -3502,7 +3511,7 @@ bool SimplifyCFGOpt::SimplifyUnreachable(UnreachableInst *UI) {
Changed = true;
}
}
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(TI)) {
} else if (auto *SI = dyn_cast<SwitchInst>(TI)) {
for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end();
i != e; ++i)
if (i.getCaseSuccessor() == BB) {
@ -3511,18 +3520,49 @@ bool SimplifyCFGOpt::SimplifyUnreachable(UnreachableInst *UI) {
--i; --e;
Changed = true;
}
} else if ((isa<InvokeInst>(TI) &&
cast<InvokeInst>(TI)->getUnwindDest() == BB) ||
isa<CatchSwitchInst>(TI)) {
removeUnwindEdge(TI->getParent());
Changed = true;
} else if (auto *II = dyn_cast<InvokeInst>(TI)) {
if (II->getUnwindDest() == BB) {
removeUnwindEdge(TI->getParent());
Changed = true;
}
} else if (auto *CSI = dyn_cast<CatchSwitchInst>(TI)) {
if (CSI->getUnwindDest() == BB) {
removeUnwindEdge(TI->getParent());
Changed = true;
continue;
}
for (CatchSwitchInst::handler_iterator I = CSI->handler_begin(),
E = CSI->handler_end();
I != E; ++I) {
if (*I == BB) {
CSI->removeHandler(I);
--I;
--E;
Changed = true;
}
}
if (CSI->getNumHandlers() == 0) {
BasicBlock *CatchSwitchBB = CSI->getParent();
if (CSI->hasUnwindDest()) {
// Redirect preds to the unwind dest
CatchSwitchBB->replaceAllUsesWith(CSI->getUnwindDest());
} else {
// Rewrite all preds to unwind to caller (or from invoke to call).
SmallVector<BasicBlock *, 8> EHPreds(predecessors(CatchSwitchBB));
for (BasicBlock *EHPred : EHPreds)
removeUnwindEdge(EHPred);
}
// The catchswitch is no longer reachable.
new UnreachableInst(CSI->getContext(), CSI);
CSI->eraseFromParent();
Changed = true;
}
} else if (isa<CleanupReturnInst>(TI)) {
new UnreachableInst(TI->getContext(), TI);
TI->eraseFromParent();
Changed = true;
}
// TODO: We can remove a catchswitch if all it's catchpads end in
// unreachable.
}
// If this block is now dead, remove it.

View File

@ -0,0 +1,115 @@
; RUN: opt < %s -simplifycfg -S | FileCheck %s
declare void @f()
declare void @llvm.foo(i32) nounwind
declare void @ProcessCLRException()
define void @test1() personality void ()* @ProcessCLRException {
entry:
invoke void @f()
to label %exit unwind label %exn.dispatch
exn.dispatch:
%cs = catchswitch within none [label %pad1, label %pad2] unwind to caller
pad1:
%cp1 = catchpad within %cs [i32 1]
call void @llvm.foo(i32 1)
catchret from %cp1 to label %exit
pad2:
%cp2 = catchpad within %cs [i32 2]
unreachable
exit:
ret void
}
; Remove unreachble catch2, leave catch1 as-is
; CHECK-LABEL: define void @test1()
; CHECK: %cs = catchswitch within none [label %pad1] unwind to caller
; CHECK-NOT: catchpad
; CHECK: %cp1 = catchpad within %cs [i32 1]
; CHECK-NOT: catchpad
; Remove both catchpads and the catchswitch from exn.dispatch
; CHECK-LABEL: define void @test2()
define void @test2() personality void ()* @ProcessCLRException {
entry:
invoke void @f()
to label %via.cleanup unwind label %exn.dispatch
; CHECK-NOT: invoke
; CHECK: call void @f()
via.cleanup:
invoke void @f()
to label %via.catchswitch unwind label %cleanup.inner
cleanup.inner:
%cp.inner = cleanuppad within none []
call void @llvm.foo(i32 0)
cleanupret from %cp.inner unwind label %exn.dispatch
; CHECK: cleanupret from %cp.inner unwind to caller
via.catchswitch:
invoke void @f()
to label %exit unwind label %dispatch.inner
dispatch.inner:
%cs.inner = catchswitch within none [label %pad.inner] unwind label %exn.dispatch
; CHECK: %cs.inner = catchswitch within none [label %pad.inner] unwind to caller
pad.inner:
%catch.inner = catchpad within %cs.inner [i32 0]
; CHECK: %catch.inner = catchpad within %cs.inner
call void @llvm.foo(i32 1)
catchret from %catch.inner to label %exit
exn.dispatch:
%cs = catchswitch within none [label %pad1, label %pad2] unwind to caller
; CHECK-NOT: catchswitch within
; CHECK-NOT: catchpad
pad1:
catchpad within %cs [i32 1]
unreachable
pad2:
catchpad within %cs [i32 2]
unreachable
exit:
ret void
}
; Same as @test2, but exn.dispatch catchswitch has an unwind dest that
; preds need to be reidrected to
; CHECK-LABEL: define void @test3()
define void @test3() personality void ()* @ProcessCLRException {
entry:
invoke void @f()
to label %via.cleanup unwind label %exn.dispatch
; CHECK: invoke void @f()
; CHECK-NEXT: to label %via.cleanup unwind label %cleanup
via.cleanup:
invoke void @f()
to label %via.catchswitch unwind label %cleanup.inner
cleanup.inner:
%cp.inner = cleanuppad within none []
call void @llvm.foo(i32 0)
cleanupret from %cp.inner unwind label %exn.dispatch
; CHECK: cleanupret from %cp.inner unwind label %cleanup
via.catchswitch:
invoke void @f()
to label %exit unwind label %dispatch.inner
dispatch.inner:
%cs.inner = catchswitch within none [label %pad.inner] unwind label %exn.dispatch
; CHECK: %cs.inner = catchswitch within none [label %pad.inner] unwind label %cleanup
pad.inner:
%catch.inner = catchpad within %cs.inner [i32 0]
; CHECK: %catch.inner = catchpad within %cs.inner
call void @llvm.foo(i32 1)
catchret from %catch.inner to label %exit
exn.dispatch:
%cs = catchswitch within none [label %pad1, label %pad2] unwind label %cleanup
; CHECK-NOT: catchswitch within
; CHECK-NOT: catchpad
pad1:
catchpad within %cs [i32 1]
unreachable
pad2:
catchpad within %cs [i32 2]
unreachable
cleanup:
%cp = cleanuppad within none []
call void @llvm.foo(i32 0)
cleanupret from %cp unwind to caller
exit:
ret void
}