@@ -55,11 +55,16 @@ class CrossModuleOptimization {
55
55
56
56
SILModule &M;
57
57
58
+ // / True, if CMO runs by default.
59
+ // / In this case, serialization decisions are made very conservatively to
60
+ // / avoid code size increase.
61
+ bool conservative;
62
+
58
63
typedef llvm::DenseMap<SILFunction *, bool > FunctionFlags;
59
64
60
65
public:
61
- CrossModuleOptimization (SILModule &M)
62
- : M(M) { }
66
+ CrossModuleOptimization (SILModule &M, bool conservative )
67
+ : M(M), conservative(conservative) { }
63
68
64
69
void serializeFunctionsInModule ();
65
70
@@ -255,6 +260,12 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
255
260
if (!canUseFromInline (callee))
256
261
return false ;
257
262
263
+ // In conservative mode we don't want to turn non-public functions into
264
+ // public functions, because that can increase code size. E.g. if the
265
+ // function is completely inlined afterwards.
266
+ if (conservative && callee->getLinkage () != SILLinkage::Public)
267
+ return false ;
268
+
258
269
return true ;
259
270
}
260
271
if (auto *KPI = dyn_cast<KeyPathInst>(inst)) {
@@ -273,6 +284,13 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
273
284
if (auto *MI = dyn_cast<MethodInst>(inst)) {
274
285
return !MI->getMember ().isForeign ;
275
286
}
287
+ if (auto *REAI = dyn_cast<RefElementAddrInst>(inst)) {
288
+ // In conservative mode, we don't support class field accesse of non-public
289
+ // properties, because that would require to make the field decl public -
290
+ // which keeps more metadata alive.
291
+ return !conservative ||
292
+ REAI->getField ()->getEffectiveAccess () >= AccessLevel::Public;
293
+ }
276
294
return true ;
277
295
}
278
296
@@ -298,6 +316,10 @@ bool CrossModuleOptimization::canSerializeType(SILType type) {
298
316
CanType subType = rawSubType->getCanonicalType ();
299
317
if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
300
318
319
+ if (conservative && subNT->getEffectiveAccess () < AccessLevel::Public) {
320
+ return true ;
321
+ }
322
+
301
323
// Exclude types which are defined in an @_implementationOnly imported
302
324
// module. Such modules are not transitively available.
303
325
if (!M.getSwiftModule ()->canBeUsedForCrossModuleOptimization (subNT)) {
@@ -348,13 +370,15 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
348
370
if (SerializeEverything)
349
371
return true ;
350
372
351
- // The basic heursitic: serialize all generic functions, because it makes a
352
- // huge difference if generic functions can be specialized or not.
353
- if (function->getLoweredFunctionType ()->isPolymorphic ())
354
- return true ;
373
+ if (!conservative) {
374
+ // The basic heursitic: serialize all generic functions, because it makes a
375
+ // huge difference if generic functions can be specialized or not.
376
+ if (function->getLoweredFunctionType ()->isPolymorphic ())
377
+ return true ;
355
378
356
- if (function->getLinkage () == SILLinkage::Shared)
357
- return true ;
379
+ if (function->getLinkage () == SILLinkage::Shared)
380
+ return true ;
381
+ }
358
382
359
383
// Also serialize "small" non-generic functions.
360
384
int size = 0 ;
@@ -401,8 +425,27 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
401
425
if (!callee->isDefinition () || callee->isAvailableExternally ())
402
426
return ;
403
427
if (canUseFromInline (callee)) {
404
- // Make the function 'public'.
405
- makeFunctionUsableFromInline (callee);
428
+ if (conservative) {
429
+ // In conservative mode, avoid making non-public functions public,
430
+ // because that can increase code size.
431
+ if (callee->getLinkage () == SILLinkage::Private ||
432
+ callee->getLinkage () == SILLinkage::Hidden) {
433
+ if (callee->getEffectiveSymbolLinkage () == SILLinkage::Public) {
434
+ // It's a internal/private class method. There is no harm in making
435
+ // it public, because it gets public symbol linkage anyway.
436
+ makeFunctionUsableFromInline (callee);
437
+ } else {
438
+ // Treat the function like a 'shared' function, e.g. like a
439
+ // specialization. This is better for code size than to make it
440
+ // public, because in conservative mode we are only do this for very
441
+ // small functions.
442
+ callee->setLinkage (SILLinkage::Shared);
443
+ }
444
+ }
445
+ } else {
446
+ // Make the function 'public'.
447
+ makeFunctionUsableFromInline (callee);
448
+ }
406
449
}
407
450
serializeFunction (callee, canSerializeFlags);
408
451
assert (callee->isSerialized () == IsSerialized ||
@@ -531,10 +574,9 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
531
574
return ;
532
575
if (!M.isWholeModule ())
533
576
return ;
534
- if (!M.getOptions ().CrossModuleOptimization )
535
- return ;
536
577
537
- CrossModuleOptimization CMO (M);
578
+ CrossModuleOptimization CMO (M,
579
+ /* conservative*/ !M.getOptions ().CrossModuleOptimization );
538
580
CMO.serializeFunctionsInModule ();
539
581
}
540
582
};
0 commit comments