@@ -53,7 +53,7 @@ namespace {
53
53
class CrossModuleOptimization {
54
54
friend class InstructionVisitor ;
55
55
56
- llvm::DenseMap<SILType , bool > typesChecked ;
56
+ llvm::DenseMap<CanType , bool > canTypesChecked ;
57
57
llvm::SmallPtrSet<TypeBase *, 16 > typesHandled;
58
58
59
59
SILModule &M;
@@ -90,14 +90,18 @@ class CrossModuleOptimization {
90
90
void trySerializeFunctions (ArrayRef<SILFunction *> functions);
91
91
92
92
bool canSerializeFunction (SILFunction *function,
93
- FunctionFlags &canSerializeFlags, int maxDepth);
93
+ FunctionFlags &canSerializeFlags,
94
+ int maxDepth);
94
95
95
- bool canSerializeInstruction (SILInstruction *inst,
96
- FunctionFlags &canSerializeFlags, int maxDepth);
96
+ bool canSerializeFieldsByInstructionKind (SILInstruction *inst,
97
+ FunctionFlags &canSerializeFlags,
98
+ int maxDepth);
97
99
98
100
bool canSerializeGlobal (SILGlobalVariable *global);
99
101
100
102
bool canSerializeType (SILType type);
103
+ bool canSerializeType (CanType type);
104
+ bool canSerializeDecl (NominalTypeDecl *decl);
101
105
102
106
bool canUseFromInline (DeclContext *declCtxt);
103
107
@@ -120,11 +124,10 @@ class CrossModuleOptimization {
120
124
void makeDeclUsableFromInline (ValueDecl *decl);
121
125
122
126
void makeTypeUsableFromInline (CanType type);
123
-
124
- void makeSubstUsableFromInline (const SubstitutionMap &substs);
125
127
};
126
128
127
- // / Visitor for making used types of an instruction inlinable.
129
+ // / Visitor for detecting if an instruction can be serialized and also making used
130
+ // / types of an instruction inlinable if so.
128
131
// /
129
132
// / We use the SILCloner for visiting types, though it sucks that we allocate
130
133
// / instructions just to delete them immediately. But it's better than to
@@ -136,12 +139,22 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
136
139
friend class SILInstructionVisitor <InstructionVisitor>;
137
140
friend class CrossModuleOptimization ;
138
141
142
+ public:
143
+ // / This visitor is used for 2 passes, 1st pass that detects whether the instruction
144
+ // / visited can be serialized, and 2nd pass that does the serializing.
145
+ enum class VisitMode {
146
+ DetectSerializableInst,
147
+ SerializeInst
148
+ };
149
+
139
150
private:
140
151
CrossModuleOptimization &CMS;
152
+ VisitMode mode;
153
+ bool isInstSerializable = true ;
141
154
142
155
public:
143
- InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS) :
144
- SILCloner (F), CMS(CMS) {}
156
+ InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS, VisitMode visitMode ) :
157
+ SILCloner (F), CMS(CMS), mode(visitMode) {}
145
158
146
159
SILType remapType (SILType Ty) {
147
160
if (Ty.hasLocalArchetype ()) {
@@ -151,7 +164,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
151
164
SubstFlags::SubstituteLocalArchetypes);
152
165
}
153
166
154
- CMS.makeTypeUsableFromInline (Ty.getASTType ());
167
+ switch (mode) {
168
+ case VisitMode::DetectSerializableInst:
169
+ if (!CMS.canSerializeType (Ty))
170
+ isInstSerializable = false ;
171
+ break ;
172
+ case VisitMode::SerializeInst:
173
+ CMS.makeTypeUsableFromInline (Ty.getASTType ());
174
+ break ;
175
+ }
155
176
return Ty;
156
177
}
157
178
@@ -162,7 +183,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
162
183
SubstFlags::SubstituteLocalArchetypes)->getCanonicalType ();
163
184
}
164
185
165
- CMS.makeTypeUsableFromInline (Ty);
186
+ switch (mode) {
187
+ case VisitMode::DetectSerializableInst:
188
+ if (!CMS.canSerializeType (Ty))
189
+ isInstSerializable = false ;
190
+ break ;
191
+ case VisitMode::SerializeInst:
192
+ CMS.makeTypeUsableFromInline (Ty);
193
+ break ;
194
+ }
166
195
return Ty;
167
196
}
168
197
@@ -173,7 +202,32 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
173
202
SubstFlags::SubstituteLocalArchetypes);
174
203
}
175
204
176
- CMS.makeSubstUsableFromInline (Subs);
205
+ for (Type replType : Subs.getReplacementTypes ()) {
206
+ switch (mode) {
207
+ case VisitMode::DetectSerializableInst:
208
+ CMS.canSerializeType (replType->getCanonicalType ());
209
+ break ;
210
+ case VisitMode::SerializeInst:
211
+ // / Ensure that all replacement types of \p Subs are usable from serialized
212
+ // / functions.
213
+ CMS.makeTypeUsableFromInline (replType->getCanonicalType ());
214
+ break ;
215
+ }
216
+ }
217
+ for (ProtocolConformanceRef pref : Subs.getConformances ()) {
218
+ if (pref.isConcrete ()) {
219
+ ProtocolConformance *concrete = pref.getConcrete ();
220
+ switch (mode) {
221
+ case VisitMode::DetectSerializableInst:
222
+ if (!CMS.canSerializeDecl (concrete->getProtocol ()))
223
+ isInstSerializable = false ;
224
+ break ;
225
+ case VisitMode::SerializeInst:
226
+ CMS.makeDeclUsableFromInline (concrete->getProtocol ());
227
+ break ;
228
+ }
229
+ }
230
+ }
177
231
return Subs;
178
232
}
179
233
@@ -185,6 +239,10 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
185
239
SILValue getMappedValue (SILValue Value) { return Value; }
186
240
187
241
SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
242
+
243
+ bool canSerializeTypesInInst (SILInstruction *inst) {
244
+ return isInstSerializable;
245
+ }
188
246
};
189
247
190
248
static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -434,32 +492,46 @@ bool CrossModuleOptimization::canSerializeFunction(
434
492
return false ;
435
493
436
494
// Check if any instruction prevents serializing the function.
495
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
437
496
for (SILBasicBlock &block : *function) {
438
497
for (SILInstruction &inst : block) {
439
- if (!canSerializeInstruction (&inst, canSerializeFlags, maxDepth)) {
498
+ visitor.getBuilder ().setInsertionPoint (&inst);
499
+ // First, visit each instruction and see if its result
500
+ // types (lowered) are serializalbe.
501
+ visitor.visit (&inst);
502
+ if (!visitor.canSerializeTypesInInst (&inst)) {
503
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
504
+ return false ;
505
+ }
506
+
507
+ // Next, check operand types (lowered) for serializability
508
+ // as they are not tracked in the visit above.
509
+ for (Operand &op : inst.getAllOperands ()) {
510
+ auto remapped = visitor.remapType (op.get ()->getType ());
511
+ if (!canSerializeType (remapped)) {
512
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
513
+ return false ;
514
+ }
515
+ }
516
+
517
+ // Next, check for any fields that weren't visited.
518
+ if (!canSerializeFieldsByInstructionKind (&inst, canSerializeFlags, maxDepth)) {
519
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
440
520
return false ;
441
521
}
442
522
}
443
523
}
524
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
525
+
444
526
canSerializeFlags[function] = true ;
445
527
return true ;
446
528
}
447
529
448
- // / Returns true if \p inst can be serialized.
530
+ // / Returns true if \p inst can be serialized by checking its fields per instruction kind .
449
531
// /
450
532
// / If \p inst is a function_ref, recursively visits the referenced function.
451
- bool CrossModuleOptimization::canSerializeInstruction (
533
+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
452
534
SILInstruction *inst, FunctionFlags &canSerializeFlags, int maxDepth) {
453
- // First check if any result or operand types prevent serialization.
454
- for (SILValue result : inst->getResults ()) {
455
- if (!canSerializeType (result->getType ()))
456
- return false ;
457
- }
458
- for (Operand &op : inst->getAllOperands ()) {
459
- if (!canSerializeType (op.get ()->getType ()))
460
- return false ;
461
- }
462
-
463
535
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
464
536
SILFunction *callee = FRI->getReferencedFunctionOrNull ();
465
537
if (!callee)
@@ -551,6 +623,45 @@ bool CrossModuleOptimization::canSerializeInstruction(
551
623
return true ;
552
624
}
553
625
626
+ bool CrossModuleOptimization::canSerializeType (SILType type) {
627
+ return canSerializeType (type.getASTType ());
628
+ }
629
+
630
+ bool CrossModuleOptimization::canSerializeType (CanType type) {
631
+ auto iter = canTypesChecked.find (type);
632
+ if (iter != canTypesChecked.end ())
633
+ return iter->getSecond ();
634
+
635
+ bool success = type.findIf (
636
+ [this ](Type rawSubType) {
637
+ CanType subType = rawSubType->getCanonicalType ();
638
+ if (auto nominal = subType->getNominalOrBoundGenericNominal ()) {
639
+ return canSerializeDecl (nominal);
640
+ }
641
+ // If reached here, the type is a Builtin type or similar,
642
+ // e.g. Builtin.Int64, Builtin.Word, Builtin.NativeObject, etc.
643
+ return true ;
644
+ });
645
+
646
+ canTypesChecked[type] = success;
647
+ return success;
648
+ }
649
+
650
+ bool CrossModuleOptimization::canSerializeDecl (NominalTypeDecl *decl) {
651
+ assert (decl && " Decl should not be null when checking if it can be serialized" );
652
+
653
+ // In conservative mode we don't want to change the access level of types.
654
+ if (conservative && decl->getEffectiveAccess () < AccessLevel::Package) {
655
+ return false ;
656
+ }
657
+ // Exclude types which are defined in an @_implementationOnly imported
658
+ // module. Such modules are not transitively available.
659
+ if (!canUseFromInline (decl)) {
660
+ return false ;
661
+ }
662
+ return true ;
663
+ }
664
+
554
665
bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
555
666
// Check for referenced functions in the initializer.
556
667
for (const SILInstruction &initInst : *global) {
@@ -572,32 +683,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
572
683
return true ;
573
684
}
574
685
575
- bool CrossModuleOptimization::canSerializeType (SILType type) {
576
- auto iter = typesChecked.find (type);
577
- if (iter != typesChecked.end ())
578
- return iter->getSecond ();
579
-
580
- bool success = !type.getASTType ().findIf (
581
- [this ](Type rawSubType) {
582
- CanType subType = rawSubType->getCanonicalType ();
583
- if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
584
-
585
- if (conservative && subNT->getEffectiveAccess () < AccessLevel::Package) {
586
- return true ;
587
- }
588
-
589
- // Exclude types which are defined in an @_implementationOnly imported
590
- // module. Such modules are not transitively available.
591
- if (!canUseFromInline (subNT)) {
592
- return true ;
593
- }
594
- }
595
- return false ;
596
- });
597
- typesChecked[type] = success;
598
- return success;
599
- }
600
-
601
686
// / Returns true if the function in \p funcCtxt could be linked statically to
602
687
// / this module.
603
688
static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -730,7 +815,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
730
815
}
731
816
function->setSerializedKind (getRightSerializedKind (M));
732
817
733
- InstructionVisitor visitor (*function, *this );
818
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
734
819
for (SILBasicBlock &block : *function) {
735
820
for (SILInstruction &inst : block) {
736
821
visitor.getBuilder ().setInsertionPoint (&inst);
@@ -929,21 +1014,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
929
1014
});
930
1015
}
931
1016
932
- // / Ensure that all replacement types of \p substs are usable from serialized
933
- // / functions.
934
- void CrossModuleOptimization::makeSubstUsableFromInline (
935
- const SubstitutionMap &substs) {
936
- for (Type replType : substs.getReplacementTypes ()) {
937
- makeTypeUsableFromInline (replType->getCanonicalType ());
938
- }
939
- for (ProtocolConformanceRef pref : substs.getConformances ()) {
940
- if (pref.isConcrete ()) {
941
- ProtocolConformance *concrete = pref.getConcrete ();
942
- makeDeclUsableFromInline (concrete->getProtocol ());
943
- }
944
- }
945
- }
946
-
947
1017
class CrossModuleOptimizationPass : public SILModuleTransform {
948
1018
void run () override {
949
1019
auto &M = *getModule ();
0 commit comments