@@ -73,11 +73,19 @@ class CrossModuleOptimization {
73
73
CrossModuleOptimization (SILModule &M, bool conservative, bool everything)
74
74
: M(M), conservative(conservative), everything(everything) { }
75
75
76
- void trySerializeFunctions (ArrayRef<SILFunction *> functions);
77
76
void serializeFunctionsInModule (SILPassManager *manager);
78
- void serializeTablesInModule ();
77
+ void serializeWitnessTablesInModule ();
78
+ void serializeVTablesInModule ();
79
79
80
80
private:
81
+ bool isReferenceSerializeCandidate (SILFunction *F, SILOptions options);
82
+ bool isReferenceSerializeCandidate (SILGlobalVariable *G, SILOptions options);
83
+ SerializedKind_t getRightSerializedKind (const SILModule &mod);
84
+ bool isSerializedWithRightKind (const SILModule &mod, SILFunction *f);
85
+ bool isSerializedWithRightKind (const SILModule &mod, SILGlobalVariable *g);
86
+
87
+ void trySerializeFunctions (ArrayRef<SILFunction *> functions);
88
+
81
89
bool canSerializeFunction (SILFunction *function,
82
90
FunctionFlags &canSerializeFlags, int maxDepth);
83
91
@@ -203,40 +211,50 @@ static bool isPackageCMOEnabled(ModuleDecl *mod) {
203
211
// / function due to `@inlinable`, funtions with [serialized_for_package] from
204
212
// / the imported module are not allowed being inlined into the client function, which
205
213
// / is the correct behavior.
206
- static bool isSerializedWithRightKind (const SILModule &mod,
214
+ bool CrossModuleOptimization:: isSerializedWithRightKind (const SILModule &mod,
207
215
SILFunction *f) {
208
216
// If Package CMO is enabled in resilient mode, return
209
217
// true if the function is [serialized] due to @inlinable
210
- // (or similar) or [serialized_for_pkg ] due to this
218
+ // (or similar) or [serialized_for_package ] due to this
211
219
// optimization.
212
220
return isPackageCMOEnabled (mod.getSwiftModule ()) ? f->isAnySerialized ()
213
221
: f->isSerialized ();
214
222
}
215
- static bool isSerializedWithRightKind (const SILModule &mod,
223
+ bool CrossModuleOptimization:: isSerializedWithRightKind (const SILModule &mod,
216
224
SILGlobalVariable *g) {
217
225
return isPackageCMOEnabled (mod.getSwiftModule ()) ? g->isAnySerialized ()
218
226
: g->isSerialized ();
219
227
}
220
- static SerializedKind_t getRightSerializedKind (const SILModule &mod) {
228
+ SerializedKind_t CrossModuleOptimization:: getRightSerializedKind (const SILModule &mod) {
221
229
return isPackageCMOEnabled (mod.getSwiftModule ()) ? IsSerializedForPackage
222
230
: IsSerialized;
223
231
}
224
232
225
233
static bool isSerializeCandidate (SILFunction *F, SILOptions options) {
226
234
auto linkage = F->getLinkage ();
227
- // We allow serializing a shared definition. For example,
228
- // `public func foo() { print("") }` is a function with a
229
- // public linkage which only references `print`; the definition
230
- // of `print` has a shared linkage and does not reference
231
- // non-serializable instructions, so it should be serialized,
232
- // thus the public `foo` could be serialized.
235
+ // If Package CMO is enabled, besides package/public definitions,
236
+ // we allow serializing private, hidden, or shared definitions
237
+ // that do not contain private or hidden symbol references (and
238
+ // their nested references). If private or internal definitions are
239
+ // serialized, they are set to a shared linkage.
240
+ //
241
+ // E.g. `public func foo() { print("") }` is a public function that
242
+ // references `print`, a shared definition which does not contain
243
+ // any private or internal symbols, thus is serialized, which in turn
244
+ // allows `foo` to be serialized.
245
+ // E.g. a protocol witness method for a package protocol member is
246
+ // set to a private linkage in SILGen. By allowing such private thunk
247
+ // to be serialized and set to shared linkage here, functions that
248
+ // reference the thunk can be serialized as well.
233
249
if (options.EnableSerializePackage )
234
- return linkage == SILLinkage::Public || linkage == SILLinkage::Package ||
235
- (linkage == SILLinkage::Shared && F->isDefinition ());
250
+ return linkage != SILLinkage::PublicExternal &&
251
+ linkage != SILLinkage::PackageExternal &&
252
+ linkage != SILLinkage::HiddenExternal;
236
253
return linkage == SILLinkage::Public;
237
254
}
238
255
239
- static bool isReferenceSerializeCandidate (SILFunction *F, SILOptions options) {
256
+ bool CrossModuleOptimization::isReferenceSerializeCandidate (SILFunction *F,
257
+ SILOptions options) {
240
258
if (options.EnableSerializePackage ) {
241
259
if (isSerializedWithRightKind (F->getModule (), F))
242
260
return true ;
@@ -246,8 +264,8 @@ static bool isReferenceSerializeCandidate(SILFunction *F, SILOptions options) {
246
264
return hasPublicVisibility (F->getLinkage ());
247
265
}
248
266
249
- static bool isReferenceSerializeCandidate (SILGlobalVariable *G,
250
- SILOptions options) {
267
+ bool CrossModuleOptimization:: isReferenceSerializeCandidate (SILGlobalVariable *G,
268
+ SILOptions options) {
251
269
if (options.EnableSerializePackage ) {
252
270
if (isSerializedWithRightKind (G->getModule (), G))
253
271
return true ;
@@ -279,85 +297,73 @@ void CrossModuleOptimization::serializeFunctionsInModule(SILPassManager *manager
279
297
trySerializeFunctions (bottomUpFunctions);
280
298
}
281
299
282
- void CrossModuleOptimization::serializeTablesInModule () {
283
- if (!M.getSwiftModule ()-> serializePackageEnabled ( ))
300
+ void CrossModuleOptimization::serializeWitnessTablesInModule () {
301
+ if (!isPackageCMOEnabled ( M.getSwiftModule ()))
284
302
return ;
285
303
286
- for (const auto &vt : M.getVTables ()) {
287
- if (vt->getSerializedKind () != getRightSerializedKind (M) &&
288
- vt->getClass ()->getEffectiveAccess () >= AccessLevel::Package) {
289
- // This checks if a vtable entry is not serialized and attempts to
290
- // serialize (and its references) if they have the right visibility.
291
- // This should not be necessary but is added to ensure all applicable
292
- // symbols are serialized. Whether serialized or not is cached so
293
- // this check shouldn't be expensive.
294
- auto unserializedClassMethodRange = llvm::make_filter_range (
295
- vt->getEntries (), [&](const SILVTableEntry &entry) {
296
- return entry.getImplementation ()->getSerializedKind () !=
297
- getRightSerializedKind (M);
298
- });
299
- std::vector<SILFunction *> classMethodsToSerialize;
300
- llvm::transform (unserializedClassMethodRange,
301
- std::back_inserter (classMethodsToSerialize),
302
- [&](const SILVTableEntry &entry) {
303
- return entry.getImplementation ();
304
- });
305
- trySerializeFunctions (classMethodsToSerialize);
306
-
307
- bool containsInternal =
308
- llvm::any_of (vt->getEntries (), [&](const SILVTableEntry &entry) {
309
- // If the entry is internal, vtable should not be serialized.
310
- // However, if the entry is not serialized but has the right
311
- // visibility, it can still be referenced, thus the vtable
312
- // should serialized.
313
- return !entry.getImplementation ()->hasValidLinkageForFragileRef (
314
- getRightSerializedKind (M));
315
- });
316
- if (!containsInternal)
317
- vt->setSerializedKind (getRightSerializedKind (M));
318
- }
319
- }
320
-
321
- // Witness thunks are not serialized, so serialize them here.
322
304
for (auto &wt : M.getWitnessTables ()) {
323
305
if (wt.getSerializedKind () != getRightSerializedKind (M) &&
324
306
hasPublicOrPackageVisibility (wt.getLinkage (), /* includePackage*/ true )) {
325
- // This checks if a wtable entry is not serialized and attempts to
326
- // serialize (and its references) if they have the right visibility.
327
- // This should not be necessary but is added to ensure all applicable
328
- // symbols are serialized. Whether serialized or not is cached so
329
- // this check shouldn't be expensive.
330
307
auto unserializedWTMethodRange = llvm::make_filter_range (
331
308
wt.getEntries (), [&](const SILWitnessTable::Entry &entry) {
332
309
return entry.getKind () == SILWitnessTable::Method &&
333
310
entry.getMethodWitness ().Witness ->getSerializedKind () !=
334
311
getRightSerializedKind (M);
335
312
});
336
- std::vector<SILFunction *> wtMethodsToSerialize;
337
- llvm::transform (unserializedWTMethodRange,
338
- std::back_inserter (wtMethodsToSerialize),
339
- [&](const SILWitnessTable::Entry &entry) {
340
- return entry.getMethodWitness ().Witness ;
341
- });
342
- trySerializeFunctions (wtMethodsToSerialize);
313
+ // In Package CMO, we try serializing witness thunks that
314
+ // are private if they don't contain hidden or private
315
+ // references. If they are serialized, they are set to
316
+ // a shared linkage. If they can't be serialized, we set
317
+ // the linkage to package so that the witness table itself
318
+ // can still be serialized, thus giving a chance for entires
319
+ // that _are_ serialized to be accessed directly.
320
+ for (const SILWitnessTable::Entry &entry: unserializedWTMethodRange) {
321
+ if (entry.getMethodWitness ().Witness ->getLinkage () == SILLinkage::Private)
322
+ entry.getMethodWitness ().Witness ->setLinkage (SILLinkage::Package);
323
+ }
343
324
344
325
bool containsInternal = llvm::any_of (
345
326
wt.getEntries (), [&](const SILWitnessTable::Entry &entry) {
346
- // If the entry is internal, wtable should not be serialized.
347
- // However, if the entry is not serialized but has the right
348
- // visibility, it can still be referenced, thus the vtable
349
- // should serialized.
350
327
return entry.getKind () == SILWitnessTable::Method &&
351
328
!entry.getMethodWitness ()
352
329
.Witness ->hasValidLinkageForFragileRef (
353
330
getRightSerializedKind (M));
354
331
});
332
+ // FIXME: This check shouldn't be necessary but added as a caution
333
+ // to ensure we don't serialize witness table if it contains an
334
+ // internal entry.
355
335
if (!containsInternal)
356
336
wt.setSerializedKind (getRightSerializedKind (M));
357
337
}
358
338
}
359
339
}
360
340
341
+ void CrossModuleOptimization::serializeVTablesInModule () {
342
+ if (!isPackageCMOEnabled (M.getSwiftModule ()))
343
+ return ;
344
+
345
+ for (const auto &vt : M.getVTables ()) {
346
+ if (vt->getSerializedKind () != getRightSerializedKind (M) &&
347
+ vt->getClass ()->getEffectiveAccess () >= AccessLevel::Package) {
348
+ bool containsInternal =
349
+ llvm::any_of (vt->getEntries (), [&](const SILVTableEntry &entry) {
350
+ return !entry.getImplementation ()->hasValidLinkageForFragileRef (
351
+ getRightSerializedKind (M));
352
+ });
353
+
354
+ // If the entries are either serialized or have package/public
355
+ // visibility, the vtable can be serialized; the non-serialized
356
+ // entries can still be referenced as they have the visibility
357
+ // outside of their defining module, and the serialized entries
358
+ // can be directly accessed since the vtable is serialized.
359
+ // However, if it contains a private/internal entry, we don't
360
+ // serialize the vtable at all.
361
+ if (!containsInternal)
362
+ vt->setSerializedKind (getRightSerializedKind (M));
363
+ }
364
+ }
365
+ }
366
+
361
367
// / Recursively walk the call graph and select functions to be serialized.
362
368
// /
363
369
// / The results are stored in \p canSerializeFlags and the result for \p
@@ -538,7 +544,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
538
544
for (const SILInstruction &initInst : *global) {
539
545
if (auto *FRI = dyn_cast<FunctionRefInst>(&initInst)) {
540
546
SILFunction *referencedFunc = FRI->getReferencedFunction ();
541
-
542
547
// In conservative mode we don't want to turn non-public functions into
543
548
// public functions, because that can increase code size. E.g. if the
544
549
// function is completely inlined afterwards.
@@ -701,6 +706,16 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
701
706
if (!canSerializeFlags.lookup (function))
702
707
return ;
703
708
709
+ if (isPackageCMOEnabled (M.getSwiftModule ())) {
710
+ // If a private thunk (such as a protocol witness method for
711
+ // a package protocol member) does not reference any private
712
+ // or internal symbols, thus is serialized, it's set to shared
713
+ // linkage, so that functions that reference the thunk can be
714
+ // serialized as well.
715
+ if (function->getLinkage () == SILLinkage::Private ||
716
+ function->getLinkage () == SILLinkage::Hidden)
717
+ function->setLinkage (SILLinkage::Shared);
718
+ }
704
719
function->setSerializedKind (getRightSerializedKind (M));
705
720
706
721
for (SILBasicBlock &block : *function) {
@@ -900,7 +915,6 @@ void CrossModuleOptimization::makeSubstUsableFromInline(
900
915
901
916
class CrossModuleOptimizationPass : public SILModuleTransform {
902
917
void run () override {
903
-
904
918
auto &M = *getModule ();
905
919
if (M.getSwiftModule ()->isResilient () &&
906
920
!M.getSwiftModule ()->serializePackageEnabled ())
@@ -933,7 +947,8 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
933
947
CMO.serializeFunctionsInModule (PM);
934
948
935
949
// Serialize SIL v-tables and witness-tables if package-cmo is enabled.
936
- CMO.serializeTablesInModule ();
950
+ CMO.serializeVTablesInModule ();
951
+ CMO.serializeWitnessTablesInModule ();
937
952
}
938
953
};
939
954
0 commit comments