2121#include " swift/SIL/SILCloner.h"
2222#include " swift/SIL/SILFunction.h"
2323#include " swift/SIL/SILModule.h"
24+ #include " swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
25+ #include " swift/SILOptimizer/Analysis/FunctionOrder.h"
2426#include " swift/SILOptimizer/PassManager/Passes.h"
2527#include " swift/SILOptimizer/PassManager/Transforms.h"
2628#include " swift/SILOptimizer/Utils/InstOptUtils.h"
@@ -70,7 +72,8 @@ class CrossModuleOptimization {
7072 CrossModuleOptimization (SILModule &M, bool conservative, bool everything)
7173 : M(M), conservative(conservative), everything(everything) { }
7274
73- void serializeFunctionsInModule ();
75+ void serializeFunctionsInModule (ArrayRef<SILFunction *> functions);
76+ void serializeTablesInModule ();
7477
7578private:
7679 bool canSerializeFunction (SILFunction *function,
@@ -81,7 +84,7 @@ class CrossModuleOptimization {
8184
8285 bool canSerializeGlobal (SILGlobalVariable *global);
8386
84- bool canSerializeType (SILType type);
87+ bool canSerializeType (SILType type, TypeExpansionContext typeExpCtx );
8588
8689 bool canUseFromInline (DeclContext *declCtxt);
8790
@@ -161,29 +164,94 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
161164 }
162165};
163166
164- static bool isVisible (SILLinkage linkage, SILOptions options) {
167+ static bool isPackageOrPublic (SILLinkage linkage, SILOptions options) {
165168 if (options.EnableSerializePackage )
166169 return linkage == SILLinkage::Public || linkage == SILLinkage::Package;
167170 return linkage == SILLinkage::Public;
168171}
169- static bool isVisible (AccessLevel accessLevel, SILOptions options) {
172+
173+ static bool isPackageOrPublic (AccessLevel accessLevel, SILOptions options) {
170174 if (options.EnableSerializePackage )
171175 return accessLevel == AccessLevel::Package || accessLevel == AccessLevel::Public;
172176 return accessLevel == AccessLevel::Public;
173177}
174178
175- // / Select functions in the module which should be serialized.
176- void CrossModuleOptimization::serializeFunctionsInModule () {
179+ static bool isSerializeCandidate (SILFunction *F, SILOptions options) {
180+ auto linkage = F->getLinkage ();
181+ // We allow serializing a shared definition. For example,
182+ // `public func foo() { print("") }` is a function with a
183+ // public linkage which only references `print`; the definition
184+ // of `print` has a shared linkage and does not reference
185+ // non-serializable instructions, so it should be serialized,
186+ // thus the public `foo` could be serialized.
187+ if (options.EnableSerializePackage )
188+ return linkage == SILLinkage::Public || linkage == SILLinkage::Package ||
189+ (linkage == SILLinkage::Shared && F->isDefinition ());
190+ return linkage == SILLinkage::Public;
191+ }
192+
193+ static bool isReferenceSerializeCandidate (SILFunction *F, SILOptions options) {
194+ if (options.EnableSerializePackage ) {
195+ if (F->isSerialized ())
196+ return true ;
197+ return hasPublicOrPackageVisibility (F->getLinkage (),
198+ /* includePackage*/ true );
199+ }
200+ return hasPublicVisibility (F->getLinkage ());
201+ }
202+
203+ static bool isReferenceSerializeCandidate (SILGlobalVariable *G,
204+ SILOptions options) {
205+ if (options.EnableSerializePackage ) {
206+ if (G->isSerialized ())
207+ return true ;
208+ return hasPublicOrPackageVisibility (G->getLinkage (),
209+ /* includePackage*/ true );
210+ }
211+ return hasPublicVisibility (G->getLinkage ());
212+ }
177213
214+ // / Select functions in the module which should be serialized.
215+ void CrossModuleOptimization::serializeFunctionsInModule (
216+ ArrayRef<SILFunction *> functions) {
178217 FunctionFlags canSerializeFlags;
179218
180- // Start with public functions.
181- for (SILFunction &F : M) {
182- if (isVisible (F.getLinkage (), M.getOptions ()) ||
183- everything) {
184- if (canSerializeFunction (&F, canSerializeFlags, /* maxDepth*/ 64 )) {
185- serializeFunction (&F, canSerializeFlags);
219+ // The passed functions are already ordered bottom-up so the most
220+ // nested referenced function is checked first.
221+ for (SILFunction *F : functions) {
222+ if (isSerializeCandidate (F, M.getOptions ()) || everything) {
223+ if (canSerializeFunction (F, canSerializeFlags, /* maxDepth*/ 64 )) {
224+ serializeFunction (F, canSerializeFlags);
225+ }
226+ }
227+ }
228+ }
229+
230+ void CrossModuleOptimization::serializeTablesInModule () {
231+ if (!M.getOptions ().EnableSerializePackage )
232+ return ;
233+
234+ for (const auto &vt : M.getVTables ()) {
235+ if (!vt->isSerialized () &&
236+ vt->getClass ()->getEffectiveAccess () >= AccessLevel::Package) {
237+ vt->setSerialized (IsSerialized);
238+ }
239+ }
240+
241+ for (auto &wt : M.getWitnessTables ()) {
242+ if (!wt.isSerialized () && hasPublicOrPackageVisibility (
243+ wt.getLinkage (), /* includePackage*/ true )) {
244+ for (auto &entry : wt.getEntries ()) {
245+ // Witness thunks are not serialized, so serialize them here.
246+ if (entry.getKind () == SILWitnessTable::Method &&
247+ !entry.getMethodWitness ().Witness ->isSerialized () &&
248+ isSerializeCandidate (entry.getMethodWitness ().Witness ,
249+ M.getOptions ())) {
250+ entry.getMethodWitness ().Witness ->setSerialized (IsSerialized);
251+ }
186252 }
253+ // Then serialize the witness table itself.
254+ wt.setSerialized (IsSerialized);
187255 }
188256 }
189257}
@@ -215,8 +283,10 @@ bool CrossModuleOptimization::canSerializeFunction(
215283 return false ;
216284 }
217285
218- if (function->isSerialized ())
286+ if (function->isSerialized ()) {
287+ canSerializeFlags[function] = true ;
219288 return true ;
289+ }
220290
221291 if (!function->isDefinition () || function->isAvailableExternally ())
222292 return false ;
@@ -258,16 +328,17 @@ bool CrossModuleOptimization::canSerializeFunction(
258328// / Returns true if \p inst can be serialized.
259329// /
260330// / If \p inst is a function_ref, recursively visits the referenced function.
261- bool CrossModuleOptimization::canSerializeInstruction (SILInstruction *inst,
262- FunctionFlags &canSerializeFlags, int maxDepth) {
263-
331+ bool CrossModuleOptimization::canSerializeInstruction (
332+ SILInstruction *inst, FunctionFlags &canSerializeFlags, int maxDepth) {
264333 // First check if any result or operand types prevent serialization.
334+ auto typeExpCtx = inst->getFunction ()->getTypeExpansionContext ();
335+
265336 for (SILValue result : inst->getResults ()) {
266- if (!canSerializeType (result->getType ()))
337+ if (!canSerializeType (result->getType (), typeExpCtx ))
267338 return false ;
268339 }
269340 for (Operand &op : inst->getAllOperands ()) {
270- if (!canSerializeType (op.get ()->getType ()))
341+ if (!canSerializeType (op.get ()->getType (), typeExpCtx ))
271342 return false ;
272343 }
273344
@@ -280,9 +351,9 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
280351 // public functions, because that can increase code size. E.g. if the
281352 // function is completely inlined afterwards.
282353 // Also, when emitting TBD files, we cannot introduce a new public symbol.
283- if (( conservative || M.getOptions ().emitTBD ) &&
284- ! hasPublicOrPackageVisibility (callee-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
285- return false ;
354+ if (conservative || M.getOptions ().emitTBD ) {
355+ if (! isReferenceSerializeCandidate (callee, M.getOptions ()))
356+ return false ;
286357 }
287358
288359 // In some project configurations imported C functions are not necessarily
@@ -301,12 +372,13 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
301372 // inline.
302373 if (!canUseFromInline (callee))
303374 return false ;
375+
304376 return true ;
305377 }
306378 if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
307379 SILGlobalVariable *global = GAI->getReferencedGlobal ();
308380 if ((conservative || M.getOptions ().emitTBD ) &&
309- !hasPublicOrPackageVisibility (global-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
381+ !isReferenceSerializeCandidate (global, M.getOptions ())) {
310382 return false ;
311383 }
312384
@@ -354,7 +426,7 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
354426 // function is completely inlined afterwards.
355427 // Also, when emitting TBD files, we cannot introduce a new public symbol.
356428 if ((conservative || M.getOptions ().emitTBD ) &&
357- !hasPublicOrPackageVisibility (referencedFunc-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
429+ !isReferenceSerializeCandidate (referencedFunc, M.getOptions ())) {
358430 return false ;
359431 }
360432
@@ -365,16 +437,28 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
365437 return true ;
366438}
367439
368- bool CrossModuleOptimization::canSerializeType (SILType type) {
440+ bool CrossModuleOptimization::canSerializeType (SILType type,
441+ TypeExpansionContext typeExpCtx) {
369442 auto iter = typesChecked.find (type);
370443 if (iter != typesChecked.end ())
371444 return iter->getSecond ();
372445
446+ if (M.getSwiftModule ()->isResilient ()) {
447+ auto minResilientCtx = TypeExpansionContext (ResilienceExpansion::Minimal,
448+ typeExpCtx.getContext (),
449+ typeExpCtx.isWholeModuleContext ());
450+ auto loadableInMinResilientCtx = M.Types .getTypeLowering (type, minResilientCtx).isLoadable ();
451+ if (!loadableInMinResilientCtx) {
452+ typesChecked[type] = false ;
453+ return false ;
454+ }
455+ }
456+
373457 bool success = !type.getASTType ().findIf (
374458 [this ](Type rawSubType) {
375459 CanType subType = rawSubType->getCanonicalType ();
376460 if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
377-
461+
378462 if (conservative && subNT->getEffectiveAccess () < AccessLevel::Package) {
379463 return true ;
380464 }
@@ -484,13 +568,17 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
484568 return true ;
485569 }
486570
487- // Also serialize "small" non-generic functions.
488- int size = 0 ;
489- for (SILBasicBlock &block : *function) {
490- for (SILInstruction &inst : block) {
491- size += (int )instructionInlineCost (inst);
492- if (size >= CMOFunctionSizeLimit)
493- return false ;
571+ // If package-cmo is enabled, we don't want to limit inlining
572+ // or should at least increase the cap.
573+ if (!M.getOptions ().EnableSerializePackage ) {
574+ // Also serialize "small" non-generic functions.
575+ int size = 0 ;
576+ for (SILBasicBlock &block : *function) {
577+ for (SILInstruction &inst : block) {
578+ size += (int )instructionInlineCost (inst);
579+ if (size >= CMOFunctionSizeLimit)
580+ return false ;
581+ }
494582 }
495583 }
496584
@@ -503,7 +591,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
503591 const FunctionFlags &canSerializeFlags) {
504592 if (function->isSerialized ())
505593 return ;
506-
594+
507595 if (!canSerializeFlags.lookup (function))
508596 return ;
509597
@@ -552,9 +640,11 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
552640 }
553641 }
554642 serializeFunction (callee, canSerializeFlags);
555- assert (callee->isSerialized () || isVisible (callee->getLinkage (), M.getOptions ()));
643+ assert (callee->isSerialized () ||
644+ isPackageOrPublic (callee->getLinkage (), M.getOptions ()));
556645 return ;
557646 }
647+
558648 if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
559649 SILGlobalVariable *global = GAI->getReferencedGlobal ();
560650 if (canSerializeGlobal (global)) {
@@ -616,7 +706,7 @@ void CrossModuleOptimization::makeDeclUsableFromInline(ValueDecl *decl) {
616706 if (M.getSwiftModule () != decl->getDeclContext ()->getParentModule ())
617707 return ;
618708
619- if (!isVisible (decl->getFormalAccess (), M.getOptions ()) &&
709+ if (!isPackageOrPublic (decl->getFormalAccess (), M.getOptions ()) &&
620710 !decl->isUsableFromInline ()) {
621711 // Mark the nominal type as "usableFromInline".
622712 // TODO: find a way to do this without modifying the AST. The AST should be
@@ -699,7 +789,8 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
699789 void run () override {
700790
701791 auto &M = *getModule ();
702- if (M.getSwiftModule ()->isResilient ())
792+ if (M.getSwiftModule ()->isResilient () &&
793+ !M.getOptions ().EnableSerializePackage )
703794 return ;
704795 if (!M.isWholeModule ())
705796 return ;
@@ -726,7 +817,17 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
726817 }
727818
728819 CrossModuleOptimization CMO (M, conservative, everything);
729- CMO.serializeFunctionsInModule ();
820+
821+ // Reorder SIL funtions in the module bottom up so we can serialize
822+ // the most nested referenced functions first and avoid unnecessary
823+ // recursive checks.
824+ BasicCalleeAnalysis *BCA = PM->getAnalysis <BasicCalleeAnalysis>();
825+ BottomUpFunctionOrder BottomUpOrder (M, BCA);
826+ auto BottomUpFunctions = BottomUpOrder.getFunctions ();
827+ CMO.serializeFunctionsInModule (BottomUpFunctions);
828+
829+ // Serialize SIL v-tables and witness-tables if package-cmo is enabled.
830+ CMO.serializeTablesInModule ();
730831 }
731832};
732833
0 commit comments