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,
@@ -161,33 +164,98 @@ 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);
186
225
}
187
226
}
188
227
}
189
228
}
190
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
+ }
252
+ }
253
+ // Then serialize the witness table itself.
254
+ wt.setSerialized (IsSerialized);
255
+ }
256
+ }
257
+ }
258
+
191
259
// / Recursively walk the call graph and select functions to be serialized.
192
260
// /
193
261
// / The results are stored in \p canSerializeFlags and the result for \p
@@ -258,9 +326,8 @@ bool CrossModuleOptimization::canSerializeFunction(
258
326
// / Returns true if \p inst can be serialized.
259
327
// /
260
328
// / If \p inst is a function_ref, recursively visits the referenced function.
261
- bool CrossModuleOptimization::canSerializeInstruction (SILInstruction *inst,
262
- FunctionFlags &canSerializeFlags, int maxDepth) {
263
-
329
+ bool CrossModuleOptimization::canSerializeInstruction (
330
+ SILInstruction *inst, FunctionFlags &canSerializeFlags, int maxDepth) {
264
331
// First check if any result or operand types prevent serialization.
265
332
for (SILValue result : inst->getResults ()) {
266
333
if (!canSerializeType (result->getType ()))
@@ -280,9 +347,9 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
280
347
// public functions, because that can increase code size. E.g. if the
281
348
// function is completely inlined afterwards.
282
349
// 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 ;
350
+ if (conservative || M.getOptions ().emitTBD ) {
351
+ if (! isReferenceSerializeCandidate (callee, M.getOptions ()))
352
+ return false ;
286
353
}
287
354
288
355
// In some project configurations imported C functions are not necessarily
@@ -301,12 +368,13 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
301
368
// inline.
302
369
if (!canUseFromInline (callee))
303
370
return false ;
371
+
304
372
return true ;
305
373
}
306
374
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
307
375
SILGlobalVariable *global = GAI->getReferencedGlobal ();
308
376
if ((conservative || M.getOptions ().emitTBD ) &&
309
- !hasPublicOrPackageVisibility (global-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
377
+ !isReferenceSerializeCandidate (global, M.getOptions ())) {
310
378
return false ;
311
379
}
312
380
@@ -333,6 +401,18 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
333
401
if (auto *MI = dyn_cast<MethodInst>(inst)) {
334
402
return !MI->getMember ().isForeign ;
335
403
}
404
+ if (auto *SEAI = dyn_cast<StructElementAddrInst>(inst)) {
405
+ // FIXME: handle struct_element_addr %field in resilient mode;
406
+ // requires non-resilience in SIL verify.
407
+ if (M.getSwiftModule ()->isResilient ())
408
+ return false ;
409
+ }
410
+ if (auto *SI = dyn_cast<StructInst>(inst)) {
411
+ // FIXME: handle `struct $Foo` in resilient mode;
412
+ // Foo is by-address, so fails in IsLodableOrOpaque check later.
413
+ if (M.getSwiftModule ()->isResilient () && !SI->getType ().isAddress ())
414
+ return false ;
415
+ }
336
416
if (auto *REAI = dyn_cast<RefElementAddrInst>(inst)) {
337
417
// In conservative mode, we don't support class field accesses of non-public
338
418
// properties, because that would require to make the field decl public -
@@ -354,7 +434,7 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
354
434
// function is completely inlined afterwards.
355
435
// Also, when emitting TBD files, we cannot introduce a new public symbol.
356
436
if ((conservative || M.getOptions ().emitTBD ) &&
357
- !hasPublicOrPackageVisibility (referencedFunc-> getLinkage () , M.getOptions (). EnableSerializePackage )) {
437
+ !isReferenceSerializeCandidate (referencedFunc, M.getOptions ())) {
358
438
return false ;
359
439
}
360
440
@@ -374,7 +454,7 @@ bool CrossModuleOptimization::canSerializeType(SILType type) {
374
454
[this ](Type rawSubType) {
375
455
CanType subType = rawSubType->getCanonicalType ();
376
456
if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
377
-
457
+
378
458
if (conservative && subNT->getEffectiveAccess () < AccessLevel::Package) {
379
459
return true ;
380
460
}
@@ -484,13 +564,17 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
484
564
return true ;
485
565
}
486
566
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 ;
567
+ // If package-cmo is enabled, we don't want to limit inlining
568
+ // or should at least increase the cap.
569
+ if (!M.getOptions ().EnableSerializePackage ) {
570
+ // Also serialize "small" non-generic functions.
571
+ int size = 0 ;
572
+ for (SILBasicBlock &block : *function) {
573
+ for (SILInstruction &inst : block) {
574
+ size += (int )instructionInlineCost (inst);
575
+ if (size >= CMOFunctionSizeLimit)
576
+ return false ;
577
+ }
494
578
}
495
579
}
496
580
@@ -503,7 +587,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
503
587
const FunctionFlags &canSerializeFlags) {
504
588
if (function->isSerialized ())
505
589
return ;
506
-
590
+
507
591
if (!canSerializeFlags.lookup (function))
508
592
return ;
509
593
@@ -552,9 +636,11 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
552
636
}
553
637
}
554
638
serializeFunction (callee, canSerializeFlags);
555
- assert (callee->isSerialized () || isVisible (callee->getLinkage (), M.getOptions ()));
639
+ assert (callee->isSerialized () ||
640
+ isPackageOrPublic (callee->getLinkage (), M.getOptions ()));
556
641
return ;
557
642
}
643
+
558
644
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
559
645
SILGlobalVariable *global = GAI->getReferencedGlobal ();
560
646
if (canSerializeGlobal (global)) {
@@ -616,7 +702,7 @@ void CrossModuleOptimization::makeDeclUsableFromInline(ValueDecl *decl) {
616
702
if (M.getSwiftModule () != decl->getDeclContext ()->getParentModule ())
617
703
return ;
618
704
619
- if (!isVisible (decl->getFormalAccess (), M.getOptions ()) &&
705
+ if (!isPackageOrPublic (decl->getFormalAccess (), M.getOptions ()) &&
620
706
!decl->isUsableFromInline ()) {
621
707
// Mark the nominal type as "usableFromInline".
622
708
// TODO: find a way to do this without modifying the AST. The AST should be
@@ -699,7 +785,8 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
699
785
void run () override {
700
786
701
787
auto &M = *getModule ();
702
- if (M.getSwiftModule ()->isResilient ())
788
+ if (M.getSwiftModule ()->isResilient () &&
789
+ !M.getOptions ().EnableSerializePackage )
703
790
return ;
704
791
if (!M.isWholeModule ())
705
792
return ;
@@ -726,7 +813,17 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
726
813
}
727
814
728
815
CrossModuleOptimization CMO (M, conservative, everything);
729
- CMO.serializeFunctionsInModule ();
816
+
817
+ // Reorder SIL funtions in the module bottom up so we can serialize
818
+ // the most nested referenced functions first and avoid unnecessary
819
+ // recursive checks.
820
+ BasicCalleeAnalysis *BCA = PM->getAnalysis <BasicCalleeAnalysis>();
821
+ BottomUpFunctionOrder BottomUpOrder (M, BCA);
822
+ auto BottomUpFunctions = BottomUpOrder.getFunctions ();
823
+ CMO.serializeFunctionsInModule (BottomUpFunctions);
824
+
825
+ // Serialize SIL v-tables and witness-tables if package-cmo is enabled.
826
+ CMO.serializeTablesInModule ();
730
827
}
731
828
};
732
829
0 commit comments