388 changes: 307 additions & 81 deletions llvm/include/llvm/Support/GenericDomTreeConstruction.h

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions llvm/lib/IR/Dominators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ template class llvm::DomTreeNodeBase<BasicBlock>;
template class llvm::DominatorTreeBase<BasicBlock, false>; // DomTreeBase
template class llvm::DominatorTreeBase<BasicBlock, true>; // PostDomTreeBase

template struct llvm::DomTreeBuilder::Update<BasicBlock *>;

template void llvm::DomTreeBuilder::Calculate<DomTreeBuilder::BBDomTree>(
DomTreeBuilder::BBDomTree &DT);
template void llvm::DomTreeBuilder::Calculate<DomTreeBuilder::BBPostDomTree>(
Expand All @@ -79,6 +81,11 @@ template void llvm::DomTreeBuilder::DeleteEdge<DomTreeBuilder::BBDomTree>(
template void llvm::DomTreeBuilder::DeleteEdge<DomTreeBuilder::BBPostDomTree>(
DomTreeBuilder::BBPostDomTree &DT, BasicBlock *From, BasicBlock *To);

template void llvm::DomTreeBuilder::ApplyUpdates<DomTreeBuilder::BBDomTree>(
DomTreeBuilder::BBDomTree &DT, DomTreeBuilder::BBUpdates);
template void llvm::DomTreeBuilder::ApplyUpdates<DomTreeBuilder::BBPostDomTree>(
DomTreeBuilder::BBPostDomTree &DT, DomTreeBuilder::BBUpdates);

template bool llvm::DomTreeBuilder::Verify<DomTreeBuilder::BBDomTree>(
const DomTreeBuilder::BBDomTree &DT);
template bool llvm::DomTreeBuilder::Verify<DomTreeBuilder::BBPostDomTree>(
Expand Down Expand Up @@ -312,6 +319,9 @@ void DominatorTree::verifyDomTree() const {
print(errs());
errs() << "\nActual:\n";
OtherDT.print(errs());
errs() << "\nCFG:\n";
F.print(errs());
errs().flush();
abort();
}
}
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set(IRSources
DebugInfoTest.cpp
DebugTypeODRUniquingTest.cpp
DominatorTreeTest.cpp
DominatorTreeBatchUpdatesTest.cpp
FunctionTest.cpp
PassBuilderCallbacksTest.cpp
IRBuilderTest.cpp
Expand Down
260 changes: 260 additions & 0 deletions llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
//===- llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp ----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <random>
#include "CFGBuilder.h"
#include "gtest/gtest.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/IR/Dominators.h"
#include "llvm/Support/GenericDomTreeConstruction.h"

#define DEBUG_TYPE "batch-update-tests"

using namespace llvm;

namespace {
const auto CFGInsert = CFGBuilder::ActionKind::Insert;
const auto CFGDelete = CFGBuilder::ActionKind::Delete;

struct PostDomTree : PostDomTreeBase<BasicBlock> {
PostDomTree(Function &F) { recalculate(F); }
};

using DomUpdate = DominatorTree::UpdateType;
static_assert(
std::is_same<DomUpdate, PostDomTree::UpdateType>::value,
"Trees differing only in IsPostDom should have the same update types");
using DomSNCA = DomTreeBuilder::SemiNCAInfo<DomTreeBuilder::BBDomTree>;
using PostDomSNCA = DomTreeBuilder::SemiNCAInfo<DomTreeBuilder::BBPostDomTree>;
const auto Insert = DominatorTree::Insert;
const auto Delete = DominatorTree::Delete;

std::vector<DomUpdate> ToDomUpdates(CFGBuilder &B,
std::vector<CFGBuilder::Update> &In) {
std::vector<DomUpdate> Res;
Res.reserve(In.size());

for (const auto &CFGU : In)
Res.push_back({CFGU.Action == CFGInsert ? Insert : Delete,
B.getOrAddBlock(CFGU.Edge.From),
B.getOrAddBlock(CFGU.Edge.To)});
return Res;
}
} // namespace

TEST(DominatorTreeBatchUpdates, LegalizeDomUpdates) {
CFGHolder Holder;
CFGBuilder Builder(Holder.F, {{"A", "B"}}, {});

BasicBlock *A = Builder.getOrAddBlock("A");
BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
BasicBlock *D = Builder.getOrAddBlock("D");

std::vector<DomUpdate> Updates = {
{Insert, B, C}, {Insert, C, D}, {Delete, B, C}, {Insert, B, C},
{Insert, B, D}, {Delete, C, D}, {Delete, A, B}};
SmallVector<DomUpdate, 4> Legalized;
DomSNCA::LegalizeUpdates(Updates, Legalized);
DEBUG(dbgs() << "Legalized updates:\t");
DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
DEBUG(dbgs() << "\n");
EXPECT_EQ(Legalized.size(), 3UL);
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, B, C}), Legalized.end());
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, B, D}), Legalized.end());
EXPECT_NE(llvm::find(Legalized, DomUpdate{Delete, A, B}), Legalized.end());
}

