Skip to content

Commit 8b51e5e

Browse files
aeubankststellar
authored andcommitted
[NewPM][Inliner] Make inlined calls to functions in same SCC as callee exponentially expensive
Introduce a new attribute "function-inline-cost-multiplier" which multiplies the inline cost of a call site (or all calls to a callee) by the multiplier. When processing the list of calls created by inlining, check each call to see if the new call's callee is in the same SCC as the original callee. If so, set the "function-inline-cost-multiplier" attribute of the new call site to double the original call site's attribute value. This does not happen when the original call site is intra-SCC. This is an alternative to D120584, which marks the call sites as noinline. Hopefully fixes PR45253. Reviewed By: davidxl Differential Revision: https://reviews.llvm.org/D121084 (cherry picked from commit 53e5e58)
1 parent 6a71312 commit 8b51e5e

File tree

6 files changed

+157
-8
lines changed

6 files changed

+157
-8
lines changed

llvm/include/llvm/Analysis/InlineCost.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const unsigned TotalAllocaSizeRecursiveCaller = 1024;
5252
/// Do not inline dynamic allocas that have been constant propagated to be
5353
/// static allocas above this amount in bytes.
5454
const uint64_t MaxSimplifiedDynamicAllocaToInline = 65536;
55+
56+
const char FunctionInlineCostMultiplierAttributeName[] =
57+
"function-inline-cost-multiplier";
5558
} // namespace InlineConstants
5659

5760
// The cost-benefit pair computed by cost-benefit analysis.
@@ -217,6 +220,8 @@ struct InlineParams {
217220
Optional<bool> AllowRecursiveCall = false;
218221
};
219222

223+
Optional<int> getStringFnAttrAsInt(CallBase &CB, StringRef AttrKind);
224+
220225
/// Generate the parameters to tune the inline cost analysis based only on the
221226
/// commandline options.
222227
InlineParams getInlineParams();

llvm/lib/Analysis/InlineCost.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,6 @@ static cl::opt<bool> DisableGEPConstOperand(
133133
cl::desc("Disables evaluation of GetElementPtr with constant operands"));
134134

135135
namespace {
136-
class InlineCostCallAnalyzer;
137-
138136
/// This function behaves more like CallBase::hasFnAttr: when it looks for the
139137
/// requested attribute, it check both the call instruction and the called
140138
/// function (if it's available and operand bundles don't prohibit that).
@@ -151,14 +149,20 @@ Attribute getFnAttr(CallBase &CB, StringRef AttrKind) {
151149

152150
return {};
153151
}
152+
} // namespace
154153

