795 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			795 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
//===- 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 <algorithm>
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,
 | 
						|
                                              StringRef ModuleStr) {
 | 
						|
  SMDiagnostic Err;
 | 
						|
  std::unique_ptr<Module> 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<Module> 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<SwitchInst>(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<DominatorTree::UpdateType> 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<UnreachableInst>(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<Module> 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<UnreachableInst>(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<Module> 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<DominatorTree::UpdateType> 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<UnreachableInst>(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<UnreachableInst>(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<Module> 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<BasicBlock *> 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<size_t>(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<UnreachableInst>(BB2->getTerminator()));
 | 
						|
  EXPECT_FALSE(DTU.isBBPendingDeletion(BB2));
 | 
						|
  DTU.callbackDeleteBB(BB2, Eraser);
 | 
						|
  EXPECT_TRUE(DTU.isBBPendingDeletion(BB2));
 | 
						|
  ASSERT_TRUE(isa<UnreachableInst>(BB2->getTerminator()));
 | 
						|
  EXPECT_EQ(BB2->getParent(), F);
 | 
						|
 | 
						|
  // Queue up the DTU updates.
 | 
						|
  std::vector<DominatorTree::UpdateType> 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<UnreachableInst>(BB1->getTerminator()));
 | 
						|
  EXPECT_FALSE(DTU.isBBPendingDeletion(BB1));
 | 
						|
  DTU.callbackDeleteBB(BB1, Eraser);
 | 
						|
  EXPECT_TRUE(DTU.isBBPendingDeletion(BB1));
 | 
						|
  ASSERT_TRUE(isa<UnreachableInst>(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<size_t>(2));
 | 
						|
  DTU.flush();
 | 
						|
  ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(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<Module> 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<DominatorTree::UpdateType> 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<UnreachableInst>(BB3->getTerminator()));
 | 
						|
  EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));
 | 
						|
  DTU.callbackDeleteBB(BB3, [&](BasicBlock *) { CallbackFlag = true; });
 | 
						|
  EXPECT_TRUE(DTU.isBBPendingDeletion(BB3));
 | 
						|
  ASSERT_TRUE(isa<UnreachableInst>(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<Module> 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<UnreachableInst>(BB0->getTerminator()));
 | 
						|
  EXPECT_FALSE(DTU.isBBPendingDeletion(BB0));
 | 
						|
  DTU.deleteBB(BB0);
 | 
						|
  EXPECT_TRUE(DTU.isBBPendingDeletion(BB0));
 | 
						|
  ASSERT_TRUE(isa<UnreachableInst>(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<Module> 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<SwitchInst>(BB0->getTerminator());
 | 
						|
  ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst.";
 | 
						|
 | 
						|
  // Delete edge bb0 -> bb3.
 | 
						|
  std::vector<DominatorTree::UpdateType> 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<UnreachableInst>(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<Module> 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<Module> 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());
 | 
						|
}
 |