Index: include/llvm/Analysis/LazyCallGraph.h =================================================================== --- include/llvm/Analysis/LazyCallGraph.h +++ include/llvm/Analysis/LazyCallGraph.h @@ -425,6 +425,32 @@ RefSCC &getOuterRefSCC() const { return *OuterRefSCC; } + /// Test if this SCC is a parent of \a C. + /// + /// Note that this is linear in the number of edges departing the current + /// SCC. + bool isParentOf(const SCC &C) const; + + /// Test if this SCC is an ancestor of \a C. + /// + /// Note that in the worst case this is linear in the number of edges + /// departing the current SCC and every SCC in the entire graph reachable + /// from this SCC. Thus this very well may walk every edge in the entire + /// call graph! Do not call this in a tight loop! + bool isAncestorOf(const SCC &C) const; + + /// Test if this SCC is a child of \a C. + /// + /// See the comments for \c isParentOf for detailed notes about the + /// complexity of this routine. + bool isChildOf(const SCC &C) const { return C.isParentOf(*this); } + + /// Test if this SCC is a descendant of \a C. + /// + /// See the comments for \c isParentOf for detailed notes about the + /// complexity of this routine. + bool isDescendantOf(const SCC &C) const { return C.isAncestorOf(*this); } + /// Provide a short name by printing this SCC to a std::string. /// /// This copes with the fact that we don't have a name per-se for an SCC Index: lib/Analysis/LazyCallGraph.cpp =================================================================== --- lib/Analysis/LazyCallGraph.cpp +++ lib/Analysis/LazyCallGraph.cpp @@ -187,6 +187,54 @@ } #endif +bool LazyCallGraph::SCC::isParentOf(const SCC &C) const { + if (this == &C) + return false; + + for (Node &N : *this) + for (Edge &E : N.calls()) + if (Node *CalleeN = E.getNode()) + if (OuterRefSCC->G->lookupSCC(*CalleeN) == &C) + return true; + + // No edges found. + return false; +} + +bool LazyCallGraph::SCC::isAncestorOf(const SCC &C) const { + if (this == &C) + return false; + + LazyCallGraph &G = *OuterRefSCC->G; + + // Start with the nodes in this SCC. + SmallPtrSet Visited(Nodes.begin(), Nodes.end()); + SmallVector Worklist(Nodes.begin(), Nodes.end()); + + // Walk down the graph until we run out of edges or find a path to C. + do { + Node &N = *Worklist.pop_back_val(); + for (Edge &E : N.calls()) { + Node *CalleeN = E.getNode(); + if (!CalleeN) + continue; + // Don't revisit nodes we've already checked (and queued). + if (!Visited.insert(CalleeN).second) + continue; + + // If this node is in C, we're done. + if (G.lookupSCC(*CalleeN) == &C) + return true; + + // Otherwise put it on the worklist. + Worklist.push_back(CalleeN); + } + } while (!Worklist.empty()); + + // No paths found. + return false; +} + LazyCallGraph::RefSCC::RefSCC(LazyCallGraph &G) : G(&G) {} void LazyCallGraph::RefSCC::dump() const { @@ -1354,10 +1402,17 @@ #ifndef NDEBUG // Check that the RefSCC is still valid when we finish. auto ExitVerifier = make_scope_exit([this] { verify(); }); + + // Check that we aren't potentially breaking some invariants of the SCC graph. + SCC &SourceC = *G->lookupSCC(SourceN); + SCC &TargetC = *G->lookupSCC(TargetN); + if (&SourceC != &TargetC) + assert(SourceC.isAncestorOf(TargetC) && + "Call edge is not trivial in the SCC graph!"); #endif - // First insert it into the source or find the existing edge. - auto InsertResult = SourceN.EdgeIndexMap.insert( - {&TargetN.getFunction(), SourceN.Edges.size()}); + // First insert it into the source or find the existing edge. + auto InsertResult = SourceN.EdgeIndexMap.insert( + {&TargetN.getFunction(), SourceN.Edges.size()}); if (!InsertResult.second) { // Already an edge, just update it. Edge &E = SourceN.Edges[InsertResult.first->second];