/// 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() &&
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);
}
#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"
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;
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);
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) {
--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.
--- /dev/null
+; 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
+}