@@ -67,12 +67,14 @@ class CrossModuleOptimization {
6767 bool everything;
6868
6969 typedef llvm::DenseMap<SILFunction *, bool > FunctionFlags;
70+ FunctionFlags canSerializeFlags;
7071
7172public:
7273 CrossModuleOptimization (SILModule &M, bool conservative, bool everything)
7374 : M(M), conservative(conservative), everything(everything) { }
7475
75- void serializeFunctionsInModule (ArrayRef<SILFunction *> functions);
76+ void trySerializeFunctions (ArrayRef<SILFunction *> functions);
77+ void serializeFunctionsInModule (SILPassManager *manager);
7678 void serializeTablesInModule ();
7779
7880private:
@@ -251,12 +253,8 @@ static bool isReferenceSerializeCandidate(SILGlobalVariable *G,
251253}
252254
253255// / Select functions in the module which should be serialized.
254- void CrossModuleOptimization::serializeFunctionsInModule (
256+ void CrossModuleOptimization::trySerializeFunctions (
255257 ArrayRef<SILFunction *> functions) {
256- FunctionFlags canSerializeFlags;
257-
258- // The passed functions are already ordered bottom-up so the most
259- // nested referenced function is checked first.
260258 for (SILFunction *F : functions) {
261259 if (isSerializeCandidate (F, M.getOptions ()) || everything) {
262260 if (canSerializeFunction (F, canSerializeFlags, /* maxDepth*/ 64 )) {
@@ -266,31 +264,81 @@ void CrossModuleOptimization::serializeFunctionsInModule(
266264 }
267265}
268266
267+ void CrossModuleOptimization::serializeFunctionsInModule (SILPassManager *manager) {
268+ // Reorder SIL funtions in the module bottom up so we can serialize
269+ // the most nested referenced functions first and avoid unnecessary
270+ // recursive checks.
271+ BasicCalleeAnalysis *BCA = manager->getAnalysis <BasicCalleeAnalysis>();
272+ BottomUpFunctionOrder BottomUpOrder (M, BCA);
273+ auto bottomUpFunctions = BottomUpOrder.getFunctions ();
274+ trySerializeFunctions (bottomUpFunctions);
275+ }
276+
269277void CrossModuleOptimization::serializeTablesInModule () {
270278 if (!M.getSwiftModule ()->serializePackageEnabled ())
271279 return ;
272280
273281 for (const auto &vt : M.getVTables ()) {
274- if (vt->isNotSerialized ( ) &&
282+ if (vt->getSerializedKind () != getRightSerializedKind (M ) &&
275283 vt->getClass ()->getEffectiveAccess () >= AccessLevel::Package) {
276- vt->setSerializedKind (getRightSerializedKind (M));
284+ // This checks if a vtable entry is not serialized and attempts to
285+ // serialize (and its references) if they have the right visibility.
286+ // This should not be necessary but is added to ensure all applicable
287+ // symbols are serialized. Whether serialized or not is cached so
288+ // this check shouldn't be expensive.
289+ auto unserializedClassMethodRange = llvm::make_filter_range (vt->getEntries (), [&](auto &entry) {
290+ return entry.getImplementation ()->getSerializedKind () != getRightSerializedKind (M);
291+ });
292+ std::vector<SILFunction *> classMethodsToSerialize;
293+ llvm::transform (unserializedClassMethodRange,
294+ std::back_inserter (classMethodsToSerialize),
295+ [&](auto entry) {
296+ return entry.getImplementation ();
297+ });
298+ trySerializeFunctions (classMethodsToSerialize);
299+
300+ bool containsInternal = llvm::any_of (classMethodsToSerialize, [&](auto &method) {
301+ // If the entry is internal, vtable should not be serialized.
302+ // However, if the entry is not serialized but has the right
303+ // visibility, it can still be referenced, thus the vtable
304+ // should serialized.
305+ return !method->hasValidLinkageForFragileRef (getRightSerializedKind (M));
306+ });
307+ if (!containsInternal)
308+ vt->setSerializedKind (getRightSerializedKind (M));
277309 }
278310 }
279311
312+ // Witness thunks are not serialized, so serialize them here.
280313 for (auto &wt : M.getWitnessTables ()) {
281- if (wt.isNotSerialized () &&
314+ if (wt.getSerializedKind () != getRightSerializedKind (M) &&
282315 hasPublicOrPackageVisibility (wt.getLinkage (), /* includePackage*/ true )) {
283- for (auto &entry : wt.getEntries ()) {
284- // Witness thunks are not serialized, so serialize them here.
285- if (entry.getKind () == SILWitnessTable::Method &&
286- entry.getMethodWitness ().Witness ->isNotSerialized () &&
287- isSerializeCandidate (entry.getMethodWitness ().Witness ,
288- M.getOptions ())) {
289- entry.getMethodWitness ().Witness ->setSerializedKind (getRightSerializedKind (M));
290- }
291- }
292- // Then serialize the witness table itself.
293- wt.setSerializedKind (getRightSerializedKind (M));
316+ // This checks if a wtable entry is not serialized and attempts to
317+ // serialize (and its references) if they have the right visibility.
318+ // This should not be necessary but is added to ensure all applicable
319+ // symbols are serialized. Whether serialized or not is cached so
320+ // this check shouldn't be expensive.
321+ auto unserializedWTMethodRange = llvm::make_filter_range (wt.getEntries (), [&](auto &entry) {
322+ return entry.getKind () == SILWitnessTable::Method &&
323+ entry.getMethodWitness ().Witness ->getSerializedKind () != getRightSerializedKind (M);
324+ });
325+ std::vector<SILFunction *> wtMethodsToSerialize;
326+ llvm::transform (unserializedWTMethodRange,
327+ std::back_inserter (wtMethodsToSerialize),
328+ [&](auto entry) {
329+ return entry.getMethodWitness ().Witness ;
330+ });
331+ trySerializeFunctions (wtMethodsToSerialize);
332+
333+ bool containsInternal = llvm::any_of (wtMethodsToSerialize, [&](auto &method) {
334+ // If the entry is internal, wtable should not be serialized.
335+ // However, if the entry is not serialized but has the right
336+ // visibility, it can still be referenced, thus the vtable
337+ // should serialized.
338+ return !method->hasValidLinkageForFragileRef (getRightSerializedKind (M));
339+ });
340+ if (!containsInternal)
341+ wt.setSerializedKind (getRightSerializedKind (M));
294342 }
295343 }
296344}
@@ -441,7 +489,13 @@ bool CrossModuleOptimization::canSerializeInstruction(
441489 return canUse;
442490 }
443491 if (auto *MI = dyn_cast<MethodInst>(inst)) {
444- return !MI->getMember ().isForeign ;
492+ // If a class_method or witness_method is internal, it can't
493+ // be serialized.
494+ auto member = MI->getMember ();
495+ auto methodAccessScope = member.getDecl ()->getFormalAccessScope (nullptr ,
496+ /* treatUsableFromInlineAsPublic*/ true );
497+ return methodAccessScope.isPublicOrPackage () &&
498+ !member.isForeign ;
445499 }
446500 if (auto *REAI = dyn_cast<RefElementAddrInst>(inst)) {
447501 // In conservative mode, we don't support class field accesses of non-public
@@ -855,14 +909,7 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
855909 }
856910
857911 CrossModuleOptimization CMO (M, conservative, everything);
858-
859- // Reorder SIL funtions in the module bottom up so we can serialize
860- // the most nested referenced functions first and avoid unnecessary
861- // recursive checks.
862- BasicCalleeAnalysis *BCA = PM->getAnalysis <BasicCalleeAnalysis>();
863- BottomUpFunctionOrder BottomUpOrder (M, BCA);
864- auto BottomUpFunctions = BottomUpOrder.getFunctions ();
865- CMO.serializeFunctionsInModule (BottomUpFunctions);
912+ CMO.serializeFunctionsInModule (PM);
866913
867914 // Serialize SIL v-tables and witness-tables if package-cmo is enabled.
868915 CMO.serializeTablesInModule ();
0 commit comments