return Changed;
}
+void llvm::removeUnwindEdge(BasicBlock *BB) {
+ TerminatorInst *TI = BB->getTerminator();
+
+ if (auto *II = dyn_cast<InvokeInst>(TI)) {
+ changeToCall(II);
+ return;
+ }
+
+ TerminatorInst *NewTI;
+ BasicBlock *UnwindDest;
+
+ if (auto *CRI = dyn_cast<CleanupReturnInst>(TI)) {
+ NewTI = CleanupReturnInst::Create(CRI->getCleanupPad(), nullptr, CRI);
+ UnwindDest = CRI->getUnwindDest();
+ } else if (auto *CEP = dyn_cast<CleanupEndPadInst>(TI)) {
+ NewTI = CleanupEndPadInst::Create(CEP->getCleanupPad(), nullptr, CEP);
+ UnwindDest = CEP->getUnwindDest();
+ } else if (auto *CEP = dyn_cast<CatchEndPadInst>(TI)) {
+ NewTI = CatchEndPadInst::Create(CEP->getContext(), nullptr, CEP);
+ UnwindDest = CEP->getUnwindDest();
+ } else if (auto *TPI = dyn_cast<TerminatePadInst>(TI)) {
+ SmallVector<Value *, 3> TerminatePadArgs;
+ for (Value *Operand : TPI->arg_operands())
+ TerminatePadArgs.push_back(Operand);
+ NewTI = TerminatePadInst::Create(TPI->getContext(), nullptr,
+ TerminatePadArgs, TPI);
+ UnwindDest = TPI->getUnwindDest();
+ } else {
+ llvm_unreachable("Could not find unwind successor");
+ }
+
+ NewTI->takeName(TI);
+ NewTI->setDebugLoc(TI->getDebugLoc());
+ UnwindDest->removePredecessor(BB);
+ TI->eraseFromParent();
+}
+
/// removeUnreachableBlocksFromFn - Remove blocks that are not reachable, even
/// if they are in a dead cycle. Return true if a change was made, false
/// otherwise.
return true;
}
-// FIXME: This seems like a pretty common thing to want to do. Consider
-// whether there is a more accessible place to put this.
-static void convertInvokeToCall(InvokeInst *II) {
- SmallVector<Value*, 8> Args(II->op_begin(), II->op_end() - 3);
- // Insert a call instruction before the invoke.
- CallInst *Call = CallInst::Create(II->getCalledValue(), Args, "", II);
- Call->takeName(II);
- Call->setCallingConv(II->getCallingConv());
- Call->setAttributes(II->getAttributes());
- Call->setDebugLoc(II->getDebugLoc());
-
- // Anything that used the value produced by the invoke instruction now uses
- // the value produced by the call instruction. Note that we do this even
- // for void functions and calls with no uses so that the callgraph edge is
- // updated.
- II->replaceAllUsesWith(Call);
- II->getUnwindDest()->removePredecessor(II->getParent());
-
- // Insert a branch to the normal destination right before the invoke.
- BranchInst::Create(II->getNormalDest(), II);
-
- // Finally, delete the invoke instruction!
- II->eraseFromParent();
-}
-
bool SimplifyCFGOpt::SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder) {
// If this is a trivial landing pad that just continues unwinding the caught
// exception then zap the landing pad, turning its invokes into calls.
// Turn all invokes that unwind here into calls and delete the basic block.
for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) {
- InvokeInst *II = cast<InvokeInst>((*PI++)->getTerminator());
- convertInvokeToCall(II);
+ BasicBlock *Pred = *PI++;
+ removeUnwindEdge(Pred);
}
// The landingpad is now unreachable. Zap it.
for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) {
// The iterator must be updated here because we are removing this pred.
BasicBlock *PredBB = *PI++;
- TerminatorInst *TI = PredBB->getTerminator();
if (UnwindDest == nullptr) {
- if (auto *II = dyn_cast<InvokeInst>(TI)) {
- // The cleanup return being simplified continues to the caller and this
- // predecessor terminated with an invoke instruction. Convert the
- // invoke to a call.
- // This call updates the predecessor/successor chain.
- convertInvokeToCall(II);
- } else {
- // In the remaining cases the predecessor's terminator unwinds to the
- // block we are removing. We need to create a new instruction that
- // unwinds to the caller. Simply setting the unwind destination to
- // nullptr would leave the objects internal data in an inconsistent
- // state.
- // FIXME: Consider whether it is better to update setUnwindDest to
- // keep things consistent.
- if (auto *CRI = dyn_cast<CleanupReturnInst>(TI)) {
- auto *NewCRI = CleanupReturnInst::Create(CRI->getCleanupPad(),
- nullptr, CRI);
- NewCRI->takeName(CRI);
- NewCRI->setDebugLoc(CRI->getDebugLoc());
- CRI->eraseFromParent();
- } else if (auto *CEP = dyn_cast<CatchEndPadInst>(TI)) {
- auto *NewCEP = CatchEndPadInst::Create(CEP->getContext(), nullptr,
- CEP);
- NewCEP->takeName(CEP);
- NewCEP->setDebugLoc(CEP->getDebugLoc());
- CEP->eraseFromParent();
- } else if (auto *TPI = dyn_cast<TerminatePadInst>(TI)) {
- SmallVector<Value *, 3> TerminatePadArgs;
- for (Value *Operand : TPI->arg_operands())
- TerminatePadArgs.push_back(Operand);
- auto *NewTPI = TerminatePadInst::Create(TPI->getContext(), nullptr,
- TerminatePadArgs, TPI);
- NewTPI->takeName(TPI);
- NewTPI->setDebugLoc(TPI->getDebugLoc());
- TPI->eraseFromParent();
- } else {
- llvm_unreachable("Unexpected predecessor to cleanup pad.");
- }
- }
+ removeUnwindEdge(PredBB);
} else {
- // If the predecessor did not terminate with an invoke instruction, it
- // must be some variety of EH pad.
TerminatorInst *TI = PredBB->getTerminator();
- // FIXME: Introducing an EH terminator base class would simplify this.
- if (auto *II = dyn_cast<InvokeInst>(TI))
- II->setUnwindDest(UnwindDest);
- else if (auto *CRI = dyn_cast<CleanupReturnInst>(TI))
- CRI->setUnwindDest(UnwindDest);
- else if (auto *CEP = dyn_cast<CatchEndPadInst>(TI))
- CEP->setUnwindDest(UnwindDest);
- else if (auto *TPI = dyn_cast<TerminatePadInst>(TI))
- TPI->setUnwindDest(UnwindDest);
- else
- llvm_unreachable("Unexpected predecessor to cleanup pad.");
+ TI->replaceUsesOfWith(BB, UnwindDest);
}
}
--i; --e;
Changed = true;
}
- } else if (InvokeInst *II = dyn_cast<InvokeInst>(TI)) {
- if (II->getUnwindDest() == BB) {
- // Convert the invoke to a call instruction. This would be a good
- // place to note that the call does not throw though.
- BranchInst *BI = Builder.CreateBr(II->getNormalDest());
- II->removeFromParent(); // Take out of symbol table
-
- // Insert the call now...
- SmallVector<Value*, 8> Args(II->op_begin(), II->op_end()-3);
- Builder.SetInsertPoint(BI);
- CallInst *CI = Builder.CreateCall(II->getCalledValue(),
- Args, II->getName());
- CI->setCallingConv(II->getCallingConv());
- CI->setAttributes(II->getAttributes());
- // If the invoke produced a value, the call does now instead.
- II->replaceAllUsesWith(CI);
- delete II;
- Changed = true;
- }
+ } else if ((isa<InvokeInst>(TI) &&
+ cast<InvokeInst>(TI)->getUnwindDest() == BB) ||
+ isa<CatchEndPadInst>(TI) || isa<TerminatePadInst>(TI)) {
+ removeUnwindEdge(TI->getParent());
+ Changed = true;
+ } else if (isa<CleanupReturnInst>(TI) || isa<CleanupEndPadInst>(TI) ||
+ isa<CatchReturnInst>(TI)) {
+ new UnreachableInst(TI->getContext(), TI);
+ TI->eraseFromParent();
+ Changed = true;
}
+ // TODO: If TI is a CatchPadInst, then (BB must be its normal dest and)
+ // we can eliminate it, redirecting its preds to its unwind successor,
+ // or to the next outer handler if the removed catch is the last for its
+ // catchendpad.
}
// If this block is now dead, remove it.
--- /dev/null
+; RUN: opt -S -simplifycfg < %s | FileCheck %s
+
+declare void @Personality()
+declare void @f()
+
+; CHECK-LABEL: define void @test1()
+define void @test1() personality i8* bitcast (void ()* @Personality to i8*) {
+entry:
+ ; CHECK: call void @f()
+ invoke void @f()
+ to label %exit unwind label %unreachable.unwind
+exit:
+ ret void
+unreachable.unwind:
+ cleanuppad []
+ unreachable
+}
+
+; CHECK-LABEL: define void @test2()
+define void @test2() personality i8* bitcast (void ()* @Personality to i8*) {
+entry:
+ invoke void @f()
+ to label %exit unwind label %catch.pad
+catch.pad:
+ ; CHECK: catchpad []
+ ; CHECK-NEXT: to label %catch.body unwind label %catch.end
+ %catch = catchpad []
+ to label %catch.body unwind label %catch.end
+catch.body:
+ ; CHECK: catch.body:
+ ; CHECK-NEXT: call void @f()
+ ; CHECK-NEXT: unreachable
+ call void @f()
+ catchret %catch to label %unreachable
+catch.end:
+ ; CHECK: catch.end:
+ ; CHECK-NEXT: catchendpad unwind to caller
+ catchendpad unwind label %unreachable.unwind
+exit:
+ ret void
+unreachable.unwind:
+ cleanuppad []
+ unreachable
+unreachable:
+ unreachable
+}
+
+; CHECK-LABEL: define void @test3()
+define void @test3() personality i8* bitcast (void ()* @Personality to i8*) {
+entry:
+ invoke void @f()
+ to label %exit unwind label %cleanup.pad
+cleanup.pad:
+ ; CHECK: %cleanup = cleanuppad []
+ ; CHECK-NEXT: call void @f()
+ ; CHECK-NEXT: unreachable
+ %cleanup = cleanuppad []
+ invoke void @f()
+ to label %cleanup.ret unwind label %cleanup.end
+cleanup.ret:
+ ; This cleanupret should be rewritten to unreachable,
+ ; and merged into the pred block.
+ cleanupret %cleanup unwind label %unreachable.unwind
+cleanup.end:
+ ; This cleanupendpad should be rewritten to unreachable,
+ ; causing the invoke to be rewritten to a call.
+ cleanupendpad %cleanup unwind label %unreachable.unwind
+exit:
+ ret void
+unreachable.unwind:
+ cleanuppad []
+ unreachable
+}
+
+; CHECK-LABEL: define void @test4()
+define void @test4() personality i8* bitcast (void ()* @Personality to i8*) {
+entry:
+ invoke void @f()
+ to label %exit unwind label %terminate.pad
+terminate.pad:
+ ; CHECK: terminatepad [] unwind to caller
+ terminatepad [] unwind label %unreachable.unwind
+exit:
+ ret void
+unreachable.unwind:
+ cleanuppad []
+ unreachable
+}