//===- DomTreeUpdaterTest.cpp - DomTreeUpdater unit tests -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Analysis/DomTreeUpdater.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" #include using namespace llvm; static std::unique_ptr makeLLVMModule(LLVMContext &Context, StringRef ModuleStr) { SMDiagnostic Err; std::unique_ptr M = parseAssemblyString(ModuleStr, Err, Context); assert(M && "Bad LLVM IR?"); return M; } TEST(DomTreeUpdater, EagerUpdateBasicOperations) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f(i32 %i, i32 *%p) { bb0: store i32 %i, i32 *%p switch i32 %i, label %bb1 [ i32 1, label %bb2 i32 2, label %bb3 ] bb1: ret i32 1 bb2: ret i32 2 bb3: ret i32 3 })"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DomTreeUpdater. DominatorTree DT(*F); PostDominatorTree PDT(*F); DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Eager); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_TRUE(DTU.hasPostDomTree()); ASSERT_TRUE(DTU.isEager()); ASSERT_FALSE(DTU.isLazy()); ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.getPostDomTree().verify()); ASSERT_FALSE(DTU.hasPendingUpdates()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; BasicBlock *BB2 = &*FI++; BasicBlock *BB3 = &*FI++; SwitchInst *SI = dyn_cast(BB0->getTerminator()); ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst."; DTU.applyUpdatesPermissive( {{DominatorTree::Insert, BB0, BB0}, {DominatorTree::Delete, BB0, BB0}}); ASSERT_FALSE(DTU.hasPendingUpdates()); // Delete edge bb0 -> bb3 and push the update twice to verify duplicate // entries are discarded. std::vector Updates; Updates.reserve(4); Updates.push_back({DominatorTree::Delete, BB0, BB3}); Updates.push_back({DominatorTree::Delete, BB0, BB3}); // Invalid Insert: no edge bb1 -> bb2 after change to bb0. Updates.push_back({DominatorTree::Insert, BB1, BB2}); // Invalid Delete: edge exists bb0 -> bb1 after change to bb0. Updates.push_back({DominatorTree::Delete, BB0, BB1}); // CFG Change: remove edge bb0 -> bb3. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u); BB3->removePredecessor(BB0); for (auto i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { if (i->getCaseSuccessor() == BB3) { SI->removeCase(i); break; } } EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u); // Deletion of a BasicBlock is an immediate event. We remove all uses to the // contained Instructions and change the Terminator to "unreachable" when // queued for deletion. ASSERT_FALSE(isa(BB3->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB3)); DTU.applyUpdatesPermissive(Updates); ASSERT_FALSE(DTU.hasPendingUpdates()); // Invalid Insert: no edge bb1 -> bb2 after change to bb0. // Invalid Delete: edge exists bb0 -> bb1 after change to bb0. DTU.applyUpdatesPermissive( {{DominatorTree::Insert, BB1, BB2}, {DominatorTree::Delete, BB0, BB1}}); // DTU working with Eager UpdateStrategy does not need to flush. ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); // Test callback utils. ASSERT_EQ(BB3->getParent(), F); DTU.callbackDeleteBB(BB3, [&F](BasicBlock *BB) { ASSERT_NE(BB->getParent(), F); }); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); ASSERT_FALSE(DTU.hasPendingUpdates()); // Unnecessary flush() test DTU.flush(); EXPECT_TRUE(DT.verify()); EXPECT_TRUE(PDT.verify()); // Remove all case branch to BB2 to test Eager recalculation. // Code section from llvm::ConstantFoldTerminator for (auto i = SI->case_begin(), e = SI->case_end(); i != e;) { if (i->getCaseSuccessor() == BB2) { // Remove this entry. BB2->removePredecessor(BB0); i = SI->removeCase(i); e = SI->case_end(); } else ++i; } ASSERT_FALSE(DT.verify()); ASSERT_FALSE(PDT.verify()); DTU.recalculate(*F); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); } TEST(DomTreeUpdater, EagerUpdateReplaceEntryBB) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f() { bb0: br label %bb1 bb1: ret i32 1 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DominatorTree DT(*F); PostDominatorTree PDT(*F); DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Eager); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_TRUE(DTU.hasPostDomTree()); ASSERT_TRUE(DTU.isEager()); ASSERT_FALSE(DTU.isLazy()); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; // Add a block as the new function entry BB. We also link it to BB0. BasicBlock *NewEntry = BasicBlock::Create(F->getContext(), "new_entry", F, BB0); BranchInst::Create(BB0, NewEntry); EXPECT_EQ(F->begin()->getName(), NewEntry->getName()); EXPECT_TRUE(&F->getEntryBlock() == NewEntry); DTU.applyUpdates({{DominatorTree::Insert, NewEntry, BB0}}); // Changing the Entry BB requires a full recalculation of DomTree. DTU.recalculate(*F); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); // CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1. EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u); NewEntry->getTerminator()->eraseFromParent(); BranchInst::Create(BB1, NewEntry); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); // Update the DTU. At this point bb0 now has no predecessors but is still a // Child of F. DTU.applyUpdates({{DominatorTree::Delete, NewEntry, BB0}, {DominatorTree::Insert, NewEntry, BB1}}); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); // Now remove bb0 from F. ASSERT_FALSE(isa(BB0->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB0)); DTU.deleteBB(BB0); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); } TEST(DomTreeUpdater, LazyUpdateDTBasicOperations) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f(i32 %i, i32 *%p) { bb0: store i32 %i, i32 *%p switch i32 %i, label %bb1 [ i32 0, label %bb2 i32 1, label %bb2 i32 2, label %bb3 ] bb1: ret i32 1 bb2: ret i32 2 bb3: ret i32 3 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DominatorTree DT(*F); PostDominatorTree *PDT = nullptr; DomTreeUpdater DTU(&DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_FALSE(DTU.hasPostDomTree()); ASSERT_FALSE(DTU.isEager()); ASSERT_TRUE(DTU.isLazy()); ASSERT_TRUE(DTU.getDomTree().verify()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; BasicBlock *BB2 = &*FI++; BasicBlock *BB3 = &*FI++; // Test discards of self-domination update. DTU.applyUpdatesPermissive({{DominatorTree::Insert, BB0, BB0}}); ASSERT_FALSE(DTU.hasPendingDomTreeUpdates()); // Delete edge bb0 -> bb3 and push the update twice to verify duplicate // entries are discarded. std::vector Updates; Updates.reserve(4); Updates.push_back({DominatorTree::Delete, BB0, BB3}); Updates.push_back({DominatorTree::Delete, BB0, BB3}); // Invalid Insert: no edge bb1 -> bb2 after change to bb0. Updates.push_back({DominatorTree::Insert, BB1, BB2}); // Invalid Delete: edge exists bb0 -> bb1 after change to bb0. Updates.push_back({DominatorTree::Delete, BB0, BB1}); // CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u); BB0->getTerminator()->eraseFromParent(); BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u); // Verify. Updates to DTU must be applied *after* all changes to the CFG // (including block deletion). DTU.applyUpdatesPermissive(Updates); ASSERT_TRUE(DTU.getDomTree().verify()); // Deletion of a BasicBlock is an immediate event. We remove all uses to the // contained Instructions and change the Terminator to "unreachable" when // queued for deletion. Its parent is still F until all the pending updates // are applied to all trees held by the DomTreeUpdater (DomTree/PostDomTree). // We don't defer this action because it can cause problems for other // transforms or analysis as it's part of the actual CFG. We only defer // updates to the DominatorTrees. This code will crash if it is placed before // the BranchInst::Create() call above. After a deletion of a BasicBlock. Only // an explicit flush event can trigger the flushing of deleteBBs. Because some // passes using Lazy UpdateStrategy rely on this behavior. ASSERT_FALSE(isa(BB3->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB3)); EXPECT_FALSE(DTU.hasPendingDeletedBB()); DTU.deleteBB(BB3); EXPECT_TRUE(DTU.isBBPendingDeletion(BB3)); EXPECT_TRUE(DTU.hasPendingDeletedBB()); ASSERT_TRUE(isa(BB3->getTerminator())); EXPECT_EQ(BB3->getParent(), F); DTU.recalculate(*F); EXPECT_FALSE(DTU.hasPendingDeletedBB()); } TEST(DomTreeUpdater, LazyUpdateDTInheritedPreds) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f(i32 %i, i32 *%p) { bb0: store i32 %i, i32 *%p switch i32 %i, label %bb1 [ i32 2, label %bb2 i32 3, label %bb3 ] bb1: br label %bb3 bb2: br label %bb3 bb3: ret i32 3 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DominatorTree DT(*F); PostDominatorTree *PDT = nullptr; DomTreeUpdater DTU(&DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_FALSE(DTU.hasPostDomTree()); ASSERT_FALSE(DTU.isEager()); ASSERT_TRUE(DTU.isLazy()); ASSERT_TRUE(DTU.getDomTree().verify()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; BasicBlock *BB2 = &*FI++; BasicBlock *BB3 = &*FI++; // There are several CFG locations where we have: // // pred1..predN // | | // +> curr <+ converted into: pred1..predN curr // | | | // v +> succ <+ // succ // // There is a specific shape of this we have to be careful of: // // pred1..predN // || | // |+> curr <+ converted into: pred1..predN curr // | | | | // | v +> succ <+ // +-> succ // // While the final CFG form is functionally identical the updates to // DTU are not. In the first case we must have // DTU.applyUpdates({{DominatorTree::Insert, Pred1, Succ}}) while in // the latter case we must *NOT* have // DTU.applyUpdates({{DominatorTree::Insert, Pred1, Succ}}). // CFG Change: bb0 now only has bb0 -> bb1 and bb0 -> bb3. We are preparing to // remove bb2. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u); BB0->getTerminator()->eraseFromParent(); BranchInst::Create(BB1, BB3, ConstantInt::getTrue(F->getContext()), BB0); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u); // Test callback utils. std::vector BasicBlocks; BasicBlocks.push_back(BB1); BasicBlocks.push_back(BB2); auto Eraser = [&](BasicBlock *BB) { BasicBlocks.erase( std::remove_if(BasicBlocks.begin(), BasicBlocks.end(), [&](const BasicBlock *i) { return i == BB; }), BasicBlocks.end()); }; ASSERT_EQ(BasicBlocks.size(), static_cast(2)); // Remove bb2 from F. This has to happen before the call to // applyUpdates() for DTU to detect there is no longer an edge between // bb2 -> bb3. The deleteBB() method converts bb2's TI into "unreachable". ASSERT_FALSE(isa(BB2->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB2)); DTU.callbackDeleteBB(BB2, Eraser); EXPECT_TRUE(DTU.isBBPendingDeletion(BB2)); ASSERT_TRUE(isa(BB2->getTerminator())); EXPECT_EQ(BB2->getParent(), F); // Queue up the DTU updates. std::vector Updates; Updates.reserve(4); Updates.push_back({DominatorTree::Delete, BB0, BB2}); Updates.push_back({DominatorTree::Delete, BB2, BB3}); // Handle the specific shape case next. // CFG Change: bb0 now only branches to bb3. We are preparing to remove bb1. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u); BB0->getTerminator()->eraseFromParent(); BranchInst::Create(BB3, BB0); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); // Remove bb1 from F. This has to happen before the call to // applyUpdates() for DTU to detect there is no longer an edge between // bb1 -> bb3. The deleteBB() method converts bb1's TI into "unreachable". ASSERT_FALSE(isa(BB1->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB1)); DTU.callbackDeleteBB(BB1, Eraser); EXPECT_TRUE(DTU.isBBPendingDeletion(BB1)); ASSERT_TRUE(isa(BB1->getTerminator())); EXPECT_EQ(BB1->getParent(), F); // Update the DTU. In this case we don't submit {DominatorTree::Insert, BB0, // BB3} because the edge previously existed at the start of this test when DT // was first created. Updates.push_back({DominatorTree::Delete, BB0, BB1}); Updates.push_back({DominatorTree::Delete, BB1, BB3}); // Verify everything. DTU.applyUpdatesPermissive(Updates); ASSERT_EQ(BasicBlocks.size(), static_cast(2)); DTU.flush(); ASSERT_EQ(BasicBlocks.size(), static_cast(0)); ASSERT_TRUE(DT.verify()); } TEST(DomTreeUpdater, LazyUpdateBasicOperations) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f(i32 %i, i32 *%p) { bb0: store i32 %i, i32 *%p switch i32 %i, label %bb1 [ i32 0, label %bb2 i32 1, label %bb2 i32 2, label %bb3 ] bb1: ret i32 1 bb2: ret i32 2 bb3: ret i32 3 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DominatorTree DT(*F); PostDominatorTree PDT(*F); DomTreeUpdater DTU(&DT, &PDT, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_TRUE(DTU.hasPostDomTree()); ASSERT_FALSE(DTU.isEager()); ASSERT_TRUE(DTU.isLazy()); ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.getPostDomTree().verify()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; BasicBlock *BB2 = &*FI++; BasicBlock *BB3 = &*FI++; // Test discards of self-domination update. DTU.applyUpdates({{DominatorTree::Delete, BB0, BB0}}); // Delete edge bb0 -> bb3 and push the update twice to verify duplicate // entries are discarded. std::vector Updates; Updates.reserve(4); Updates.push_back({DominatorTree::Delete, BB0, BB3}); Updates.push_back({DominatorTree::Delete, BB0, BB3}); // Unnecessary Insert: no edge bb1 -> bb2 after change to bb0. Updates.push_back({DominatorTree::Insert, BB1, BB2}); // Unnecessary Delete: edge exists bb0 -> bb1 after change to bb0. Updates.push_back({DominatorTree::Delete, BB0, BB1}); // CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u); BB0->getTerminator()->eraseFromParent(); BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u); // Deletion of a BasicBlock is an immediate event. We remove all uses to the // contained Instructions and change the Terminator to "unreachable" when // queued for deletion. Its parent is still F until DTU.flushDomTree is // called. We don't defer this action because it can cause problems for other // transforms or analysis as it's part of the actual CFG. We only defer // updates to the DominatorTree. This code will crash if it is placed before // the BranchInst::Create() call above. bool CallbackFlag = false; ASSERT_FALSE(isa(BB3->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB3)); DTU.callbackDeleteBB(BB3, [&](BasicBlock *) { CallbackFlag = true; }); EXPECT_TRUE(DTU.isBBPendingDeletion(BB3)); ASSERT_TRUE(isa(BB3->getTerminator())); EXPECT_EQ(BB3->getParent(), F); // Verify. Updates to DTU must be applied *after* all changes to the CFG // (including block deletion). DTU.applyUpdatesPermissive(Updates); ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.hasPendingUpdates()); ASSERT_TRUE(DTU.hasPendingPostDomTreeUpdates()); ASSERT_FALSE(DTU.hasPendingDomTreeUpdates()); ASSERT_TRUE(DTU.hasPendingDeletedBB()); ASSERT_TRUE(DTU.getPostDomTree().verify()); ASSERT_FALSE(DTU.hasPendingUpdates()); ASSERT_FALSE(DTU.hasPendingPostDomTreeUpdates()); ASSERT_FALSE(DTU.hasPendingDomTreeUpdates()); ASSERT_FALSE(DTU.hasPendingDeletedBB()); ASSERT_EQ(CallbackFlag, true); } TEST(DomTreeUpdater, LazyUpdateReplaceEntryBB) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f() { bb0: br label %bb1 bb1: ret i32 1 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DominatorTree DT(*F); PostDominatorTree PDT(*F); DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_TRUE(DTU.hasPostDomTree()); ASSERT_FALSE(DTU.isEager()); ASSERT_TRUE(DTU.isLazy()); ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.getPostDomTree().verify()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; // Add a block as the new function entry BB. We also link it to BB0. BasicBlock *NewEntry = BasicBlock::Create(F->getContext(), "new_entry", F, BB0); BranchInst::Create(BB0, NewEntry); EXPECT_EQ(F->begin()->getName(), NewEntry->getName()); EXPECT_TRUE(&F->getEntryBlock() == NewEntry); // Insert the new edge between new_entry -> bb0. Without this the // recalculate() call below will not actually recalculate the DT as there // are no changes pending and no blocks deleted. DTU.applyUpdates({{DominatorTree::Insert, NewEntry, BB0}}); // Changing the Entry BB requires a full recalculation. DTU.recalculate(*F); ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.getPostDomTree().verify()); // CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1. EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u); NewEntry->getTerminator()->eraseFromParent(); BranchInst::Create(BB1, NewEntry); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); // Update the DTU. At this point bb0 now has no predecessors but is still a // Child of F. DTU.applyUpdates({{DominatorTree::Delete, NewEntry, BB0}, {DominatorTree::Insert, NewEntry, BB1}}); DTU.flush(); ASSERT_TRUE(DT.verify()); ASSERT_TRUE(PDT.verify()); // Now remove bb0 from F. ASSERT_FALSE(isa(BB0->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB0)); DTU.deleteBB(BB0); EXPECT_TRUE(DTU.isBBPendingDeletion(BB0)); ASSERT_TRUE(isa(BB0->getTerminator())); EXPECT_EQ(BB0->getParent(), F); // Perform a full recalculation of the DTU. It is not necessary here but we // do this to test the case when there are no pending DT updates but there are // pending deleted BBs. ASSERT_TRUE(DTU.hasPendingDeletedBB()); DTU.recalculate(*F); ASSERT_FALSE(DTU.hasPendingDeletedBB()); } TEST(DomTreeUpdater, LazyUpdateStepTest) { // This test focus on testing a DTU holding both trees applying multiple // updates and DT/PDT not flushed together. StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f(i32 %i, i32 *%p) { bb0: store i32 %i, i32 *%p switch i32 %i, label %bb1 [ i32 0, label %bb1 i32 1, label %bb2 i32 2, label %bb3 i32 3, label %bb1 ] bb1: ret i32 1 bb2: ret i32 2 bb3: ret i32 3 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DomTreeUpdater. DominatorTree DT(*F); PostDominatorTree PDT(*F); DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_TRUE(DTU.hasDomTree()); ASSERT_TRUE(DTU.hasPostDomTree()); ASSERT_FALSE(DTU.isEager()); ASSERT_TRUE(DTU.isLazy()); ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.getPostDomTree().verify()); ASSERT_FALSE(DTU.hasPendingUpdates()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; FI++; BasicBlock *BB2 = &*FI++; BasicBlock *BB3 = &*FI++; SwitchInst *SI = dyn_cast(BB0->getTerminator()); ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst."; // Delete edge bb0 -> bb3. std::vector Updates; Updates.reserve(1); Updates.push_back({DominatorTree::Delete, BB0, BB3}); // CFG Change: remove edge bb0 -> bb3. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 5u); BB3->removePredecessor(BB0); for (auto i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { if (i->getCaseIndex() == 2) { SI->removeCase(i); break; } } EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u); // Deletion of a BasicBlock is an immediate event. We remove all uses to the // contained Instructions and change the Terminator to "unreachable" when // queued for deletion. ASSERT_FALSE(isa(BB3->getTerminator())); EXPECT_FALSE(DTU.isBBPendingDeletion(BB3)); DTU.applyUpdates(Updates); // Only flush DomTree. ASSERT_TRUE(DTU.getDomTree().verify()); ASSERT_TRUE(DTU.hasPendingPostDomTreeUpdates()); ASSERT_FALSE(DTU.hasPendingDomTreeUpdates()); ASSERT_EQ(BB3->getParent(), F); DTU.deleteBB(BB3); Updates.clear(); // Remove all case branch to BB2 to test Eager recalculation. // Code section from llvm::ConstantFoldTerminator for (auto i = SI->case_begin(), e = SI->case_end(); i != e;) { if (i->getCaseSuccessor() == BB2) { // Remove this entry. BB2->removePredecessor(BB0); i = SI->removeCase(i); e = SI->case_end(); Updates.push_back({DominatorTree::Delete, BB0, BB2}); } else ++i; } DTU.applyUpdatesPermissive(Updates); // flush PostDomTree ASSERT_TRUE(DTU.getPostDomTree().verify()); ASSERT_FALSE(DTU.hasPendingPostDomTreeUpdates()); ASSERT_TRUE(DTU.hasPendingDomTreeUpdates()); // flush both trees DTU.flush(); ASSERT_TRUE(DT.verify()); } TEST(DomTreeUpdater, NoTreeTest) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f() { bb0: ret i32 0 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DomTreeUpdater DTU(nullptr, nullptr, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_FALSE(DTU.hasDomTree()); ASSERT_FALSE(DTU.hasPostDomTree()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; // Test whether PendingDeletedBB is flushed after the recalculation. DTU.deleteBB(BB0); ASSERT_TRUE(DTU.hasPendingDeletedBB()); DTU.recalculate(*F); ASSERT_FALSE(DTU.hasPendingDeletedBB()); } TEST(DomTreeUpdater, LazyUpdateDeduplicationTest) { StringRef FuncName = "f"; StringRef ModuleString = R"( define i32 @f() { bb0: br label %bb1 bb1: ret i32 1 bb2: ret i32 1 } )"; // Make the module. LLVMContext Context; std::unique_ptr M = makeLLVMModule(Context, ModuleString); Function *F = M->getFunction(FuncName); // Make the DTU. DominatorTree DT(*F); DomTreeUpdater DTU(&DT, nullptr, DomTreeUpdater::UpdateStrategy::Lazy); ASSERT_TRUE(DTU.getDomTree().verify()); Function::iterator FI = F->begin(); BasicBlock *BB0 = &*FI++; BasicBlock *BB1 = &*FI++; BasicBlock *BB2 = &*FI++; // CFG Change: remove bb0 -> bb1 and add back bb0 -> bb1. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); BB0->getTerminator()->eraseFromParent(); BranchInst::Create(BB1, BB0); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); // Update the DTU and simulate duplicates. DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1}, {DominatorTree::Delete, BB0, BB1}, {DominatorTree::Insert, BB0, BB1}, {DominatorTree::Insert, BB0, BB1}, {DominatorTree::Insert, BB0, BB1}}); // The above operations result in a no-op. ASSERT_FALSE(DTU.hasPendingUpdates()); // Update the DTU. Simulate an invalid update. DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1}}); ASSERT_FALSE(DTU.hasPendingUpdates()); // CFG Change: remove bb0 -> bb1. EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); BB0->getTerminator()->eraseFromParent(); // Update the DTU and simulate invalid updates. DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1}, {DominatorTree::Insert, BB0, BB1}, {DominatorTree::Delete, BB0, BB1}, {DominatorTree::Insert, BB0, BB1}, {DominatorTree::Insert, BB0, BB1}}); ASSERT_TRUE(DTU.hasPendingUpdates()); // CFG Change: add bb0 -> bb2. BranchInst::Create(BB2, BB0); EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u); DTU.applyUpdates({{DominatorTree::Insert, BB0, BB2}}); ASSERT_TRUE(DTU.getDomTree().verify()); }