TEST(DominatorTreeBatchUpdates, LegalizePostDomUpdates) {
CFGHolder Holder;
CFGBuilder Builder(Holder.F, {{"A", "B"}}, {});

BasicBlock *A = Builder.getOrAddBlock("A");
BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
BasicBlock *D = Builder.getOrAddBlock("D");

std::vector<DomUpdate> Updates = {
{Insert, B, C}, {Insert, C, D}, {Delete, B, C}, {Insert, B, C},
{Insert, B, D}, {Delete, C, D}, {Delete, A, B}};
SmallVector<DomUpdate, 4> Legalized;
PostDomSNCA::LegalizeUpdates(Updates, Legalized);
DEBUG(dbgs() << "Legalized postdom updates:\t");
DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
DEBUG(dbgs() << "\n");
EXPECT_EQ(Legalized.size(), 3UL);
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, C, B}), Legalized.end());
EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, D, B}), Legalized.end());
EXPECT_NE(llvm::find(Legalized, DomUpdate{Delete, B, A}), Legalized.end());
}

TEST(DominatorTreeBatchUpdates, SingleInsertion) {
CFGHolder Holder;
CFGBuilder Builder(Holder.F, {{"A", "B"}}, {{CFGInsert, {"B", "C"}}});

DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
PostDomTree PDT(*Holder.F);
EXPECT_TRUE(DT.verify());

BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
std::vector<DomUpdate> Updates = {{Insert, B, C}};

ASSERT_TRUE(Builder.applyUpdate());

DT.applyUpdates(Updates);
EXPECT_TRUE(DT.verify());
PDT.applyUpdates(Updates);
EXPECT_TRUE(PDT.verify());
}

TEST(DominatorTreeBatchUpdates, SingleDeletion) {
CFGHolder Holder;
CFGBuilder Builder(Holder.F, {{"A", "B"}, {"B", "C"}},
{{CFGDelete, {"B", "C"}}});

DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
PostDomTree PDT(*Holder.F);
EXPECT_TRUE(DT.verify());

BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
std::vector<DomUpdate> Updates = {{Delete, B, C}};

ASSERT_TRUE(Builder.applyUpdate());

DT.applyUpdates(Updates);
EXPECT_TRUE(DT.verify());
PDT.applyUpdates(Updates);
EXPECT_TRUE(PDT.verify());
}

TEST(DominatorTreeBatchUpdates, FewInsertion) {
std::vector<CFGBuilder::Update> CFGUpdates = {{CFGInsert, {"B", "C"}},
{CFGInsert, {"C", "B"}},
{CFGInsert, {"C", "D"}},
{CFGInsert, {"D", "E"}}};

CFGHolder Holder;
CFGBuilder Builder(Holder.F, {{"A", "B"}}, CFGUpdates);

DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
PostDomTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());

