aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h')
-rw-r--r--contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h333
1 files changed, 172 insertions, 161 deletions
diff --git a/contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 971e8305a112..ccceba881718 100644
--- a/contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/contrib/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -1,9 +1,8 @@
//===- GenericDomTreeConstruction.h - Dominator Calculation ------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
/// \file
@@ -16,9 +15,12 @@
/// Loukas Georgiadis, Princeton University, November 2005, pp. 21-23:
/// ftp://ftp.cs.princeton.edu/reports/2005/737.pdf
///
-/// This implements the O(n*log(n)) versions of EVAL and LINK, because it turns
-/// out that the theoretically slower O(n*log(n)) implementation is actually
-/// faster than the almost-linear O(n*alpha(n)) version, even for large CFGs.
+/// Semi-NCA algorithm runs in O(n^2) worst-case time but usually slightly
+/// faster than Simple Lengauer-Tarjan in practice.
+///
+/// O(n^2) worst cases happen when the computation of nearest common ancestors
+/// requires O(n) average time, which is very unlikely in real world. If this
+/// ever turns out to be an issue, consider implementing a hybrid algorithm.
///
/// The file uses the Depth Based Search algorithm to perform incremental
/// updates (insertion and deletions). The implemented algorithm is based on
@@ -255,42 +257,47 @@ struct SemiNCAInfo {
return LastNum;
}
- NodePtr eval(NodePtr VIn, unsigned LastLinked) {
- auto &VInInfo = NodeToInfo[VIn];
- if (VInInfo.DFSNum < LastLinked)
- return VIn;
-
- SmallVector<NodePtr, 32> Work;
- SmallPtrSet<NodePtr, 32> Visited;
-
- if (VInInfo.Parent >= LastLinked)
- Work.push_back(VIn);
-
- while (!Work.empty()) {
- NodePtr V = Work.back();
- auto &VInfo = NodeToInfo[V];
- NodePtr VAncestor = NumToNode[VInfo.Parent];
-
- // Process Ancestor first
- if (Visited.insert(VAncestor).second && VInfo.Parent >= LastLinked) {
- Work.push_back(VAncestor);
- continue;
- }
- Work.pop_back();
-
- // Update VInfo based on Ancestor info
- if (VInfo.Parent < LastLinked)
- continue;
-
- auto &VAInfo = NodeToInfo[VAncestor];
- NodePtr VAncestorLabel = VAInfo.Label;
- NodePtr VLabel = VInfo.Label;
- if (NodeToInfo[VAncestorLabel].Semi < NodeToInfo[VLabel].Semi)
- VInfo.Label = VAncestorLabel;
- VInfo.Parent = VAInfo.Parent;
- }
-
- return VInInfo.Label;
+ // V is a predecessor of W. eval() returns V if V < W, otherwise the minimum
+ // of sdom(U), where U > W and there is a virtual forest path from U to V. The
+ // virtual forest consists of linked edges of processed vertices.
+ //
+ // We can follow Parent pointers (virtual forest edges) to determine the
+ // ancestor U with minimum sdom(U). But it is slow and thus we employ the path
+ // compression technique to speed up to O(m*log(n)). Theoretically the virtual
+ // forest can be organized as balanced trees to achieve almost linear
+ // O(m*alpha(m,n)) running time. But it requires two auxiliary arrays (Size
+ // and Child) and is unlikely to be faster than the simple implementation.
+ //
+ // For each vertex V, its Label points to the vertex with the minimal sdom(U)
+ // (Semi) in its path from V (included) to NodeToInfo[V].Parent (excluded).
+ NodePtr eval(NodePtr V, unsigned LastLinked,
+ SmallVectorImpl<InfoRec *> &Stack) {
+ InfoRec *VInfo = &NodeToInfo[V];
+ if (VInfo->Parent < LastLinked)
+ return VInfo->Label;
+
+ // Store ancestors except the last (root of a virtual tree) into a stack.
+ assert(Stack.empty());
+ do {
+ Stack.push_back(VInfo);
+ VInfo = &NodeToInfo[NumToNode[VInfo->Parent]];
+ } while (VInfo->Parent >= LastLinked);
+
+ // Path compression. Point each vertex's Parent to the root and update its
+ // Label if any of its ancestors (PInfo->Label) has a smaller Semi.
+ const InfoRec *PInfo = VInfo;
+ const InfoRec *PLabelInfo = &NodeToInfo[PInfo->Label];
+ do {
+ VInfo = Stack.pop_back_val();
+ VInfo->Parent = PInfo->Parent;
+ const InfoRec *VLabelInfo = &NodeToInfo[VInfo->Label];
+ if (PLabelInfo->Semi < VLabelInfo->Semi)
+ VInfo->Label = PInfo->Label;
+ else
+ PLabelInfo = VLabelInfo;
+ PInfo = VInfo;
+ } while (!Stack.empty());
+ return VInfo->Label;
}
// This function requires DFS to be run before calling it.
@@ -304,6 +311,7 @@ struct SemiNCAInfo {
}
// Step #1: Calculate the semidominators of all vertices.
+ SmallVector<InfoRec *, 32> EvalStack;
for (unsigned i = NextDFSNum - 1; i >= 2; --i) {
NodePtr W = NumToNode[i];
auto &WInfo = NodeToInfo[W];
@@ -319,7 +327,7 @@ struct SemiNCAInfo {
if (TN && TN->getLevel() < MinLevel)
continue;
- unsigned SemiU = NodeToInfo[eval(N, i + 1)].Semi;
+ unsigned SemiU = NodeToInfo[eval(N, i + 1, EvalStack)].Semi;
if (SemiU < WInfo.Semi) WInfo.Semi = SemiU;
}
}
@@ -620,21 +628,22 @@ struct SemiNCAInfo {
// Helper struct used during edge insertions.
struct InsertionInfo {
- using BucketElementTy = std::pair<unsigned, TreeNodePtr>;
- struct DecreasingLevel {
- bool operator()(const BucketElementTy &First,
- const BucketElementTy &Second) const {
- return First.first > Second.first;
+ struct Compare {
+ bool operator()(TreeNodePtr LHS, TreeNodePtr RHS) const {
+ return LHS->getLevel() < RHS->getLevel();
}
};
- std::priority_queue<BucketElementTy, SmallVector<BucketElementTy, 8>,
- DecreasingLevel>
- Bucket; // Queue of tree nodes sorted by level in descending order.
- SmallDenseSet<TreeNodePtr, 8> Affected;
- SmallDenseMap<TreeNodePtr, unsigned, 8> Visited;
- SmallVector<TreeNodePtr, 8> AffectedQueue;
- SmallVector<TreeNodePtr, 8> VisitedNotAffectedQueue;
+ // Bucket queue of tree nodes ordered by descending level. For simplicity,
+ // we use a priority_queue here.
+ std::priority_queue<TreeNodePtr, SmallVector<TreeNodePtr, 8>,
+ Compare>
+ Bucket;
+ SmallDenseSet<TreeNodePtr, 8> Visited;
+ SmallVector<TreeNodePtr, 8> Affected;
+#ifndef NDEBUG
+ SmallVector<TreeNodePtr, 8> VisitedUnaffected;
+#endif
};
static void InsertEdge(DomTreeT &DT, const BatchUpdatePtr BUI,
@@ -689,6 +698,17 @@ struct SemiNCAInfo {
return true;
}
+ static bool isPermutation(const SmallVectorImpl<NodePtr> &A,
+ const SmallVectorImpl<NodePtr> &B) {
+ if (A.size() != B.size())
+ return false;
+ SmallPtrSet<NodePtr, 4> Set(A.begin(), A.end());
+ for (NodePtr N : B)
+ if (Set.count(N) == 0)
+ return false;
+ return true;
+ }
+
// Updates the set of roots after insertion or deletion. This ensures that
// roots are the same when after a series of updates and when the tree would
// be built from scratch.
@@ -702,9 +722,8 @@ struct SemiNCAInfo {
return;
// Recalculate the set of roots.
- auto Roots = FindRoots(DT, BUI);
- if (DT.Roots.size() != Roots.size() ||
- !std::is_permutation(DT.Roots.begin(), DT.Roots.end(), Roots.begin())) {
+ RootsT Roots = FindRoots(DT, BUI);
+ if (!isPermutation(DT.Roots, Roots)) {
// The roots chosen in the CFG have changed. This is because the
// incremental algorithm does not really know or use the set of roots and
// can make a different (implicit) decision about which node within an
@@ -715,7 +734,6 @@ struct SemiNCAInfo {
// It may be possible to update the tree without recalculating it, but
// we do not know yet how to do it, and it happens rarely in practise.
CalculateFromScratch(DT, BUI);
- return;
}
}
@@ -737,128 +755,113 @@ struct SemiNCAInfo {
assert(NCD);
LLVM_DEBUG(dbgs() << "\t\tNCA == " << BlockNamePrinter(NCD) << "\n");
- const TreeNodePtr ToIDom = To->getIDom();
+ const unsigned NCDLevel = NCD->getLevel();
- // Nothing affected -- NCA property holds.
- // (Based on the lemma 2.5 from the second paper.)
- if (NCD == To || NCD == ToIDom) return;
+ // Based on Lemma 2.5 from the second paper, after insertion of (From,To), v
+ // is affected iff depth(NCD)+1 < depth(v) && a path P from To to v exists
+ // where every w on P s.t. depth(v) <= depth(w)
+ //
+ // This reduces to a widest path problem (maximizing the depth of the
+ // minimum vertex in the path) which can be solved by a modified version of
+ // Dijkstra with a bucket queue (named depth-based search in the paper).
+
+ // To is in the path, so depth(NCD)+1 < depth(v) <= depth(To). Nothing
+ // affected if this does not hold.
+ if (NCDLevel + 1 >= To->getLevel())
+ return;
- // Identify and collect affected nodes.
InsertionInfo II;
- LLVM_DEBUG(dbgs() << "Marking " << BlockNamePrinter(To)
- << " as affected\n");
- II.Affected.insert(To);
- const unsigned ToLevel = To->getLevel();
- LLVM_DEBUG(dbgs() << "Putting " << BlockNamePrinter(To)
- << " into a Bucket\n");
- II.Bucket.push({ToLevel, To});
+ SmallVector<TreeNodePtr, 8> UnaffectedOnCurrentLevel;
+ II.Bucket.push(To);
+ II.Visited.insert(To);
while (!II.Bucket.empty()) {
- const TreeNodePtr CurrentNode = II.Bucket.top().second;
- const unsigned CurrentLevel = CurrentNode->getLevel();
+ TreeNodePtr TN = II.Bucket.top();
II.Bucket.pop();
- LLVM_DEBUG(dbgs() << "\tAdding to Visited and AffectedQueue: "
- << BlockNamePrinter(CurrentNode) << "\n");
-
- II.Visited.insert({CurrentNode, CurrentLevel});
- II.AffectedQueue.push_back(CurrentNode);
+ II.Affected.push_back(TN);
+
+ const unsigned CurrentLevel = TN->getLevel();
+ LLVM_DEBUG(dbgs() << "Mark " << BlockNamePrinter(TN) <<
+ "as affected, CurrentLevel " << CurrentLevel << "\n");
+
+ assert(TN->getBlock() && II.Visited.count(TN) && "Preconditions!");
+
+ while (true) {
+ // Unlike regular Dijkstra, we have an inner loop to expand more
+ // vertices. The first iteration is for the (affected) vertex popped
+ // from II.Bucket and the rest are for vertices in
+ // UnaffectedOnCurrentLevel, which may eventually expand to affected
+ // vertices.
+ //
+ // Invariant: there is an optimal path from `To` to TN with the minimum
+ // depth being CurrentLevel.
+ for (const NodePtr Succ :
+ ChildrenGetter<IsPostDom>::Get(TN->getBlock(), BUI)) {
+ const TreeNodePtr SuccTN = DT.getNode(Succ);
+ assert(SuccTN &&
+ "Unreachable successor found at reachable insertion");
+ const unsigned SuccLevel = SuccTN->getLevel();
+
+ LLVM_DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ)
+ << ", level = " << SuccLevel << "\n");
+
+ // There is an optimal path from `To` to Succ with the minimum depth
+ // being min(CurrentLevel, SuccLevel).
+ //
+ // If depth(NCD)+1 < depth(Succ) is not satisfied, Succ is unaffected
+ // and no affected vertex may be reached by a path passing through it.
+ // Stop here. Also, Succ may be visited by other predecessors but the
+ // first visit has the optimal path. Stop if Succ has been visited.
+ if (SuccLevel <= NCDLevel + 1 || !II.Visited.insert(SuccTN).second)
+ continue;
+
+ if (SuccLevel > CurrentLevel) {
+ // Succ is unaffected but it may (transitively) expand to affected
+ // vertices. Store it in UnaffectedOnCurrentLevel.
+ LLVM_DEBUG(dbgs() << "\t\tMarking visited not affected "
+ << BlockNamePrinter(Succ) << "\n");
+ UnaffectedOnCurrentLevel.push_back(SuccTN);
+#ifndef NDEBUG
+ II.VisitedUnaffected.push_back(SuccTN);
+#endif
+ } else {
+ // The condition is satisfied (Succ is affected). Add Succ to the
+ // bucket queue.
+ LLVM_DEBUG(dbgs() << "\t\tAdd " << BlockNamePrinter(Succ)
+ << " to a Bucket\n");
+ II.Bucket.push(SuccTN);
+ }
+ }
- // Discover and collect affected successors of the current node.
- VisitInsertion(DT, BUI, CurrentNode, CurrentLevel, NCD, II);
+ if (UnaffectedOnCurrentLevel.empty())
+ break;
+ TN = UnaffectedOnCurrentLevel.pop_back_val();
+ LLVM_DEBUG(dbgs() << " Next: " << BlockNamePrinter(TN) << "\n");
+ }
}
// Finish by updating immediate dominators and levels.
UpdateInsertion(DT, BUI, NCD, II);
}
- // Visits an affected node and collect its affected successors.
- static void VisitInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
- const TreeNodePtr TN, const unsigned RootLevel,
- const TreeNodePtr NCD, InsertionInfo &II) {
- const unsigned NCDLevel = NCD->getLevel();
- LLVM_DEBUG(dbgs() << "Visiting " << BlockNamePrinter(TN) << ", RootLevel "
- << RootLevel << "\n");
-
- SmallVector<TreeNodePtr, 8> Stack = {TN};
- assert(TN->getBlock() && II.Visited.count(TN) && "Preconditions!");
-
- SmallPtrSet<TreeNodePtr, 8> Processed;
-
- do {
- TreeNodePtr Next = Stack.pop_back_val();
- LLVM_DEBUG(dbgs() << " Next: " << BlockNamePrinter(Next) << "\n");
-
- for (const NodePtr Succ :
- ChildrenGetter<IsPostDom>::Get(Next->getBlock(), BUI)) {
- const TreeNodePtr SuccTN = DT.getNode(Succ);
- assert(SuccTN && "Unreachable successor found at reachable insertion");
- const unsigned SuccLevel = SuccTN->getLevel();
-
- LLVM_DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ)
- << ", level = " << SuccLevel << "\n");
-
- // Do not process the same node multiple times.
- if (Processed.count(Next) > 0)
- continue;
-
- // Succ dominated by subtree From -- not affected.
- // (Based on the lemma 2.5 from the second paper.)
- if (SuccLevel > RootLevel) {
- LLVM_DEBUG(dbgs() << "\t\tDominated by subtree From\n");
- if (II.Visited.count(SuccTN) != 0) {
- LLVM_DEBUG(dbgs() << "\t\t\talready visited at level "
- << II.Visited[SuccTN] << "\n\t\t\tcurrent level "
- << RootLevel << ")\n");
-
- // A node can be necessary to visit again if we see it again at
- // a lower level than before.
- if (II.Visited[SuccTN] >= RootLevel)
- continue;
- }
-
- LLVM_DEBUG(dbgs() << "\t\tMarking visited not affected "
- << BlockNamePrinter(Succ) << "\n");
- II.Visited.insert({SuccTN, RootLevel});
- II.VisitedNotAffectedQueue.push_back(SuccTN);
- Stack.push_back(SuccTN);
- } else if ((SuccLevel > NCDLevel + 1) &&
- II.Affected.count(SuccTN) == 0) {
- LLVM_DEBUG(dbgs() << "\t\tMarking affected and adding "
- << BlockNamePrinter(Succ) << " to a Bucket\n");
- II.Affected.insert(SuccTN);
- II.Bucket.push({SuccLevel, SuccTN});
- }
- }
-
- Processed.insert(Next);
- } while (!Stack.empty());
- }
-
// Updates immediate dominators and levels after insertion.
static void UpdateInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
const TreeNodePtr NCD, InsertionInfo &II) {
LLVM_DEBUG(dbgs() << "Updating NCD = " << BlockNamePrinter(NCD) << "\n");
- for (const TreeNodePtr TN : II.AffectedQueue) {
+ for (const TreeNodePtr TN : II.Affected) {
LLVM_DEBUG(dbgs() << "\tIDom(" << BlockNamePrinter(TN)
<< ") = " << BlockNamePrinter(NCD) << "\n");
TN->setIDom(NCD);
}
- UpdateLevelsAfterInsertion(II);
- if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI);
- }
-
- static void UpdateLevelsAfterInsertion(InsertionInfo &II) {
- LLVM_DEBUG(
- dbgs() << "Updating levels for visited but not affected nodes\n");
+#ifndef NDEBUG
+ for (const TreeNodePtr TN : II.VisitedUnaffected)
+ assert(TN->getLevel() == TN->getIDom()->getLevel() + 1 &&
+ "TN should have been updated by an affected ancestor");
+#endif
- for (const TreeNodePtr TN : II.VisitedNotAffectedQueue) {
- LLVM_DEBUG(dbgs() << "\tlevel(" << BlockNamePrinter(TN) << ") = ("
- << BlockNamePrinter(TN->getIDom()) << ") "
- << TN->getIDom()->getLevel() << " + 1\n");
- TN->UpdateLevel();
- }
+ if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI);
}
// Handles insertion to previously unreachable nodes.
@@ -1182,6 +1185,10 @@ struct SemiNCAInfo {
BUI.FuturePredecessors[U.getTo()].push_back({U.getFrom(), U.getKind()});
}
+#if 0
+ // FIXME: The LLVM_DEBUG macro only plays well with a modular
+ // build of LLVM when the header is marked as textual, but doing
+ // so causes redefinition errors.
LLVM_DEBUG(dbgs() << "About to apply " << NumLegalized << " updates\n");
LLVM_DEBUG(if (NumLegalized < 32) for (const auto &U
: reverse(BUI.Updates)) {
@@ -1190,6 +1197,7 @@ struct SemiNCAInfo {
dbgs() << "\n";
});
LLVM_DEBUG(dbgs() << "\n");
+#endif
// Recalculate the DominatorTree when the number of updates
// exceeds a threshold, which usually makes direct updating slower than
@@ -1215,8 +1223,13 @@ struct SemiNCAInfo {
static void ApplyNextUpdate(DomTreeT &DT, BatchUpdateInfo &BUI) {
assert(!BUI.Updates.empty() && "No updates to apply!");
UpdateT CurrentUpdate = BUI.Updates.pop_back_val();
+#if 0
+ // FIXME: The LLVM_DEBUG macro only plays well with a modular
+ // build of LLVM when the header is marked as textual, but doing
+ // so causes redefinition errors.
LLVM_DEBUG(dbgs() << "Applying update: ");
LLVM_DEBUG(CurrentUpdate.dump(); dbgs() << "\n");
+#endif
// Move to the next snapshot of the CFG by removing the reverse-applied
// current update. Since updates are performed in the same order they are
@@ -1270,9 +1283,7 @@ struct SemiNCAInfo {
}
RootsT ComputedRoots = FindRoots(DT, nullptr);
- if (DT.Roots.size() != ComputedRoots.size() ||
- !std::is_permutation(DT.Roots.begin(), DT.Roots.end(),
- ComputedRoots.begin())) {
+ if (!isPermutation(DT.Roots, ComputedRoots)) {
errs() << "Tree has different roots than freshly computed ones!\n";
errs() << "\tPDT roots: ";
for (const NodePtr N : DT.Roots) errs() << BlockNamePrinter(N) << ", ";