[Dominators] Introduce batch updates
Summary:
This patch introduces a way of informing the (Post)DominatorTree about multiple CFG updates that happened since the last tree update. This makes performing tree updates much easier, as it internally takes care of applying the updates in lockstep with the (virtual) updates to the CFG, which is done by reverse-applying future CFG updates.
The batch updater is able to remove redundant updates that cancel each other out. In the future, it should be also possible to reorder updates to reduce the amount of work needed to perform the updates.
Reviewers: dberlin, sanjoy, grosser, davide, brzycki
Reviewed By: brzycki
Subscribers: mgorny, llvm-commits
Differential Revision: https://reviews.llvm.org/D36167
llvm-svn: 311015
diff --git a/llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp b/llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp
new file mode 100644
index 0000000..4ad1f69
--- /dev/null
+++ b/llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp
@@ -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());
+ }
+}