diff --git a/llvm/lib/Analysis/LazyCallGraph.cpp b/llvm/lib/Analysis/LazyCallGraph.cpp --- a/llvm/lib/Analysis/LazyCallGraph.cpp +++ b/llvm/lib/Analysis/LazyCallGraph.cpp @@ -721,8 +721,14 @@ // Set the edge kind. SourceN->setEdgeKind(TargetN, Edge::Ref); - // Otherwise we are removing a call edge from a single SCC. This may break - // the cycle. In order to compute the new set of SCCs, we need to do a small + // Otherwise, we are removing a call edge from a single SCC. This may break + // the cycle. However, one trivial case remains that we check for now: a + // node is always strongly connected to itself, so demoting an edge from a + // node to itself cannot break a cycle, and does not introduce any new SCCs. + if (&SourceN == &TargetN) + return range(iterator(), iterator()); + + // In order to compute the new set of SCCs, we need to do a small // DFS over the nodes within the SCC to form any sub-cycles that remain as // distinct SCCs and compute a postorder over the resulting SCCs. // 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 @@ -1560,5 +1560,55 @@ MPM.run(*M, MAM); } +TEST_F(CGSCCPassManagerTest, TestDemotionOfSelfReferentialCallEdge) { + std::unique_ptr M = parseIR("define void @f() {\n" + "entry:\n" + " call void @f()\n" + " ret void\n" + "}\n"); + + CGSCCPassManager CGPM(/*DebugLogging*/ true); + CGPM.addPass(LambdaSCCPassNoPreserve( + [&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG, + CGSCCUpdateResult &UR) { + CallGraphUpdater CGUpdater(CG, AM, UR); + for (auto &N : C) { + auto &F = N.getFunction(); + if (F.getName() != "f") + continue; + auto *Call = dyn_cast(F.begin()->begin()); + if (!Call || Call->getCalledFunction()->getName() != "f") + continue; + + // "Demote" the 'f -> f' call egde to a ref edge. + // 1. Erase the call edge from 'f' to 'f'. + Call->eraseFromParent(); + // 2. Insert a ref edge from 'f' to 'f'. + (void)CastInst::CreatePointerCast(&F, + Type::getInt8PtrTy(F.getContext()), + "f.ref", &*F.begin()->begin()); + + // Create a new function 'g' and use the CallGraphUpdater to insert + // a node for 'g' into the call graph. This also connects creates a + // ref edge 'f -> g'. + auto *G = + llvm::Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + llvm::BasicBlock::Create(F.getParent()->getContext(), "entry", G); + CGUpdater.registerOutlinedFunction(*G, &C); + + ASSERT_NO_FATAL_FAILURE( + updateCGAndAnalysisManagerForCGSCCPass(CG, C, N, AM, UR)) + << "Updating the call graph with a demoted, self-referential " + "call edge 'f -> f', and a newly inserted ref edge 'f -> g', " + "caused a fatal failure"; + } + })); + + ModulePassManager MPM(/*DebugLogging*/ true); + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); + MPM.run(*M, MAM); +} + #endif } // namespace