BasicBlock *B = Builder.getOrAddBlock("B");
BasicBlock *C = Builder.getOrAddBlock("C");
BasicBlock *D = Builder.getOrAddBlock("D");
BasicBlock *E = Builder.getOrAddBlock("E");

std::vector<DomUpdate> Updates = {
{Insert, B, C}, {Insert, C, B}, {Insert, C, D}, {Insert, D, E}};

while (Builder.applyUpdate())
;

DT.applyUpdates(Updates);
EXPECT_TRUE(DT.verify());
PDT.applyUpdates(Updates);
EXPECT_TRUE(PDT.verify());
}

TEST(DominatorTreeBatchUpdates, FewDeletions) {
std::vector<CFGBuilder::Update> CFGUpdates = {{CFGDelete, {"B", "C"}},
{CFGDelete, {"C", "B"}},
{CFGDelete, {"B", "D"}},
{CFGDelete, {"D", "E"}}};

CFGHolder Holder;
CFGBuilder Builder(
Holder.F, {{"A", "B"}, {"B", "C"}, {"B", "D"}, {"D", "E"}, {"C", "B"}},
CFGUpdates);

DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
PostDomTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());

auto Updates = ToDomUpdates(Builder, CFGUpdates);

while (Builder.applyUpdate())
;

DT.applyUpdates(Updates);
EXPECT_TRUE(DT.verify());
PDT.applyUpdates(Updates);
EXPECT_TRUE(PDT.verify());
}

TEST(DominatorTreeBatchUpdates, InsertDelete) {
std::vector<CFGBuilder::Arc> Arcs = {
{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},
{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};

std::vector<CFGBuilder::Update> Updates = {
{CFGInsert, {"2", "4"}}, {CFGInsert, {"12", "10"}},
{CFGInsert, {"10", "9"}}, {CFGInsert, {"7", "6"}},
{CFGInsert, {"7", "5"}}, {CFGDelete, {"3", "8"}},
{CFGInsert, {"10", "7"}}, {CFGInsert, {"2", "8"}},
{CFGDelete, {"3", "4"}}, {CFGDelete, {"8", "9"}},
{CFGDelete, {"11", "12"}}};

CFGHolder Holder;
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
PostDomTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());

while (B.applyUpdate())
;

auto DomUpdates = ToDomUpdates(B, Updates);
DT.applyUpdates(DomUpdates);
EXPECT_TRUE(DT.verify());
PDT.applyUpdates(DomUpdates);
EXPECT_TRUE(PDT.verify());
}

TEST(DominatorTreeBatchUpdates, InsertDeleteExhaustive) {
std::vector<CFGBuilder::Arc> Arcs = {
{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},
{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};

std::vector<CFGBuilder::Update> Updates = {
{CFGInsert, {"2", "4"}}, {CFGInsert, {"12", "10"}},
{CFGInsert, {"10", "9"}}, {CFGInsert, {"7", "6"}},
{CFGInsert, {"7", "5"}}, {CFGDelete, {"3", "8"}},
{CFGInsert, {"10", "7"}}, {CFGInsert, {"2", "8"}},
{CFGDelete, {"3", "4"}}, {CFGDelete, {"8", "9"}},
{CFGDelete, {"11", "12"}}};

std::mt19937 Generator(0);
for (unsigned i = 0; i < 16; ++i) {
std::shuffle(Updates.begin(), Updates.end(), Generator);
CFGHolder Holder;
CFGBuilder B(Holder.F, Arcs, Updates);
DominatorTree DT(*Holder.F);
EXPECT_TRUE(DT.verify());
PostDomTree PDT(*Holder.F);
EXPECT_TRUE(PDT.verify());

while (B.applyUpdate())
;

auto DomUpdates = ToDomUpdates(B, Updates);
DT.applyUpdates(DomUpdates);
EXPECT_TRUE(DT.verify());
PDT.applyUpdates(DomUpdates);
EXPECT_TRUE(PDT.verify());
}
}