21
21
#include " swift/SIL/SILCloner.h"
22
22
#include " swift/SIL/SILFunction.h"
23
23
#include " swift/SIL/SILModule.h"
24
+ #include " swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
25
+ #include " swift/SILOptimizer/Analysis/FunctionOrder.h"
24
26
#include " swift/SILOptimizer/PassManager/Passes.h"
25
27
#include " swift/SILOptimizer/PassManager/Transforms.h"
26
28
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
@@ -70,7 +72,8 @@ class CrossModuleOptimization {
70
72
CrossModuleOptimization (SILModule &M, bool conservative, bool everything)
71
73
: M(M), conservative(conservative), everything(everything) { }
72
74
73
- void serializeFunctionsInModule ();
75
+ void serializeFunctionsInModule (ArrayRef<SILFunction *> functions);
76
+ void serializeTablesInModule ();
74
77
75
78
private:
76
79
bool canSerializeFunction (SILFunction *function,
@@ -81,7 +84,7 @@ class CrossModuleOptimization {
81
84
82
85
bool canSerializeGlobal (SILGlobalVariable *global);
83
86
84
- bool canSerializeType (SILType type);
87
+ bool canSerializeType (SILType type, TypeExpansionContext typeExpCtx );
85
88
86
89
bool canUseFromInline (DeclContext *declCtxt);
87
90
@@ -161,29 +164,94 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
161
164
}
162
165
};
163
166
164
- static bool isVisible (SILLinkage linkage, SILOptions options) {
167
+ static bool isPackageOrPublic (SILLinkage linkage, SILOptions options) {
165
168
if (options.EnableSerializePackage )
166
169
return linkage == SILLinkage::Public || linkage == SILLinkage::Package;
167
170
return linkage == SILLinkage::Public;
168
171
}
169
- static bool isVisible (AccessLevel accessLevel, SILOptions options) {
172
+
173
+ static bool isPackageOrPublic (AccessLevel accessLevel, SILOptions options) {
170
174
if (options.EnableSerializePackage )
171
175
return accessLevel == AccessLevel::Package || accessLevel == AccessLevel::Public;
172
176
return accessLevel == AccessLevel::Public;
173
177
}
174
178
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
+ }
177
213
214
+ // / Select functions in the module which should be serialized.
215
+ void CrossModuleOptimization::serializeFunctionsInModule (
216
+ ArrayRef<SILFunction *> functions) {
178
217
FunctionFlags canSerializeFlags;
179
218
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
+ }
186
252
}
253
+ // Then serialize the witness table itself.
254
+ wt.setSerialized (IsSerialized);
187
255
}
188
256
}
189
257
}
@@ -215,8 +283,10 @@ bool CrossModuleOptimization::canSerializeFunction(
215
283
return false ;
216
284
}
217
285
218
- if (function->isSerialized ())
286
+ if (function->isSerialized ()) {
287
+ canSerializeFlags[function] = true ;
219
288
return true ;
289
+ }
220
290
221
291
if (!function->isDefinition () || function->isAvailableExternally ())
222
292
return false ;
@@ -258,16 +328,17 @@ bool CrossModuleOptimization::canSerializeFunction(
258
328
// / Returns true if \p inst can be serialized.
259
329
// /
260
330
// / 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) {
264
333
// First check if any result or operand types prevent serialization.
334
+ auto typeExpCtx = inst->getFunction ()->getTypeExpansionContext ();
335
+
265
336
for (SILValue result : inst->getResults ()) {
266
- if (!canSerializeType (result->getType ()))
337
+ if (!canSerializeType (result->getType (), typeExpCtx ))
267
338
return false ;
268
339
}
269
340
for (Operand &op : inst->getAllOperands ()) {
270
- if (!canSerializeType (op.get ()->getType ()))
341
+ if (!canSerializeType (op.get ()->getType (), typeExpCtx ))
271
342
return false ;
272
343
}
273
344
@@ -280,9 +351,9 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
280
351
// public functions, because that can increase code size. E.g. if the
281
352
// function is completely inlined afterwards.
282
353
// 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 ;
286
357
}
287
358
288
359
// In some project configurations imported C functions are not necessarily
@@ -301,12 +372,13 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
301
372
// inline.
302
373
if (!canUseFromInline (callee))
303
374
return false ;
375
+
304
376
return true ;
305
377
}
306
378
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
307
379
SILGlobalVariable *global = GAI->getReferencedGlobal ();
308
380
if ((conservative || M.getOptions ().emitTBD ) &&
309
- !hasPublicOrPackageVisibility (global-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
381
+ !isReferenceSerializeCandidate (global, M.getOptions ())) {
310
382
return false ;
311
383
}
312
384
@@ -354,7 +426,7 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
354
426
// function is completely inlined afterwards.
355
427
// Also, when emitting TBD files, we cannot introduce a new public symbol.
356
428
if ((conservative || M.getOptions ().emitTBD ) &&
357
- !hasPublicOrPackageVisibility (referencedFunc-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
429
+ !isReferenceSerializeCandidate (referencedFunc, M.getOptions ())) {
358
430
return false ;
359
431
}
360
432
@@ -365,16 +437,28 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
365
437
return true ;
366
438
}
367
439
368
- bool CrossModuleOptimization::canSerializeType (SILType type) {
440
+ bool CrossModuleOptimization::canSerializeType (SILType type,
441
+ TypeExpansionContext typeExpCtx) {
369
442
auto iter = typesChecked.find (type);
370
443
if (iter != typesChecked.end ())
371
444
return iter->getSecond ();
372
445
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
+
373
457
bool success = !type.getASTType ().findIf (
374
458
[this ](Type rawSubType) {
375
459
CanType subType = rawSubType->getCanonicalType ();
376
460
if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
377
-
461
+
378
462
if (conservative && subNT->getEffectiveAccess () < AccessLevel::Package) {
379
463
return true ;
380
464
}
@@ -484,13 +568,17 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
484
568
return true ;
485
569
}
486
570
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
+ }
494
582
}
495
583
}
496
584
@@ -503,7 +591,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
503
591
const FunctionFlags &canSerializeFlags) {
504
592
if (function->isSerialized ())
505
593
return ;
506
-
594
+
507
595
if (!canSerializeFlags.lookup (function))
508
596
return ;
509
597
@@ -552,9 +640,11 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
552
640
}
553
641
}
554
642
serializeFunction (callee, canSerializeFlags);
555
- assert (callee->isSerialized () || isVisible (callee->getLinkage (), M.getOptions ()));
643
+ assert (callee->isSerialized () ||
644
+ isPackageOrPublic (callee->getLinkage (), M.getOptions ()));
556
645
return ;
557
646
}
647
+
558
648
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
559
649
SILGlobalVariable *global = GAI->getReferencedGlobal ();
560
650
if (canSerializeGlobal (global)) {
@@ -616,7 +706,7 @@ void CrossModuleOptimization::makeDeclUsableFromInline(ValueDecl *decl) {
616
706
if (M.getSwiftModule () != decl->getDeclContext ()->getParentModule ())
617
707
return ;
618
708
619
- if (!isVisible (decl->getFormalAccess (), M.getOptions ()) &&
709
+ if (!isPackageOrPublic (decl->getFormalAccess (), M.getOptions ()) &&
620
710
!decl->isUsableFromInline ()) {
621
711
// Mark the nominal type as "usableFromInline".
622
712
// TODO: find a way to do this without modifying the AST. The AST should be
@@ -699,7 +789,8 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
699
789
void run () override {
700
790
701
791
auto &M = *getModule ();
702
- if (M.getSwiftModule ()->isResilient ())
792
+ if (M.getSwiftModule ()->isResilient () &&
793
+ !M.getOptions ().EnableSerializePackage )
703
794
return ;
704
795
if (!M.isWholeModule ())
705
796
return ;
@@ -726,7 +817,17 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
726
817
}
727
818
728
819
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 ();
730
831
}
731
832
};
732
833
0 commit comments