diff --git a/llvm/lib/Transforms/IPO/Inliner.cpp b/llvm/lib/Transforms/IPO/Inliner.cpp index 64a085c70b68..3f4731c937d1 100644 --- a/llvm/lib/Transforms/IPO/Inliner.cpp +++ b/llvm/lib/Transforms/IPO/Inliner.cpp @@ -886,16 +886,18 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC, continue; Changed = true; - // Add all the inlined callees' edges to the caller. These are by - // definition trivial edges as we already had a transitive call edge to the - // callee. + // Add all the inlined callees' edges as ref edges to the caller. These are + // by definition trivial edges as we always have *some* transitive ref edge + // chain. While in some cases these edges are direct calls inside the + // callee, they have to be modeled in the inliner as reference edges as + // there may be a reference edge anywhere along the chain from the current + // caller to the callee that causes the whole thing to appear like + // a (transitive) reference edge that will require promotion to a call edge + // below. for (Function *InlinedCallee : InlinedCallees) { LazyCallGraph::Node &CalleeN = *CG.lookup(*InlinedCallee); for (LazyCallGraph::Edge &E : CalleeN) - if (E.isCall()) - RC->insertTrivialCallEdge(N, *E.getNode()); - else - RC->insertTrivialRefEdge(N, *E.getNode()); + RC->insertTrivialRefEdge(N, *E.getNode()); } InlinedCallees.clear(); diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll index cf1ac4d1203c..77666c3bb718 100644 --- a/llvm/test/Transforms/Inline/cgscc-update.ll +++ b/llvm/test/Transforms/Inline/cgscc-update.ll @@ -143,3 +143,42 @@ exit: call void @test3_maybe_unknown(i1 false) ret void } + + +; The 'test4_' prefixed functions are designed to trigger forming a new direct +; call in the inlined body of the function similar to 'test1_'. However, after +; that we continue to inline another edge of the graph forcing us to do a more +; interesting call graph update for the new call edge. Eventually, we still +; form a new SCC and should use that can deduce precise function attrs. + +; This function should have had 'readnone' deduced for its SCC. +; CHECK: Function Attrs: noinline readnone +; CHECK-NEXT: define void @test4_f1() +define void @test4_f1() noinline { +entry: + call void @test4_h() + ret void +} + +; CHECK-NOT: @test4_f2 +define internal void @test4_f2() { +entry: + call void @test4_f1() + ret void +} + +; CHECK-NOT: @test4_g +define internal void @test4_g(void()* %p) { +entry: + call void %p() + ret void +} + +; This function should have had 'readnone' deduced for its SCC. +; CHECK: Function Attrs: noinline readnone +; CHECK-NEXT: define void @test4_h() +define void @test4_h() noinline { +entry: + call void @test4_g(void()* @test4_f2) + ret void +}