diff --git a/include/llvm/Analysis/LazyCallGraph.h b/include/llvm/Analysis/LazyCallGraph.h index c0fbadb73dc..ea63b837ba7 100644 --- a/include/llvm/Analysis/LazyCallGraph.h +++ b/include/llvm/Analysis/LazyCallGraph.h @@ -1061,6 +1061,10 @@ public: /// Introduce a node for the function \p NewF in the SCC \p C. void addNewFunctionIntoSCC(Function &NewF, SCC &C); + /// Introduce a node for the function \p NewF, as a single node in a + /// new SCC, in the RefSCC \p RC. + void addNewFunctionIntoRefSCC(Function &NewF, RefSCC &RC); + ///@} ///@{ @@ -1167,6 +1171,13 @@ private: /// Helper to update pointers back to the graph object during moves. void updateGraphPtrs(); + /// Helper to insert a new function, add it to the NodeMap, and populate its + /// node. + Node &createNode(Function &F); + + /// Helper to add the given Node \p N to the SCCMap, mapped to the SCC \p C. + void addNodeToSCC(SCC &C, Node &N); + /// Allocates an SCC and constructs it using the graph allocator. /// /// The arguments are forwarded to the constructor. diff --git a/lib/Analysis/LazyCallGraph.cpp b/lib/Analysis/LazyCallGraph.cpp index cdf1d55f5ba..0d647cba709 100644 --- a/lib/Analysis/LazyCallGraph.cpp +++ b/lib/Analysis/LazyCallGraph.cpp @@ -1567,12 +1567,17 @@ void LazyCallGraph::removeDeadFunction(Function &F) { } void LazyCallGraph::addNewFunctionIntoSCC(Function &NewF, SCC &C) { - Node &CGNode = get(NewF); - CGNode.DFSNumber = CGNode.LowLink = -1; - CGNode.populate(); - C.Nodes.push_back(&CGNode); - SCCMap[&CGNode] = &C; - NodeMap[&NewF] = &CGNode; + addNodeToSCC(C, createNode(NewF)); +} + +void LazyCallGraph::addNewFunctionIntoRefSCC(Function &NewF, RefSCC &RC) { + Node &N = createNode(NewF); + + auto *C = createSCC(RC, SmallVector()); + addNodeToSCC(*C, N); + + RC.SCCIndices[C] = RC.SCCIndices.size(); + RC.SCCs.push_back(C); } LazyCallGraph::Node &LazyCallGraph::insertInto(Function &F, Node *&MappedN) { @@ -1589,6 +1594,21 @@ void LazyCallGraph::updateGraphPtrs() { RC->G = this; } +LazyCallGraph::Node &LazyCallGraph::createNode(Function &F) { + assert(!lookup(F) && "node already exists"); + + Node &N = get(F); + NodeMap[&F] = &N; + N.DFSNumber = N.LowLink = -1; + N.populate(); + return N; +} + +void LazyCallGraph::addNodeToSCC(LazyCallGraph::SCC &C, Node &N) { + C.Nodes.push_back(&N); + SCCMap[&N] = &C; +} + template void LazyCallGraph::buildGenericSCCs(RootsT &&Roots, GetBeginT &&GetBegin, diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp index 3be46364687..a507367518a 100644 --- a/unittests/Analysis/LazyCallGraphTest.cpp +++ b/unittests/Analysis/LazyCallGraphTest.cpp @@ -2210,4 +2210,46 @@ TEST(LazyCallGraphTest, RemoveFunctionWithSpurriousRef) { EXPECT_EQ(&RC2, &*I++); EXPECT_EQ(CG.postorder_ref_scc_end(), I); } + +TEST(LazyCallGraphTest, AddNewFunctionIntoRefSCC) { + LLVMContext Context; + // Build and populate a graph composed of a single, self-referential node. + std::unique_ptr M = parseAssembly(Context, "define void @f() {\n" + "entry:\n" + " call void @f()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + CG.buildRefSCCs(); + + // At this point 'f' is in the call graph. + auto &F = lookupFunction(*M, "f"); + LazyCallGraph::Node *FN = CG.lookup(F); + EXPECT_NE(FN, nullptr); + + // And it has an SCC, of course. + auto *FSCC = CG.lookupSCC(*FN); + EXPECT_NE(FSCC, nullptr); + + // Now, create a new function 'g'. + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock::Create(F.getParent()->getContext(), "entry", G); + + // Instruct the LazyCallGraph to create a new node for 'g', within the same + // RefSCC as 'f', but in a separate SCC. + CG.addNewFunctionIntoRefSCC(*G, FSCC->getOuterRefSCC()); + + // 'g' should now be in the call graph. + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_NE(GN, nullptr); + // 'g' should have an SCC, composed of the singular node 'g'. + // ('f' should not be included in the 'g' SCC.) + LazyCallGraph::SCC *GSCC = CG.lookupSCC(*GN); + EXPECT_NE(GSCC, nullptr); + EXPECT_EQ(GSCC->size(), 1); + EXPECT_NE(GSCC, FSCC); + // 'g' and 'f' should be part of the same RefSCC. + EXPECT_EQ(&GSCC->getOuterRefSCC(), &FSCC->getOuterRefSCC()); +} }