@@ -55,11 +55,16 @@ class CrossModuleOptimization {
5555
5656 SILModule &M;
5757
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+
5863 typedef llvm::DenseMap<SILFunction *, bool > FunctionFlags;
5964
6065public:
61- CrossModuleOptimization (SILModule &M)
62- : M(M) { }
66+ CrossModuleOptimization (SILModule &M, bool conservative )
67+ : M(M), conservative(conservative) { }
6368
6469 void serializeFunctionsInModule ();
6570
@@ -255,6 +260,12 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
255260 if (!canUseFromInline (callee))
256261 return false ;
257262
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+
258269 return true ;
259270 }
260271 if (auto *KPI = dyn_cast<KeyPathInst>(inst)) {
@@ -273,6 +284,13 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
273284 if (auto *MI = dyn_cast<MethodInst>(inst)) {
274285 return !MI->getMember ().isForeign ;
275286 }
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+ }
276294 return true ;
277295}
278296
@@ -298,6 +316,10 @@ bool CrossModuleOptimization::canSerializeType(SILType type) {
298316 CanType subType = rawSubType->getCanonicalType ();
299317 if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
300318
319+ if (conservative && subNT->getEffectiveAccess () < AccessLevel::Public) {
320+ return true ;
321+ }
322+
301323 // Exclude types which are defined in an @_implementationOnly imported
302324 // module. Such modules are not transitively available.
303325 if (!M.getSwiftModule ()->canBeUsedForCrossModuleOptimization (subNT)) {
@@ -348,13 +370,15 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
348370 if (SerializeEverything)
349371 return true ;
350372
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 ;
355378
356- if (function->getLinkage () == SILLinkage::Shared)
357- return true ;
379+ if (function->getLinkage () == SILLinkage::Shared)
380+ return true ;
381+ }
358382
359383 // Also serialize "small" non-generic functions.
360384 int size = 0 ;
@@ -401,8 +425,27 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
401425 if (!callee->isDefinition () || callee->isAvailableExternally ())
402426 return ;
403427 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+ }
406449 }
407450 serializeFunction (callee, canSerializeFlags);
408451 assert (callee->isSerialized () == IsSerialized ||
@@ -531,10 +574,9 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
531574 return ;
532575 if (!M.isWholeModule ())
533576 return ;
534- if (!M.getOptions ().CrossModuleOptimization )
535- return ;
536577
537- CrossModuleOptimization CMO (M);
578+ CrossModuleOptimization CMO (M,
579+ /* conservative*/ !M.getOptions ().CrossModuleOptimization );
538580 CMO.serializeFunctionsInModule ();
539581 }
540582};
0 commit comments