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.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/lib/Analysis/LazyCallGraph.cpp b/lib/Analysis/LazyCallGraph.cpp
index a86c969716..48d43ea02e 100644
--- a/lib/Analysis/LazyCallGraph.cpp
+++ b/lib/Analysis/LazyCallGraph.cpp
@@ -131,6 +131,237 @@ LazyCallGraph &LazyCallGraph::operator=(LazyCallGraph &&G) {
return *this;
}
+void LazyCallGraph::SCC::removeEdge(LazyCallGraph &G, Function &Caller,
+ Function &Callee, SCC &CalleeC) {
+ assert(std::find(G.LeafSCCs.begin(), G.LeafSCCs.end(), this) ==
+ G.LeafSCCs.end() &&
+ "Cannot have a leaf SCC caller with a different SCC callee.");
+
+ bool HasOtherCallToCalleeC = false;
+ bool HasOtherCallOutsideSCC = false;
+ for (Node *N : *this) {
+ for (Node *Callee : *N) {
+ SCC *OtherCalleeC = G.SCCMap.lookup(&Callee->F);
+ if (OtherCalleeC == &CalleeC) {
+ HasOtherCallToCalleeC = true;
+ break;
+ }
+ if (OtherCalleeC != this)
+ HasOtherCallOutsideSCC = true;
+ }
+ if (HasOtherCallToCalleeC)
+ 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 caller no longer a parent of the callee. Walk the other call edges
+ // in the caller to tell.
+ if (!HasOtherCallToCalleeC) {
+ bool Removed = CalleeC.ParentSCCs.remove(this);
+ (void)Removed;
+ assert(Removed &&
+ "Did not find the caller SCC in the callee 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 (CalleeC.ParentSCCs.empty())
+ DEBUG(dbgs() << "LCG: Update removing " << Caller.getName() << " -> "
+ << Callee.getName() << " edge orphaned the callee's SCC!\n");
+ }
+
+ // It may make the Caller SCC a leaf SCC.
+ if (!HasOtherCallOutsideSCC)
+ G.LeafSCCs.push_back(this);
+}
+
+SmallVector<LazyCallGraph::SCC *, 1>
+LazyCallGraph::SCC::removeInternalEdge(LazyCallGraph &G, Node &Caller,
+ Node &Callee) {
+ // We return a list of the resulting SCCs, where 'this' is always the first
+ // element.
+ SmallVector<SCC *, 1> ResultSCCs;
+ ResultSCCs.push_back(this);
+
+ // We're going to do a full mini-Tarjan's walk using a local stack here.
+ int NextDFSNumber = 1;
+ SmallVector<std::pair<Node *, Node::iterator>, 4> DFSStack;
+
+ // The worklist is every node in the original SCC. FIXME: switch the SCC to
+ // use a SmallSetVector and swap here.
+ SmallSetVector<Node *, 1> Worklist;
+ for (Node *N : Nodes) {
+ // Clear these to 0 while we re-run Tarjan's over the SCC.
+ N->DFSNumber = 0;
+ N->LowLink = 0;
+ Worklist.insert(N);
+ }
+
+ // The callee can already reach every node in this SCC (by definition). It is
+ // the only node we know will stay inside this SCC. Everything which
+ // transitively reaches Callee will also remain in the SCC. To model this we
+ // incrementally add any chain of nodes which reaches something in the new
+ // node set to the new node set. This short circuits one side of the Tarjan's
+ // walk.
+ SmallSetVector<Node *, 1> NewNodes;
+ NewNodes.insert(&Callee);
+
+ for (;;) {
+ if (DFSStack.empty()) {
+ if (Worklist.empty())
+ break;
+ Node *N = Worklist.pop_back_val();
+ DFSStack.push_back(std::make_pair(N, N->begin()));
+ }
+
+ Node *N = DFSStack.back().first;
+
+ // Check if we have reached a node in the new (known connected) set. If so,
+ // the entire stack is necessarily in that set and we can re-start.
+ if (NewNodes.count(N)) {
+ DFSStack.pop_back();
+ while (!DFSStack.empty())
+ NewNodes.insert(DFSStack.pop_back_val().first);
+ continue;
+ }
+
+ if (N->DFSNumber == 0) {
+ N->LowLink = N->DFSNumber = NextDFSNumber++;
+ Worklist.remove(N);
+ }
+
+ auto SI = DFSStack.rbegin();
+ bool PushedChildNode = false;
+ do {
+ N = SI->first;
+ for (auto I = SI->second, E = N->end(); I != E; ++I) {
+ Node *ChildN = *I;
+ // If this child isn't currently in this SCC, no need to process it.
+ // However, we do need to remove this SCC from its SCC's parent set.
+ SCC *ChildSCC = G.SCCMap.lookup(&ChildN->F);
+ assert(ChildSCC &&
+ "Everything reachable must already be in *some* SCC");
+ if (ChildSCC != this) {
+ ChildSCC->ParentSCCs.remove(this);
+ continue;
+ }
+
+ if (ChildN->DFSNumber == 0) {
+ // Mark that we should start at this child when next this node is the
+ // top of the stack. We don't start at the next child to ensure this
+ // child's lowlink is reflected.
+ SI->second = I;
+
+ // Recurse onto this node via a tail call.
+ DFSStack.push_back(std::make_pair(ChildN, ChildN->begin()));
+ PushedChildNode = true;
+ break;
+ }
+
+ // Track the lowest link of the childen, if any are still in the stack.
+ // Any child not on the stack will have a LowLink of -1.
+ assert(ChildN->LowLink != 0 &&
+ "Impossible with a non-zero DFS number.");
+ if (ChildN->LowLink >= 0 && ChildN->LowLink < N->LowLink)
+ N->LowLink = ChildN->LowLink;
+ }
+ if (!PushedChildNode)
+ // No more children to process for this stack entry.
+ SI->second = N->end();
+
+ ++SI;
+ // If nothing is new on the stack and this isn't the SCC root, walk
+ // upward.
+ } while (!PushedChildNode && N->LowLink != N->DFSNumber &&
+ SI != DFSStack.rend());
+
+ if (PushedChildNode)
+ continue;
+
+ // Form the new SCC out of the top of the DFS stack.
+ ResultSCCs.push_back(G.formSCCFromDFSStack(DFSStack, SI.base()));
+ }
+
+ // Replace this SCC with the NewNodes we collected above.
+ // FIXME: Simplify this when the SCC's datastructure is just a list.
+ Nodes.clear();
+ NodeSet.clear();
+
+ // Now we need to reconnect the current SCC to the graph.
+ bool IsLeafSCC = true;
+ for (Node *N : NewNodes) {
+ N->DFSNumber = -1;
+ N->LowLink = -1;
+ Nodes.push_back(N);
+ NodeSet.insert(&N->getFunction());
+ for (Node *ChildN : *N) {
+ if (NewNodes.count(ChildN))
+ continue;
+ SCC *ChildSCC = G.SCCMap.lookup(&ChildN->getFunction());
+ assert(ChildSCC &&
+ "Must have all child SCCs processed when building a new SCC!");
+ ChildSCC->ParentSCCs.insert(this);
+ IsLeafSCC = false;
+ }
+ }
+#ifndef NDEBUG
+ if (ResultSCCs.size() > 1)
+ assert(!IsLeafSCC && "This SCC cannot be a leaf as we have split out new "
+ "SCCs by removing this edge.");
+ if (!std::any_of(G.LeafSCCs.begin(), G.LeafSCCs.end(),
+ [&](SCC *C) { return C == this; }))
+ assert(!IsLeafSCC && "This SCC cannot be a leaf as it already had child "
+ "SCCs before we removed this edge.");
+#endif
+ // If this SCC stopped being a leaf through this edge removal, remove it from
+ // the leaf SCC list.
+ if (!IsLeafSCC && ResultSCCs.size() > 1)
+ G.LeafSCCs.erase(std::remove(G.LeafSCCs.begin(), G.LeafSCCs.end(), this),
+ G.LeafSCCs.end());
+
+ // Return the new list of SCCs.
+ return ResultSCCs;
+}
+
+void LazyCallGraph::removeEdge(Node &CallerN, Function &Callee) {
+ auto IndexMapI = CallerN.CalleeIndexMap.find(&Callee);
+ assert(IndexMapI != CallerN.CalleeIndexMap.end() &&
+ "Callee not in the callee set for the caller?");
+
+ Node *CalleeN = CallerN.Callees[IndexMapI->second].dyn_cast<Node *>();
+ CallerN.Callees.erase(CallerN.Callees.begin() + IndexMapI->second);
+ CallerN.CalleeIndexMap.erase(IndexMapI);
+
+ SCC *CallerC = SCCMap.lookup(&CallerN.F);
+ if (!CallerC) {
+ // We can only remove edges when the edge isn't actively participating in
+ // a DFS walk. Either it must have been popped into an SCC, or it must not
+ // yet have been reached by the DFS walk. Assert the latter here.
+ assert(std::all_of(DFSStack.begin(), DFSStack.end(),
+ [&](const std::pair<Node *, iterator> &StackEntry) {
+ return StackEntry.first != &CallerN;
+ }) &&
+ "Found the caller on the DFSStack!");
+ return;
+ }
+
+ assert(CalleeN && "If the caller is in an SCC, we have to have explored all "
+ "its transitively called functions.");
+
+ SCC *CalleeC = SCCMap.lookup(&Callee);
+ assert(CalleeC &&
+ "The caller has an SCC, and thus by necessity so does the callee.");
+
+ // The easy case is when they are different SCCs.
+ if (CallerC != CalleeC) {
+ CallerC->removeEdge(*this, CallerN.getFunction(), Callee, *CalleeC);
+ return;
+ }
+
+ // The hard case is when we remove an edge within a SCC. This may cause new
+ // SCCs to need to be added to the graph.
+ CallerC->removeInternalEdge(*this, CallerN, *CalleeN);
+}
+
LazyCallGraph::Node *LazyCallGraph::insertInto(Function &F, Node *&MappedN) {
return new (MappedN = BPA.Allocate()) Node(*this, F);
}
@@ -212,6 +443,8 @@ LazyCallGraph::SCC *LazyCallGraph::getNextSCCInPostOrder() {
if (SI->first->DFSNumber == 0) {
// This node hasn't been visited before, assign it a DFS number and remove
// it from the entry set.
+ assert(!SCCMap.count(&SI->first->getFunction()) &&
+ "Found a node with 0 DFS number but already in an SCC!");
SI->first->LowLink = SI->first->DFSNumber = NextDFSNumber++;
SCCEntryNodes.remove(&SI->first->getFunction());
}