Skip to content

[rust] Mark inlined calls to functions in same SCC as callee as noinline #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions llvm/lib/Transforms/IPO/Inliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,8 +876,8 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
// trigger infinite inlining, much like is prevented within the inliner
// itself by the InlineHistory above, but spread across CGSCC iterations
// and thus hidden from the full inline history.
if (CG.lookupSCC(*CG.lookup(Callee)) == C &&
UR.InlinedInternalEdges.count({&N, C})) {
LazyCallGraph::SCC *CalleeSCC = CG.lookupSCC(*CG.lookup(Callee));
if (CalleeSCC == C && UR.InlinedInternalEdges.count({&N, C})) {
LLVM_DEBUG(dbgs() << "Skipping inlining internal SCC edge from a node "
"previously split out of this SCC by inlining: "
<< F.getName() << " -> " << Callee.getName() << "\n");
Expand Down Expand Up @@ -935,9 +935,24 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
if (tryPromoteCall(*ICB))
NewCallee = ICB->getCalledFunction();
}
if (NewCallee)
if (!NewCallee->isDeclaration())
if (NewCallee) {
if (!NewCallee->isDeclaration()) {
Calls->push({ICB, NewHistoryID});
// Continually inlining through an SCC can result in huge compile
// times and bloated code since we arbitrarily stop at some point
// when the inliner decides it's not profitable to inline
// anymore. We put a stop at the first potential attempt at
// inlining through an SCC by marking the call site as noinline.
// This doesn't apply to calls in the same SCC since if we do
// inline through the SCC the function will end up being
// self-recursive which the inliner bails out on, and inlining
// within an SCC is necessary for performance.
if (CalleeSCC != C &&
CalleeSCC == CG.lookupSCC(CG.get(*NewCallee))) {
ICB->addFnAttr(Attribute::NoInline);
}
}
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions llvm/test/Transforms/Inline/mut-rec-scc-2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
; RUN: opt -S -passes='inline' < %s | FileCheck %s

; Make sure we don't mark calls within the same SCC as original function with noinline.
; CHECK-NOT: noinline

define void @samescc1() {
call void @samescc2()
ret void
}

define void @samescc2() {
call void @samescc3()
ret void
}

define void @samescc3() {
call void @samescc1()
ret void
}
68 changes: 68 additions & 0 deletions llvm/test/Transforms/Inline/mut-rec-scc.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
; RUN: opt -S -passes='cgscc(inline,instcombine)' < %s | FileCheck %s

; We use call to a dummy function to avoid inlining test1 into test2 or vice
; versa, such that we aren't left with a trivial cycle, as trivial cycles are
; special-cased to never be inlined.
; However, InstCombine will eliminate these calls after inlining, and thus
; make the functions eligible for inlining in their callers.
declare void @dummy() readnone nounwind willreturn

define void @test1() {
; CHECK-LABEL: define void @test1(
; CHECK-NEXT: call void @test2()
; CHECK-NEXT: call void @test2()
; CHECK-NEXT: ret void
;
call void @test2()
call void @test2()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
ret void
}

define void @test2() {
; CHECK-LABEL: define void @test2(
; CHECK-NEXT: call void @test1()
; CHECK-NEXT: call void @test1()
; CHECK-NEXT: ret void
;
call void @test1()
call void @test1()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
call void @dummy()
ret void
}

; We should inline the @test2 calls and mark the inlined @test1 calls as noinline
define void @test3() {
; CHECK-LABEL: define void @test3(
; CHECK-NEXT: call void @test1() #[[NOINLINE:[0-9]+]]
; CHECK-NEXT: call void @test1() #[[NOINLINE]]
; CHECK-NEXT: call void @test1() #[[NOINLINE]]
; CHECK-NEXT: call void @test1() #[[NOINLINE]]
; CHECK-NEXT: ret void
;
call void @test2()
call void @test2()
ret void
}

; CHECK: [[NOINLINE]] = { noinline }