summaryrefslogtreecommitdiff
path: root/lib/Analysis/LazyCallGraph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/LazyCallGraph.cpp')
-rw-r--r--lib/Analysis/LazyCallGraph.cpp564
1 files changed, 190 insertions, 374 deletions
diff --git a/lib/Analysis/LazyCallGraph.cpp b/lib/Analysis/LazyCallGraph.cpp
index d287f81985fd..54299d078be5 100644
--- a/lib/Analysis/LazyCallGraph.cpp
+++ b/lib/Analysis/LazyCallGraph.cpp
@@ -8,15 +8,31 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/IR/CallSite.h"
-#include "llvm/IR/InstVisitor.h"
-#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+#include <string>
+#include <tuple>
#include <utility>
using namespace llvm;
@@ -175,7 +191,7 @@ LazyCallGraph::LazyCallGraph(Module &M, TargetLibraryInfo &TLI) {
LazyCallGraph::LazyCallGraph(LazyCallGraph &&G)
: BPA(std::move(G.BPA)), NodeMap(std::move(G.NodeMap)),
EntryEdges(std::move(G.EntryEdges)), SCCBPA(std::move(G.SCCBPA)),
- SCCMap(std::move(G.SCCMap)), LeafRefSCCs(std::move(G.LeafRefSCCs)),
+ SCCMap(std::move(G.SCCMap)),
LibFunctions(std::move(G.LibFunctions)) {
updateGraphPtrs();
}
@@ -186,7 +202,6 @@ LazyCallGraph &LazyCallGraph::operator=(LazyCallGraph &&G) {
EntryEdges = std::move(G.EntryEdges);
SCCBPA = std::move(G.SCCBPA);
SCCMap = std::move(G.SCCMap);
- LeafRefSCCs = std::move(G.LeafRefSCCs);
LibFunctions = std::move(G.LibFunctions);
updateGraphPtrs();
return *this;
@@ -212,7 +227,7 @@ void LazyCallGraph::SCC::verify() {
assert(N->LowLink == -1 &&
"Must set low link to -1 when adding a node to an SCC!");
for (Edge &E : **N)
- assert(E.getNode() && "Can't have an unpopulated node!");
+ assert(E.getNode().isPopulated() && "Can't have an unpopulated node!");
}
}
#endif
@@ -313,38 +328,49 @@ void LazyCallGraph::RefSCC::verify() {
"Edge between SCCs violates post-order relationship.");
continue;
}
- assert(TargetSCC.getOuterRefSCC().Parents.count(this) &&
- "Edge to a RefSCC missing us in its parent set.");
}
}
-
- // Check that our parents are actually parents.
- for (RefSCC *ParentRC : Parents) {
- assert(ParentRC != this && "Cannot be our own parent!");
- auto HasConnectingEdge = [&] {
- for (SCC &C : *ParentRC)
- for (Node &N : C)
- for (Edge &E : *N)
- if (G->lookupRefSCC(E.getNode()) == this)
- return true;
- return false;
- };
- assert(HasConnectingEdge() && "No edge connects the parent to us!");
- }
}
#endif
-bool LazyCallGraph::RefSCC::isDescendantOf(const RefSCC &C) const {
- // Walk up the parents of this SCC and verify that we eventually find C.
- SmallVector<const RefSCC *, 4> AncestorWorklist;
- AncestorWorklist.push_back(this);
+bool LazyCallGraph::RefSCC::isParentOf(const RefSCC &RC) const {
+ if (&RC == this)
+ return false;
+
+ // Search all edges to see if this is a parent.
+ for (SCC &C : *this)
+ for (Node &N : C)
+ for (Edge &E : *N)
+ if (G->lookupRefSCC(E.getNode()) == &RC)
+ return true;
+
+ return false;
+}
+
+bool LazyCallGraph::RefSCC::isAncestorOf(const RefSCC &RC) const {
+ if (&RC == this)
+ return false;
+
+ // For each descendant of this RefSCC, see if one of its children is the
+ // argument. If not, add that descendant to the worklist and continue
+ // searching.
+ SmallVector<const RefSCC *, 4> Worklist;
+ SmallPtrSet<const RefSCC *, 4> Visited;
+ Worklist.push_back(this);
+ Visited.insert(this);
do {
- const RefSCC *AncestorC = AncestorWorklist.pop_back_val();
- if (AncestorC->isChildOf(C))
- return true;
- for (const RefSCC *ParentC : AncestorC->Parents)
- AncestorWorklist.push_back(ParentC);
- } while (!AncestorWorklist.empty());
+ const RefSCC &DescendantRC = *Worklist.pop_back_val();
+ for (SCC &C : DescendantRC)
+ for (Node &N : C)
+ for (Edge &E : *N) {
+ auto *ChildRC = G->lookupRefSCC(E.getNode());
+ if (ChildRC == &RC)
+ return true;
+ if (!ChildRC || !Visited.insert(ChildRC).second)
+ continue;
+ Worklist.push_back(ChildRC);
+ }
+ } while (!Worklist.empty());
return false;
}
@@ -907,17 +933,13 @@ void LazyCallGraph::RefSCC::insertOutgoingEdge(Node &SourceN, Node &TargetN,
assert(G->lookupRefSCC(SourceN) == this && "Source must be in this RefSCC.");
- RefSCC &TargetC = *G->lookupRefSCC(TargetN);
- assert(&TargetC != this && "Target must not be in this RefSCC.");
+ assert(G->lookupRefSCC(TargetN) != this &&
+ "Target must not be in this RefSCC.");
#ifdef EXPENSIVE_CHECKS
- assert(TargetC.isDescendantOf(*this) &&
+ assert(G->lookupRefSCC(TargetN)->isDescendantOf(*this) &&
"Target must be a descendant of the Source.");
#endif
- // The only change required is to add this SCC to the parent set of the
- // callee.
- TargetC.Parents.insert(this);
-
#ifndef NDEBUG
// Check that the RefSCC is still valid.
verify();
@@ -957,22 +979,20 @@ LazyCallGraph::RefSCC::insertIncomingRefEdge(Node &SourceN, Node &TargetN) {
// RefSCCs (and their edges) are visited here.
auto ComputeSourceConnectedSet = [&](SmallPtrSetImpl<RefSCC *> &Set) {
Set.insert(&SourceC);
- SmallVector<RefSCC *, 4> Worklist;
- Worklist.push_back(&SourceC);
- do {
- RefSCC &RC = *Worklist.pop_back_val();
- for (RefSCC &ParentRC : RC.parents()) {
- // Skip any RefSCCs outside the range of source to target in the
- // postorder sequence.
- int ParentIdx = G->getRefSCCIndex(ParentRC);
- assert(ParentIdx > SourceIdx && "Parent cannot precede source in postorder!");
- if (ParentIdx > TargetIdx)
- continue;
- if (Set.insert(&ParentRC).second)
- // First edge connecting to this parent, add it to our worklist.
- Worklist.push_back(&ParentRC);
- }
- } while (!Worklist.empty());
+ auto IsConnected = [&](RefSCC &RC) {
+ for (SCC &C : RC)
+ for (Node &N : C)
+ for (Edge &E : *N)
+ if (Set.count(G->lookupRefSCC(E.getNode())))
+ return true;
+
+ return false;
+ };
+
+ for (RefSCC *C : make_range(G->PostOrderRefSCCs.begin() + SourceIdx + 1,
+ G->PostOrderRefSCCs.begin() + TargetIdx + 1))
+ if (IsConnected(*C))
+ Set.insert(C);
};
// Use a normal worklist to find which SCCs the target connects to. We still
@@ -1023,12 +1043,6 @@ LazyCallGraph::RefSCC::insertIncomingRefEdge(Node &SourceN, Node &TargetN) {
assert(RC != this && "We're merging into the target RefSCC, so it "
"shouldn't be in the range.");
- // Merge the parents which aren't part of the merge into the our parents.
- for (RefSCC *ParentRC : RC->Parents)
- if (!MergeSet.count(ParentRC))
- Parents.insert(ParentRC);
- RC->Parents.clear();
-
// Walk the inner SCCs to update their up-pointer and walk all the edges to
// update any parent sets.
// FIXME: We should try to find a way to avoid this (rather expensive) edge
@@ -1036,16 +1050,8 @@ LazyCallGraph::RefSCC::insertIncomingRefEdge(Node &SourceN, Node &TargetN) {
for (SCC &InnerC : *RC) {
InnerC.OuterRefSCC = this;
SCCIndices[&InnerC] = SCCIndex++;
- for (Node &N : InnerC) {
+ for (Node &N : InnerC)
G->SCCMap[&N] = &InnerC;
- for (Edge &E : *N) {
- RefSCC &ChildRC = *G->lookupRefSCC(E.getNode());
- if (MergeSet.count(&ChildRC))
- continue;
- ChildRC.Parents.erase(RC);
- ChildRC.Parents.insert(this);
- }
- }
}
// Now merge in the SCCs. We can actually move here so try to reuse storage
@@ -1087,12 +1093,8 @@ LazyCallGraph::RefSCC::insertIncomingRefEdge(Node &SourceN, Node &TargetN) {
void LazyCallGraph::RefSCC::removeOutgoingEdge(Node &SourceN, Node &TargetN) {
assert(G->lookupRefSCC(SourceN) == this &&
"The source must be a member of this RefSCC.");
-
- RefSCC &TargetRC = *G->lookupRefSCC(TargetN);
- assert(&TargetRC != this && "The target must not be a member of this RefSCC");
-
- assert(!is_contained(G->LeafRefSCCs, this) &&
- "Cannot have a leaf RefSCC source.");
+ assert(G->lookupRefSCC(TargetN) != this &&
+ "The target must not be a member of this RefSCC");
#ifndef NDEBUG
// In a debug build, verify the RefSCC is valid to start with and when this
@@ -1105,122 +1107,72 @@ void LazyCallGraph::RefSCC::removeOutgoingEdge(Node &SourceN, Node &TargetN) {
bool Removed = SourceN->removeEdgeInternal(TargetN);
(void)Removed;
assert(Removed && "Target not in the edge set for this caller?");
-
- bool HasOtherEdgeToChildRC = false;
- bool HasOtherChildRC = false;
- for (SCC *InnerC : SCCs) {
- for (Node &N : *InnerC) {
- for (Edge &E : *N) {
- RefSCC &OtherChildRC = *G->lookupRefSCC(E.getNode());
- if (&OtherChildRC == &TargetRC) {
- HasOtherEdgeToChildRC = true;
- break;
- }
- if (&OtherChildRC != this)
- HasOtherChildRC = true;
- }
- if (HasOtherEdgeToChildRC)
- break;
- }
- if (HasOtherEdgeToChildRC)
- break;
- }
- // Because the SCCs form a DAG, deleting such an edge cannot change the set
- // of SCCs in the graph. However, it may cut an edge of the SCC DAG, making
- // the source SCC no longer connected to the target SCC. If so, we need to
- // update the target SCC's map of its parents.
- if (!HasOtherEdgeToChildRC) {
- bool Removed = TargetRC.Parents.erase(this);
- (void)Removed;
- assert(Removed &&
- "Did not find the source SCC in the target SCC's parent list!");
-
- // It may orphan an SCC if it is the last edge reaching it, but that does
- // not violate any invariants of the graph.
- if (TargetRC.Parents.empty())
- DEBUG(dbgs() << "LCG: Update removing " << SourceN.getFunction().getName()
- << " -> " << TargetN.getFunction().getName()
- << " edge orphaned the callee's SCC!\n");
-
- // It may make the Source SCC a leaf SCC.
- if (!HasOtherChildRC)
- G->LeafRefSCCs.push_back(this);
- }
}
SmallVector<LazyCallGraph::RefSCC *, 1>
-LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN, Node &TargetN) {
- assert(!(*SourceN)[TargetN].isCall() &&
- "Cannot remove a call edge, it must first be made a ref edge");
+LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN,
+ ArrayRef<Node *> TargetNs) {
+ // We return a list of the resulting *new* RefSCCs in post-order.
+ SmallVector<RefSCC *, 1> Result;
#ifndef NDEBUG
- // In a debug build, verify the RefSCC is valid to start with and when this
- // routine finishes.
+ // In a debug build, verify the RefSCC is valid to start with and that either
+ // we return an empty list of result RefSCCs and this RefSCC remains valid,
+ // or we return new RefSCCs and this RefSCC is dead.
verify();
- auto VerifyOnExit = make_scope_exit([&]() { verify(); });
+ auto VerifyOnExit = make_scope_exit([&]() {
+ // If we didn't replace our RefSCC with new ones, check that this one
+ // remains valid.
+ if (G)
+ verify();
+ });
#endif
- // First remove the actual edge.
- bool Removed = SourceN->removeEdgeInternal(TargetN);
- (void)Removed;
- assert(Removed && "Target not in the edge set for this caller?");
+ // First remove the actual edges.
+ for (Node *TargetN : TargetNs) {
+ assert(!(*SourceN)[*TargetN].isCall() &&
+ "Cannot remove a call edge, it must first be made a ref edge");
- // We return a list of the resulting *new* RefSCCs in post-order.
- SmallVector<RefSCC *, 1> Result;
+ bool Removed = SourceN->removeEdgeInternal(*TargetN);
+ (void)Removed;
+ assert(Removed && "Target not in the edge set for this caller?");
+ }
- // Direct recursion doesn't impact the SCC graph at all.
- if (&SourceN == &TargetN)
+ // Direct self references don't impact the ref graph at all.
+ if (llvm::all_of(TargetNs,
+ [&](Node *TargetN) { return &SourceN == TargetN; }))
return Result;
- // If this ref edge is within an SCC then there are sufficient other edges to
- // form a cycle without this edge so removing it is a no-op.
+ // If all targets are in the same SCC as the source, because no call edges
+ // were removed there is no RefSCC structure change.
SCC &SourceC = *G->lookupSCC(SourceN);
- SCC &TargetC = *G->lookupSCC(TargetN);
- if (&SourceC == &TargetC)
+ if (llvm::all_of(TargetNs, [&](Node *TargetN) {
+ return G->lookupSCC(*TargetN) == &SourceC;
+ }))
return Result;
// We build somewhat synthetic new RefSCCs by providing a postorder mapping
- // for each inner SCC. We also store these associated with *nodes* rather
- // than SCCs because this saves a round-trip through the node->SCC map and in
- // the common case, SCCs are small. We will verify that we always give the
- // same number to every node in the SCC such that these are equivalent.
- const int RootPostOrderNumber = 0;
- int PostOrderNumber = RootPostOrderNumber + 1;
- SmallDenseMap<Node *, int> PostOrderMapping;
-
- // Every node in the target SCC can already reach every node in this RefSCC
- // (by definition). It is the only node we know will stay inside this RefSCC.
- // Everything which transitively reaches Target will also remain in the
- // RefSCC. We handle this by pre-marking that the nodes in the target SCC map
- // back to the root post order number.
- //
- // This also enables us to take a very significant short-cut in the standard
- // Tarjan walk to re-form RefSCCs below: whenever we build an edge that
- // references the target node, we know that the target node eventually
- // references all other nodes in our walk. As a consequence, we can detect
- // and handle participants in that cycle without walking all the edges that
- // form the connections, and instead by relying on the fundamental guarantee
- // coming into this operation.
- for (Node &N : TargetC)
- PostOrderMapping[&N] = RootPostOrderNumber;
+ // for each inner SCC. We store these inside the low-link field of the nodes
+ // rather than associated with SCCs because this saves a round-trip through
+ // the node->SCC map and in the common case, SCCs are small. We will verify
+ // that we always give the same number to every node in the SCC such that
+ // these are equivalent.
+ int PostOrderNumber = 0;
// Reset all the other nodes to prepare for a DFS over them, and add them to
// our worklist.
SmallVector<Node *, 8> Worklist;
for (SCC *C : SCCs) {
- if (C == &TargetC)
- continue;
-
for (Node &N : *C)
N.DFSNumber = N.LowLink = 0;
Worklist.append(C->Nodes.begin(), C->Nodes.end());
}
- auto MarkNodeForSCCNumber = [&PostOrderMapping](Node &N, int Number) {
- N.DFSNumber = N.LowLink = -1;
- PostOrderMapping[&N] = Number;
- };
+ // Track the number of nodes in this RefSCC so that we can quickly recognize
+ // an important special case of the edge removal not breaking the cycle of
+ // this RefSCC.
+ const int NumRefSCCNodes = Worklist.size();
SmallVector<std::pair<Node *, EdgeSequence::iterator>, 4> DFSStack;
SmallVector<Node *, 4> PendingRefSCCStack;
@@ -1267,31 +1219,8 @@ LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN, Node &TargetN) {
continue;
}
if (ChildN.DFSNumber == -1) {
- // Check if this edge's target node connects to the deleted edge's
- // target node. If so, we know that every node connected will end up
- // in this RefSCC, so collapse the entire current stack into the root
- // slot in our SCC numbering. See above for the motivation of
- // optimizing the target connected nodes in this way.
- auto PostOrderI = PostOrderMapping.find(&ChildN);
- if (PostOrderI != PostOrderMapping.end() &&
- PostOrderI->second == RootPostOrderNumber) {
- MarkNodeForSCCNumber(*N, RootPostOrderNumber);
- while (!PendingRefSCCStack.empty())
- MarkNodeForSCCNumber(*PendingRefSCCStack.pop_back_val(),
- RootPostOrderNumber);
- while (!DFSStack.empty())
- MarkNodeForSCCNumber(*DFSStack.pop_back_val().first,
- RootPostOrderNumber);
- // Ensure we break all the way out of the enclosing loop.
- N = nullptr;
- break;
- }
-
// If this child isn't currently in this RefSCC, no need to process
- // it. However, we do need to remove this RefSCC from its RefSCC's
- // parent set.
- RefSCC &ChildRC = *G->lookupRefSCC(ChildN);
- ChildRC.Parents.erase(this);
+ // it.
++I;
continue;
}
@@ -1304,9 +1233,6 @@ LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN, Node &TargetN) {
N->LowLink = ChildN.LowLink;
++I;
}
- if (!N)
- // We short-circuited this node.
- break;
// We've finished processing N and its descendents, put it on our pending
// stack to eventually get merged into a RefSCC.
@@ -1321,146 +1247,98 @@ LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN, Node &TargetN) {
}
// Otherwise, form a new RefSCC from the top of the pending node stack.
+ int RefSCCNumber = PostOrderNumber++;
int RootDFSNumber = N->DFSNumber;
+
// Find the range of the node stack by walking down until we pass the
- // root DFS number.
- auto RefSCCNodes = make_range(
- PendingRefSCCStack.rbegin(),
- find_if(reverse(PendingRefSCCStack), [RootDFSNumber](const Node *N) {
- return N->DFSNumber < RootDFSNumber;
- }));
+ // root DFS number. Update the DFS numbers and low link numbers in the
+ // process to avoid re-walking this list where possible.
+ auto StackRI = find_if(reverse(PendingRefSCCStack), [&](Node *N) {
+ if (N->DFSNumber < RootDFSNumber)
+ // We've found the bottom.
+ return true;
- // Mark the postorder number for these nodes and clear them off the
- // stack. We'll use the postorder number to pull them into RefSCCs at the
- // end. FIXME: Fuse with the loop above.
- int RefSCCNumber = PostOrderNumber++;
- for (Node *N : RefSCCNodes)
- MarkNodeForSCCNumber(*N, RefSCCNumber);
+ // Update this node and keep scanning.
+ N->DFSNumber = -1;
+ // Save the post-order number in the lowlink field so that we can use
+ // it to map SCCs into new RefSCCs after we finish the DFS.
+ N->LowLink = RefSCCNumber;
+ return false;
+ });
+ auto RefSCCNodes = make_range(StackRI.base(), PendingRefSCCStack.end());
+
+ // If we find a cycle containing all nodes originally in this RefSCC then
+ // the removal hasn't changed the structure at all. This is an important
+ // special case and we can directly exit the entire routine more
+ // efficiently as soon as we discover it.
+ if (std::distance(RefSCCNodes.begin(), RefSCCNodes.end()) ==
+ NumRefSCCNodes) {
+ // Clear out the low link field as we won't need it.
+ for (Node *N : RefSCCNodes)
+ N->LowLink = -1;
+ // Return the empty result immediately.
+ return Result;
+ }
- PendingRefSCCStack.erase(RefSCCNodes.end().base(),
- PendingRefSCCStack.end());
+ // We've already marked the nodes internally with the RefSCC number so
+ // just clear them off the stack and continue.
+ PendingRefSCCStack.erase(RefSCCNodes.begin(), PendingRefSCCStack.end());
} while (!DFSStack.empty());
assert(DFSStack.empty() && "Didn't flush the entire DFS stack!");
assert(PendingRefSCCStack.empty() && "Didn't flush all pending nodes!");
} while (!Worklist.empty());
- // We now have a post-order numbering for RefSCCs and a mapping from each
- // node in this RefSCC to its final RefSCC. We create each new RefSCC node
- // (re-using this RefSCC node for the root) and build a radix-sort style map
- // from postorder number to the RefSCC. We then append SCCs to each of these
- // RefSCCs in the order they occured in the original SCCs container.
- for (int i = 1; i < PostOrderNumber; ++i)
+ assert(PostOrderNumber > 1 &&
+ "Should never finish the DFS when the existing RefSCC remains valid!");
+
+ // Otherwise we create a collection of new RefSCC nodes and build
+ // a radix-sort style map from postorder number to these new RefSCCs. We then
+ // append SCCs to each of these RefSCCs in the order they occured in the
+ // original SCCs container.
+ for (int i = 0; i < PostOrderNumber; ++i)
Result.push_back(G->createRefSCC(*G));
// Insert the resulting postorder sequence into the global graph postorder
- // sequence before the current RefSCC in that sequence. The idea being that
- // this RefSCC is the target of the reference edge removed, and thus has
- // a direct or indirect edge to every other RefSCC formed and so must be at
- // the end of any postorder traversal.
+ // sequence before the current RefSCC in that sequence, and then remove the
+ // current one.
//
// FIXME: It'd be nice to change the APIs so that we returned an iterator
// range over the global postorder sequence and generally use that sequence
// rather than building a separate result vector here.
- if (!Result.empty()) {
- int Idx = G->getRefSCCIndex(*this);
- G->PostOrderRefSCCs.insert(G->PostOrderRefSCCs.begin() + Idx,
- Result.begin(), Result.end());
- for (int i : seq<int>(Idx, G->PostOrderRefSCCs.size()))
- G->RefSCCIndices[G->PostOrderRefSCCs[i]] = i;
- assert(G->PostOrderRefSCCs[G->getRefSCCIndex(*this)] == this &&
- "Failed to update this RefSCC's index after insertion!");
- }
+ int Idx = G->getRefSCCIndex(*this);
+ G->PostOrderRefSCCs.erase(G->PostOrderRefSCCs.begin() + Idx);
+ G->PostOrderRefSCCs.insert(G->PostOrderRefSCCs.begin() + Idx, Result.begin(),
+ Result.end());
+ for (int i : seq<int>(Idx, G->PostOrderRefSCCs.size()))
+ G->RefSCCIndices[G->PostOrderRefSCCs[i]] = i;
for (SCC *C : SCCs) {
- auto PostOrderI = PostOrderMapping.find(&*C->begin());
- assert(PostOrderI != PostOrderMapping.end() &&
- "Cannot have missing mappings for nodes!");
- int SCCNumber = PostOrderI->second;
-#ifndef NDEBUG
- for (Node &N : *C)
- assert(PostOrderMapping.find(&N)->second == SCCNumber &&
+ // We store the SCC number in the node's low-link field above.
+ int SCCNumber = C->begin()->LowLink;
+ // Clear out all of the SCC's node's low-link fields now that we're done
+ // using them as side-storage.
+ for (Node &N : *C) {
+ assert(N.LowLink == SCCNumber &&
"Cannot have different numbers for nodes in the same SCC!");
-#endif
- if (SCCNumber == 0)
- // The root node is handled separately by removing the SCCs.
- continue;
+ N.LowLink = -1;
+ }
- RefSCC &RC = *Result[SCCNumber - 1];
+ RefSCC &RC = *Result[SCCNumber];
int SCCIndex = RC.SCCs.size();
RC.SCCs.push_back(C);
RC.SCCIndices[C] = SCCIndex;
C->OuterRefSCC = &RC;
}
- // FIXME: We re-walk the edges in each RefSCC to establish whether it is
- // a leaf and connect it to the rest of the graph's parents lists. This is
- // really wasteful. We should instead do this during the DFS to avoid yet
- // another edge walk.
- for (RefSCC *RC : Result)
- G->connectRefSCC(*RC);
-
- // Now erase all but the root's SCCs.
- SCCs.erase(remove_if(SCCs,
- [&](SCC *C) {
- return PostOrderMapping.lookup(&*C->begin()) !=
- RootPostOrderNumber;
- }),
- SCCs.end());
+ // Now that we've moved things into the new RefSCCs, clear out our current
+ // one.
+ G = nullptr;
+ SCCs.clear();
SCCIndices.clear();
- for (int i = 0, Size = SCCs.size(); i < Size; ++i)
- SCCIndices[SCCs[i]] = i;
#ifndef NDEBUG
- // Now we need to reconnect the current (root) SCC to the graph. We do this
- // manually because we can special case our leaf handling and detect errors.
- bool IsLeaf = true;
-#endif
- for (SCC *C : SCCs)
- for (Node &N : *C) {
- for (Edge &E : *N) {
- RefSCC &ChildRC = *G->lookupRefSCC(E.getNode());
- if (&ChildRC == this)
- continue;
- ChildRC.Parents.insert(this);
-#ifndef NDEBUG
- IsLeaf = false;
-#endif
- }
- }
-#ifndef NDEBUG
- if (!Result.empty())
- assert(!IsLeaf && "This SCC cannot be a leaf as we have split out new "
- "SCCs by removing this edge.");
- if (none_of(G->LeafRefSCCs, [&](RefSCC *C) { return C == this; }))
- assert(!IsLeaf && "This SCC cannot be a leaf as it already had child "
- "SCCs before we removed this edge.");
-#endif
- // And connect both this RefSCC and all the new ones to the correct parents.
- // The easiest way to do this is just to re-analyze the old parent set.
- SmallVector<RefSCC *, 4> OldParents(Parents.begin(), Parents.end());
- Parents.clear();
- for (RefSCC *ParentRC : OldParents)
- for (SCC &ParentC : *ParentRC)
- for (Node &ParentN : ParentC)
- for (Edge &E : *ParentN) {
- RefSCC &RC = *G->lookupRefSCC(E.getNode());
- if (&RC != ParentRC)
- RC.Parents.insert(ParentRC);
- }
-
- // If this SCC stopped being a leaf through this edge removal, remove it from
- // the leaf SCC list. Note that this DTRT in the case where this was never
- // a leaf.
- // FIXME: As LeafRefSCCs could be very large, we might want to not walk the
- // entire list if this RefSCC wasn't a leaf before the edge removal.
- if (!Result.empty())
- G->LeafRefSCCs.erase(
- std::remove(G->LeafRefSCCs.begin(), G->LeafRefSCCs.end(), this),
- G->LeafRefSCCs.end());
-
-#ifndef NDEBUG
- // Verify all of the new RefSCCs.
+ // Verify the new RefSCCs we've built.
for (RefSCC *RC : Result)
RC->verify();
#endif
@@ -1477,18 +1355,13 @@ void LazyCallGraph::RefSCC::handleTrivialEdgeInsertion(Node &SourceN,
// after this edge insertion.
assert(G->lookupRefSCC(SourceN) == this && "Source must be in this RefSCC.");
RefSCC &TargetRC = *G->lookupRefSCC(TargetN);
- if (&TargetRC == this) {
-
+ if (&TargetRC == this)
return;
- }
#ifdef EXPENSIVE_CHECKS
assert(TargetRC.isDescendantOf(*this) &&
"Target must be a descendant of the Source.");
#endif
- // The only change required is to add this RefSCC to the parent set of the
- // target. This is a set and so idempotent if the edge already existed.
- TargetRC.Parents.insert(this);
}
void LazyCallGraph::RefSCC::insertTrivialCallEdge(Node &SourceN,
@@ -1646,24 +1519,6 @@ void LazyCallGraph::removeDeadFunction(Function &F) {
assert(C.size() == 1 && "Dead functions must be in a singular SCC");
assert(RC.size() == 1 && "Dead functions must be in a singular RefSCC");
- // Clean up any remaining reference edges. Note that we walk an unordered set
- // here but are just removing and so the order doesn't matter.
- for (RefSCC &ParentRC : RC.parents())
- for (SCC &ParentC : ParentRC)
- for (Node &ParentN : ParentC)
- if (ParentN)
- ParentN->removeEdgeInternal(N);
-
- // Now remove this RefSCC from any parents sets and the leaf list.
- for (Edge &E : *N)
- if (RefSCC *TargetRC = lookupRefSCC(E.getNode()))
- TargetRC->Parents.erase(&RC);
- // FIXME: This is a linear operation which could become hot and benefit from
- // an index map.
- auto LRI = find(LeafRefSCCs, &RC);
- if (LRI != LeafRefSCCs.end())
- LeafRefSCCs.erase(LRI);
-
auto RCIndexI = RefSCCIndices.find(&RC);
int RCIndex = RCIndexI->second;
PostOrderRefSCCs.erase(PostOrderRefSCCs.begin() + RCIndex);
@@ -1674,8 +1529,11 @@ void LazyCallGraph::removeDeadFunction(Function &F) {
// Finally clear out all the data structures from the node down through the
// components.
N.clear();
+ N.G = nullptr;
+ N.F = nullptr;
C.clear();
RC.clear();
+ RC.G = nullptr;
// Nothing to delete as all the objects are allocated in stable bump pointer
// allocators.
@@ -1686,32 +1544,13 @@ LazyCallGraph::Node &LazyCallGraph::insertInto(Function &F, Node *&MappedN) {
}
void LazyCallGraph::updateGraphPtrs() {
- // Process all nodes updating the graph pointers.
- {
- SmallVector<Node *, 16> Worklist;
- for (Edge &E : EntryEdges)
- Worklist.push_back(&E.getNode());
-
- while (!Worklist.empty()) {
- Node &N = *Worklist.pop_back_val();
- N.G = this;
- if (N)
- for (Edge &E : *N)
- Worklist.push_back(&E.getNode());
- }
- }
+ // Walk the node map to update their graph pointers. While this iterates in
+ // an unstable order, the order has no effect so it remains correct.
+ for (auto &FunctionNodePair : NodeMap)
+ FunctionNodePair.second->G = this;
- // Process all SCCs updating the graph pointers.
- {
- SmallVector<RefSCC *, 16> Worklist(LeafRefSCCs.begin(), LeafRefSCCs.end());
-
- while (!Worklist.empty()) {
- RefSCC &C = *Worklist.pop_back_val();
- C.G = this;
- for (RefSCC &ParentC : C.parents())
- Worklist.push_back(&ParentC);
- }
- }
+ for (auto *RC : PostOrderRefSCCs)
+ RC->G = this;
}
template <typename RootsT, typename GetBeginT, typename GetEndT,
@@ -1719,7 +1558,7 @@ template <typename RootsT, typename GetBeginT, typename GetEndT,
void LazyCallGraph::buildGenericSCCs(RootsT &&Roots, GetBeginT &&GetBegin,
GetEndT &&GetEnd, GetNodeT &&GetNode,
FormSCCCallbackT &&FormSCC) {
- typedef decltype(GetBegin(std::declval<Node &>())) EdgeItT;
+ using EdgeItT = decltype(GetBegin(std::declval<Node &>()));
SmallVector<std::pair<Node *, EdgeItT>, 16> DFSStack;
SmallVector<Node *, 16> PendingSCCStack;
@@ -1871,7 +1710,6 @@ void LazyCallGraph::buildRefSCCs() {
[this](node_stack_range Nodes) {
RefSCC *NewRC = createRefSCC(*this);
buildSCCs(*NewRC, Nodes);
- connectRefSCC(*NewRC);
// Push the new node into the postorder list and remember its position
// in the index map.
@@ -1886,28 +1724,6 @@ void LazyCallGraph::buildRefSCCs() {
});
}
-// FIXME: We should move callers of this to embed the parent linking and leaf
-// tracking into their DFS in order to remove a full walk of all edges.
-void LazyCallGraph::connectRefSCC(RefSCC &RC) {
- // Walk all edges in the RefSCC (this remains linear as we only do this once
- // when we build the RefSCC) to connect it to the parent sets of its
- // children.
- bool IsLeaf = true;
- for (SCC &C : RC)
- for (Node &N : C)
- for (Edge &E : *N) {
- RefSCC &ChildRC = *lookupRefSCC(E.getNode());
- if (&ChildRC == &RC)
- continue;
- ChildRC.Parents.insert(&RC);
- IsLeaf = false;
- }
-
- // For the SCCs where we find no child SCCs, add them to the leaf list.
- if (IsLeaf)
- LeafRefSCCs.push_back(&RC);
-}
-
AnalysisKey LazyCallGraphAnalysis::Key;
LazyCallGraphPrinterPass::LazyCallGraphPrinterPass(raw_ostream &OS) : OS(OS) {}