llvm.org GIT mirror llvm / f39b8ab
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@311015 91177308-0d34-0410-b5e6-96231b3b80d8 Jakub Kuderski 2 years ago
6 changed file(s) with 670 addition(s) and 99 deletion(s). Raw diff Collapse all Expand all
4040 using BBDomTree = DomTreeBase;
4141 using BBPostDomTree = PostDomTreeBase;
4242
43 extern template struct Update;
44
45 using BBUpdates = ArrayRef>;
46
4347 extern template void Calculate(BBDomTree &DT);
4448 extern template void Calculate(BBPostDomTree &DT);
4549
5458 extern template void DeleteEdge(BBPostDomTree &DT,
5559 BasicBlock *From,
5660 BasicBlock *To);
61
62 extern template void ApplyUpdates(BBDomTree &DT, BBUpdates);
63 extern template void ApplyUpdates(BBPostDomTree &DT, BBUpdates);
5764
5865 extern template bool Verify(const BBDomTree &DT);
5966 extern template bool Verify(const BBPostDomTree &DT);
2323 #ifndef LLVM_SUPPORT_GENERICDOMTREE_H
2424 #define LLVM_SUPPORT_GENERICDOMTREE_H
2525
26 #include "llvm/ADT/DenseMap.h"
27 #include "llvm/ADT/GraphTraits.h"
28 #include "llvm/ADT/STLExtras.h"
29 #include "llvm/ADT/SmallPtrSet.h"
30 #include "llvm/ADT/SmallVector.h"
31 #include "llvm/Support/raw_ostream.h"
3226 #include
3327 #include
3428 #include
3731 #include
3832 #include
3933 #include
34 #include "llvm/ADT/DenseMap.h"
35 #include "llvm/ADT/GraphTraits.h"
36 #include "llvm/ADT/PointerIntPair.h"
37 #include "llvm/ADT/STLExtras.h"
38 #include "llvm/ADT/SmallPtrSet.h"
39 #include "llvm/ADT/SmallVector.h"
40 #include "llvm/Support/raw_ostream.h"
4041
4142 namespace llvm {
4243
4445 class DominatorTreeBase;
4546
4647 namespace DomTreeBuilder {
47 template <class DomTreeT>
48 template <typename DomTreeT>
4849 struct SemiNCAInfo;
4950 } // namespace DomTreeBuilder
5051
189190 template
190191 void Calculate(DomTreeT &DT);
191192
192 template <class DomTreeT>
193 template <typename DomTreeT>
193194 void InsertEdge(DomTreeT &DT, typename DomTreeT::NodePtr From,
194195 typename DomTreeT::NodePtr To);
195196
196 template <class DomTreeT>
197 template <typename DomTreeT>
197198 void DeleteEdge(DomTreeT &DT, typename DomTreeT::NodePtr From,
198199 typename DomTreeT::NodePtr To);
200
201 // UpdateKind and Update are used by the batch update API and it's easiest to
202 // define them here.
203 enum class UpdateKind : unsigned char { Insert, Delete };
204
205 template
206 struct Update {
207 using NodeKindPair = PointerIntPair;
208
209 NodePtr From;
210 NodeKindPair ToAndKind;
211
212 Update(UpdateKind Kind, NodePtr From, NodePtr To)
213 : From(From), ToAndKind(To, Kind) {}
214
215 UpdateKind getKind() const { return ToAndKind.getInt(); }
216 NodePtr getFrom() const { return From; }
217 NodePtr getTo() const { return ToAndKind.getPointer(); }
218 bool operator==(const Update &RHS) const {
219 return From == RHS.From && ToAndKind == RHS.ToAndKind;
220 }
221
222 friend raw_ostream &operator<<(raw_ostream &OS, const Update &U) {
223 OS << (U.getKind() == UpdateKind::Insert ? "Insert " : "Delete ");
224 U.getFrom()->printAsOperand(OS, false);
225 OS << " -> ";
226 U.getTo()->printAsOperand(OS, false);
227 return OS;
228 }
229 };
230
231 template
232 void ApplyUpdates(DomTreeT &DT,
233 ArrayRef Updates);
199234
200235 template
201236 bool Verify(const DomTreeT &DT);
218253 using ParentType = typename std::remove_pointer::type;
219254 static constexpr bool IsPostDominator = IsPostDom;
220255
256 using UpdateType = DomTreeBuilder::Update;
257 using UpdateKind = DomTreeBuilder::UpdateKind;
258 static constexpr UpdateKind Insert = UpdateKind::Insert;
259 static constexpr UpdateKind Delete = UpdateKind::Delete;
260
221261 protected:
222262 // Dominators always have a single root, postdominators can have more.
223263 SmallVector Roots;
462502 // API to update (Post)DominatorTree information based on modifications to
463503 // the CFG...
464504
505 /// Inform the dominator tree about a sequence of CFG edge insertions and
506 /// deletions and perform a batch update on the tree.
507 ///
508 /// This function should be used when there were multiple CFG updates after
509 /// the last dominator tree update. It takes care of performing the updates
510 /// in sync with the CFG and optimizes away the redundant operations that
511 /// cancel each other.
512 /// The functions expects the sequence of updates to be balanced. Eg.:
513 /// - {{Insert, A, B}, {Delete, A, B}, {Insert, A, B}} is fine, because
514 /// logically it results in a single insertions.
515 /// - {{Insert, A, B}, {Insert, A, B}} is invalid, because it doesn't make
516 /// sense to insert the same edge twice.
517 ///
518 /// What's more, the functions assumes that it's safe to ask every node in the
519 /// CFG about its children and inverse children. This implies that deletions
520 /// of CFG edges must not delete the CFG nodes before calling this function.
521 ///
522 /// Batch updates should be generally faster when performing longer sequences
523 /// of updates than calling insertEdge/deleteEdge manually multiple times, as
524 /// they can reorder the updates and remove redundant ones internally.
525 ///
526 /// Note that for postdominators it automatically takes care of applying
527 /// updates on reverse edges internally (so there's no need to swap the
528 /// From and To pointers when constructing DominatorTree::UpdateType).
529 /// The type of updates is the same for DomTreeBase and PostDomTreeBase
530 /// with the same template parameter T.
531 ///
532 /// \param Updates An unordered sequence of updates to perform.
533 ///
534 void applyUpdates(ArrayRef Updates) {
535 DomTreeBuilder::ApplyUpdates(*this, Updates);
536 }
537
465538 /// Inform the dominator tree about a CFG edge insertion and update the tree.
466539 ///
467540 /// This function has to be called just before or just after making the update
485558 /// CFG. An internal functions checks if the edge doesn't exist in the CFG in
486559 /// DEBUG mode. There cannot be any other updates that the
487560 /// dominator tree doesn't know about.
488 ///
489 /// However, it is fine to perform multiple CFG deletions that make different
490 /// subtrees forward-unreachable and to inform the DomTree about them all at
491 /// the same time, as the incremental algorithm doesn't walk the tree above
492 /// the NearestCommonDominator of a deleted edge
493561 ///
494562 /// Note that for postdominators it automatically takes care of deleting
495563 /// a reverse edge internally (so there's no need to swap the parameters).
677745
678746 /// recalculate - compute a dominator tree for the given function
679747 void recalculate(ParentType &Func) {
680 reset();
681748 Parent = &Func;
682749 DomTreeBuilder::Calculate(*this);
683750 }
3333 #define LLVM_SUPPORT_GENERICDOMTREECONSTRUCTION_H
3434
3535 #include
36 #include "llvm/ADT/ArrayRef.h"
3637 #include "llvm/ADT/DenseSet.h"
3738 #include "llvm/ADT/DepthFirstIterator.h"
39 #include "llvm/ADT/PointerIntPair.h"
3840 #include "llvm/ADT/SmallPtrSet.h"
3941 #include "llvm/Support/Debug.h"
4042 #include "llvm/Support/GenericDomTree.h"
4345
4446 namespace llvm {
4547 namespace DomTreeBuilder {
46
47 template
48 struct ChildrenGetter {
49 static auto Get(NodePtr N) -> decltype(reverse(children(N))) {
50 return reverse(children(N));
51 }
52 };
53
54 template
55 struct ChildrenGetter {
56 static auto Get(NodePtr N) -> decltype(inverse_children(N)) {
57 return inverse_children(N);
58 }
59 };
6048
6149 template
6250 struct SemiNCAInfo {
8169 std::vector NumToNode = {nullptr};
8270 DenseMap NodeToInfo;
8371
72 using UpdateT = typename DomTreeT::UpdateType;
73 struct BatchUpdateInfo {
74 SmallVector Updates;
75 using NodePtrAndKind = PointerIntPair;
76
77 // In order to be able to walk a CFG that is out of sync with the CFG
78 // DominatorTree last knew about, use the list of updates to reconstruct
79 // previous CFG versions of the current CFG. For each node, we store a set
80 // of its virtually added/deleted future successors and predecessors.
81 // Note that these children are from the future relative to what the
82 // DominatorTree knows about -- using them to gets us some snapshot of the
83 // CFG from the past (relative to the state of the CFG).
84 DenseMap> FutureSuccessors;
85 DenseMap> FuturePredecessors;
86 // Remembers if the whole tree was recalculated at some point during the
87 // current batch update.
88 bool IsRecalculated = false;
89 };
90
91 BatchUpdateInfo *BatchUpdates;
92 using BatchUpdatePtr = BatchUpdateInfo *;
93
94 // If BUI is a nullptr, then there's no batch update in progress.
95 SemiNCAInfo(BatchUpdatePtr BUI) : BatchUpdates(BUI) {}
96
8497 void clear() {
8598 NumToNode = {nullptr}; // Restore to initial state with a dummy start node.
8699 NodeToInfo.clear();
87 }
100 // Don't reset the pointer to BatchUpdateInfo here -- if there's an update
101 // in progress, we need this information to continue it.
102 }
103
104 template
105 struct ChildrenGetter {
106 using ResultTy = SmallVector;
107
108 static ResultTy Get(NodePtr N, std::integral_constant) {
109 auto RChildren = reverse(children(N));
110 return ResultTy(RChildren.begin(), RChildren.end());
111 }
112
113 static ResultTy Get(NodePtr N, std::integral_constant) {
114 auto IChildren = inverse_children(N);
115 return ResultTy(IChildren.begin(), IChildren.end());
116 }
117
118 using Tag = std::integral_constant;
119
120 // The function below is the core part of the batch updater. It allows the
121 // Depth Based Search algorithm to perform incremental updates in lockstep
122 // with updates to the CFG. We emulated lockstep CFG updates by getting its
123 // next snapshots by reverse-applying future updates.
124 static ResultTy Get(NodePtr N, BatchUpdatePtr BUI) {
125 ResultTy Res = Get(N, Tag());
126 // If there's no batch update in progress, simply return node's children.
127 if (!BUI) return Res;
128
129 // CFG children are actually its *most current* children, and we have to
130 // reverse-apply the future updates to get the node's children at the
131 // point in time the update was performed.
132 auto &FutureChildren = (Inverse != IsPostDom) ? BUI->FuturePredecessors
133 : BUI->FutureSuccessors;
134 auto FCIt = FutureChildren.find(N);
135 if (FCIt == FutureChildren.end()) return Res;
136
137 for (auto ChildAndKind : FCIt->second) {
138 const NodePtr Child = ChildAndKind.getPointer();
139 const UpdateKind UK = ChildAndKind.getInt();
140
141 // Reverse-apply the future update.
142 if (UK == UpdateKind::Insert) {
143 // If there's an insertion in the future, it means that the edge must
144 // exist in the current CFG, but was not present in it before.
145 assert(llvm::find(Res, Child) != Res.end()
146 && "Expected child not found in the CFG");
147 Res.erase(std::remove(Res.begin(), Res.end(), Child), Res.end());
148 DEBUG(dbgs() << "\tHiding edge " << BlockNamePrinter(N) << " -> "
149 << BlockNamePrinter(Child) << "\n");
150 } else {
151 // If there's an deletion in the future, it means that the edge cannot
152 // exist in the current CFG, but existed in it before.
153 assert(llvm::find(Res, Child) == Res.end() &&
154 "Unexpected child found in the CFG");
155 DEBUG(dbgs() << "\tShowing virtual edge " << BlockNamePrinter(N)
156 << " -> " << BlockNamePrinter(Child) << "\n");
157 Res.push_back(Child);
158 }
159 }
160
161 return Res;
162 }
163 };
88164
89165 NodePtr getIDom(NodePtr BB) const {
90166 auto InfoIt = NodeToInfo.find(BB);
153229 NumToNode.push_back(BB);
154230
155231 constexpr bool Direction = IsReverse != IsPostDom; // XOR.
156 for (const NodePtr Succ : ChildrenGetter::Get(BB)) {
232 for (const NodePtr Succ :
233 ChildrenGetter::Get(BB, BatchUpdates)) {
157234 const auto SIT = NodeToInfo.find(Succ);
158235 // Don't visit nodes more than once but remember to collect
159236 // ReverseChildren.
280357 // For postdominators, nodes with no forward successors are trivial roots that
281358 // are always selected as tree roots. Roots with forward successors correspond
282359 // to CFG nodes within infinite loops.
283 static bool HasForwardSuccessors(const NodePtr N) {
360 static bool HasForwardSuccessors(const NodePtr N, BatchUpdatePtr BUI) {
284361 assert(N && "N must be a valid node");
285 using TraitsTy = GraphTraits;
286 return TraitsTy::child_begin(N) != TraitsTy::child_end(N);
362 return !ChildrenGetter::Get(N, BUI).empty();
287363 }
288364
289365 static NodePtr GetEntryNode(const DomTreeT &DT) {
294370 // Finds all roots without relaying on the set of roots already stored in the
295371 // tree.
296372 // We define roots to be some non-redundant set of the CFG nodes
297 static RootsT FindRoots(const DomTreeT &DT) {
373 static RootsT FindRoots(const DomTreeT &DT, BatchUpdatePtr BUI) {
298374 assert(DT.Parent && "Parent pointer is not set");
299375 RootsT Roots;
300376
304380 return Roots;
305381 }
306382
307 SemiNCAInfo SNCA;
383 SemiNCAInfo SNCA(BUI);
308384
309385 // PostDominatorTree always has a virtual root.
310386 SNCA.addVirtualRoot();
315391 // Step #1: Find all the trivial roots that are going to will definitely
316392 // remain tree roots.
317393 unsigned Total = 0;
394 // It may happen that there are some new nodes in the CFG that are result of
395 // the ongoing batch update, but we cannot really pretend that they don't
396 // exist -- we won't see any outgoing or incoming edges to them, so it's
397 // fine to discover them here, as they would end up appearing in the CFG at
398 // some point anyway.
318399 for (const NodePtr N : nodes(DT.Parent)) {
319400 ++Total;
320401 // If it has no *successors*, it is definitely a root.
321 if (!HasForwardSuccessors(N)) {
402 if (!HasForwardSuccessors(N, BUI)) {
322403 Roots.push_back(N);
323404 // Run DFS not to walk this part of CFG later.
324405 Num = SNCA.runDFS(N, Num, AlwaysDescend, 1);
397478 assert((Total + 1 == Num) && "Everything should have been visited");
398479
399480 // Step #3: If we found some non-trivial roots, make them non-redundant.
400 if (HasNonTrivialRoots) RemoveRedundantRoots(DT, Roots);
481 if (HasNonTrivialRoots) RemoveRedundantRoots(DT, BUI, Roots);
401482
402483 DEBUG(dbgs() << "Found roots: ");
403484 DEBUG(for (auto *Root : Roots) dbgs() << BlockNamePrinter(Root) << " ");
414495 // the non-trivial roots are reverse-reachable from other non-trivial roots,
415496 // which makes them redundant. This function removes them from the set of
416497 // input roots.
417 static void RemoveRedundantRoots(const DomTreeT &DT, RootsT &Roots) {
498 static void RemoveRedundantRoots(const DomTreeT &DT, BatchUpdatePtr BUI,
499 RootsT &Roots) {
418500 assert(IsPostDom && "This function is for postdominators only");
419501 DEBUG(dbgs() << "Removing redundant roots\n");
420502
421 SemiNCAInfo SNCA;
503 SemiNCAInfo SNCA(BUI);
422504
423505 for (unsigned i = 0; i < Roots.size(); ++i) {
424506 auto &Root = Roots[i];
425507 // Trivial roots are always non-redundant.
426 if (!HasForwardSuccessors(Root)) continue;
508 if (!HasForwardSuccessors(Root, BUI)) continue;
427509 DEBUG(dbgs() << "\tChecking if " << BlockNamePrinter(Root)
428510 << " remains a root\n");
429511 SNCA.clear();
465547 for (const NodePtr Root : DT.Roots) Num = runDFS(Root, Num, DC, 0);
466548 }
467549
468 void calculateFromScratch(DomTreeT &DT) {
550 static void CalculateFromScratch(DomTreeT &DT, BatchUpdatePtr BUI) {
551 auto *Parent = DT.Parent;
552 DT.reset();
553 DT.Parent = Parent;
554 SemiNCAInfo SNCA(nullptr); // Since we are rebuilding the whole tree,
555 // there's no point doing it incrementally.
556
469557 // Step #0: Number blocks in depth-first order and initialize variables used
470558 // in later stages of the algorithm.
471 DT.Roots = FindRoots(DT);
472 doFullDFSWalk(DT, AlwaysDescend);
473
474 runSemiNCA(DT);
559 DT.Roots = FindRoots(DT, nullptr);
560 SNCA.doFullDFSWalk(DT, AlwaysDescend);
561
562 SNCA.runSemiNCA(DT);
563 if (BUI) {
564 BUI->IsRecalculated = true;
565 DEBUG(dbgs() << "DomTree recalculated, skipping future batch updates\n");
566 }
475567
476568 if (DT.Roots.empty()) return;
477569
483575 DT.RootNode = (DT.DomTreeNodes[Root] =
484576 llvm::make_unique>(Root, nullptr))
485577 .get();
486 attachNewSubtree(DT, DT.RootNode);
578 SNCA.attachNewSubtree(DT, DT.RootNode);
487579 }
488580
489581 void attachNewSubtree(DomTreeT& DT, const TreeNodePtr AttachTo) {
540632 SmallVector VisitedNotAffectedQueue;
541633 };
542634
543 static void InsertEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) {
635 static void InsertEdge(DomTreeT &DT, const BatchUpdatePtr BUI,
636 const NodePtr From, const NodePtr To) {
544637 assert((From || IsPostDom) &&
545638 "From has to be a valid CFG node or a virtual root");
546639 assert(To && "Cannot be a nullptr");
565658
566659 const TreeNodePtr ToTN = DT.getNode(To);
567660 if (!ToTN)
568 InsertUnreachable(DT, FromTN, To);
661 InsertUnreachable(DT, BUI, FromTN, To);
569662 else
570 InsertReachable(DT, FromTN, ToTN);
663 InsertReachable(DT, BUI, FromTN, ToTN);
571664 }
572665
573666 // Determines if some existing root becomes reverse-reachable after the
574667 // insertion. Rebuilds the whole tree if that situation happens.
575 static bool UpdateRootsBeforeInsertion(DomTreeT &DT, const TreeNodePtr From,
668 static bool UpdateRootsBeforeInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
669 const TreeNodePtr From,
576670 const TreeNodePtr To) {
577671 assert(IsPostDom && "This function is only for postdominators");
578672 // Destination node is not attached to the virtual root, so it cannot be a
586680 DEBUG(dbgs() << "\t\tAfter the insertion, " << BlockNamePrinter(To)
587681 << " is no longer a root\n\t\tRebuilding the tree!!!\n");
588682
589 DT.recalculate(*DT.Parent);
683 CalculateFromScratch(DT, BUI);
590684 return true;
591685 }
592686
593687 // Updates the set of roots after insertion or deletion. This ensures that
594688 // roots are the same when after a series of updates and when the tree would
595689 // be built from scratch.
596 static void UpdateRootsAfterUpdate(DomTreeT &DT) {
690 static void UpdateRootsAfterUpdate(DomTreeT &DT, const BatchUpdatePtr BUI) {
597691 assert(IsPostDom && "This function is only for postdominators");
598692
599693 // The tree has only trivial roots -- nothing to update.
600 if (std::none_of(DT.Roots.begin(), DT.Roots.end(), HasForwardSuccessors))
694 if (std::none_of(DT.Roots.begin(), DT.Roots.end(), [BUI](const NodePtr N) {
695 return HasForwardSuccessors(N, BUI);
696 }))
601697 return;
602698
603699 // Recalculate the set of roots.
604 DT.Roots = FindRoots(DT);
700 DT.Roots = FindRoots(DT, BUI);
605701 for (const NodePtr R : DT.Roots) {
606702 const TreeNodePtr TN = DT.getNode(R);
607703 // A CFG node was selected as a tree root, but the corresponding tree node
616712 // It should be possible to rotate the subtree instead of recalculating
617713 // the whole tree, but this situation happens extremely rarely in
618714 // practice.
619 DT.recalculate(*DT.Parent);
715 CalculateFromScratch(DT, BUI);
620716 return;
621717 }
622718 }
623719 }
624720
625721 // Handles insertion to a node already in the dominator tree.
626 static void InsertReachable(DomTreeT &DT, const TreeNodePtr From,
627 const TreeNodePtr To) {
722 static void InsertReachable(DomTreeT &DT, const BatchUpdatePtr BUI,
723 const TreeNodePtr From, const TreeNodePtr To) {
628724 DEBUG(dbgs() << "\tReachable " << BlockNamePrinter(From->getBlock())
629725 << " -> " << BlockNamePrinter(To->getBlock()) << "\n");
630 if (IsPostDom && UpdateRootsBeforeInsertion(DT, From, To)) return;
726 if (IsPostDom && UpdateRootsBeforeInsertion(DT, BUI, From, To)) return;
631727 // DT.findNCD expects both pointers to be valid. When From is a virtual
632728 // root, then its CFG block pointer is a nullptr, so we have to 'compute'
633729 // the NCD manually.
663759 II.AffectedQueue.push_back(CurrentNode);
664760
665761 // Discover and collect affected successors of the current node.
666 VisitInsertion(DT, CurrentNode, CurrentNode->getLevel(), NCD, II);
762 VisitInsertion(DT, BUI, CurrentNode, CurrentNode->getLevel(), NCD, II);
667763 }
668764
669765 // Finish by updating immediate dominators and levels.
670 UpdateInsertion(DT, NCD, II);
766 UpdateInsertion(DT, BUI, NCD, II);
671767 }
672768
673769 // Visits an affected node and collect its affected successors.
674 static void VisitInsertion(DomTreeT &DT, const TreeNodePtr TN,
675 const unsigned RootLevel, const TreeNodePtr NCD,
676 InsertionInfo &II) {
770 static void VisitInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
771 const TreeNodePtr TN, const unsigned RootLevel,
772 const TreeNodePtr NCD, InsertionInfo &II) {
677773 const unsigned NCDLevel = NCD->getLevel();
678774 DEBUG(dbgs() << "Visiting " << BlockNamePrinter(TN) << "\n");
679775
684780 TreeNodePtr Next = Stack.pop_back_val();
685781
686782 for (const NodePtr Succ :
687 ChildrenGetter<NodePtr, IsPostDom>::Get(Next->getBlock())) {
783 ChildrenGetter<IsPostDom>::Get(Next->getBlock(), BUI)) {
688784 const TreeNodePtr SuccTN = DT.getNode(Succ);
689785 assert(SuccTN && "Unreachable successor found at reachable insertion");
690786 const unsigned SuccLevel = SuccTN->getLevel();
705801 II.VisitedNotAffectedQueue.push_back(SuccTN);
706802 Stack.push_back(SuccTN);
707803 } else if ((SuccLevel > NCDLevel + 1) &&
708 II.Affected.count(SuccTN) == 0) {
804 II.Affected.count(SuccTN) == 0) {
709805 DEBUG(dbgs() << "\t\tMarking affected and adding "
710806 << BlockNamePrinter(Succ) << " to a Bucket\n");
711807 II.Affected.insert(SuccTN);
716812 }
717813
718814 // Updates immediate dominators and levels after insertion.
719 static void UpdateInsertion(DomTreeT &DT, const TreeNodePtr NCD,
720 InsertionInfo &II) {
815 static void UpdateInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
816 const TreeNodePtr NCD, InsertionInfo &II) {
721817 DEBUG(dbgs() << "Updating NCD = " << BlockNamePrinter(NCD) << "\n");
722818
723819 for (const TreeNodePtr TN : II.AffectedQueue) {
727823 }
728824
729825 UpdateLevelsAfterInsertion(II);
730 if (IsPostDom) UpdateRootsAfterUpdate(DT);
826 if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI);
731827 }
732828
733829 static void UpdateLevelsAfterInsertion(InsertionInfo &II) {
742838 }
743839
744840 // Handles insertion to previously unreachable nodes.
745 static void InsertUnreachable(DomTreeT &DT, const TreeNodePtr From,
746 const NodePtr To) {
841 static void InsertUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI,
842 const TreeNodePtr From, const NodePtr To) {
747843 DEBUG(dbgs() << "Inserting " << BlockNamePrinter(From)
748844 << " -> (unreachable) " << BlockNamePrinter(To) << "\n");
749845
750846 // Collect discovered edges to already reachable nodes.
751847 SmallVector, 8> DiscoveredEdgesToReachable;
752848 // Discover and connect nodes that became reachable with the insertion.
753 ComputeUnreachableDominators(DT, To, From, DiscoveredEdgesToReachable);
849 ComputeUnreachableDominators(DT, BUI, To, From, DiscoveredEdgesToReachable);
754850
755851 DEBUG(dbgs() << "Inserted " << BlockNamePrinter(From)
756852 << " -> (prev unreachable) " << BlockNamePrinter(To) << "\n");
763859 DEBUG(dbgs() << "\tInserting discovered connecting edge "
764860 << BlockNamePrinter(Edge.first) << " -> "
765861 << BlockNamePrinter(Edge.second) << "\n");
766 InsertReachable(DT, DT.getNode(Edge.first), Edge.second);
862 InsertReachable(DT, BUI, DT.getNode(Edge.first), Edge.second);
767863 }
768864 }
769865
770866 // Connects nodes that become reachable with an insertion.
771867 static void ComputeUnreachableDominators(
772 DomTreeT &DT, const NodePtr Root, const TreeNodePtr Incoming,
868 DomTreeT &DT, const BatchUpdatePtr BUI, const NodePtr Root,
869 const TreeNodePtr Incoming,
773870 SmallVectorImpl>
774 &DiscoveredConnectingEdges) {
871 &DiscoveredConnectingEdges) {
775872 assert(!DT.getNode(Root) && "Root must not be reachable");
776873
777874 // Visit only previously unreachable nodes.
784881 return false;
785882 };
786883
787 SemiNCAInfo SNCA;
884 SemiNCAInfo SNCA(BUI);
788885 SNCA.runDFS(Root, 0, UnreachableDescender, 0);
789886 SNCA.runSemiNCA(DT);
790887 SNCA.attachNewSubtree(DT, Incoming);
793890 DEBUG(DT.print(dbgs()));
794891 }
795892
796 static void DeleteEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) {
893 static void DeleteEdge(DomTreeT &DT, const BatchUpdatePtr BUI,
894 const NodePtr From, const NodePtr To) {
797895 assert(From && To && "Cannot disconnect nullptrs");
798896 DEBUG(dbgs() << "Deleting edge " << BlockNamePrinter(From) << " -> "
799897 << BlockNamePrinter(To) << "\n");
802900 // Ensure that the edge was in fact deleted from the CFG before informing
803901 // the DomTree about it.
804902 // The check is O(N), so run it only in debug configuration.
805 auto IsSuccessor = [](const NodePtr SuccCandidate, const NodePtr Of) {
806 auto Successors = ChildrenGetter::Get(Of);
903 auto IsSuccessor = [BUI](const NodePtr SuccCandidate, const NodePtr Of) {
904 auto Successors = ChildrenGetter::Get(Of, BUI);
807905 return llvm::find(Successors, SuccCandidate) != Successors.end();
808906 };
809907 (void)IsSuccessor;
828926
829927 // To remains reachable after deletion.
830928 // (Based on the caption under Figure 4. from the second paper.)
831 if (FromTN != ToIDom || HasProperSupport(DT, ToTN))
832 DeleteReachable(DT, FromTN, ToTN);
929 if (FromTN != ToIDom || HasProperSupport(DT, BUI, ToTN))
930 DeleteReachable(DT, BUI, FromTN, ToTN);
833931 else
834 DeleteUnreachable(DT, ToTN);
835
836 if (IsPostDom) UpdateRootsAfterUpdate(DT);
932 DeleteUnreachable(DT, BUI, ToTN);
933
934 if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI);
837935 }
838936
839937 // Handles deletions that leave destination nodes reachable.
840 static void DeleteReachable(DomTreeT &DT, const TreeNodePtr FromTN,
938 static void DeleteReachable(DomTreeT &DT, const BatchUpdatePtr BUI,
939 const TreeNodePtr FromTN,
841940 const TreeNodePtr ToTN) {
842941 DEBUG(dbgs() << "Deleting reachable " << BlockNamePrinter(FromTN) << " -> "
843942 << BlockNamePrinter(ToTN) << "\n");
855954 // scratch.
856955 if (!PrevIDomSubTree) {
857956 DEBUG(dbgs() << "The entire tree needs to be rebuilt\n");
858 DT.recalculate(*DT.Parent);
957 CalculateFromScratch(DT, BUI);
859958 return;
860959 }
861960
867966
868967 DEBUG(dbgs() << "\tTop of subtree: " << BlockNamePrinter(ToIDomTN) << "\n");
869968
870 SemiNCAInfo SNCA;
969 SemiNCAInfo SNCA(BUI);
871970 SNCA.runDFS(ToIDom, 0, DescendBelow, 0);
872971 DEBUG(dbgs() << "\tRunning Semi-NCA\n");
873972 SNCA.runSemiNCA(DT, Level);
876975
877976 // Checks if a node has proper support, as defined on the page 3 and later
878977 // explained on the page 7 of the second paper.
879 static bool HasProperSupport(DomTreeT &DT, const TreeNodePtr TN) {
978 static bool HasProperSupport(DomTreeT &DT, const BatchUpdatePtr BUI,
979 const TreeNodePtr TN) {
880980 DEBUG(dbgs() << "IsReachableFromIDom " << BlockNamePrinter(TN) << "\n");
881981 for (const NodePtr Pred :
882 ChildrenGetter::Get(TN->getBlock())) {
982 ChildrenGetter::Get(TN->getBlock(), BUI)) {
883983 DEBUG(dbgs() << "\tPred " << BlockNamePrinter(Pred) << "\n");
884984 if (!DT.getNode(Pred)) continue;
885985
899999
9001000 // Handle deletions that make destination node unreachable.
9011001 // (Based on the lemma 2.7 from the second paper.)
902 static void DeleteUnreachable(DomTreeT &DT, const TreeNodePtr ToTN) {
1002 static void DeleteUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI,
1003 const TreeNodePtr ToTN) {
9031004 DEBUG(dbgs() << "Deleting unreachable subtree " << BlockNamePrinter(ToTN)
9041005 << "\n");
9051006 assert(ToTN);
9121013 DEBUG(dbgs() << "\tDeletion made a region reverse-unreachable\n");
9131014 DEBUG(dbgs() << "\tAdding new root " << BlockNamePrinter(ToTN) << "\n");
9141015 DT.Roots.push_back(ToTN->getBlock());
915 InsertReachable(DT, DT.getNode(nullptr), ToTN);
1016 InsertReachable(DT, BUI, DT.getNode(nullptr), ToTN);
9161017 return;
9171018 }
9181019
9311032 return false;
9321033 };
9331034
934 SemiNCAInfo SNCA;
1035 SemiNCAInfo SNCA(BUI);
9351036 unsigned LastDFSNum =
9361037 SNCA.runDFS(ToTN->getBlock(), 0, DescendAndCollect, 0);
9371038
9561057 // Root reached, rebuild the whole tree from scratch.
9571058 if (!MinNode->getIDom()) {
9581059 DEBUG(dbgs() << "The entire tree needs to be rebuilt\n");
959 DT.recalculate(*DT.Parent);
1060 CalculateFromScratch(DT, BUI);
9601061 return;
9611062 }
9621063
10091110 IDom->Children.pop_back();
10101111
10111112 DT.DomTreeNodes.erase(TN->getBlock());
1113 }
1114
1115 //~~
1116 //===--------------------- DomTree Batch Updater --------------------------===
1117 //~~
1118
1119 static void ApplyUpdates(DomTreeT &DT, ArrayRef Updates) {
1120 BatchUpdateInfo BUI;
1121 LegalizeUpdates(Updates, BUI.Updates);
1122
1123 const size_t NumLegalized = BUI.Updates.size();
1124 BUI.FutureSuccessors.reserve(NumLegalized);
1125 BUI.FuturePredecessors.reserve(NumLegalized);
1126
1127 // Use the legalized future updates to initialize future successors and
1128 // predecessors. Note that these sets will only decrease size over time, as
1129 // the next CFG snapshots slowly approach the actual (current) CFG.
1130 for (UpdateT &U : BUI.Updates) {
1131 BUI.FutureSuccessors[U.getFrom()].insert({U.getTo(), U.getKind()});
1132 BUI.FuturePredecessors[U.getTo()].insert({U.getFrom(), U.getKind()});
1133 }
1134
1135 DEBUG(dbgs() << "About to apply " << NumLegalized << " updates\n");
1136 DEBUG(if (NumLegalized < 32) for (const auto &U
1137 : reverse(BUI.Updates)) dbgs()
1138 << '\t' << U << "\n");
1139 DEBUG(dbgs() << "\n");
1140
1141 // If the DominatorTree was recalculated at some point, stop the batch
1142 // updates. Full recalculations ignore batch updates and look at the actual
1143 // CFG.
1144 for (size_t i = 0; i < NumLegalized && !BUI.IsRecalculated; ++i)
1145 ApplyNextUpdate(DT, BUI);
1146 }
1147
1148 // This function serves double purpose:
1149 // a) It removes redundant updates, which makes it easier to reverse-apply
1150 // them when traversing CFG.
1151 // b) It optimizes away updates that cancel each other out, as the end result
1152 // is the same.
1153 //
1154 // It relies on the property of the incremental updates that says that the
1155 // order of updates doesn't matter. This allows us to reorder them and end up
1156 // with the exact same DomTree every time.
1157 //
1158 // Following the same logic, the function doesn't care about the order of
1159 // input updates, so it's OK to pass it an unordered sequence of updates, that
1160 // doesn't make sense when applied sequentially, eg. performing double
1161 // insertions or deletions and then doing an opposite update.
1162 //
1163 // In the future, it should be possible to schedule updates in way that
1164 // minimizes the amount of work needed done during incremental updates.
1165 static void LegalizeUpdates(ArrayRef AllUpdates,
1166 SmallVectorImpl &Result) {
1167 DEBUG(dbgs() << "Legalizing " << AllUpdates.size() << " updates\n");
1168 // Count the total number of inserions of each edge.
1169 // Each insertion adds 1 and deletion subtracts 1. The end number should be
1170 // one of {-1 (deletion), 0 (NOP), +1 (insertion)}. Otherwise, the sequence
1171 // of updates contains multiple updates of the same kind and we assert for
1172 // that case.
1173 SmallDenseMap, int, 4> Operations;
1174 Operations.reserve(AllUpdates.size());
1175
1176 for (const auto &U : AllUpdates) {
1177 NodePtr From = U.getFrom();
1178 NodePtr To = U.getTo();
1179 if (IsPostDom) std::swap(From, To); // Reverse edge for postdominators.
1180
1181 Operations[{From, To}] += (U.getKind() == UpdateKind::Insert ? 1 : -1);
1182 }
1183
1184 Result.clear();
1185 Result.reserve(Operations.size());
1186 for (auto &Op : Operations) {
1187 const int NumInsertions = Op.second;
1188 assert(std::abs(NumInsertions) <= 1 && "Unbalanced operations!");
1189 if (NumInsertions == 0) continue;
1190 const UpdateKind UK =
1191 NumInsertions > 0 ? UpdateKind::Insert : UpdateKind::Delete;
1192 Result.push_back({UK, Op.first.first, Op.first.second});
1193 }
1194
1195 // Make the order consistent by not relying on pointer values within the
1196 // set. Reuse the old Operations map.
1197 // In the future, we should sort by something else to minimize the amount
1198 // of work needed to perform the series of updates.
1199 for (size_t i = 0, e = AllUpdates.size(); i != e; ++i) {
1200 const auto &U = AllUpdates[i];
1201 if (!IsPostDom)
1202 Operations[{U.getFrom(), U.getTo()}] = int(i);
1203 else
1204 Operations[{U.getTo(), U.getFrom()}] = int(i);
1205 }
1206
1207 std::sort(Result.begin(), Result.end(),
1208 [&Operations](const UpdateT &A, const UpdateT &B) {
1209 return Operations[{A.getFrom(), A.getTo()}] >
1210 Operations[{B.getFrom(), B.getTo()}];
1211 });
1212 }
1213
1214 static void ApplyNextUpdate(DomTreeT &DT, BatchUpdateInfo &BUI) {
1215 assert(!BUI.Updates.empty() && "No updates to apply!");
1216 UpdateT CurrentUpdate = BUI.Updates.pop_back_val();
1217 DEBUG(dbgs() << "Applying update: " << CurrentUpdate << "\n");
1218
1219 // Move to the next snapshot of the CFG by removing the reverse-applied
1220 // current update.
1221 auto &FS = BUI.FutureSuccessors[CurrentUpdate.getFrom()];
1222 FS.erase({CurrentUpdate.getTo(), CurrentUpdate.getKind()});
1223 if (FS.empty()) BUI.FutureSuccessors.erase(CurrentUpdate.getFrom());
1224
1225 auto &FP = BUI.FuturePredecessors[CurrentUpdate.getTo()];
1226 FP.erase({CurrentUpdate.getFrom(), CurrentUpdate.getKind()});
1227 if (FP.empty()) BUI.FuturePredecessors.erase(CurrentUpdate.getTo());
1228
1229 if (CurrentUpdate.getKind() == UpdateKind::Insert)
1230 InsertEdge(DT, &BUI, CurrentUpdate.getFrom(), CurrentUpdate.getTo());
1231 else
1232 DeleteEdge(DT, &BUI, CurrentUpdate.getFrom(), CurrentUpdate.getTo());
10121233 }
10131234
10141235 //~~
10401261 }
10411262 }
10421263
1043 RootsT ComputedRoots = FindRoots(DT);
1264 RootsT ComputedRoots = FindRoots(DT, nullptr);
10441265 if (DT.Roots.size() != ComputedRoots.size() ||
10451266 !std::is_permutation(DT.Roots.begin(), DT.Roots.end(),
10461267 ComputedRoots.begin())) {
12671488
12681489 template
12691490 void Calculate(DomTreeT &DT) {
1270 SemiNCAInfo SNCA;
1271 SNCA.calculateFromScratch(DT);
1491 SemiNCAInfo::CalculateFromScratch(DT, nullptr);
12721492 }
12731493
12741494 template
12751495 void InsertEdge(DomTreeT &DT, typename DomTreeT::NodePtr From,
12761496 typename DomTreeT::NodePtr To) {
12771497 if (DT.isPostDominator()) std::swap(From, To);
1278 SemiNCAInfo::InsertEdge(DT, From, To);
1498 SemiNCAInfo::InsertEdge(DT, nullptr, From, To);
12791499 }
12801500
12811501 template
12821502 void DeleteEdge(DomTreeT &DT, typename DomTreeT::NodePtr From,
12831503 typename DomTreeT::NodePtr To) {
12841504 if (DT.isPostDominator()) std::swap(From, To);
1285 SemiNCAInfo::DeleteEdge(DT, From, To);
1505 SemiNCAInfo::DeleteEdge(DT, nullptr, From, To);
1506 }
1507
1508 template
1509 void ApplyUpdates(DomTreeT &DT,
1510 ArrayRef Updates) {
1511 SemiNCAInfo::ApplyUpdates(DT, Updates);
12861512 }
12871513
12881514 template
12891515 bool Verify(const DomTreeT &DT) {
1290 SemiNCAInfo SNCA;
1516 SemiNCAInfo SNCA(nullptr);
12911517 return SNCA.verifyRoots(DT) && SNCA.verifyReachability(DT) &&
12921518 SNCA.VerifyLevels(DT) && SNCA.verifyNCD(DT) &&
12931519 SNCA.verifyParentProperty(DT) && SNCA.verifySiblingProperty(DT);
6363 template class llvm::DominatorTreeBase; // DomTreeBase
6464 template class llvm::DominatorTreeBase; // PostDomTreeBase
6565
66 template struct llvm::DomTreeBuilder::Update;
67
6668 template void llvm::DomTreeBuilder::Calculate(
6769 DomTreeBuilder::BBDomTree &DT);
6870 template void llvm::DomTreeBuilder::Calculate(
7779 DomTreeBuilder::BBDomTree &DT, BasicBlock *From, BasicBlock *To);
7880 template void llvm::DomTreeBuilder::DeleteEdge(
7981 DomTreeBuilder::BBPostDomTree &DT, BasicBlock *From, BasicBlock *To);
82
83 template void llvm::DomTreeBuilder::ApplyUpdates(
84 DomTreeBuilder::BBDomTree &DT, DomTreeBuilder::BBUpdates);
85 template void llvm::DomTreeBuilder::ApplyUpdates(
86 DomTreeBuilder::BBPostDomTree &DT, DomTreeBuilder::BBUpdates);
8087
8188 template bool llvm::DomTreeBuilder::Verify(
8289 const DomTreeBuilder::BBDomTree &DT);
311318 print(errs());
312319 errs() << "\nActual:\n";
313320 OtherDT.print(errs());
321 errs() << "\nCFG:\n";
322 F.print(errs());
323 errs().flush();
314324 abort();
315325 }
316326 }
1515 DebugInfoTest.cpp
1616 DebugTypeODRUniquingTest.cpp
1717 DominatorTreeTest.cpp
18 DominatorTreeBatchUpdatesTest.cpp
1819 FunctionTest.cpp
1920 PassBuilderCallbacksTest.cpp
2021 IRBuilderTest.cpp
0 //===- llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp ----------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include
10 #include "CFGBuilder.h"
11 #include "gtest/gtest.h"
12 #include "llvm/Analysis/PostDominators.h"
13 #include "llvm/IR/Dominators.h"
14 #include "llvm/Support/GenericDomTreeConstruction.h"
15
16 #define DEBUG_TYPE "batch-update-tests"
17
18 using namespace llvm;
19
20 namespace {
21 const auto CFGInsert = CFGBuilder::ActionKind::Insert;
22 const auto CFGDelete = CFGBuilder::ActionKind::Delete;
23
24 struct PostDomTree : PostDomTreeBase {
25 PostDomTree(Function &F) { recalculate(F); }
26 };
27
28 using DomUpdate = DominatorTree::UpdateType;
29 static_assert(
30 std::is_same::value,
31 "Trees differing only in IsPostDom should have the same update types");
32 using DomSNCA = DomTreeBuilder::SemiNCAInfo;
33 using PostDomSNCA = DomTreeBuilder::SemiNCAInfo;
34 const auto Insert = DominatorTree::Insert;
35 const auto Delete = DominatorTree::Delete;
36
37 std::vector ToDomUpdates(CFGBuilder &B,
38 std::vector &In) {
39 std::vector Res;
40 Res.reserve(In.size());
41
42 for (const auto &CFGU : In)
43 Res.push_back({CFGU.Action == CFGInsert ? Insert : Delete,
44 B.getOrAddBlock(CFGU.Edge.From),
45 B.getOrAddBlock(CFGU.Edge.To)});
46 return Res;
47 }
48 } // namespace
49
50 TEST(DominatorTreeBatchUpdates, LegalizeDomUpdates) {
51 CFGHolder Holder;
52 CFGBuilder Builder(Holder.F, {{"A", "B"}}, {});
53
54 BasicBlock *A = Builder.getOrAddBlock("A");
55 BasicBlock *B = Builder.getOrAddBlock("B");
56 BasicBlock *C = Builder.getOrAddBlock("C");
57 BasicBlock *D = Builder.getOrAddBlock("D");
58
59 std::vector Updates = {
60 {Insert, B, C}, {Insert, C, D}, {Delete, B, C}, {Insert, B, C},
61 {Insert, B, D}, {Delete, C, D}, {Delete, A, B}};
62 SmallVector Legalized;
63 DomSNCA::LegalizeUpdates(Updates, Legalized);
64 DEBUG(dbgs() << "Legalized updates:\t");
65 DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
66 DEBUG(dbgs() << "\n");
67 EXPECT_EQ(Legalized.size(), 3UL);
68 EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, B, C}), Legalized.end());
69 EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, B, D}), Legalized.end());
70 EXPECT_NE(llvm::find(Legalized, DomUpdate{Delete, A, B}), Legalized.end());
71 }
72
73 TEST(DominatorTreeBatchUpdates, LegalizePostDomUpdates) {
74 CFGHolder Holder;
75 CFGBuilder Builder(Holder.F, {{"A", "B"}}, {});
76
77 BasicBlock *A = Builder.getOrAddBlock("A");
78 BasicBlock *B = Builder.getOrAddBlock("B");
79 BasicBlock *C = Builder.getOrAddBlock("C");
80 BasicBlock *D = Builder.getOrAddBlock("D");
81
82 std::vector Updates = {
83 {Insert, B, C}, {Insert, C, D}, {Delete, B, C}, {Insert, B, C},
84 {Insert, B, D}, {Delete, C, D}, {Delete, A, B}};
85 SmallVector Legalized;
86 PostDomSNCA::LegalizeUpdates(Updates, Legalized);
87 DEBUG(dbgs() << "Legalized postdom updates:\t");
88 DEBUG(for (auto &U : Legalized) dbgs() << U << ", ");
89 DEBUG(dbgs() << "\n");
90 EXPECT_EQ(Legalized.size(), 3UL);
91 EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, C, B}), Legalized.end());
92 EXPECT_NE(llvm::find(Legalized, DomUpdate{Insert, D, B}), Legalized.end());
93 EXPECT_NE(llvm::find(Legalized, DomUpdate{Delete, B, A}), Legalized.end());
94 }
95
96 TEST(DominatorTreeBatchUpdates, SingleInsertion) {
97 CFGHolder Holder;
98 CFGBuilder Builder(Holder.F, {{"A", "B"}}, {{CFGInsert, {"B", "C"}}});
99
100 DominatorTree DT(*Holder.F);
101 EXPECT_TRUE(DT.verify());
102 PostDomTree PDT(*Holder.F);
103 EXPECT_TRUE(DT.verify());
104
105 BasicBlock *B = Builder.getOrAddBlock("B");
106 BasicBlock *C = Builder.getOrAddBlock("C");
107 std::vector Updates = {{Insert, B, C}};
108
109 ASSERT_TRUE(Builder.applyUpdate());
110
111 DT.applyUpdates(Updates);
112 EXPECT_TRUE(DT.verify());
113 PDT.applyUpdates(Updates);
114 EXPECT_TRUE(PDT.verify());
115 }
116
117 TEST(DominatorTreeBatchUpdates, SingleDeletion) {
118 CFGHolder Holder;
119 CFGBuilder Builder(Holder.F, {{"A", "B"}, {"B", "C"}},
120 {{CFGDelete, {"B", "C"}}});
121
122 DominatorTree DT(*Holder.F);
123 EXPECT_TRUE(DT.verify());
124 PostDomTree PDT(*Holder.F);
125 EXPECT_TRUE(DT.verify());
126
127 BasicBlock *B = Builder.getOrAddBlock("B");
128 BasicBlock *C = Builder.getOrAddBlock("C");
129 std::vector Updates = {{Delete, B, C}};
130
131 ASSERT_TRUE(Builder.applyUpdate());
132
133 DT.applyUpdates(Updates);
134 EXPECT_TRUE(DT.verify());
135 PDT.applyUpdates(Updates);
136 EXPECT_TRUE(PDT.verify());
137 }
138
139 TEST(DominatorTreeBatchUpdates, FewInsertion) {
140 std::vector CFGUpdates = {{CFGInsert, {"B", "C"}},
141 {CFGInsert, {"C", "B"}},
142 {CFGInsert, {"C", "D"}},
143 {CFGInsert, {"D", "E"}}};
144
145 CFGHolder Holder;
146 CFGBuilder Builder(Holder.F, {{"A", "B"}}, CFGUpdates);
147
148 DominatorTree DT(*Holder.F);
149 EXPECT_TRUE(DT.verify());
150 PostDomTree PDT(*Holder.F);
151 EXPECT_TRUE(PDT.verify());
152
153 BasicBlock *B = Builder.getOrAddBlock("B");
154 BasicBlock *C = Builder.getOrAddBlock("C");
155 BasicBlock *D = Builder.getOrAddBlock("D");
156 BasicBlock *E = Builder.getOrAddBlock("E");
157
158 std::vector Updates = {
159 {Insert, B, C}, {Insert, C, B}, {Insert, C, D}, {Insert, D, E}};
160
161 while (Builder.applyUpdate())
162 ;
163
164 DT.applyUpdates(Updates);
165 EXPECT_TRUE(DT.verify());
166 PDT.applyUpdates(Updates);
167 EXPECT_TRUE(PDT.verify());
168 }
169
170 TEST(DominatorTreeBatchUpdates, FewDeletions) {
171 std::vector CFGUpdates = {{CFGDelete, {"B", "C"}},
172 {CFGDelete, {"C", "B"}},
173 {CFGDelete, {"B", "D"}},
174 {CFGDelete, {"D", "E"}}};
175
176 CFGHolder Holder;
177 CFGBuilder Builder(
178 Holder.F, {{"A", "B"}, {"B", "C"}, {"B", "D"}, {"D", "E"}, {"C", "B"}},
179 CFGUpdates);
180
181 DominatorTree DT(*Holder.F);
182 EXPECT_TRUE(DT.verify());
183 PostDomTree PDT(*Holder.F);
184 EXPECT_TRUE(PDT.verify());
185
186 auto Updates = ToDomUpdates(Builder, CFGUpdates);
187
188 while (Builder.applyUpdate())
189 ;
190
191 DT.applyUpdates(Updates);
192 EXPECT_TRUE(DT.verify());
193 PDT.applyUpdates(Updates);
194 EXPECT_TRUE(PDT.verify());
195 }
196
197 TEST(DominatorTreeBatchUpdates, InsertDelete) {
198 std::vector Arcs = {
199 {"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},
200 {"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};
201
202 std::vector Updates = {
203 {CFGInsert, {"2", "4"}}, {CFGInsert, {"12", "10"}},
204 {CFGInsert, {"10", "9"}}, {CFGInsert, {"7", "6"}},
205 {CFGInsert, {"7", "5"}}, {CFGDelete, {"3", "8"}},
206 {CFGInsert, {"10", "7"}}, {CFGInsert, {"2", "8"}},
207 {CFGDelete, {"3", "4"}}, {CFGDelete, {"8", "9"}},
208 {CFGDelete, {"11", "12"}}};
209
210 CFGHolder Holder;
211 CFGBuilder B(Holder.F, Arcs, Updates);
212 DominatorTree DT(*Holder.F);
213 EXPECT_TRUE(DT.verify());
214 PostDomTree PDT(*Holder.F);
215 EXPECT_TRUE(PDT.verify());
216
217 while (B.applyUpdate())
218 ;
219
220 auto DomUpdates = ToDomUpdates(B, Updates);
221 DT.applyUpdates(DomUpdates);
222 EXPECT_TRUE(DT.verify());
223 PDT.applyUpdates(DomUpdates);
224 EXPECT_TRUE(PDT.verify());
225 }
226
227 TEST(DominatorTreeBatchUpdates, InsertDeleteExhaustive) {
228 std::vector Arcs = {
229 {"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},
230 {"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};
231
232 std::vector Updates = {
233 {CFGInsert, {"2", "4"}}, {CFGInsert, {"12", "10"}},
234 {CFGInsert, {"10", "9"}}, {CFGInsert, {"7", "6"}},
235 {CFGInsert, {"7", "5"}}, {CFGDelete, {"3", "8"}},
236 {CFGInsert, {"10", "7"}}, {CFGInsert, {"2", "8"}},
237 {CFGDelete, {"3", "4"}}, {CFGDelete, {"8", "9"}},
238 {CFGDelete, {"11", "12"}}};
239
240 std::mt19937 Generator(0);
241 for (unsigned i = 0; i < 16; ++i) {
242 std::shuffle(Updates.begin(), Updates.end(), Generator);
243 CFGHolder Holder;
244 CFGBuilder B(Holder.F, Arcs, Updates);
245 DominatorTree DT(*Holder.F);
246 EXPECT_TRUE(DT.verify());
247 PostDomTree PDT(*Holder.F);
248 EXPECT_TRUE(PDT.verify());
249
250 while (B.applyUpdate())
251 ;
252
253 auto DomUpdates = ToDomUpdates(B, Updates);
254 DT.applyUpdates(DomUpdates);
255 EXPECT_TRUE(DT.verify());
256 PDT.applyUpdates(DomUpdates);
257 EXPECT_TRUE(PDT.verify());
258 }
259 }