summaryrefslogtreecommitdiff
path: root/include/llvm/Support/GenericDomTreeConstruction.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/llvm/Support/GenericDomTreeConstruction.h')
-rw-r--r--include/llvm/Support/GenericDomTreeConstruction.h1008
1 files changed, 818 insertions, 190 deletions
diff --git a/include/llvm/Support/GenericDomTreeConstruction.h b/include/llvm/Support/GenericDomTreeConstruction.h
index be90afa4c3c8..8f801662d0fb 100644
--- a/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/include/llvm/Support/GenericDomTreeConstruction.h
@@ -21,8 +21,8 @@
/// faster than the almost-linear O(n*alpha(n)) version, even for large CFGs.
///
/// The file uses the Depth Based Search algorithm to perform incremental
-/// upates (insertion and deletions). The implemented algorithm is based on this
-/// publication:
+/// updates (insertion and deletions). The implemented algorithm is based on
+/// this publication:
///
/// An Experimental Study of Dynamic Dominators
/// Loukas Georgiadis, et al., April 12 2016, pp. 5-7, 9-10:
@@ -34,8 +34,10 @@
#define LLVM_SUPPORT_GENERICDOMTREECONSTRUCTION_H
#include <queue>
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/GenericDomTree.h"
@@ -45,25 +47,12 @@
namespace llvm {
namespace DomTreeBuilder {
-template <typename NodePtr, bool Inverse>
-struct ChildrenGetter {
- static auto Get(NodePtr N) -> decltype(reverse(children<NodePtr>(N))) {
- return reverse(children<NodePtr>(N));
- }
-};
-
-template <typename NodePtr>
-struct ChildrenGetter<NodePtr, true> {
- static auto Get(NodePtr N) -> decltype(inverse_children<NodePtr>(N)) {
- return inverse_children<NodePtr>(N);
- }
-};
-
template <typename DomTreeT>
struct SemiNCAInfo {
using NodePtr = typename DomTreeT::NodePtr;
using NodeT = typename DomTreeT::NodeType;
using TreeNodePtr = DomTreeNodeBase<NodeT> *;
+ using RootsT = decltype(DomTreeT::Roots);
static constexpr bool IsPostDom = DomTreeT::IsPostDominator;
// Information record used by Semi-NCA during tree construction.
@@ -81,11 +70,99 @@ struct SemiNCAInfo {
std::vector<NodePtr> NumToNode = {nullptr};
DenseMap<NodePtr, InfoRec> NodeToInfo;
+ using UpdateT = typename DomTreeT::UpdateType;
+ struct BatchUpdateInfo {
+ SmallVector<UpdateT, 4> Updates;
+ using NodePtrAndKind = PointerIntPair<NodePtr, 1, UpdateKind>;
+
+ // In order to be able to walk a CFG that is out of sync with the CFG
+ // DominatorTree last knew about, use the list of updates to reconstruct
+ // previous CFG versions of the current CFG. For each node, we store a set
+ // of its virtually added/deleted future successors and predecessors.
+ // Note that these children are from the future relative to what the
+ // DominatorTree knows about -- using them to gets us some snapshot of the
+ // CFG from the past (relative to the state of the CFG).
+ DenseMap<NodePtr, SmallDenseSet<NodePtrAndKind, 4>> FutureSuccessors;
+ DenseMap<NodePtr, SmallDenseSet<NodePtrAndKind, 4>> FuturePredecessors;
+ // Remembers if the whole tree was recalculated at some point during the
+ // current batch update.
+ bool IsRecalculated = false;
+ };
+
+ BatchUpdateInfo *BatchUpdates;
+ using BatchUpdatePtr = BatchUpdateInfo *;
+
+ // If BUI is a nullptr, then there's no batch update in progress.
+ SemiNCAInfo(BatchUpdatePtr BUI) : BatchUpdates(BUI) {}
+
void clear() {
NumToNode = {nullptr}; // Restore to initial state with a dummy start node.
NodeToInfo.clear();
+ // Don't reset the pointer to BatchUpdateInfo here -- if there's an update
+ // in progress, we need this information to continue it.
}
+ template <bool Inverse>
+ struct ChildrenGetter {
+ using ResultTy = SmallVector<NodePtr, 8>;
+
+ static ResultTy Get(NodePtr N, std::integral_constant<bool, false>) {
+ auto RChildren = reverse(children<NodePtr>(N));
+ return ResultTy(RChildren.begin(), RChildren.end());
+ }
+
+ static ResultTy Get(NodePtr N, std::integral_constant<bool, true>) {
+ auto IChildren = inverse_children<NodePtr>(N);
+ return ResultTy(IChildren.begin(), IChildren.end());
+ }
+
+ using Tag = std::integral_constant<bool, Inverse>;
+
+ // The function below is the core part of the batch updater. It allows the
+ // Depth Based Search algorithm to perform incremental updates in lockstep
+ // with updates to the CFG. We emulated lockstep CFG updates by getting its
+ // next snapshots by reverse-applying future updates.
+ static ResultTy Get(NodePtr N, BatchUpdatePtr BUI) {
+ ResultTy Res = Get(N, Tag());
+ // If there's no batch update in progress, simply return node's children.
+ if (!BUI) return Res;
+
+ // CFG children are actually its *most current* children, and we have to
+ // reverse-apply the future updates to get the node's children at the
+ // point in time the update was performed.
+ auto &FutureChildren = (Inverse != IsPostDom) ? BUI->FuturePredecessors
+ : BUI->FutureSuccessors;
+ auto FCIt = FutureChildren.find(N);
+ if (FCIt == FutureChildren.end()) return Res;
+
+ for (auto ChildAndKind : FCIt->second) {
+ const NodePtr Child = ChildAndKind.getPointer();
+ const UpdateKind UK = ChildAndKind.getInt();
+
+ // Reverse-apply the future update.
+ if (UK == UpdateKind::Insert) {
+ // If there's an insertion in the future, it means that the edge must
+ // exist in the current CFG, but was not present in it before.
+ assert(llvm::find(Res, Child) != Res.end()
+ && "Expected child not found in the CFG");
+ Res.erase(std::remove(Res.begin(), Res.end(), Child), Res.end());
+ DEBUG(dbgs() << "\tHiding edge " << BlockNamePrinter(N) << " -> "
+ << BlockNamePrinter(Child) << "\n");
+ } else {
+ // If there's an deletion in the future, it means that the edge cannot
+ // exist in the current CFG, but existed in it before.
+ assert(llvm::find(Res, Child) == Res.end() &&
+ "Unexpected child found in the CFG");
+ DEBUG(dbgs() << "\tShowing virtual edge " << BlockNamePrinter(N)
+ << " -> " << BlockNamePrinter(Child) << "\n");
+ Res.push_back(Child);
+ }
+ }
+
+ return Res;
+ }
+ };
+
NodePtr getIDom(NodePtr BB) const {
auto InfoIt = NodeToInfo.find(BB);
if (InfoIt == NodeToInfo.end()) return nullptr;
@@ -131,7 +208,11 @@ struct SemiNCAInfo {
// Custom DFS implementation which can skip nodes based on a provided
// predicate. It also collects ReverseChildren so that we don't have to spend
// time getting predecessors in SemiNCA.
- template <bool Inverse, typename DescendCondition>
+ //
+ // If IsReverse is set to true, the DFS walk will be performed backwards
+ // relative to IsPostDom -- using reverse edges for dominators and forward
+ // edges for postdominators.
+ template <bool IsReverse = false, typename DescendCondition>
unsigned runDFS(NodePtr V, unsigned LastNum, DescendCondition Condition,
unsigned AttachToNum) {
assert(V);
@@ -148,10 +229,12 @@ struct SemiNCAInfo {
BBInfo.Label = BB;
NumToNode.push_back(BB);
- for (const NodePtr Succ : ChildrenGetter<NodePtr, Inverse>::Get(BB)) {
+ constexpr bool Direction = IsReverse != IsPostDom; // XOR.
+ for (const NodePtr Succ :
+ ChildrenGetter<Direction>::Get(BB, BatchUpdates)) {
const auto SIT = NodeToInfo.find(Succ);
// Don't visit nodes more than once but remember to collect
- // RerverseChildren.
+ // ReverseChildren.
if (SIT != NodeToInfo.end() && SIT->second.DFSNum != 0) {
if (Succ != BB) SIT->second.ReverseChildren.push_back(BB);
continue;
@@ -256,51 +339,244 @@ struct SemiNCAInfo {
}
}
- template <typename DescendCondition>
- unsigned doFullDFSWalk(const DomTreeT &DT, DescendCondition DC) {
- unsigned Num = 0;
+ // PostDominatorTree always has a virtual root that represents a virtual CFG
+ // node that serves as a single exit from the function. All the other exits
+ // (CFG nodes with terminators and nodes in infinite loops are logically
+ // connected to this virtual CFG exit node).
+ // This functions maps a nullptr CFG node to the virtual root tree node.
+ void addVirtualRoot() {
+ assert(IsPostDom && "Only postdominators have a virtual root");
+ assert(NumToNode.size() == 1 && "SNCAInfo must be freshly constructed");
- if (DT.Roots.size() > 1) {
- auto &BBInfo = NodeToInfo[nullptr];
- BBInfo.DFSNum = BBInfo.Semi = ++Num;
- BBInfo.Label = nullptr;
+ auto &BBInfo = NodeToInfo[nullptr];
+ BBInfo.DFSNum = BBInfo.Semi = 1;
+ BBInfo.Label = nullptr;
- NumToNode.push_back(nullptr); // NumToNode[n] = V;
+ NumToNode.push_back(nullptr); // NumToNode[1] = nullptr;
+ }
+
+ // For postdominators, nodes with no forward successors are trivial roots that
+ // are always selected as tree roots. Roots with forward successors correspond
+ // to CFG nodes within infinite loops.
+ static bool HasForwardSuccessors(const NodePtr N, BatchUpdatePtr BUI) {
+ assert(N && "N must be a valid node");
+ return !ChildrenGetter<false>::Get(N, BUI).empty();
+ }
+
+ static NodePtr GetEntryNode(const DomTreeT &DT) {
+ assert(DT.Parent && "Parent not set");
+ return GraphTraits<typename DomTreeT::ParentPtr>::getEntryNode(DT.Parent);
+ }
+
+ // Finds all roots without relaying on the set of roots already stored in the
+ // tree.
+ // We define roots to be some non-redundant set of the CFG nodes
+ static RootsT FindRoots(const DomTreeT &DT, BatchUpdatePtr BUI) {
+ assert(DT.Parent && "Parent pointer is not set");
+ RootsT Roots;
+
+ // For dominators, function entry CFG node is always a tree root node.
+ if (!IsPostDom) {
+ Roots.push_back(GetEntryNode(DT));
+ return Roots;
}
- if (DT.isPostDominator()) {
- for (auto *Root : DT.Roots) Num = runDFS<true>(Root, Num, DC, 1);
- } else {
- assert(DT.Roots.size() == 1);
- Num = runDFS<false>(DT.Roots[0], Num, DC, Num);
+ SemiNCAInfo SNCA(BUI);
+
+ // PostDominatorTree always has a virtual root.
+ SNCA.addVirtualRoot();
+ unsigned Num = 1;
+
+ DEBUG(dbgs() << "\t\tLooking for trivial roots\n");
+
+ // Step #1: Find all the trivial roots that are going to will definitely
+ // remain tree roots.
+ unsigned Total = 0;
+ // It may happen that there are some new nodes in the CFG that are result of
+ // the ongoing batch update, but we cannot really pretend that they don't
+ // exist -- we won't see any outgoing or incoming edges to them, so it's
+ // fine to discover them here, as they would end up appearing in the CFG at
+ // some point anyway.
+ for (const NodePtr N : nodes(DT.Parent)) {
+ ++Total;
+ // If it has no *successors*, it is definitely a root.
+ if (!HasForwardSuccessors(N, BUI)) {
+ Roots.push_back(N);
+ // Run DFS not to walk this part of CFG later.
+ Num = SNCA.runDFS(N, Num, AlwaysDescend, 1);
+ DEBUG(dbgs() << "Found a new trivial root: " << BlockNamePrinter(N)
+ << "\n");
+ DEBUG(dbgs() << "Last visited node: "
+ << BlockNamePrinter(SNCA.NumToNode[Num]) << "\n");
+ }
}
- return Num;
+ DEBUG(dbgs() << "\t\tLooking for non-trivial roots\n");
+
+ // Step #2: Find all non-trivial root candidates. Those are CFG nodes that
+ // are reverse-unreachable were not visited by previous DFS walks (i.e. CFG
+ // nodes in infinite loops).
+ bool HasNonTrivialRoots = false;
+ // Accounting for the virtual exit, see if we had any reverse-unreachable
+ // nodes.
+ if (Total + 1 != Num) {
+ HasNonTrivialRoots = true;
+ // Make another DFS pass over all other nodes to find the
+ // reverse-unreachable blocks, and find the furthest paths we'll be able
+ // to make.
+ // Note that this looks N^2, but it's really 2N worst case, if every node
+ // is unreachable. This is because we are still going to only visit each
+ // unreachable node once, we may just visit it in two directions,
+ // depending on how lucky we get.
+ SmallPtrSet<NodePtr, 4> ConnectToExitBlock;
+ for (const NodePtr I : nodes(DT.Parent)) {
+ if (SNCA.NodeToInfo.count(I) == 0) {
+ DEBUG(dbgs() << "\t\t\tVisiting node " << BlockNamePrinter(I)
+ << "\n");
+ // Find the furthest away we can get by following successors, then
+ // follow them in reverse. This gives us some reasonable answer about
+ // the post-dom tree inside any infinite loop. In particular, it
+ // guarantees we get to the farthest away point along *some*
+ // path. This also matches the GCC's behavior.
+ // If we really wanted a totally complete picture of dominance inside
+ // this infinite loop, we could do it with SCC-like algorithms to find
+ // the lowest and highest points in the infinite loop. In theory, it
+ // would be nice to give the canonical backedge for the loop, but it's
+ // expensive and does not always lead to a minimal set of roots.
+ DEBUG(dbgs() << "\t\t\tRunning forward DFS\n");
+
+ const unsigned NewNum = SNCA.runDFS<true>(I, Num, AlwaysDescend, Num);
+ const NodePtr FurthestAway = SNCA.NumToNode[NewNum];
+ DEBUG(dbgs() << "\t\t\tFound a new furthest away node "
+ << "(non-trivial root): "
+ << BlockNamePrinter(FurthestAway) << "\n");
+ ConnectToExitBlock.insert(FurthestAway);
+ Roots.push_back(FurthestAway);
+ DEBUG(dbgs() << "\t\t\tPrev DFSNum: " << Num << ", new DFSNum: "
+ << NewNum << "\n\t\t\tRemoving DFS info\n");
+ for (unsigned i = NewNum; i > Num; --i) {
+ const NodePtr N = SNCA.NumToNode[i];
+ DEBUG(dbgs() << "\t\t\t\tRemoving DFS info for "
+ << BlockNamePrinter(N) << "\n");
+ SNCA.NodeToInfo.erase(N);
+ SNCA.NumToNode.pop_back();
+ }
+ const unsigned PrevNum = Num;
+ DEBUG(dbgs() << "\t\t\tRunning reverse DFS\n");
+ Num = SNCA.runDFS(FurthestAway, Num, AlwaysDescend, 1);
+ for (unsigned i = PrevNum + 1; i <= Num; ++i)
+ DEBUG(dbgs() << "\t\t\t\tfound node "
+ << BlockNamePrinter(SNCA.NumToNode[i]) << "\n");
+ }
+ }
+ }
+
+ DEBUG(dbgs() << "Total: " << Total << ", Num: " << Num << "\n");
+ DEBUG(dbgs() << "Discovered CFG nodes:\n");
+ DEBUG(for (size_t i = 0; i <= Num; ++i) dbgs()
+ << i << ": " << BlockNamePrinter(SNCA.NumToNode[i]) << "\n");
+
+ assert((Total + 1 == Num) && "Everything should have been visited");
+
+ // Step #3: If we found some non-trivial roots, make them non-redundant.
+ if (HasNonTrivialRoots) RemoveRedundantRoots(DT, BUI, Roots);
+
+ DEBUG(dbgs() << "Found roots: ");
+ DEBUG(for (auto *Root : Roots) dbgs() << BlockNamePrinter(Root) << " ");
+ DEBUG(dbgs() << "\n");
+
+ return Roots;
}
- void calculateFromScratch(DomTreeT &DT, const unsigned NumBlocks) {
+ // This function only makes sense for postdominators.
+ // We define roots to be some set of CFG nodes where (reverse) DFS walks have
+ // to start in order to visit all the CFG nodes (including the
+ // reverse-unreachable ones).
+ // When the search for non-trivial roots is done it may happen that some of
+ // the non-trivial roots are reverse-reachable from other non-trivial roots,
+ // which makes them redundant. This function removes them from the set of
+ // input roots.
+ static void RemoveRedundantRoots(const DomTreeT &DT, BatchUpdatePtr BUI,
+ RootsT &Roots) {
+ assert(IsPostDom && "This function is for postdominators only");
+ DEBUG(dbgs() << "Removing redundant roots\n");
+
+ SemiNCAInfo SNCA(BUI);
+
+ for (unsigned i = 0; i < Roots.size(); ++i) {
+ auto &Root = Roots[i];
+ // Trivial roots are always non-redundant.
+ if (!HasForwardSuccessors(Root, BUI)) continue;
+ DEBUG(dbgs() << "\tChecking if " << BlockNamePrinter(Root)
+ << " remains a root\n");
+ SNCA.clear();
+ // Do a forward walk looking for the other roots.
+ const unsigned Num = SNCA.runDFS<true>(Root, 0, AlwaysDescend, 0);
+ // Skip the start node and begin from the second one (note that DFS uses
+ // 1-based indexing).
+ for (unsigned x = 2; x <= Num; ++x) {
+ const NodePtr N = SNCA.NumToNode[x];
+ // If we wound another root in a (forward) DFS walk, remove the current
+ // root from the set of roots, as it is reverse-reachable from the other
+ // one.
+ if (llvm::find(Roots, N) != Roots.end()) {
+ DEBUG(dbgs() << "\tForward DFS walk found another root "
+ << BlockNamePrinter(N) << "\n\tRemoving root "
+ << BlockNamePrinter(Root) << "\n");
+ std::swap(Root, Roots.back());
+ Roots.pop_back();
+
+ // Root at the back takes the current root's place.
+ // Start the next loop iteration with the same index.
+ --i;
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename DescendCondition>
+ void doFullDFSWalk(const DomTreeT &DT, DescendCondition DC) {
+ if (!IsPostDom) {
+ assert(DT.Roots.size() == 1 && "Dominators should have a singe root");
+ runDFS(DT.Roots[0], 0, DC, 0);
+ return;
+ }
+
+ addVirtualRoot();
+ unsigned Num = 1;
+ for (const NodePtr Root : DT.Roots) Num = runDFS(Root, Num, DC, 0);
+ }
+
+ static void CalculateFromScratch(DomTreeT &DT, BatchUpdatePtr BUI) {
+ auto *Parent = DT.Parent;
+ DT.reset();
+ DT.Parent = Parent;
+ SemiNCAInfo SNCA(nullptr); // Since we are rebuilding the whole tree,
+ // there's no point doing it incrementally.
+
// Step #0: Number blocks in depth-first order and initialize variables used
// in later stages of the algorithm.
- const unsigned LastDFSNum = doFullDFSWalk(DT, AlwaysDescend);
+ DT.Roots = FindRoots(DT, nullptr);
+ SNCA.doFullDFSWalk(DT, AlwaysDescend);
- runSemiNCA(DT);
+ SNCA.runSemiNCA(DT);
+ if (BUI) {
+ BUI->IsRecalculated = true;
+ DEBUG(dbgs() << "DomTree recalculated, skipping future batch updates\n");
+ }
if (DT.Roots.empty()) return;
- // Add a node for the root. This node might be the actual root, if there is
- // one exit block, or it may be the virtual exit (denoted by
- // (BasicBlock *)0) which postdominates all real exits if there are multiple
- // exit blocks, or an infinite loop.
- // It might be that some blocks did not get a DFS number (e.g., blocks of
- // infinite loops). In these cases an artificial exit node is required.
- const bool MultipleRoots = DT.Roots.size() > 1 || (DT.isPostDominator() &&
- LastDFSNum != NumBlocks);
- NodePtr Root = !MultipleRoots ? DT.Roots[0] : nullptr;
+ // Add a node for the root. If the tree is a PostDominatorTree it will be
+ // the virtual exit (denoted by (BasicBlock *) nullptr) which postdominates
+ // all real exits (including multiple exit blocks, infinite loops).
+ NodePtr Root = IsPostDom ? nullptr : DT.Roots[0];
DT.RootNode = (DT.DomTreeNodes[Root] =
llvm::make_unique<DomTreeNodeBase<NodeT>>(Root, nullptr))
.get();
- attachNewSubtree(DT, DT.RootNode);
+ SNCA.attachNewSubtree(DT, DT.RootNode);
}
void attachNewSubtree(DomTreeT& DT, const TreeNodePtr AttachTo) {
@@ -317,11 +593,11 @@ struct SemiNCAInfo {
NodePtr ImmDom = getIDom(W);
- // Get or calculate the node for the immediate dominator
+ // Get or calculate the node for the immediate dominator.
TreeNodePtr IDomNode = getNodeForBlock(ImmDom, DT);
// Add a new tree node for this BasicBlock, and link it as a child of
- // IDomNode
+ // IDomNode.
DT.DomTreeNodes[W] = IDomNode->addChild(
llvm::make_unique<DomTreeNodeBase<NodeT>>(W, IDomNode));
}
@@ -357,31 +633,105 @@ struct SemiNCAInfo {
SmallVector<TreeNodePtr, 8> VisitedNotAffectedQueue;
};
- static void InsertEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) {
- assert(From && To && "Cannot connect nullptrs");
+ static void InsertEdge(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const NodePtr From, const NodePtr To) {
+ assert((From || IsPostDom) &&
+ "From has to be a valid CFG node or a virtual root");
+ assert(To && "Cannot be a nullptr");
DEBUG(dbgs() << "Inserting edge " << BlockNamePrinter(From) << " -> "
<< BlockNamePrinter(To) << "\n");
- const TreeNodePtr FromTN = DT.getNode(From);
-
- // Ignore edges from unreachable nodes.
- if (!FromTN) return;
+ TreeNodePtr FromTN = DT.getNode(From);
+
+ if (!FromTN) {
+ // Ignore edges from unreachable nodes for (forward) dominators.
+ if (!IsPostDom) return;
+
+ // The unreachable node becomes a new root -- a tree node for it.
+ TreeNodePtr VirtualRoot = DT.getNode(nullptr);
+ FromTN =
+ (DT.DomTreeNodes[From] = VirtualRoot->addChild(
+ llvm::make_unique<DomTreeNodeBase<NodeT>>(From, VirtualRoot)))
+ .get();
+ DT.Roots.push_back(From);
+ }
DT.DFSInfoValid = false;
const TreeNodePtr ToTN = DT.getNode(To);
if (!ToTN)
- InsertUnreachable(DT, FromTN, To);
+ InsertUnreachable(DT, BUI, FromTN, To);
else
- InsertReachable(DT, FromTN, ToTN);
+ InsertReachable(DT, BUI, FromTN, ToTN);
+ }
+
+ // Determines if some existing root becomes reverse-reachable after the
+ // insertion. Rebuilds the whole tree if that situation happens.
+ static bool UpdateRootsBeforeInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr From,
+ const TreeNodePtr To) {
+ assert(IsPostDom && "This function is only for postdominators");
+ // Destination node is not attached to the virtual root, so it cannot be a
+ // root.
+ if (!DT.isVirtualRoot(To->getIDom())) return false;
+
+ auto RIt = llvm::find(DT.Roots, To->getBlock());
+ if (RIt == DT.Roots.end())
+ return false; // To is not a root, nothing to update.
+
+ DEBUG(dbgs() << "\t\tAfter the insertion, " << BlockNamePrinter(To)
+ << " is no longer a root\n\t\tRebuilding the tree!!!\n");
+
+ CalculateFromScratch(DT, BUI);
+ 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.
+ static void UpdateRootsAfterUpdate(DomTreeT &DT, const BatchUpdatePtr BUI) {
+ assert(IsPostDom && "This function is only for postdominators");
+
+ // The tree has only trivial roots -- nothing to update.
+ if (std::none_of(DT.Roots.begin(), DT.Roots.end(), [BUI](const NodePtr N) {
+ return HasForwardSuccessors(N, BUI);
+ }))
+ return;
+
+ // Recalculate the set of roots.
+ DT.Roots = FindRoots(DT, BUI);
+ for (const NodePtr R : DT.Roots) {
+ const TreeNodePtr TN = DT.getNode(R);
+ // A CFG node was selected as a tree root, but the corresponding tree node
+ // is not connected to the virtual root. 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 nodes within an infinite loop
+ // becomes a root.
+ if (DT.isVirtualRoot(TN->getIDom())) {
+ DEBUG(dbgs() << "Root " << BlockNamePrinter(R)
+ << " is not virtual root's child\n"
+ << "The entire tree needs to be rebuilt\n");
+ // It should be possible to rotate the subtree instead of recalculating
+ // the whole tree, but this situation happens extremely rarely in
+ // practice.
+ CalculateFromScratch(DT, BUI);
+ return;
+ }
+ }
}
// Handles insertion to a node already in the dominator tree.
- static void InsertReachable(DomTreeT &DT, const TreeNodePtr From,
- const TreeNodePtr To) {
+ static void InsertReachable(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr From, const TreeNodePtr To) {
DEBUG(dbgs() << "\tReachable " << BlockNamePrinter(From->getBlock())
<< " -> " << BlockNamePrinter(To->getBlock()) << "\n");
+ if (IsPostDom && UpdateRootsBeforeInsertion(DT, BUI, From, To)) return;
+ // DT.findNCD expects both pointers to be valid. When From is a virtual
+ // root, then its CFG block pointer is a nullptr, so we have to 'compute'
+ // the NCD manually.
const NodePtr NCDBlock =
- DT.findNearestCommonDominator(From->getBlock(), To->getBlock());
+ (From->getBlock() && To->getBlock())
+ ? DT.findNearestCommonDominator(From->getBlock(), To->getBlock())
+ : nullptr;
assert(NCDBlock || DT.isPostDominator());
const TreeNodePtr NCD = DT.getNode(NCDBlock);
assert(NCD);
@@ -410,53 +760,61 @@ struct SemiNCAInfo {
II.AffectedQueue.push_back(CurrentNode);
// Discover and collect affected successors of the current node.
- VisitInsertion(DT, CurrentNode, CurrentNode->getLevel(), NCD, II);
+ VisitInsertion(DT, BUI, CurrentNode, CurrentNode->getLevel(), NCD, II);
}
// Finish by updating immediate dominators and levels.
- UpdateInsertion(DT, NCD, II);
+ UpdateInsertion(DT, BUI, NCD, II);
}
// Visits an affected node and collect its affected successors.
- static void VisitInsertion(DomTreeT &DT, const TreeNodePtr TN,
- const unsigned RootLevel, const TreeNodePtr NCD,
- InsertionInfo &II) {
+ static void VisitInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr TN, const unsigned RootLevel,
+ const TreeNodePtr NCD, InsertionInfo &II) {
const unsigned NCDLevel = NCD->getLevel();
DEBUG(dbgs() << "Visiting " << BlockNamePrinter(TN) << "\n");
- assert(TN->getBlock());
- for (const NodePtr Succ :
- ChildrenGetter<NodePtr, IsPostDom>::Get(TN->getBlock())) {
- const TreeNodePtr SuccTN = DT.getNode(Succ);
- assert(SuccTN && "Unreachable successor found at reachable insertion");
- const unsigned SuccLevel = SuccTN->getLevel();
-
- DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ)
- << ", level = " << SuccLevel << "\n");
-
- // Succ dominated by subtree From -- not affected.
- // (Based on the lemma 2.5 from the second paper.)
- if (SuccLevel > RootLevel) {
- DEBUG(dbgs() << "\t\tDominated by subtree From\n");
- if (II.Visited.count(SuccTN) != 0) continue;
-
- DEBUG(dbgs() << "\t\tMarking visited not affected "
- << BlockNamePrinter(Succ) << "\n");
- II.Visited.insert(SuccTN);
- II.VisitedNotAffectedQueue.push_back(SuccTN);
- VisitInsertion(DT, SuccTN, RootLevel, NCD, II);
- } else if ((SuccLevel > NCDLevel + 1) && II.Affected.count(SuccTN) == 0) {
- DEBUG(dbgs() << "\t\tMarking affected and adding "
- << BlockNamePrinter(Succ) << " to a Bucket\n");
- II.Affected.insert(SuccTN);
- II.Bucket.push({SuccLevel, SuccTN});
+ SmallVector<TreeNodePtr, 8> Stack = {TN};
+ assert(TN->getBlock() && II.Visited.count(TN) && "Preconditions!");
+
+ do {
+ TreeNodePtr Next = Stack.pop_back_val();
+
+ 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();
+
+ DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ)
+ << ", level = " << SuccLevel << "\n");
+
+ // Succ dominated by subtree From -- not affected.
+ // (Based on the lemma 2.5 from the second paper.)
+ if (SuccLevel > RootLevel) {
+ DEBUG(dbgs() << "\t\tDominated by subtree From\n");
+ if (II.Visited.count(SuccTN) != 0)
+ continue;
+
+ DEBUG(dbgs() << "\t\tMarking visited not affected "
+ << BlockNamePrinter(Succ) << "\n");
+ II.Visited.insert(SuccTN);
+ II.VisitedNotAffectedQueue.push_back(SuccTN);
+ Stack.push_back(SuccTN);
+ } else if ((SuccLevel > NCDLevel + 1) &&
+ II.Affected.count(SuccTN) == 0) {
+ DEBUG(dbgs() << "\t\tMarking affected and adding "
+ << BlockNamePrinter(Succ) << " to a Bucket\n");
+ II.Affected.insert(SuccTN);
+ II.Bucket.push({SuccLevel, SuccTN});
+ }
}
- }
+ } while (!Stack.empty());
}
// Updates immediate dominators and levels after insertion.
- static void UpdateInsertion(DomTreeT &DT, const TreeNodePtr NCD,
- InsertionInfo &II) {
+ static void UpdateInsertion(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr NCD, InsertionInfo &II) {
DEBUG(dbgs() << "Updating NCD = " << BlockNamePrinter(NCD) << "\n");
for (const TreeNodePtr TN : II.AffectedQueue) {
@@ -466,6 +824,7 @@ struct SemiNCAInfo {
}
UpdateLevelsAfterInsertion(II);
+ if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI);
}
static void UpdateLevelsAfterInsertion(InsertionInfo &II) {
@@ -480,36 +839,35 @@ struct SemiNCAInfo {
}
// Handles insertion to previously unreachable nodes.
- static void InsertUnreachable(DomTreeT &DT, const TreeNodePtr From,
- const NodePtr To) {
+ static void InsertUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr From, const NodePtr To) {
DEBUG(dbgs() << "Inserting " << BlockNamePrinter(From)
<< " -> (unreachable) " << BlockNamePrinter(To) << "\n");
// Collect discovered edges to already reachable nodes.
SmallVector<std::pair<NodePtr, TreeNodePtr>, 8> DiscoveredEdgesToReachable;
// Discover and connect nodes that became reachable with the insertion.
- ComputeUnreachableDominators(DT, To, From, DiscoveredEdgesToReachable);
+ ComputeUnreachableDominators(DT, BUI, To, From, DiscoveredEdgesToReachable);
DEBUG(dbgs() << "Inserted " << BlockNamePrinter(From)
<< " -> (prev unreachable) " << BlockNamePrinter(To) << "\n");
- DEBUG(DT.print(dbgs()));
-
// Used the discovered edges and inset discovered connecting (incoming)
// edges.
for (const auto &Edge : DiscoveredEdgesToReachable) {
DEBUG(dbgs() << "\tInserting discovered connecting edge "
<< BlockNamePrinter(Edge.first) << " -> "
<< BlockNamePrinter(Edge.second) << "\n");
- InsertReachable(DT, DT.getNode(Edge.first), Edge.second);
+ InsertReachable(DT, BUI, DT.getNode(Edge.first), Edge.second);
}
}
// Connects nodes that become reachable with an insertion.
static void ComputeUnreachableDominators(
- DomTreeT &DT, const NodePtr Root, const TreeNodePtr Incoming,
+ DomTreeT &DT, const BatchUpdatePtr BUI, const NodePtr Root,
+ const TreeNodePtr Incoming,
SmallVectorImpl<std::pair<NodePtr, TreeNodePtr>>
- &DiscoveredConnectingEdges) {
+ &DiscoveredConnectingEdges) {
assert(!DT.getNode(Root) && "Root must not be reachable");
// Visit only previously unreachable nodes.
@@ -522,50 +880,16 @@ struct SemiNCAInfo {
return false;
};
- SemiNCAInfo SNCA;
- SNCA.runDFS<IsPostDom>(Root, 0, UnreachableDescender, 0);
+ SemiNCAInfo SNCA(BUI);
+ SNCA.runDFS(Root, 0, UnreachableDescender, 0);
SNCA.runSemiNCA(DT);
SNCA.attachNewSubtree(DT, Incoming);
DEBUG(dbgs() << "After adding unreachable nodes\n");
- DEBUG(DT.print(dbgs()));
}
- // Checks if the tree contains all reachable nodes in the input graph.
- bool verifyReachability(const DomTreeT &DT) {
- clear();
- doFullDFSWalk(DT, AlwaysDescend);
-
- for (auto &NodeToTN : DT.DomTreeNodes) {
- const TreeNodePtr TN = NodeToTN.second.get();
- const NodePtr BB = TN->getBlock();
-
- // Virtual root has a corresponding virtual CFG node.
- if (DT.isVirtualRoot(TN)) continue;
-
- if (NodeToInfo.count(BB) == 0) {
- errs() << "DomTree node " << BlockNamePrinter(BB)
- << " not found by DFS walk!\n";
- errs().flush();
-
- return false;
- }
- }
-
- for (const NodePtr N : NumToNode) {
- if (N && !DT.getNode(N)) {
- errs() << "CFG node " << BlockNamePrinter(N)
- << " not found in the DomTree!\n";
- errs().flush();
-
- return false;
- }
- }
-
- return true;
- }
-
- static void DeleteEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) {
+ static void DeleteEdge(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const NodePtr From, const NodePtr To) {
assert(From && To && "Cannot disconnect nullptrs");
DEBUG(dbgs() << "Deleting edge " << BlockNamePrinter(From) << " -> "
<< BlockNamePrinter(To) << "\n");
@@ -574,8 +898,8 @@ struct SemiNCAInfo {
// Ensure that the edge was in fact deleted from the CFG before informing
// the DomTree about it.
// The check is O(N), so run it only in debug configuration.
- auto IsSuccessor = [](const NodePtr SuccCandidate, const NodePtr Of) {
- auto Successors = ChildrenGetter<NodePtr, IsPostDom>::Get(Of);
+ auto IsSuccessor = [BUI](const NodePtr SuccCandidate, const NodePtr Of) {
+ auto Successors = ChildrenGetter<IsPostDom>::Get(Of, BUI);
return llvm::find(Successors, SuccCandidate) != Successors.end();
};
(void)IsSuccessor;
@@ -587,27 +911,37 @@ struct SemiNCAInfo {
if (!FromTN) return;
const TreeNodePtr ToTN = DT.getNode(To);
- assert(ToTN && "To already unreachable -- there is no edge to delete");
+ if (!ToTN) {
+ DEBUG(dbgs() << "\tTo (" << BlockNamePrinter(To)
+ << ") already unreachable -- there is no edge to delete\n");
+ return;
+ }
+
const NodePtr NCDBlock = DT.findNearestCommonDominator(From, To);
const TreeNodePtr NCD = DT.getNode(NCDBlock);
// To dominates From -- nothing to do.
if (ToTN == NCD) return;
+ DT.DFSInfoValid = false;
+
const TreeNodePtr ToIDom = ToTN->getIDom();
DEBUG(dbgs() << "\tNCD " << BlockNamePrinter(NCD) << ", ToIDom "
<< BlockNamePrinter(ToIDom) << "\n");
// To remains reachable after deletion.
// (Based on the caption under Figure 4. from the second paper.)
- if (FromTN != ToIDom || HasProperSupport(DT, ToTN))
- DeleteReachable(DT, FromTN, ToTN);
+ if (FromTN != ToIDom || HasProperSupport(DT, BUI, ToTN))
+ DeleteReachable(DT, BUI, FromTN, ToTN);
else
- DeleteUnreachable(DT, ToTN);
+ DeleteUnreachable(DT, BUI, ToTN);
+
+ if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI);
}
// Handles deletions that leave destination nodes reachable.
- static void DeleteReachable(DomTreeT &DT, const TreeNodePtr FromTN,
+ static void DeleteReachable(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr FromTN,
const TreeNodePtr ToTN) {
DEBUG(dbgs() << "Deleting reachable " << BlockNamePrinter(FromTN) << " -> "
<< BlockNamePrinter(ToTN) << "\n");
@@ -625,7 +959,7 @@ struct SemiNCAInfo {
// scratch.
if (!PrevIDomSubTree) {
DEBUG(dbgs() << "The entire tree needs to be rebuilt\n");
- DT.recalculate(*DT.Parent);
+ CalculateFromScratch(DT, BUI);
return;
}
@@ -637,8 +971,8 @@ struct SemiNCAInfo {
DEBUG(dbgs() << "\tTop of subtree: " << BlockNamePrinter(ToIDomTN) << "\n");
- SemiNCAInfo SNCA;
- SNCA.runDFS<IsPostDom>(ToIDom, 0, DescendBelow, 0);
+ SemiNCAInfo SNCA(BUI);
+ SNCA.runDFS(ToIDom, 0, DescendBelow, 0);
DEBUG(dbgs() << "\tRunning Semi-NCA\n");
SNCA.runSemiNCA(DT, Level);
SNCA.reattachExistingSubtree(DT, PrevIDomSubTree);
@@ -646,10 +980,11 @@ struct SemiNCAInfo {
// Checks if a node has proper support, as defined on the page 3 and later
// explained on the page 7 of the second paper.
- static bool HasProperSupport(DomTreeT &DT, const TreeNodePtr TN) {
+ static bool HasProperSupport(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr TN) {
DEBUG(dbgs() << "IsReachableFromIDom " << BlockNamePrinter(TN) << "\n");
for (const NodePtr Pred :
- ChildrenGetter<NodePtr, !IsPostDom>::Get(TN->getBlock())) {
+ ChildrenGetter<!IsPostDom>::Get(TN->getBlock(), BUI)) {
DEBUG(dbgs() << "\tPred " << BlockNamePrinter(Pred) << "\n");
if (!DT.getNode(Pred)) continue;
@@ -669,12 +1004,24 @@ struct SemiNCAInfo {
// Handle deletions that make destination node unreachable.
// (Based on the lemma 2.7 from the second paper.)
- static void DeleteUnreachable(DomTreeT &DT, const TreeNodePtr ToTN) {
+ static void DeleteUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI,
+ const TreeNodePtr ToTN) {
DEBUG(dbgs() << "Deleting unreachable subtree " << BlockNamePrinter(ToTN)
<< "\n");
assert(ToTN);
assert(ToTN->getBlock());
+ if (IsPostDom) {
+ // Deletion makes a region reverse-unreachable and creates a new root.
+ // Simulate that by inserting an edge from the virtual root to ToTN and
+ // adding it as a new root.
+ DEBUG(dbgs() << "\tDeletion made a region reverse-unreachable\n");
+ DEBUG(dbgs() << "\tAdding new root " << BlockNamePrinter(ToTN) << "\n");
+ DT.Roots.push_back(ToTN->getBlock());
+ InsertReachable(DT, BUI, DT.getNode(nullptr), ToTN);
+ return;
+ }
+
SmallVector<NodePtr, 16> AffectedQueue;
const unsigned Level = ToTN->getLevel();
@@ -690,13 +1037,13 @@ struct SemiNCAInfo {
return false;
};
- SemiNCAInfo SNCA;
+ SemiNCAInfo SNCA(BUI);
unsigned LastDFSNum =
- SNCA.runDFS<IsPostDom>(ToTN->getBlock(), 0, DescendAndCollect, 0);
+ SNCA.runDFS(ToTN->getBlock(), 0, DescendAndCollect, 0);
TreeNodePtr MinNode = ToTN;
- // Identify the top of the subtree to rebuilt by finding the NCD of all
+ // Identify the top of the subtree to rebuild by finding the NCD of all
// the affected nodes.
for (const NodePtr N : AffectedQueue) {
const TreeNodePtr TN = DT.getNode(N);
@@ -715,7 +1062,7 @@ struct SemiNCAInfo {
// Root reached, rebuild the whole tree from scratch.
if (!MinNode->getIDom()) {
DEBUG(dbgs() << "The entire tree needs to be rebuilt\n");
- DT.recalculate(*DT.Parent);
+ CalculateFromScratch(DT, BUI);
return;
}
@@ -744,7 +1091,7 @@ struct SemiNCAInfo {
const TreeNodePtr ToTN = DT.getNode(To);
return ToTN && ToTN->getLevel() > MinLevel;
};
- SNCA.runDFS<IsPostDom>(MinNode->getBlock(), 0, DescendBelow, 0);
+ SNCA.runDFS(MinNode->getBlock(), 0, DescendBelow, 0);
DEBUG(dbgs() << "Previous IDom(MinNode) = " << BlockNamePrinter(PrevIDom)
<< "\nRunning Semi-NCA\n");
@@ -771,9 +1118,222 @@ struct SemiNCAInfo {
}
//~~
+ //===--------------------- DomTree Batch Updater --------------------------===
+ //~~
+
+ static void ApplyUpdates(DomTreeT &DT, ArrayRef<UpdateT> Updates) {
+ const size_t NumUpdates = Updates.size();
+ if (NumUpdates == 0)
+ return;
+
+ // Take the fast path for a single update and avoid running the batch update
+ // machinery.
+ if (NumUpdates == 1) {
+ const auto &Update = Updates.front();
+ if (Update.getKind() == UpdateKind::Insert)
+ DT.insertEdge(Update.getFrom(), Update.getTo());
+ else
+ DT.deleteEdge(Update.getFrom(), Update.getTo());
+
+ return;
+ }
+
+ BatchUpdateInfo BUI;
+ LegalizeUpdates(Updates, BUI.Updates);
+
+ const size_t NumLegalized = BUI.Updates.size();
+ BUI.FutureSuccessors.reserve(NumLegalized);
+ BUI.FuturePredecessors.reserve(NumLegalized);
+
+ // Use the legalized future updates to initialize future successors and
+ // predecessors. Note that these sets will only decrease size over time, as
+ // the next CFG snapshots slowly approach the actual (current) CFG.
+ for (UpdateT &U : BUI.Updates) {
+ BUI.FutureSuccessors[U.getFrom()].insert({U.getTo(), U.getKind()});
+ BUI.FuturePredecessors[U.getTo()].insert({U.getFrom(), U.getKind()});
+ }
+
+ DEBUG(dbgs() << "About to apply " << NumLegalized << " updates\n");
+ DEBUG(if (NumLegalized < 32) for (const auto &U
+ : reverse(BUI.Updates)) dbgs()
+ << '\t' << U << "\n");
+ DEBUG(dbgs() << "\n");
+
+ // If the DominatorTree was recalculated at some point, stop the batch
+ // updates. Full recalculations ignore batch updates and look at the actual
+ // CFG.
+ for (size_t i = 0; i < NumLegalized && !BUI.IsRecalculated; ++i)
+ ApplyNextUpdate(DT, BUI);
+ }
+
+ // This function serves double purpose:
+ // a) It removes redundant updates, which makes it easier to reverse-apply
+ // them when traversing CFG.
+ // b) It optimizes away updates that cancel each other out, as the end result
+ // is the same.
+ //
+ // It relies on the property of the incremental updates that says that the
+ // order of updates doesn't matter. This allows us to reorder them and end up
+ // with the exact same DomTree every time.
+ //
+ // Following the same logic, the function doesn't care about the order of
+ // input updates, so it's OK to pass it an unordered sequence of updates, that
+ // doesn't make sense when applied sequentially, eg. performing double
+ // insertions or deletions and then doing an opposite update.
+ //
+ // In the future, it should be possible to schedule updates in way that
+ // minimizes the amount of work needed done during incremental updates.
+ static void LegalizeUpdates(ArrayRef<UpdateT> AllUpdates,
+ SmallVectorImpl<UpdateT> &Result) {
+ DEBUG(dbgs() << "Legalizing " << AllUpdates.size() << " updates\n");
+ // Count the total number of inserions of each edge.
+ // Each insertion adds 1 and deletion subtracts 1. The end number should be
+ // one of {-1 (deletion), 0 (NOP), +1 (insertion)}. Otherwise, the sequence
+ // of updates contains multiple updates of the same kind and we assert for
+ // that case.
+ SmallDenseMap<std::pair<NodePtr, NodePtr>, int, 4> Operations;
+ Operations.reserve(AllUpdates.size());
+
+ for (const auto &U : AllUpdates) {
+ NodePtr From = U.getFrom();
+ NodePtr To = U.getTo();
+ if (IsPostDom) std::swap(From, To); // Reverse edge for postdominators.
+
+ Operations[{From, To}] += (U.getKind() == UpdateKind::Insert ? 1 : -1);
+ }
+
+ Result.clear();
+ Result.reserve(Operations.size());
+ for (auto &Op : Operations) {
+ const int NumInsertions = Op.second;
+ assert(std::abs(NumInsertions) <= 1 && "Unbalanced operations!");
+ if (NumInsertions == 0) continue;
+ const UpdateKind UK =
+ NumInsertions > 0 ? UpdateKind::Insert : UpdateKind::Delete;
+ Result.push_back({UK, Op.first.first, Op.first.second});
+ }
+
+ // Make the order consistent by not relying on pointer values within the
+ // set. Reuse the old Operations map.
+ // In the future, we should sort by something else to minimize the amount
+ // of work needed to perform the series of updates.
+ for (size_t i = 0, e = AllUpdates.size(); i != e; ++i) {
+ const auto &U = AllUpdates[i];
+ if (!IsPostDom)
+ Operations[{U.getFrom(), U.getTo()}] = int(i);
+ else
+ Operations[{U.getTo(), U.getFrom()}] = int(i);
+ }
+
+ std::sort(Result.begin(), Result.end(),
+ [&Operations](const UpdateT &A, const UpdateT &B) {
+ return Operations[{A.getFrom(), A.getTo()}] >
+ Operations[{B.getFrom(), B.getTo()}];
+ });
+ }
+
+ static void ApplyNextUpdate(DomTreeT &DT, BatchUpdateInfo &BUI) {
+ assert(!BUI.Updates.empty() && "No updates to apply!");
+ UpdateT CurrentUpdate = BUI.Updates.pop_back_val();
+ DEBUG(dbgs() << "Applying update: " << CurrentUpdate << "\n");
+
+ // Move to the next snapshot of the CFG by removing the reverse-applied
+ // current update.
+ auto &FS = BUI.FutureSuccessors[CurrentUpdate.getFrom()];
+ FS.erase({CurrentUpdate.getTo(), CurrentUpdate.getKind()});
+ if (FS.empty()) BUI.FutureSuccessors.erase(CurrentUpdate.getFrom());
+
+ auto &FP = BUI.FuturePredecessors[CurrentUpdate.getTo()];
+ FP.erase({CurrentUpdate.getFrom(), CurrentUpdate.getKind()});
+ if (FP.empty()) BUI.FuturePredecessors.erase(CurrentUpdate.getTo());
+
+ if (CurrentUpdate.getKind() == UpdateKind::Insert)
+ InsertEdge(DT, &BUI, CurrentUpdate.getFrom(), CurrentUpdate.getTo());
+ else
+ DeleteEdge(DT, &BUI, CurrentUpdate.getFrom(), CurrentUpdate.getTo());
+ }
+
+ //~~
//===--------------- DomTree correctness verification ---------------------===
//~~
+ // Check if the tree has correct roots. A DominatorTree always has a single
+ // root which is the function's entry node. A PostDominatorTree can have
+ // multiple roots - one for each node with no successors and for infinite
+ // loops.
+ bool verifyRoots(const DomTreeT &DT) {
+ if (!DT.Parent && !DT.Roots.empty()) {
+ errs() << "Tree has no parent but has roots!\n";
+ errs().flush();
+ return false;
+ }
+
+ if (!IsPostDom) {
+ if (DT.Roots.empty()) {
+ errs() << "Tree doesn't have a root!\n";
+ errs().flush();
+ return false;
+ }
+
+ if (DT.getRoot() != GetEntryNode(DT)) {
+ errs() << "Tree's root is not its parent's entry node!\n";
+ errs().flush();
+ return false;
+ }
+ }
+
+ RootsT ComputedRoots = FindRoots(DT, nullptr);
+ if (DT.Roots.size() != ComputedRoots.size() ||
+ !std::is_permutation(DT.Roots.begin(), DT.Roots.end(),
+ ComputedRoots.begin())) {
+ errs() << "Tree has different roots than freshly computed ones!\n";
+ errs() << "\tPDT roots: ";
+ for (const NodePtr N : DT.Roots) errs() << BlockNamePrinter(N) << ", ";
+ errs() << "\n\tComputed roots: ";
+ for (const NodePtr N : ComputedRoots)
+ errs() << BlockNamePrinter(N) << ", ";
+ errs() << "\n";
+ errs().flush();
+ return false;
+ }
+
+ return true;
+ }
+
+ // Checks if the tree contains all reachable nodes in the input graph.
+ bool verifyReachability(const DomTreeT &DT) {
+ clear();
+ doFullDFSWalk(DT, AlwaysDescend);
+
+ for (auto &NodeToTN : DT.DomTreeNodes) {
+ const TreeNodePtr TN = NodeToTN.second.get();
+ const NodePtr BB = TN->getBlock();
+
+ // Virtual root has a corresponding virtual CFG node.
+ if (DT.isVirtualRoot(TN)) continue;
+
+ if (NodeToInfo.count(BB) == 0) {
+ errs() << "DomTree node " << BlockNamePrinter(BB)
+ << " not found by DFS walk!\n";
+ errs().flush();
+
+ return false;
+ }
+ }
+
+ for (const NodePtr N : NumToNode) {
+ if (N && !DT.getNode(N)) {
+ errs() << "CFG node " << BlockNamePrinter(N)
+ << " not found in the DomTree!\n";
+ errs().flush();
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
// Check if for every parent with a level L in the tree all of its children
// have level L + 1.
static bool VerifyLevels(const DomTreeT &DT) {
@@ -805,35 +1365,97 @@ struct SemiNCAInfo {
return true;
}
- // Checks if for every edge From -> To in the graph
- // NCD(From, To) == IDom(To) or To.
- bool verifyNCD(const DomTreeT &DT) {
- clear();
- doFullDFSWalk(DT, AlwaysDescend);
+ // Check if the computed DFS numbers are correct. Note that DFS info may not
+ // be valid, and when that is the case, we don't verify the numbers.
+ static bool VerifyDFSNumbers(const DomTreeT &DT) {
+ if (!DT.DFSInfoValid || !DT.Parent)
+ return true;
- for (auto &BlockToInfo : NodeToInfo) {
- auto &Info = BlockToInfo.second;
+ const NodePtr RootBB = IsPostDom ? nullptr : DT.getRoots()[0];
+ const TreeNodePtr Root = DT.getNode(RootBB);
- const NodePtr From = NumToNode[Info.Parent];
- if (!From) continue;
+ auto PrintNodeAndDFSNums = [](const TreeNodePtr TN) {
+ errs() << BlockNamePrinter(TN) << " {" << TN->getDFSNumIn() << ", "
+ << TN->getDFSNumOut() << '}';
+ };
- const NodePtr To = BlockToInfo.first;
- const TreeNodePtr ToTN = DT.getNode(To);
- assert(ToTN);
-
- const NodePtr NCD = DT.findNearestCommonDominator(From, To);
- const TreeNodePtr NCDTN = DT.getNode(NCD);
- const TreeNodePtr ToIDom = ToTN->getIDom();
- if (NCDTN != ToTN && NCDTN != ToIDom) {
- errs() << "NearestCommonDominator verification failed:\n\tNCD(From:"
- << BlockNamePrinter(From) << ", To:" << BlockNamePrinter(To)
- << ") = " << BlockNamePrinter(NCD)
- << ",\t (should be To or IDom[To]: " << BlockNamePrinter(ToIDom)
- << ")\n";
+ // Verify the root's DFS In number. Although DFS numbering would also work
+ // if we started from some other value, we assume 0-based numbering.
+ if (Root->getDFSNumIn() != 0) {
+ errs() << "DFSIn number for the tree root is not:\n\t";
+ PrintNodeAndDFSNums(Root);
+ errs() << '\n';
+ errs().flush();
+ return false;
+ }
+
+ // For each tree node verify if children's DFS numbers cover their parent's
+ // DFS numbers with no gaps.
+ for (const auto &NodeToTN : DT.DomTreeNodes) {
+ const TreeNodePtr Node = NodeToTN.second.get();
+
+ // Handle tree leaves.
+ if (Node->getChildren().empty()) {
+ if (Node->getDFSNumIn() + 1 != Node->getDFSNumOut()) {
+ errs() << "Tree leaf should have DFSOut = DFSIn + 1:\n\t";
+ PrintNodeAndDFSNums(Node);
+ errs() << '\n';
+ errs().flush();
+ return false;
+ }
+
+ continue;
+ }
+
+ // Make a copy and sort it such that it is possible to check if there are
+ // no gaps between DFS numbers of adjacent children.
+ SmallVector<TreeNodePtr, 8> Children(Node->begin(), Node->end());
+ std::sort(Children.begin(), Children.end(),
+ [](const TreeNodePtr Ch1, const TreeNodePtr Ch2) {
+ return Ch1->getDFSNumIn() < Ch2->getDFSNumIn();
+ });
+
+ auto PrintChildrenError = [Node, &Children, PrintNodeAndDFSNums](
+ const TreeNodePtr FirstCh, const TreeNodePtr SecondCh) {
+ assert(FirstCh);
+
+ errs() << "Incorrect DFS numbers for:\n\tParent ";
+ PrintNodeAndDFSNums(Node);
+
+ errs() << "\n\tChild ";
+ PrintNodeAndDFSNums(FirstCh);
+
+ if (SecondCh) {
+ errs() << "\n\tSecond child ";
+ PrintNodeAndDFSNums(SecondCh);
+ }
+
+ errs() << "\nAll children: ";
+ for (const TreeNodePtr Ch : Children) {
+ PrintNodeAndDFSNums(Ch);
+ errs() << ", ";
+ }
+
+ errs() << '\n';
errs().flush();
+ };
+ if (Children.front()->getDFSNumIn() != Node->getDFSNumIn() + 1) {
+ PrintChildrenError(Children.front(), nullptr);
return false;
}
+
+ if (Children.back()->getDFSNumOut() + 1 != Node->getDFSNumOut()) {
+ PrintChildrenError(Children.back(), nullptr);
+ return false;
+ }
+
+ for (size_t i = 0, e = Children.size() - 1; i != e; ++i) {
+ if (Children[i]->getDFSNumOut() + 1 != Children[i + 1]->getDFSNumIn()) {
+ PrintChildrenError(Children[i], Children[i + 1]);
+ return false;
+ }
+ }
}
return true;
@@ -888,6 +1510,8 @@ struct SemiNCAInfo {
const NodePtr BB = TN->getBlock();
if (!BB || TN->getChildren().empty()) continue;
+ DEBUG(dbgs() << "Verifying parent property of node "
+ << BlockNamePrinter(TN) << "\n");
clear();
doFullDFSWalk(DT, [BB](NodePtr From, NodePtr To) {
return From != BB && To != BB;
@@ -945,33 +1569,37 @@ struct SemiNCAInfo {
}
};
-
-template <class DomTreeT, class FuncT>
-void Calculate(DomTreeT &DT, FuncT &F) {
- SemiNCAInfo<DomTreeT> SNCA;
- SNCA.calculateFromScratch(DT, GraphTraits<FuncT *>::size(&F));
+template <class DomTreeT>
+void Calculate(DomTreeT &DT) {
+ SemiNCAInfo<DomTreeT>::CalculateFromScratch(DT, nullptr);
}
template <class DomTreeT>
void InsertEdge(DomTreeT &DT, typename DomTreeT::NodePtr From,
typename DomTreeT::NodePtr To) {
if (DT.isPostDominator()) std::swap(From, To);
- SemiNCAInfo<DomTreeT>::InsertEdge(DT, From, To);
+ SemiNCAInfo<DomTreeT>::InsertEdge(DT, nullptr, From, To);
}
template <class DomTreeT>
void DeleteEdge(DomTreeT &DT, typename DomTreeT::NodePtr From,
typename DomTreeT::NodePtr To) {
if (DT.isPostDominator()) std::swap(From, To);
- SemiNCAInfo<DomTreeT>::DeleteEdge(DT, From, To);
+ SemiNCAInfo<DomTreeT>::DeleteEdge(DT, nullptr, From, To);
+}
+
+template <class DomTreeT>
+void ApplyUpdates(DomTreeT &DT,
+ ArrayRef<typename DomTreeT::UpdateType> Updates) {
+ SemiNCAInfo<DomTreeT>::ApplyUpdates(DT, Updates);
}
template <class DomTreeT>
bool Verify(const DomTreeT &DT) {
- SemiNCAInfo<DomTreeT> SNCA;
- return SNCA.verifyReachability(DT) && SNCA.VerifyLevels(DT) &&
- SNCA.verifyNCD(DT) && SNCA.verifyParentProperty(DT) &&
- SNCA.verifySiblingProperty(DT);
+ SemiNCAInfo<DomTreeT> SNCA(nullptr);
+ return SNCA.verifyRoots(DT) && SNCA.verifyReachability(DT) &&
+ SNCA.VerifyLevels(DT) && SNCA.verifyParentProperty(DT) &&
+ SNCA.verifySiblingProperty(DT) && SNCA.VerifyDFSNumbers(DT);
}
} // namespace DomTreeBuilder