diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp --- a/llvm/lib/Analysis/CGSCCPassManager.cpp +++ b/llvm/lib/Analysis/CGSCCPassManager.cpp @@ -922,9 +922,8 @@ if (auto *CB = dyn_cast(&I)) { if (Function *Callee = CB->getCalledFunction()) { if (Visited.insert(Callee).second && !Callee->isDeclaration()) { - Node *CalleeN = G.lookup(*Callee); - if (!CalleeN) { - CalleeN = &G.get(*Callee); + Node *CalleeN = &G.get(*Callee); + if (!CalleeN->isPopulated()) { NewNodes.insert(CalleeN); } Edge *E = N->lookup(*CalleeN); @@ -960,9 +959,8 @@ Worklist.push_back(OpC); auto VisitRef = [&](Function &Referee) { - Node *RefereeN = G.lookup(Referee); - if (!RefereeN) { - RefereeN = &G.get(Referee); + Node *RefereeN = &G.get(Referee); + if (!RefereeN->isPopulated()) { NewNodes.insert(RefereeN); } Edge *E = N->lookup(*RefereeN); @@ -980,9 +978,39 @@ }; LazyCallGraph::visitReferences(Worklist, Visited, VisitRef); - for (Node *NewNode : NewNodes) + // Find potential new nodes referenced by previous new nodes that wouldn't be + // found by only scanning the original function. + while (!NewNodes.empty()) { + Node *NewNode = NewNodes.pop_back_val(); + if (NewNode->isPopulated()) + continue; + // We treat newly added functions as initially being in the same SCC as the + // current function. Newly added functions shouldn't reference functions + // that the original function did not. + // FIXME: We currently skip running the CGSCC pipeline on newly added + // functions. Currently coro-split works around this by manually adding the + // current SCC into UR.CWorklist. G.initNode(*NewNode, *C); + SmallVector NewNodeWorklist; + SmallPtrSet NewNodeVisited; + + for (Instruction &I : instructions(NewNode->getFunction())) + for (Value *Op : I.operand_values()) + if (auto *C = dyn_cast(Op)) + if (NewNodeVisited.insert(C).second) + NewNodeWorklist.push_back(C); + + auto CheckNewNode = [&](Function &Referee) { + auto &N = G.get(Referee); + if (!N.isPopulated()) + NewNodes.insert(&N); + }; + + LazyCallGraph::visitReferences(NewNodeWorklist, NewNodeVisited, + CheckNewNode); + } + // Handle new ref edges. for (Node *RefTarget : NewRefEdges) { SCC &TargetC = *G.lookupSCC(*RefTarget); diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll --- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -coro-split -coro-cleanup -S | FileCheck %s +; RUN: opt < %s -passes=coro-split,coro-cleanup -S | FileCheck %s define i8* @f(i8* %buffer, i32 %n) "coroutine.presplit"="1" { entry: diff --git a/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp b/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp --- a/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp +++ b/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp @@ -1765,6 +1765,51 @@ MPM.run(*M, MAM); } +TEST_F(CGSCCPassManagerTest, TestInsertionOfNewRefSCCIndirect) { + std::unique_ptr M = parseIR("define void @f() {\n" + "entry:\n" + " ret void\n" + "}\n"); + + CGSCCPassManager CGPM(/*DebugLogging*/ true); + CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, + LazyCallGraph &CG, + CGSCCUpdateResult &UR) { + auto &FAM = + AM.getResult(C, CG).getManager(); + + for (auto &N : C) { + auto &F = N.getFunction(); + if (F.getName() != "f") + continue; + + // Create new functions g and h. + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + auto *H = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "h", F.getParent()); + // Create f -> g -> h call edges. + BasicBlock *GBB = + BasicBlock::Create(F.getParent()->getContext(), "entry", G); + BasicBlock *HBB = + BasicBlock::Create(F.getParent()->getContext(), "entry", H); + (void)CallInst::Create(G, {}, "", &F.getEntryBlock().front()); + (void)CallInst::Create(H, {}, "", GBB); + (void)ReturnInst::Create(G->getContext(), GBB); + (void)ReturnInst::Create(H->getContext(), HBB); + + ASSERT_NO_FATAL_FAILURE( + updateCGAndAnalysisManagerForCGSCCPass(CG, C, N, AM, UR, FAM)) + << "Updating the call graph with f -> g -> h caused a fatal failure"; + } + })); + + ModulePassManager MPM(/*DebugLogging*/ true); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); + MPM.run(*M, MAM); +} + TEST_F(CGSCCPassManagerTest, TestInsertionOfNewRefSCCMutuallyRecursive) { std::unique_ptr M = parseIR("define void @f() {\n" "entry:\n"