Skip to content

Commit a296ced

Browse files
authored
Generic inline (#5467)
* [sil-performance-inliner] Re-factor the isProfitableToInline logic. NFC. * [sil-performance-inliner] Introduce a pre-filter to decide of a generic function should be inlined This logic is unconditional and is does not require any complex cost models. The outcome of the check is one of: - yes, inline this generic function - no, do not inline this generic function - None, don't know if it should be inline. Further more complex checks are required. * [sil-performance-inliner] Handle inlining of generic functions into cold blocks Generic functions should be inlined into cold blocks only if they should be unconditionally inlined (e.g. when they are always_inline or transparent). * Allow generic inlining under -sil-inline-generics.
1 parent d64fed7 commit a296ced

File tree

1 file changed

+95
-22
lines changed

1 file changed

+95
-22
lines changed

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/SILOptimizer/PassManager/Passes.h"
1515
#include "swift/SILOptimizer/PassManager/Transforms.h"
1616
#include "swift/SILOptimizer/Utils/PerformanceInlinerUtils.h"
17+
#include "swift/Strings.h"
1718
#include "llvm/ADT/Statistic.h"
1819
#include "llvm/Support/Debug.h"
1920
#include "llvm/Support/CommandLine.h"
@@ -26,6 +27,10 @@ llvm::cl::opt<bool> PrintShortestPathInfo(
2627
"print-shortest-path-info", llvm::cl::init(false),
2728
llvm::cl::desc("Print shortest-path information for inlining"));
2829

30+
llvm::cl::opt<bool> EnableSILInliningOfGenerics(
31+
"sil-inline-generics", llvm::cl::init(false),
32+
llvm::cl::desc("Enable inlining of generics"));
33+
2934
//===----------------------------------------------------------------------===//
3035
// Performance Inliner
3136
//===----------------------------------------------------------------------===//
@@ -117,12 +122,18 @@ class SILPerformanceInliner {
117122

118123
SILFunction *getEligibleFunction(FullApplySite AI);
119124

120-
bool isProfitableToInlineNonGeneric(FullApplySite AI,
125+
bool isProfitableToInline(FullApplySite AI,
121126
Weight CallerWeight,
122-
ConstantTracker &constTracker,
123-
int &NumCallerBlocks);
127+
ConstantTracker &callerTracker,
128+
int &NumCallerBlocks,
129+
bool IsGeneric);
130+
131+
bool decideInWarmBlock(FullApplySite AI,
132+
Weight CallerWeight,
133+
ConstantTracker &callerTracker,
134+
int &NumCallerBlocks);
124135

125-
bool isProfitableInColdBlock(FullApplySite AI, SILFunction *Callee);
136+
bool decideInColdBlock(FullApplySite AI, SILFunction *Callee);
126137

127138
void visitColdBlocks(SmallVectorImpl<FullApplySite> &AppliesToInline,
128139
SILBasicBlock *root, DominanceInfo *DT);
@@ -239,19 +250,12 @@ SILFunction *SILPerformanceInliner::getEligibleFunction(FullApplySite AI) {
239250
return Callee;
240251
}
241252

242-
/// Return true if inlining this call site is profitable.
243-
bool SILPerformanceInliner::isProfitableToInlineNonGeneric(FullApplySite AI,
244-
Weight CallerWeight,
245-
ConstantTracker &callerTracker,
246-
int &NumCallerBlocks) {
247-
assert(AI.getSubstitutions().empty() &&
248-
"Expected a non-generic apply");
249-
253+
bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
254+
Weight CallerWeight,
255+
ConstantTracker &callerTracker,
256+
int &NumCallerBlocks,
257+
bool IsGeneric) {
250258
SILFunction *Callee = AI.getReferencedFunction();
251-
252-
if (Callee->getInlineStrategy() == AlwaysInline)
253-
return true;
254-
255259
SILLoopInfo *LI = LA->get(Callee);
256260
ShortestPathAnalysis *SPA = getSPA(Callee, LI);
257261
assert(SPA->isValid());
@@ -369,9 +373,80 @@ bool SILPerformanceInliner::isProfitableToInlineNonGeneric(FullApplySite AI,
369373
return true;
370374
}
371375

376+
/// Checks if a given generic apply should be inlined unconditionally, i.e.
377+
/// without any complex analysis using e.g. a cost model.
378+
/// It returns true if a function should be inlined.
379+
/// It returns false if a function should not be inlined.
380+
/// It returns None if the decision cannot be made without a more complex
381+
/// analysis.
382+
static Optional<bool> shouldInlineGeneric(FullApplySite AI) {
383+
assert(!AI.getSubstitutions().empty() &&
384+
"Expected a generic apply");
385+
386+
if (!EnableSILInliningOfGenerics)
387+
return false;
388+
389+
// If all substitutions are concrete, then there is no need to perform the
390+
// generic inlining. Let the generic specializer create a specialized
391+
// function and then decide if it is beneficial to inline it.
392+
if (!hasUnboundGenericTypes(AI.getSubstitutions()))
393+
return false;
394+
395+
SILFunction *Callee = AI.getReferencedFunction();
396+
397+
// Do not inline @_semantics functions when compiling the stdlib,
398+
// because they need to be preserved, so that the optimizer
399+
// can properly optimize a user code later.
400+
auto ModuleName = Callee->getModule().getSwiftModule()->getName().str();
401+
if (Callee->hasSemanticsAttrThatStartsWith("array.") &&
402+
(ModuleName == STDLIB_NAME || ModuleName == SWIFT_ONONE_SUPPORT))
403+
return false;
404+
405+
// Always inline generic functions which are marked as
406+
// AlwaysInline or transparent.
407+
if (Callee->getInlineStrategy() == AlwaysInline || Callee->isTransparent())
408+
return true;
409+
410+
// Only inline if we decided to inline or we are asked to inline all
411+
// generic functions.
412+
return None;
413+
}
414+
415+
bool SILPerformanceInliner::
416+
decideInWarmBlock(FullApplySite AI,
417+
Weight CallerWeight,
418+
ConstantTracker &callerTracker,
419+
int &NumCallerBlocks) {
420+
if (!AI.getSubstitutions().empty()) {
421+
// Only inline generics if definitively clear that it should be done.
422+
auto ShouldInlineGeneric = shouldInlineGeneric(AI);
423+
if (ShouldInlineGeneric.hasValue())
424+
return ShouldInlineGeneric.getValue();
425+
426+
return false;
427+
}
428+
429+
SILFunction *Callee = AI.getReferencedFunction();
430+
431+
if (Callee->getInlineStrategy() == AlwaysInline)
432+
return true;
433+
434+
return isProfitableToInline(AI, CallerWeight, callerTracker, NumCallerBlocks,
435+
/* IsGeneric */ false);
436+
}
437+
372438
/// Return true if inlining this call site into a cold block is profitable.
373-
bool SILPerformanceInliner::isProfitableInColdBlock(FullApplySite AI,
374-
SILFunction *Callee) {
439+
bool SILPerformanceInliner::decideInColdBlock(FullApplySite AI,
440+
SILFunction *Callee) {
441+
if (!AI.getSubstitutions().empty()) {
442+
// Only inline generics if definitively clear that it should be done.
443+
auto ShouldInlineGeneric = shouldInlineGeneric(AI);
444+
if (ShouldInlineGeneric.hasValue())
445+
return ShouldInlineGeneric.getValue();
446+
447+
return false;
448+
}
449+
375450
if (Callee->getInlineStrategy() == AlwaysInline)
376451
return true;
377452

@@ -482,9 +557,7 @@ void SILPerformanceInliner::collectAppliesToInline(
482557
// The actual weight including a possible weight correction.
483558
Weight W(BlockWeight, WeightCorrections.lookup(AI));
484559

485-
bool IsProfitableToInline = isProfitableToInlineNonGeneric(
486-
AI, W, constTracker, NumCallerBlocks);
487-
if (IsProfitableToInline)
560+
if (decideInWarmBlock(AI, W, constTracker, NumCallerBlocks))
488561
InitialCandidates.push_back(AI);
489562
}
490563
}
@@ -591,7 +664,7 @@ void SILPerformanceInliner::visitColdBlocks(
591664
continue;
592665

593666
auto *Callee = getEligibleFunction(AI);
594-
if (Callee && isProfitableInColdBlock(AI, Callee)) {
667+
if (Callee && decideInColdBlock(AI, Callee)) {
595668
AppliesToInline.push_back(AI);
596669
}
597670
}

0 commit comments

Comments
 (0)