@@ -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
@@ -182,9 +236,36 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
182
236
Cloned->eraseFromParent ();
183
237
}
184
238
185
- SILValue getMappedValue (SILValue Value) { return Value; }
239
+ // This method retrieves the operand passed as \p Value as mapped
240
+ // in a previous instruction.
241
+ SILValue getMappedValue (SILValue Value) {
242
+ switch (mode) {
243
+ case VisitMode::DetectSerializableInst:
244
+ // Typically, the type of the operand (\p Value) is already checked
245
+ // and remapped as the resulting type of a previous instruction, so
246
+ // rechecking the type isn't necessary. However, certain instructions
247
+ // have operands that weren’t previously mapped, such as:
248
+ //
249
+ // ```
250
+ // bb0(%0 : $*Foo):
251
+ // %1 = struct_element_addr %0 : $*Foo, #Foo.bar
252
+ // ```
253
+ // where the operand of the first instruction is the argument of the
254
+ // basic block. In such case, an explicit check for the operand's type
255
+ // is required to ensure serializability.
256
+ remapType (Value->getType ());
257
+ break ;
258
+ case VisitMode::SerializeInst:
259
+ break ;
260
+ }
261
+ return Value;
262
+ }
186
263
187
264
SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
265
+
266
+ bool canSerializeTypesInInst (SILInstruction *inst) {
267
+ return isInstSerializable;
268
+ }
188
269
};
189
270
190
271
static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -434,32 +515,36 @@ bool CrossModuleOptimization::canSerializeFunction(
434
515
return false ;
435
516
436
517
// Check if any instruction prevents serializing the function.
518
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
519
+
437
520
for (SILBasicBlock &block : *function) {
438
521
for (SILInstruction &inst : block) {
439
- if (!canSerializeInstruction (&inst, canSerializeFlags, maxDepth)) {
522
+ visitor.getBuilder ().setInsertionPoint (&inst);
523
+ // First, visit each instruction and see if its
524
+ // canonical or substituted types are serializalbe.
525
+ visitor.visit (&inst);
526
+ if (!visitor.canSerializeTypesInInst (&inst)) {
527
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
528
+ return false ;
529
+ }
530
+ // Next, check for any fields that weren't visited.
531
+ if (!canSerializeFieldsByInstructionKind (&inst, canSerializeFlags, maxDepth)) {
532
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
440
533
return false ;
441
534
}
442
535
}
443
536
}
537
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
538
+
444
539
canSerializeFlags[function] = true ;
445
540
return true ;
446
541
}
447
542
448
- // / Returns true if \p inst can be serialized.
543
+ // / Returns true if \p inst can be serialized by checking its fields per instruction kind .
449
544
// /
450
545
// / If \p inst is a function_ref, recursively visits the referenced function.
451
- bool CrossModuleOptimization::canSerializeInstruction (
546
+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
452
547
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
548
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
464
549
SILFunction *callee = FRI->getReferencedFunctionOrNull ();
465
550
if (!callee)
@@ -551,6 +636,45 @@ bool CrossModuleOptimization::canSerializeInstruction(
551
636
return true ;
552
637
}
553
638
639
+ bool CrossModuleOptimization::canSerializeType (SILType type) {
640
+ return canSerializeType (type.getASTType ());
641
+ }
642
+
643
+ bool CrossModuleOptimization::canSerializeType (CanType type) {
644
+ auto iter = canTypesChecked.find (type);
645
+ if (iter != canTypesChecked.end ())
646
+ return iter->getSecond ();
647
+
648
+ bool success = type.findIf (
649
+ [this ](Type rawSubType) {
650
+ CanType subType = rawSubType->getCanonicalType ();
651
+ if (auto nominal = subType->getNominalOrBoundGenericNominal ()) {
652
+ return canSerializeDecl (nominal);
653
+ }
654
+ // If reached here, the type is a Builtin type or similar,
655
+ // e.g. Builtin.Int64, Builtin.Word, Builtin.NativeObject, etc.
656
+ return true ;
657
+ });
658
+
659
+ canTypesChecked[type] = success;
660
+ return success;
661
+ }
662
+
663
+ bool CrossModuleOptimization::canSerializeDecl (NominalTypeDecl *decl) {
664
+ assert (decl && " Decl should not be null when checking if it can be serialized" );
665
+
666
+ // In conservative mode we don't want to change the access level of types.
667
+ if (conservative && decl->getEffectiveAccess () < AccessLevel::Package) {
668
+ return false ;
669
+ }
670
+ // Exclude types which are defined in an @_implementationOnly imported
671
+ // module. Such modules are not transitively available.
672
+ if (!canUseFromInline (decl)) {
673
+ return false ;
674
+ }
675
+ return true ;
676
+ }
677
+
554
678
bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
555
679
// Check for referenced functions in the initializer.
556
680
for (const SILInstruction &initInst : *global) {
@@ -572,32 +696,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
572
696
return true ;
573
697
}
574
698
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
699
// / Returns true if the function in \p funcCtxt could be linked statically to
602
700
// / this module.
603
701
static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -730,7 +828,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
730
828
}
731
829
function->setSerializedKind (getRightSerializedKind (M));
732
830
733
- InstructionVisitor visitor (*function, *this );
831
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
734
832
for (SILBasicBlock &block : *function) {
735
833
for (SILInstruction &inst : block) {
736
834
visitor.getBuilder ().setInsertionPoint (&inst);
@@ -929,21 +1027,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
929
1027
});
930
1028
}
931
1029
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
1030
class CrossModuleOptimizationPass : public SILModuleTransform {
948
1031
void run () override {
949
1032
auto &M = *getModule ();
0 commit comments