diff --git a/lib/Transforms/IPO/Inliner.cpp b/lib/Transforms/IPO/Inliner.cpp index 5ea26a86eb8..31589ac99ee 100644 --- a/lib/Transforms/IPO/Inliner.cpp +++ b/lib/Transforms/IPO/Inliner.cpp @@ -903,6 +903,12 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC, // made dead by this operation on other functions). Callee.removeDeadConstantUsers(); if (Callee.use_empty()) { + Calls.erase( + std::remove_if(Calls.begin() + i + 1, Calls.end(), + [&Callee](const std::pair &Call) { + return Call.first.getCaller() == &Callee; + }), + Calls.end()); // Clear the body and queue the function itself for deletion when we // finish inlining and call graph updates. // Note that after this point, it is an error to do anything other diff --git a/test/Transforms/Inline/internal-scc-members.ll b/test/Transforms/Inline/internal-scc-members.ll new file mode 100644 index 00000000000..258ce00744c --- /dev/null +++ b/test/Transforms/Inline/internal-scc-members.ll @@ -0,0 +1,31 @@ +; Test that the inliner can handle deleting functions within an SCC while still +; processing the calls in that SCC. +; +; RUN: opt < %s -S -inline | FileCheck %s +; RUN: opt < %s -S -passes=inline | FileCheck %s + +; CHECK-LABEL: define internal void @test1_scc0() +; CHECK-NOT: call +; CHECK: call void @test1_scc0() +; CHECK-NOT: call +; CHECK: ret +define internal void @test1_scc0() { +entry: + call void @test1_scc1() + ret void +} + +; CHECK-NOT: @test1_scc1 +define internal void @test1_scc1() { +entry: + call void @test1_scc0() + ret void +} + +; CHECK-LABEL: define void @test1() +; CHECK: call void @test1_scc0() +define void @test1() { +entry: + call void @test1_scc0() noinline + ret void +}