13
13
#define DEBUG_TYPE " sil-inliner"
14
14
#include " swift/SILOptimizer/PassManager/Passes.h"
15
15
#include " swift/SILOptimizer/PassManager/Transforms.h"
16
+ #include " swift/SILOptimizer/Utils/Devirtualize.h"
17
+ #include " swift/SILOptimizer/Utils/Generics.h"
16
18
#include " swift/SILOptimizer/Utils/PerformanceInlinerUtils.h"
17
19
#include " swift/Strings.h"
18
20
#include " llvm/ADT/Statistic.h"
@@ -91,6 +93,13 @@ class SILPerformanceInliner {
91
93
// / The benefit of a onFastPath builtin.
92
94
FastPathBuiltinBenefit = RemovedCallBenefit + 40 ,
93
95
96
+ // / The benefit of being able to devirtualize a call.
97
+ DevirtualizedCallBenefit = RemovedCallBenefit + 300 ,
98
+
99
+ // / The benefit of being able to produce a generic
100
+ // / specializatio for a call.
101
+ GenericSpecializationBenefit = RemovedCallBenefit + 300 ,
102
+
94
103
// / Approximately up to this cost level a function can be inlined without
95
104
// / increasing the code size.
96
105
TrivialFunctionThreshold = 18 ,
@@ -125,8 +134,7 @@ class SILPerformanceInliner {
125
134
bool isProfitableToInline (FullApplySite AI,
126
135
Weight CallerWeight,
127
136
ConstantTracker &callerTracker,
128
- int &NumCallerBlocks,
129
- bool IsGeneric);
137
+ int &NumCallerBlocks);
130
138
131
139
bool decideInWarmBlock (FullApplySite AI,
132
140
Weight CallerWeight,
@@ -161,6 +169,53 @@ static bool calleeIsSelfRecursive(SILFunction *Callee) {
161
169
return false ;
162
170
}
163
171
172
+ // Returns true if the callee contains a partial apply instruction,
173
+ // whose substitutions list would contain opened existentials after
174
+ // inlining.
175
+ static bool calleeHasPartialApplyWithOpenedExistentials (FullApplySite AI) {
176
+ if (!AI.hasSubstitutions ())
177
+ return false ;
178
+
179
+ SILFunction *Callee = AI.getReferencedFunction ();
180
+ auto Subs = AI.getSubstitutions ();
181
+
182
+ // Bail if there are no open existnetials in the list of substitutions.
183
+ bool HasNoOpenedExistentials = true ;
184
+ for (auto Sub : Subs) {
185
+ if (Sub.getReplacement ()->hasOpenedExistential ()) {
186
+ HasNoOpenedExistentials = false ;
187
+ break ;
188
+ }
189
+ }
190
+
191
+ if (HasNoOpenedExistentials)
192
+ return false ;
193
+
194
+ auto SubsMap = Callee->getGenericEnvironment ()->getSubstitutionMap (Subs);
195
+
196
+ for (auto &BB : *Callee) {
197
+ for (auto &I : BB) {
198
+ if (auto PAI = dyn_cast<PartialApplyInst>(&I)) {
199
+ auto PAISubs = PAI->getSubstitutions ();
200
+ if (PAISubs.empty ())
201
+ continue ;
202
+ // Check if any of substitutions would contain open existentials
203
+ // after inlining.
204
+ for (auto PAISub : PAISubs) {
205
+ if (!PAISub.getReplacement ()->hasArchetype ())
206
+ continue ;
207
+ auto NewPAISub =
208
+ PAISub.subst (AI.getModule ().getSwiftModule (), SubsMap);
209
+ if (NewPAISub.getReplacement ()->hasOpenedExistential ())
210
+ return true ;
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ return false ;
217
+ }
218
+
164
219
// Returns the callee of an apply_inst if it is basically inlineable.
165
220
SILFunction *SILPerformanceInliner::getEligibleFunction (FullApplySite AI) {
166
221
@@ -202,10 +257,6 @@ SILFunction *SILPerformanceInliner::getEligibleFunction(FullApplySite AI) {
202
257
return nullptr ;
203
258
}
204
259
205
- // We don't support this yet.
206
- if (AI.hasSubstitutions ())
207
- return nullptr ;
208
-
209
260
SILFunction *Caller = AI.getFunction ();
210
261
211
262
// We don't support inlining a function that binds dynamic self because we
@@ -247,15 +298,36 @@ SILFunction *SILPerformanceInliner::getEligibleFunction(FullApplySite AI) {
247
298
return nullptr ;
248
299
}
249
300
301
+ // IRGen cannot handle partial_applies containing opened_extistentials
302
+ // in its substitutions list.
303
+ if (calleeHasPartialApplyWithOpenedExistentials (AI)) {
304
+ return nullptr ;
305
+ }
306
+
250
307
return Callee;
251
308
}
252
309
310
+ // Returns true if it is possible to perform a generic
311
+ // specialization for a given call.
312
+ static bool canSpecializeGeneric (ApplySite AI, SILFunction *F,
313
+ SubstitutionList Subs) {
314
+ ReabstractionInfo ReInfo (AI, F, Subs);
315
+ return ReInfo.canBeSpecialized ();
316
+ }
317
+
253
318
bool SILPerformanceInliner::isProfitableToInline (FullApplySite AI,
254
319
Weight CallerWeight,
255
320
ConstantTracker &callerTracker,
256
- int &NumCallerBlocks,
257
- bool IsGeneric) {
321
+ int &NumCallerBlocks) {
258
322
SILFunction *Callee = AI.getReferencedFunction ();
323
+ bool IsGeneric = !AI.getSubstitutions ().empty ();
324
+
325
+ // Bail out if this generic call can be optimized by means of
326
+ // the generic specialization, because we prefer generic specialization
327
+ // to inlining of generics.
328
+ if (IsGeneric && canSpecializeGeneric (AI, Callee, AI.getSubstitutions ()))
329
+ return false ;
330
+
259
331
SILLoopInfo *LI = LA->get (Callee);
260
332
ShortestPathAnalysis *SPA = getSPA (Callee, LI);
261
333
assert (SPA->isValid ());
@@ -268,11 +340,13 @@ bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
268
340
// Calculate the inlining cost of the callee.
269
341
int CalleeCost = 0 ;
270
342
int Benefit = 0 ;
271
-
343
+
272
344
// Start with a base benefit.
273
345
int BaseBenefit = RemovedCallBenefit;
346
+
347
+ SubstitutionMap CalleeSubstMap;
274
348
const SILOptions &Opts = Callee->getModule ().getOptions ();
275
-
349
+
276
350
// For some reason -Ounchecked can accept a higher base benefit without
277
351
// increasing the code size too much.
278
352
if (Opts.Optimization == SILOptions::SILOptMode::OptimizeUnchecked)
@@ -288,16 +362,98 @@ bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
288
362
289
363
for (SILInstruction &I : *block) {
290
364
constTracker.trackInst (&I);
291
-
365
+
292
366
CalleeCost += (int )instructionInlineCost (I);
293
367
294
- if (FullApplySite AI = FullApplySite::isa (&I)) {
295
-
368
+ if (FullApplySite FAI = FullApplySite::isa (&I)) {
296
369
// Check if the callee is passed as an argument. If so, increase the
297
370
// threshold, because inlining will (probably) eliminate the closure.
298
- SILInstruction *def = constTracker.getDefInCaller (AI .getCallee ());
371
+ SILInstruction *def = constTracker.getDefInCaller (FAI .getCallee ());
299
372
if (def && (isa<FunctionRefInst>(def) || isa<PartialApplyInst>(def)))
300
373
BlockW.updateBenefit (Benefit, RemovedClosureBenefit);
374
+ // Check if inlining the callee would allow for further
375
+ // optimizations like devirtualization or generic specialization.
376
+ if (!def)
377
+ def = dyn_cast_or_null<SILInstruction>(FAI.getCallee ());
378
+
379
+ if (!def)
380
+ continue ;
381
+
382
+ auto Subs = FAI.getSubstitutions ();
383
+
384
+ // Bail if it is not a generic call.
385
+ if (Subs.empty ())
386
+ continue ;
387
+
388
+ if (!isa<FunctionRefInst>(def) && !isa<ClassMethodInst>(def) &&
389
+ !isa<WitnessMethodInst>(def))
390
+ continue ;
391
+
392
+ SmallVector<Substitution, 32 > NewSubs;
393
+ SubstitutionMap SubstMap;
394
+ GenericSignature *GenSig = nullptr ;
395
+
396
+ if (auto FRI = dyn_cast<FunctionRefInst>(def)) {
397
+ auto Callee = FRI->getReferencedFunction ();
398
+ if (Callee) {
399
+ GenSig = Callee->getLoweredFunctionType ()->getGenericSignature ();
400
+ }
401
+ } else if (auto CMI = dyn_cast<ClassMethodInst>(def)) {
402
+ GenSig = CMI->getType ()
403
+ .getSwiftRValueType ()
404
+ ->castTo <SILFunctionType>()
405
+ ->getGenericSignature ();
406
+ } else if (auto WMI = dyn_cast<WitnessMethodInst>(def)) {
407
+ GenSig = WMI->getType ()
408
+ .getSwiftRValueType ()
409
+ ->castTo <SILFunctionType>()
410
+ ->getGenericSignature ();
411
+ }
412
+
413
+ // It is a generic call inside the callee. Check if after inlining
414
+ // it will be possible to perform a generic specialization or
415
+ // devirtualization of this call.
416
+
417
+ // Create the list of substitutions as they will be after
418
+ // inlining.
419
+ for (auto Sub : Subs) {
420
+ if (!Sub.getReplacement ()->hasArchetype ()) {
421
+ // This substitution is a concrete type.
422
+ NewSubs.push_back (Sub);
423
+ continue ;
424
+ }
425
+ // This substitution is not a concrete type.
426
+ if (IsGeneric && CalleeSubstMap.empty ()) {
427
+ CalleeSubstMap =
428
+ Callee->getGenericEnvironment ()->getSubstitutionMap (
429
+ AI.getSubstitutions ());
430
+ }
431
+ auto NewSub = Sub.subst (AI.getModule ().getSwiftModule (), CalleeSubstMap);
432
+ NewSubs.push_back (NewSub);
433
+ }
434
+
435
+ // Check if the call can be devirtualized.
436
+ if (isa<ClassMethodInst>(def) || isa<WitnessMethodInst>(def) ||
437
+ isa<SuperMethodInst>(def)) {
438
+ // TODO: Take AI.getSubstitutions() into account.
439
+ if (canDevirtualizeApply (FAI, nullptr )) {
440
+ DEBUG (llvm::dbgs () << " Devirtualization will be possible after "
441
+ " inlining for the call:\n " ;
442
+ FAI.getInstruction ()->dumpInContext ());
443
+ BlockW.updateBenefit (Benefit, DevirtualizedCallBenefit);
444
+ }
445
+ }
446
+
447
+ // Check if a generic specialization would be possible.
448
+ if (isa<FunctionRefInst>(def)) {
449
+ auto CalleeF = FAI.getCalleeFunction ();
450
+ if (!canSpecializeGeneric (FAI, CalleeF, NewSubs))
451
+ continue ;
452
+ DEBUG (llvm::dbgs () << " Generic specialization will be possible after "
453
+ " inlining for the call:\n " ;
454
+ FAI.getInstruction ()->dumpInContext ());
455
+ BlockW.updateBenefit (Benefit, GenericSpecializationBenefit);
456
+ }
301
457
} else if (auto *LI = dyn_cast<LoadInst>(&I)) {
302
458
// Check if it's a load from a stack location in the caller. Such a load
303
459
// might be optimized away if inlined.
@@ -341,7 +497,6 @@ bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
341
497
return false ;
342
498
343
499
DEBUG (
344
-
345
500
dumpCaller (AI.getFunction ());
346
501
llvm::dbgs () << " decision {" << CalleeCost << " into thunk} " <<
347
502
Callee->getName () << ' \n ' ;
@@ -379,14 +534,11 @@ bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
379
534
// / It returns true if a function should be inlined.
380
535
// / It returns false if a function should not be inlined.
381
536
// / It returns None if the decision cannot be made without a more complex
382
- // / analysis.
537
+ // / analysis.
383
538
static Optional<bool > shouldInlineGeneric (FullApplySite AI) {
384
539
assert (!AI.getSubstitutions ().empty () &&
385
540
" Expected a generic apply" );
386
541
387
- if (!EnableSILInliningOfGenerics)
388
- return false ;
389
-
390
542
// If all substitutions are concrete, then there is no need to perform the
391
543
// generic inlining. Let the generic specializer create a specialized
392
544
// function and then decide if it is beneficial to inline it.
@@ -408,8 +560,10 @@ static Optional<bool> shouldInlineGeneric(FullApplySite AI) {
408
560
if (Callee->getInlineStrategy () == AlwaysInline || Callee->isTransparent ())
409
561
return true ;
410
562
411
- // Only inline if we decided to inline or we are asked to inline all
412
- // generic functions.
563
+ if (!EnableSILInliningOfGenerics)
564
+ return false ;
565
+
566
+ // It is not clear yet if this function should be decided or not.
413
567
return None;
414
568
}
415
569
@@ -423,17 +577,14 @@ decideInWarmBlock(FullApplySite AI,
423
577
auto ShouldInlineGeneric = shouldInlineGeneric (AI);
424
578
if (ShouldInlineGeneric.hasValue ())
425
579
return ShouldInlineGeneric.getValue ();
426
-
427
- return false ;
428
580
}
429
581
430
582
SILFunction *Callee = AI.getReferencedFunction ();
431
583
432
584
if (Callee->getInlineStrategy () == AlwaysInline)
433
585
return true ;
434
586
435
- return isProfitableToInline (AI, CallerWeight, callerTracker, NumCallerBlocks,
436
- /* IsGeneric */ false );
587
+ return isProfitableToInline (AI, CallerWeight, callerTracker, NumCallerBlocks);
437
588
}
438
589
439
590
// / Return true if inlining this call site into a cold block is profitable.
0 commit comments