Index: llvm/include/llvm/IR/BasicBlock.h =================================================================== --- llvm/include/llvm/IR/BasicBlock.h +++ llvm/include/llvm/IR/BasicBlock.h @@ -33,6 +33,7 @@ namespace llvm { class CallInst; +class DominatorTree; class Function; class LandingPadInst; class LLVMContext; @@ -340,6 +341,29 @@ /// except operator delete. void dropAllReferences(); + /// \brief Remove the edge between this and To. It does not alter the contents + /// of To, only the terminating instruction of this. To must be a successor of + /// this or an assert will be raised. + /// + /// If DT is valid the routine will preserve dominance. + /// + /// Invoke terminator instructions will raise an assert unless removeInvoke is + /// set to true. Invoke instructions cannot be reduced from two successors to + /// one: the entire instruction (and two edges) must be removed at the same + /// time. + /// + /// Note: it is not safe to assume one call to removeEdge decrements + /// this->getTerminator()->getNumSuccessors() by one. Several terminator + /// instructions permit branches to the same basic block and count each jump + /// as a unique successor. The removeEdge routine removes *all* instances of + /// To from the terminating instruction in this. + /// + /// Note: the routine may change the terminator instruction type and may also + /// create a new basic block. These changes are dependant upon on the type of + /// the terminator instruction and the requested edge to remove. + void removeEdge(BasicBlock *To, DominatorTree *DT = nullptr, + bool removeInvoke = false); + /// \brief Notify the BasicBlock that the predecessor \p Pred is no longer /// able to reach it. /// Index: llvm/lib/IR/BasicBlock.cpp =================================================================== --- llvm/lib/IR/BasicBlock.cpp +++ llvm/lib/IR/BasicBlock.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" @@ -267,6 +268,166 @@ return make_range(dyn_cast(&front()), nullptr); } +void BasicBlock::removeEdge(BasicBlock *To, DominatorTree *DT, + bool removeInvoke) { + // Terminator instruction types: BranchInst(1/2), + // SwitchInst(getNumOperands()/2), IndirectBrInst(getNumOperands()-1), + // InvokeInst(2), CleanupReturnInst(1/0), CatchReturnInst(1), + // CatchSwitchInst(getNumOperands()-1) + // + // ResumeInst(0), UnreachableInst(0) are not checked because they never + // have successors. + assert(To && "To cannot be nullptr."); + assert(find(succ_begin(this), succ_end(this), To) != succ_end(this) && + "There exists no edge between this and To."); + + SmallVector Updates; + TerminatorInst *TI = getTerminator(); + + if (CleanupReturnInst *CI = dyn_cast(TI)) { + // To is the only successor. + new UnreachableInst(getContext(), this); + CI->eraseFromParent(); + } else if (CatchReturnInst *CI = dyn_cast(TI)) { + // To is the only successor. + new UnreachableInst(getContext(), this); + CI->eraseFromParent(); + } else if (InvokeInst *II = dyn_cast(TI)) { + assert(removeInvoke && "Invokes must have two edges removed at once."); + if (DT) { + BasicBlock *NormalDestBB = II->getNormalDest(); + BasicBlock *UnwindDestBB = II->getUnwindDest(); + if (this != NormalDestBB) + Updates.push_back({DominatorTree::Delete, this, NormalDestBB}); + if (this != UnwindDestBB && NormalDestBB != UnwindDestBB) + Updates.push_back({DominatorTree::Delete, this, UnwindDestBB}); + } + new UnreachableInst(getContext(), this); + II->eraseFromParent(); + } else if (BranchInst *BI = dyn_cast(TI)) { + if (BI->isConditional()) { + BasicBlock *L = BI->getSuccessor(0); + BasicBlock *R = BI->getSuccessor(1); + if (To == L && To == R) { + new UnreachableInst(getContext(), this); + } else if (To == L) { + BranchInst::Create(R, BI); + } else { + BranchInst::Create(L, BI); + } + } else { + new UnreachableInst(getContext(), this); + } + BI->eraseFromParent(); + } else if (IndirectBrInst *II = dyn_cast(TI)) { + // There are 1+ instances of To in the list of destinations. + bool RemovedDest; + do { + unsigned NumDests = II->getNumDestinations(); + if (NumDests == 1) { + if (II->getDestination(0) == To) { + // The only remaining destination is To. + new UnreachableInst(getContext(), this); + II->eraseFromParent(); + } + break; + } else { + RemovedDest = false; + for (unsigned I = 0; I < NumDests; ++I) + if (II->getDestination(I) == To) { + II->removeDestination(I); + RemovedDest = true; + break; + } + } + } while (RemovedDest); + } else if (SwitchInst *SI = dyn_cast(TI)) { + // To is a successor of one or more case statements (including the + // default case). The first task is to remove all non-default case + // statements that branch to To. + bool RemovedCase; + do { + RemovedCase = false; + for (auto I = SI->case_begin(), E = SI->case_end(); I != E; ++I) { + if (I == SI->case_default()) + continue; + if (I->getCaseSuccessor() == To) { + SI->removeCase(I); + RemovedCase = true; + break; + } + } + } while (RemovedCase); + // Now check the default case. + if (SI->getSuccessor(0) == To) { + if (SI->getNumSuccessors() == 1) { + // The only remaining successor is the default case. + new UnreachableInst(getContext(), this); + SI->eraseFromParent(); + } else { + // The SwitchInst has non-default cases remaining. Create a new + // BasicBlock, set it as the new default destination, and denote + // the new BasicBlock as unreachable. + BasicBlock *UnreachBB = BasicBlock::Create( + getContext(), Twine(getName()) + ".unreachable_switch_default", + getParent()); + SI->setDefaultDest(UnreachBB); + new UnreachableInst(UnreachBB->getContext(), UnreachBB); + if (DT) + Updates.push_back({DominatorTree::Insert, this, UnreachBB}); + } + } + if (DT && this != To) + Updates.push_back({DominatorTree::Delete, this, To}); + } else if (CatchSwitchInst *CI = dyn_cast(TI)) { + // To is a successor of one or more handlers and may also be the unwind + // destination. The first task is to remove all the handlers that branch + // to To. + bool RemovedHandler; + do { + RemovedHandler = false; + for (auto I = CI->handler_begin(), E = CI->handler_end(); I != E; ++I) + if (*I == To) { + CI->removeHandler(I); + RemovedHandler = true; + break; + } + } while (RemovedHandler); + // Now check the unwind destination. + if (CI->getUnwindDest() == To) { + if (CI->getNumHandlers() == 0) { + // The only remaining successor is the unwind destination. + new UnreachableInst(getContext(), this); + CI->eraseFromParent(); + } else { + // The CatchSwitchInst has one or more handlers. Create a new + // BasicBlock, set it as the new unwind destination, and denote + // the new BasicBlock as unreachable. + BasicBlock *UnreachBB = BasicBlock::Create( + getContext(), Twine(getName()) + ".unreachable_unwind_default", + getParent()); + CI->setUnwindDest(UnreachBB); + new UnreachableInst(UnreachBB->getContext(), UnreachBB); + if (DT) + Updates.push_back({DominatorTree::Insert, this, UnreachBB}); + } + } + if (DT && this != To) + Updates.push_back({DominatorTree::Delete, this, To}); + } else { + llvm_unreachable("Unexpected TerminatorInst"); + } + // Remove our edge from the dominator tree. If we used Updates above just + // apply them. Otherwise remove the edge between this and To provided it's not + // a loop back to itself. + if (DT) { + if (!Updates.empty()) + DT->applyUpdates(Updates); + else if (this != To) + DT->deleteEdge(this, To); + } +} + /// This method is used to notify a BasicBlock that the /// specified Predecessor of the block is no longer able to reach it. This is /// actually not used to update the Predecessor list, but is actually used to Index: llvm/unittests/IR/BasicBlockRemoveEdgeTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/IR/BasicBlockRemoveEdgeTest.cpp @@ -0,0 +1,846 @@ +//===- llvm/unittest/IR/BasicBlockRemoveEdgeTest.cpp - BBremoveEdge tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/NoFolder.h" +#include "llvm/IR/Verifier.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class BBremoveEdgeTest : public testing::Test { +protected: + void SetUp() override { + M.reset(new Module("MyModule", Ctx)); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), + /*isVarArg=*/false); + F = Function::Create(FTy, Function::ExternalLinkage, "MyFunc", M.get()); + for (int i = 0; i < 5; ++i) { + BasicBlock *B; + if (i == 0) { + B = BasicBlock::Create(Ctx, "entry", F); + } else { + B = BasicBlock::Create(Ctx, "bb" + std::to_string(i), F); + IRBuilder<> Builder(B); + Builder.CreateRetVoid(); + } + BB.push_back(B); + } + GV = new GlobalVariable(*M, Type::getFloatTy(Ctx), true, + GlobalValue::ExternalLinkage, nullptr); + DT = new DominatorTree(*F); + ParentPad = llvm::ConstantTokenNone::get(Ctx); + } + + void TearDown() override { + BB.clear(); + F = nullptr; + GV = nullptr; + DT = nullptr; + ParentPad = nullptr; + M.reset(); + } + + std::unique_ptr M; + std::vector BB; + LLVMContext Ctx; + Function *F; + DominatorTree *DT; + GlobalVariable *GV; + ConstantTokenNone *ParentPad; +}; + +TEST_F(BBremoveEdgeTest, CleanupReturn) { + IRBuilder<> Builder(BB[0]); + CleanupPadInst *CleanPI = Builder.CreateCleanupPad(ParentPad); + Builder.CreateCleanupRet(CleanPI, BB[1]); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchReturn) { + IRBuilder<> Builder(BB[0]); + CatchPadInst *CatchPI = Builder.CreateCatchPad(ParentPad, nullptr); + Builder.CreateCatchRet(CatchPI, BB[1]); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Invoke) { + IRBuilder<> Builder(BB[0]); + Builder.CreateInvoke(F, BB[1], BB[2]); + DT->recalculate(*F); // cannot insert two edges at once with insertEdge() + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[1], DT, /* removeInvoke */ true); + EXPECT_TRUE(DT->verify()); + // Both invoke successors are removed with the same call due to the atomicity + // of the instruction. We no longer have any successors after a single + // removeEdge() call. + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, ConditionalBranch1) { + // Both conditions connect to the same successor. + IRBuilder<> Builder(BB[0]); + Builder.CreateCondBr( + ConstantInt::get(IntegerType::get(M->getContext(), 1), 0), BB[1], BB[1]); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, ConditionalBranch2) { + // Remove the first successor then the second. + IRBuilder<> Builder(BB[0]); + Builder.CreateCondBr( + ConstantInt::get(IntegerType::get(M->getContext(), 1), 0), BB[1], BB[2]); + DT->recalculate(*F); // cannot insert two edges at once with insertEdge() + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BranchInst *BI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(BI, nullptr); + if (BI) + EXPECT_FALSE(BI->isConditional()); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, ConditionalBranch3) { + // Remove the second successor then the first. + IRBuilder<> Builder(BB[0]); + Builder.CreateCondBr( + ConstantInt::get(IntegerType::get(M->getContext(), 1), 0), BB[1], BB[2]); + DT->recalculate(*F); // cannot insert two edges at once with insertEdge() + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BranchInst *BI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(BI, nullptr); + if (BI) + EXPECT_FALSE(BI->isConditional()); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, UnconditionalBranch) { + IRBuilder<> Builder(BB[0]); + Builder.CreateBr(BB[1]); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, IndirectBranch1) { + // There is exactly one branch to remove. + IRBuilder<> Builder(BB[0]); + IndirectBrInst *IBI = Builder.CreateIndirectBr(F, 1); + IBI->addDestination(BB[1]); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, IndirectBranch2) { + // Remove only one successor (single instance). + IRBuilder<> Builder(BB[0]); + IndirectBrInst *IBI = Builder.CreateIndirectBr(F, 3); + IBI->addDestination(BB[1]); + DT->insertEdge(BB[0], BB[1]); + IBI->addDestination(BB[2]); + DT->insertEdge(BB[0], BB[2]); + IBI->addDestination(BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + IBI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(IBI, nullptr); +} + +TEST_F(BBremoveEdgeTest, IndirectBranch3) { + // Remove only one successor (multiple instances). + IRBuilder<> Builder(BB[0]); + IndirectBrInst *IBI = Builder.CreateIndirectBr(F, 5); + IBI->addDestination(BB[1]); + DT->insertEdge(BB[0], BB[1]); + IBI->addDestination(BB[2]); + IBI->addDestination(BB[2]); + DT->insertEdge(BB[0], BB[2]); + IBI->addDestination(BB[3]); + DT->insertEdge(BB[0], BB[3]); + IBI->addDestination(BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 5u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + IBI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(IBI, nullptr); +} + +TEST_F(BBremoveEdgeTest, IndirectBranch4) { + // Remove all successors. + IRBuilder<> Builder(BB[0]); + IndirectBrInst *IBI = Builder.CreateIndirectBr(F, 5); + IBI->addDestination(BB[1]); + DT->insertEdge(BB[0], BB[1]); + IBI->addDestination(BB[2]); + IBI->addDestination(BB[2]); + DT->insertEdge(BB[0], BB[2]); + IBI->addDestination(BB[3]); + DT->insertEdge(BB[0], BB[3]); + IBI->addDestination(BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 5u); + BB[0]->removeEdge(BB[3], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch1) { + // Switch only has a default case. + IRBuilder<> Builder(BB[0]); + Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch2) { + // Remove default case with non-default cases remaining. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + // We still have 3 successors: removeEdge() added a replacement block as the + // default destination to preserve the integrity of the instruction. + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch3) { + // Remove one non-default case, no non-defaults remaining. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[2]); + DT->insertEdge(BB[0], BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch4) { + // Remove multiple non-default cases, no non-defaults remaining. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch5) { + // Remove one non-default case with non-defaults remaining. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + + +TEST_F(BBremoveEdgeTest, Switch6) { + // Remove multiple non-default cases with non-defaults remaining. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[3]); + DT->insertEdge(BB[0], BB[3]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 4), BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 5), BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch7) { + // Remove default case and one non-default case (non-default cases remain). + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + // We have 3 successors: removeEdge() added a replacement block as the + // default destination to preserve the integrity of the instruction. We also + // removed a single non-default case (4 - (1 + 1) + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch8) { + // Remove default case and multiple non-default cases (non-default cases + // remain). + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 4), BB[3]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 5), BB[1]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + // We have 3 successors: removeEdge() added a replacement block as the + // default destination to preserve the integrity of the instruction. We also + // removed 3 non-default cases (6 - (1 + 3) + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch9) { + // Remove all cases, don't delete default case last. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 4), BB[3]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 5), BB[1]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + // We have 3 successors: removeEdge() added a replacement block as the + // default destination to preserve the integrity of the instruction. We also + // removed 3 non-default cases (6 - (1 + 3) + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[3], DT); + EXPECT_TRUE(DT->verify()); + // The previously-inserted default basic block is the only remaining + // successor. + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + SI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(SI, nullptr); +} + +TEST_F(BBremoveEdgeTest, Switch10) { + // Remove all cases and delete default case last. + IRBuilder<> Builder(BB[0]); + SwitchInst *SI = Builder.CreateSwitch( + ConstantInt::get(IntegerType::get(M->getContext(), 8), 0), BB[1]); + DT->insertEdge(BB[0], BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 1), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 2), BB[2]); + DT->insertEdge(BB[0], BB[2]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 3), BB[1]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 4), BB[3]); + SI->addCase(ConstantInt::get(IntegerType::get(M->getContext(), 8), 5), BB[1]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[3], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 5u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + // All remaining cases (default and non-default) were to BB[1]. We can safely + // remove the entire instruction without replacing the default case to a new + // BasicBlock. + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch1) { + // CatchSwitch only has unwind case. + IRBuilder<> Builder(BB[0]); + Builder.CreateCatchSwitch(ParentPad, BB[1], 0); + DT->insertEdge(BB[0], BB[1]); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch2) { + // Remove the unwind case with handlers remaining. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 2); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + // We have 3 successors: removeEdge() added a replacement block as the + // Unwind destination to preserve the integrity of the instruction + // (3 - 1 + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch3) { + // Remove one handler, no handlers remaining. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 1); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch4) { + // Remove multiple handlers, no handlers remaining. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 3); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[2]); + CI->addHandler(BB[2]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch5) { + // Remove one handler with handlers remaining. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 2); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch6) { + // Remove multiple handlers with handlers remaining. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 4); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + CI->addHandler(BB[2]); + CI->addHandler(BB[4]); + DT->insertEdge(BB[0], BB[4]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 5u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch7) { + // Remove unwind and one handler (some handlers remain). + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 3); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[1]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + // We have 3 successors: removeEdge() added a replacement block as the + // Unwind destination to preserve the integrity of the instruction. We also + // removed one handler (4 - (1 + 1) + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch8) { + // Remove multiple handlers and unwind (some handlers remain). + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 5); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + CI->addHandler(BB[1]); + CI->addHandler(BB[4]); + DT->insertEdge(BB[0], BB[4]); + CI->addHandler(BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + // We have 4 successors: removeEdge() added a replacement block as the + // Unwind destination to preserve the integrity of the instruction. We also + // removed two handlers (6 - (1 + 2) + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch9) { + // Remove all successors, don't delete unwind last. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 5); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + CI->addHandler(BB[1]); + CI->addHandler(BB[4]); + DT->insertEdge(BB[0], BB[4]); + CI->addHandler(BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + // We have 4 successors: removeEdge() added a replacement block as the + // Unwind destination to preserve the integrity of the instruction. We also + // removed two handlers (6 - (1 + 2) + 1). + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[3], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 2u); + BB[0]->removeEdge(BB[4], DT); + EXPECT_TRUE(DT->verify()); + // The previously-inserted unwind basic block is the only remaining + // successor. + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 1u); + CI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(CI, nullptr); +} + +TEST_F(BBremoveEdgeTest, CatchSwitch10) { + // Remove all successors and delete unwind last. + IRBuilder<> Builder(BB[0]); + CatchSwitchInst *CI = Builder.CreateCatchSwitch(ParentPad, BB[1], 5); + DT->insertEdge(BB[0], BB[1]); + CI->addHandler(BB[2]); + DT->insertEdge(BB[0], BB[2]); + CI->addHandler(BB[3]); + DT->insertEdge(BB[0], BB[3]); + CI->addHandler(BB[1]); + CI->addHandler(BB[4]); + DT->insertEdge(BB[0], BB[4]); + CI->addHandler(BB[1]); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[4])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 6u); + BB[0]->removeEdge(BB[4], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[3])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 5u); + BB[0]->removeEdge(BB[3], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[2])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 4u); + BB[0]->removeEdge(BB[2], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_TRUE(DT->properlyDominates(BB[0], BB[1])); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 3u); + BB[0]->removeEdge(BB[1], DT); + EXPECT_TRUE(DT->verify()); + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + // All remaining cases (unwind and handler) were to BB[1]. We can safely + // remove the entire instruction without replacing the unwind case to a new + // BasicBlock. + EXPECT_EQ(BB[0]->getTerminator()->getNumSuccessors(), 0u); + UnreachableInst *UI = dyn_cast(BB[0]->getTerminator()); + EXPECT_NE(UI, nullptr); +} + +} // namespace Index: llvm/unittests/IR/CMakeLists.txt =================================================================== --- llvm/unittests/IR/CMakeLists.txt +++ llvm/unittests/IR/CMakeLists.txt @@ -9,6 +9,7 @@ set(IRSources AsmWriterTest.cpp AttributesTest.cpp + BasicBlockRemoveEdgeTest.cpp BasicBlockTest.cpp CFGBuilder.cpp ConstantRangeTest.cpp