154+
namespace llvm {
155155
Optional<int> getStringFnAttrAsInt(CallBase &CB, StringRef AttrKind) {
156156
Attribute Attr = getFnAttr(CB, AttrKind);
157157
int AttrValue;
158158
if (Attr.getValueAsString().getAsInteger(10, AttrValue))
159159
return None;
160160
return AttrValue;
161161
}
162+
} // namespace llvm
163+
164+
namespace {
165+
class InlineCostCallAnalyzer;
162166

163167
// This struct is used to store information about inline cost of a
164168
// particular instruction
@@ -904,6 +908,11 @@ class InlineCostCallAnalyzer final : public CallAnalyzer {
904908
getStringFnAttrAsInt(CandidateCall, "function-inline-cost"))
905909
Cost = *AttrCost;
906910

911+
if (Optional<int> AttrCostMult = getStringFnAttrAsInt(
912+
CandidateCall,
913+
InlineConstants::FunctionInlineCostMultiplierAttributeName))
914+
Cost *= *AttrCostMult;
915+
907916
if (Optional<int> AttrThreshold =
908917
getStringFnAttrAsInt(CandidateCall, "function-inline-threshold"))
909918
Threshold = *AttrThreshold;

llvm/lib/Transforms/IPO/Inliner.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ADT/SmallPtrSet.h"
2323
#include "llvm/ADT/SmallVector.h"
2424
#include "llvm/ADT/Statistic.h"
25+
#include "llvm/ADT/StringExtras.h"
2526
#include "llvm/ADT/StringRef.h"
2627
#include "llvm/Analysis/AssumptionCache.h"
2728
#include "llvm/Analysis/BasicAliasAnalysis.h"
@@ -92,6 +93,18 @@ static cl::opt<bool>
9293
DisableInlinedAllocaMerging("disable-inlined-alloca-merging",
9394
cl::init(false), cl::Hidden);
9495

96+
static cl::opt<int> IntraSCCCostMultiplier(
97+
"intra-scc-cost-multiplier", cl::init(2), cl::Hidden,
98+
cl::desc(
99+
"Cost multiplier to multiply onto inlined call sites where the "
100+
"new call was previously an intra-SCC call (not relevant when the "
101+
"original call was already intra-SCC). This can accumulate over "
102+
"multiple inlinings (e.g. if a call site already had a cost "
103+
"multiplier and one of its inlined calls was also subject to "
104+
"this, the inlined call would have the original multiplier "
105+
"multiplied by intra-scc-cost-multiplier). This is to prevent tons of "
106+
"inlining through a child SCC which can cause terrible compile times"));
107+
95108
/// A flag for test, so we can print the content of the advisor when running it
96109
/// as part of the default (e.g. -O3) pipeline.
97110
static cl::opt<bool> KeepAdvisorForPrinting("keep-inline-advisor-for-printing",
@@ -876,8 +889,8 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
876889
// trigger infinite inlining, much like is prevented within the inliner
877890
// itself by the InlineHistory above, but spread across CGSCC iterations
878891
// and thus hidden from the full inline history.
879-
if (CG.lookupSCC(*CG.lookup(Callee)) == C &&
880-
UR.InlinedInternalEdges.count({&N, C})) {
892+
LazyCallGraph::SCC *CalleeSCC = CG.lookupSCC(*CG.lookup(Callee));
893+
if (CalleeSCC == C && UR.InlinedInternalEdges.count({&N, C})) {
881894
LLVM_DEBUG(dbgs() << "Skipping inlining internal SCC edge from a node "
882895
"previously split out of this SCC by inlining: "
883896
<< F.getName() << " -> " << Callee.getName() << "\n");
@@ -897,6 +910,11 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
897910
continue;
898911
}
899912

913+
int CBCostMult =
914+
getStringFnAttrAsInt(
915+
*CB, InlineConstants::FunctionInlineCostMultiplierAttributeName)
916+
.getValueOr(1);
917+
900918
// Setup the data structure used to plumb customization into the
901919
// `InlineFunction` routine.
902920
InlineFunctionInfo IFI(
@@ -935,9 +953,28 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
935953
if (tryPromoteCall(*ICB))
936954
NewCallee = ICB->getCalledFunction();
937955
}
938-
if (NewCallee)
939-
if (!NewCallee->isDeclaration())
956+
if (NewCallee) {
957+
if (!NewCallee->isDeclaration()) {
940958
Calls->push({ICB, NewHistoryID});
959+
// Continually inlining through an SCC can result in huge compile
960+
// times and bloated code since we arbitrarily stop at some point
961+
// when the inliner decides it's not profitable to inline anymore.
962+
// We attempt to mitigate this by making these calls exponentially
963+
// more expensive.
964+
// This doesn't apply to calls in the same SCC since if we do
965+
// inline through the SCC the function will end up being
966+
// self-recursive which the inliner bails out on, and inlining
967+
// within an SCC is necessary for performance.
968+
if (CalleeSCC != C &&
969+
CalleeSCC == CG.lookupSCC(CG.get(*NewCallee))) {
970+
Attribute NewCBCostMult = Attribute::get(
971+
M.getContext(),
972+
InlineConstants::FunctionInlineCostMultiplierAttributeName,
973+
itostr(CBCostMult * IntraSCCCostMultiplier));
974+
ICB->addFnAttr(NewCBCostMult);
975+
}
976+
}
977+
}
941978
}
942979
}
943980

llvm/test/Transforms/Inline/inline-cost-attributes.ll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ entry:
1111

1212
define void @fn2() "function-inline-threshold"="41" {
1313
; INLINER-LABEL: Inlining calls in: fn2
14-
; INLINER-NEXT: Function size: 6
14+
; INLINER-NEXT: Function size: 7
1515
; INLINER-NEXT: NOT Inlining (cost=321, threshold=123), Call: call void @fn1()
16+
; INLINER-NEXT: NOT Inlining (cost=963, threshold=123), Call: call void @fn1()
1617
; INLINER-NEXT: NOT Inlining (cost=321, threshold=321), Call: call void @fn1()
1718
; INLINER-NEXT: NOT Inlining (cost=197, threshold=123), Call: call void @fn1()
1819
; INLINER-NEXT: Inlining (cost=197, threshold=321), Call: call void @fn1()
@@ -23,6 +24,8 @@ define void @fn2() "function-inline-threshold"="41" {
2324
; COST-NEXT: call void @extern()
2425
; COST-NEXT: cost delta = 132, threshold delta = 193
2526
; COST-NEXT: call void @fn1()
27+
; COST-NEXT: cost delta = 132, threshold delta = 193
28+
; COST-NEXT: call void @fn1()
2629
; COST-NEXT: cost delta = 0
2730
; COST-NEXT: call void @fn1()
2831
; COST-NEXT: cost delta = 271, threshold delta = 17
@@ -33,6 +36,7 @@ define void @fn2() "function-inline-threshold"="41" {
3336
entry:
3437
call void @extern()
3538
call void @fn1() "call-inline-cost"="132" "call-threshold-bonus"="193"
39+
call void @fn1() "call-inline-cost"="132" "call-threshold-bonus"="193" "function-inline-cost-multiplier"="3"
3640
call void @fn1() "call-inline-cost"="0" "function-inline-threshold"="321"
3741
call void @fn1() "call-threshold-bonus"="17" "function-inline-cost"="197"
3842
call void @fn1() "call-inline-cost"="473" "function-inline-cost"="197" "function-inline-threshold"="321"
@@ -44,7 +48,7 @@ define void @fn3() {
4448
; INLINER-NEXT: Function size: 3
4549
; INLINER-NEXT: Inlining (cost=386, threshold=849), Call: call void @fn1()
4650
; INLINER-NEXT: Size after inlining: 2
47-
; INLINER-NEXT: NOT Inlining (cost=403, threshold=41), Call: call void @fn2()
51+
; INLINER-NEXT: NOT Inlining (cost=535, threshold=41), Call: call void @fn2()
4852

4953
entry:
5054
call void @fn1() "function-inline-cost"="386" "function-inline-threshold"="849"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
; RUN: opt -S -passes='inline' < %s | FileCheck %s
2+
3+
; Make sure we don't mark calls within the same SCC as original function with noinline.
4+
; CHECK-NOT: function-inline-cost-multiplier
5+
6+
define void @samescc1() {
7+
call void @samescc2()
8+
ret void
9+
}
10+
11+
define void @samescc2() {
12+
call void @samescc3()
13+
ret void
14+
}
15+
16+
define void @samescc3() {
17+
call void @samescc1()
18+
ret void
19+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
; RUN: opt -S -passes='cgscc(inline,instcombine)' < %s | FileCheck %s
2+
; RUN: opt -S -intra-scc-cost-multiplier=3 -passes='cgscc(inline,instcombine)' < %s | FileCheck %s --check-prefix=THREE
3+
4+
; We use call to a dummy function to avoid inlining test1 into test2 or vice
5+
; versa, such that we aren't left with a trivial cycle, as trivial cycles are
6+
; special-cased to never be inlined.
7+
; However, InstCombine will eliminate these calls after inlining, and thus
8+
; make the functions eligible for inlining in their callers.
9+
declare void @dummy() readnone nounwind willreturn
10+
11+
define void @test1() {
12+
; CHECK-LABEL: define void @test1(
13+
; CHECK-NEXT: call void @test2()
14+
; CHECK-NEXT: call void @test2()
15+
; CHECK-NEXT: ret void
16+
;
17+
call void @test2()
18+
call void @test2()
19+
call void @dummy()
20+
call void @dummy()
21+
call void @dummy()
22+
call void @dummy()
23+
call void @dummy()
24+
call void @dummy()
25+
call void @dummy()
26+
call void @dummy()
27+
call void @dummy()
28+
call void @dummy()
29+
call void @dummy()
30+
ret void
31+
}
32+
33+
define void @test2() {
34+
; CHECK-LABEL: define void @test2(
35+
; CHECK-NEXT: call void @test1()
36+
; CHECK-NEXT: call void @test1()
37+
; CHECK-NEXT: ret void
38+
;
39+
call void @test1()
40+
call void @test1()
41+
call void @dummy()
42+
call void @dummy()
43+
call void @dummy()
44+
call void @dummy()
45+
call void @dummy()
46+
call void @dummy()
47+
call void @dummy()
48+
call void @dummy()
49+
call void @dummy()
50+
call void @dummy()
51+
call void @dummy()
52+
ret void
53+
}
54+
55+
; The inlined call sites should have the "function-inline-cost-multiplier" call site attribute.
56+
; This test is a bit fragile in the exact number of inlining that happens based on thresholds.
57+
define void @test3() {
58+
; CHECK-LABEL: define void @test3(
59+
; CHECK-NEXT: call void @test2() #[[COSTMULT:[0-9]+]]
60+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
61+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
62+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
63+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
64+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
65+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
66+
; CHECK-NEXT: call void @test2() #[[COSTMULT]]
67+
; CHECK-NEXT: ret void
68+
;
69+
call void @test2()
70+
call void @test2()
71+
ret void
72+
}
73+
74+
; CHECK: [[COSTMULT]] = { "function-inline-cost-multiplier"="4" }
75+
; THREE: "function-inline-cost-multiplier"="9"

0 commit comments

Comments
 (0)