@@ -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
@@ -118,11 +122,10 @@ class CrossModuleOptimization {
118
122
void makeDeclUsableFromInline (ValueDecl *decl);
119
123
120
124
void makeTypeUsableFromInline (CanType type);
121
-
122
- void makeSubstUsableFromInline (const SubstitutionMap &substs);
123
125
};
124
126
125
- // / Visitor for making used types of an instruction inlinable.
127
+ // / Visitor for detecting if an instruction can be serialized and also making used
128
+ // / types of an instruction inlinable if so.
126
129
// /
127
130
// / We use the SILCloner for visiting types, though it sucks that we allocate
128
131
// / instructions just to delete them immediately. But it's better than to
@@ -134,12 +137,22 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
134
137
friend class SILInstructionVisitor <InstructionVisitor>;
135
138
friend class CrossModuleOptimization ;
136
139
140
+ public:
141
+ // / This visitor is used for 2 passes, 1st pass that detects whether the instruction
142
+ // / visited can be serialized, and 2nd pass that does the serializing.
143
+ enum class VisitMode {
144
+ DetectSerializableInst,
145
+ SerializeInst
146
+ };
147
+
137
148
private:
138
149
CrossModuleOptimization &CMS;
150
+ VisitMode mode;
151
+ bool isInstSerializable = true ;
139
152
140
153
public:
141
- InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS) :
142
- SILCloner (F), CMS(CMS) {}
154
+ InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS, VisitMode visitMode ) :
155
+ SILCloner (F), CMS(CMS), mode(visitMode) {}
143
156
144
157
SILType remapType (SILType Ty) {
145
158
if (Ty.hasLocalArchetype ()) {
@@ -149,7 +162,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
149
162
SubstFlags::SubstituteLocalArchetypes);
150
163
}
151
164
152
- CMS.makeTypeUsableFromInline (Ty.getASTType ());
165
+ switch (mode) {
166
+ case VisitMode::DetectSerializableInst:
167
+ if (!CMS.canSerializeType (Ty))
168
+ isInstSerializable = false ;
169
+ break ;
170
+ case VisitMode::SerializeInst:
171
+ CMS.makeTypeUsableFromInline (Ty.getASTType ());
172
+ break ;
173
+ }
153
174
return Ty;
154
175
}
155
176
@@ -160,7 +181,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
160
181
SubstFlags::SubstituteLocalArchetypes)->getCanonicalType ();
161
182
}
162
183
163
- CMS.makeTypeUsableFromInline (Ty);
184
+ switch (mode) {
185
+ case VisitMode::DetectSerializableInst:
186
+ if (!CMS.canSerializeType (Ty))
187
+ isInstSerializable = false ;
188
+ break ;
189
+ case VisitMode::SerializeInst:
190
+ CMS.makeTypeUsableFromInline (Ty);
191
+ break ;
192
+ }
164
193
return Ty;
165
194
}
166
195
@@ -171,7 +200,32 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
171
200
SubstFlags::SubstituteLocalArchetypes);
172
201
}
173
202
174
- CMS.makeSubstUsableFromInline (Subs);
203
+ for (Type replType : Subs.getReplacementTypes ()) {
204
+ switch (mode) {
205
+ case VisitMode::DetectSerializableInst:
206
+ CMS.canSerializeType (replType->getCanonicalType ());
207
+ break ;
208
+ case VisitMode::SerializeInst:
209
+ // / Ensure that all replacement types of \p Subs are usable from serialized
210
+ // / functions.
211
+ CMS.makeTypeUsableFromInline (replType->getCanonicalType ());
212
+ break ;
213
+ }
214
+ }
215
+ for (ProtocolConformanceRef pref : Subs.getConformances ()) {
216
+ if (pref.isConcrete ()) {
217
+ ProtocolConformance *concrete = pref.getConcrete ();
218
+ switch (mode) {
219
+ case VisitMode::DetectSerializableInst:
220
+ if (!CMS.canSerializeDecl (concrete->getProtocol ()))
221
+ isInstSerializable = false ;
222
+ break ;
223
+ case VisitMode::SerializeInst:
224
+ CMS.makeDeclUsableFromInline (concrete->getProtocol ());
225
+ break ;
226
+ }
227
+ }
228
+ }
175
229
return Subs;
176
230
}
177
231
@@ -180,9 +234,36 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
180
234
Cloned->eraseFromParent ();
181
235
}
182
236
183
- SILValue getMappedValue (SILValue Value) { return Value; }
237
+ // This method retrieves the operand passed as \p Value as mapped
238
+ // in a previous instruction.
239
+ SILValue getMappedValue (SILValue Value) {
240
+ switch (mode) {
241
+ case VisitMode::DetectSerializableInst:
242
+ // Typically, the type of the operand (\p Value) is already checked
243
+ // and remapped as the resulting type of a previous instruction, so
244
+ // rechecking the type isn't necessary. However, certain instructions
245
+ // have operands that weren’t previously mapped, such as:
246
+ //
247
+ // ```
248
+ // bb0(%0 : $*Foo):
249
+ // %1 = struct_element_addr %0 : $*Foo, #Foo.bar
250
+ // ```
251
+ // where the operand of the first instruction is the argument of the
252
+ // basic block. In such case, an explicit check for the operand's type
253
+ // is required to ensure serializability.
254
+ remapType (Value->getType ());
255
+ break ;
256
+ case VisitMode::SerializeInst:
257
+ break ;
258
+ }
259
+ return Value;
260
+ }
184
261
185
262
SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
263
+
264
+ bool canSerializeTypesInInst (SILInstruction *inst) {
265
+ return isInstSerializable;
266
+ }
186
267
};
187
268
188
269
static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -456,32 +537,36 @@ bool CrossModuleOptimization::canSerializeFunction(
456
537
}
457
538
458
539
// Check if any instruction prevents serializing the function.
540
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
541
+
459
542
for (SILBasicBlock &block : *function) {
460
543
for (SILInstruction &inst : block) {
461
- if (!canSerializeInstruction (&inst, canSerializeFlags, maxDepth)) {
544
+ visitor.getBuilder ().setInsertionPoint (&inst);
545
+ // First, visit each instruction and see if its
546
+ // canonical or substituted types are serializalbe.
547
+ visitor.visit (&inst);
548
+ if (!visitor.canSerializeTypesInInst (&inst)) {
549
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
550
+ return false ;
551
+ }
552
+ // Next, check for any fields that weren't visited.
553
+ if (!canSerializeFieldsByInstructionKind (&inst, canSerializeFlags, maxDepth)) {
554
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
462
555
return false ;
463
556
}
464
557
}
465
558
}
559
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
560
+
466
561
canSerializeFlags[function] = true ;
467
562
return true ;
468
563
}
469
564
470
- // / Returns true if \p inst can be serialized.
565
+ // / Returns true if \p inst can be serialized by checking its fields per instruction kind .
471
566
// /
472
567
// / If \p inst is a function_ref, recursively visits the referenced function.
473
- bool CrossModuleOptimization::canSerializeInstruction (
568
+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
474
569
SILInstruction *inst, FunctionFlags &canSerializeFlags, int maxDepth) {
475
- // First check if any result or operand types prevent serialization.
476
- for (SILValue result : inst->getResults ()) {
477
- if (!canSerializeType (result->getType ()))
478
- return false ;
479
- }
480
- for (Operand &op : inst->getAllOperands ()) {
481
- if (!canSerializeType (op.get ()->getType ()))
482
- return false ;
483
- }
484
-
485
570
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
486
571
SILFunction *callee = FRI->getReferencedFunctionOrNull ();
487
572
if (!callee)
@@ -573,6 +658,45 @@ bool CrossModuleOptimization::canSerializeInstruction(
573
658
return true ;
574
659
}
575
660
661
+ bool CrossModuleOptimization::canSerializeType (SILType type) {
662
+ return canSerializeType (type.getASTType ());
663
+ }
664
+
665
+ bool CrossModuleOptimization::canSerializeType (CanType type) {
666
+ auto iter = canTypesChecked.find (type);
667
+ if (iter != canTypesChecked.end ())
668
+ return iter->getSecond ();
669
+
670
+ bool success = type.findIf (
671
+ [this ](Type rawSubType) {
672
+ CanType subType = rawSubType->getCanonicalType ();
673
+ if (auto nominal = subType->getNominalOrBoundGenericNominal ()) {
674
+ return canSerializeDecl (nominal);
675
+ }
676
+ // If reached here, the type is a Builtin type or similar,
677
+ // e.g. Builtin.Int64, Builtin.Word, Builtin.NativeObject, etc.
678
+ return true ;
679
+ });
680
+
681
+ canTypesChecked[type] = success;
682
+ return success;
683
+ }
684
+
685
+ bool CrossModuleOptimization::canSerializeDecl (NominalTypeDecl *decl) {
686
+ assert (decl && " Decl should not be null when checking if it can be serialized" );
687
+
688
+ // In conservative mode we don't want to change the access level of types.
689
+ if (conservative && decl->getEffectiveAccess () < AccessLevel::Package) {
690
+ return false ;
691
+ }
692
+ // Exclude types which are defined in an @_implementationOnly imported
693
+ // module. Such modules are not transitively available.
694
+ if (!canUseFromInline (decl)) {
695
+ return false ;
696
+ }
697
+ return true ;
698
+ }
699
+
576
700
bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
577
701
// Check for referenced functions in the initializer.
578
702
for (const SILInstruction &initInst : *global) {
@@ -594,32 +718,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
594
718
return true ;
595
719
}
596
720
597
- bool CrossModuleOptimization::canSerializeType (SILType type) {
598
- auto iter = typesChecked.find (type);
599
- if (iter != typesChecked.end ())
600
- return iter->getSecond ();
601
-
602
- bool success = !type.getASTType ().findIf (
603
- [this ](Type rawSubType) {
604
- CanType subType = rawSubType->getCanonicalType ();
605
- if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal ()) {
606
-
607
- if (conservative && subNT->getEffectiveAccess () < AccessLevel::Package) {
608
- return true ;
609
- }
610
-
611
- // Exclude types which are defined in an @_implementationOnly imported
612
- // module. Such modules are not transitively available.
613
- if (!canUseFromInline (subNT)) {
614
- return true ;
615
- }
616
- }
617
- return false ;
618
- });
619
- typesChecked[type] = success;
620
- return success;
621
- }
622
-
623
721
// / Returns true if the function in \p funcCtxt could be linked statically to
624
722
// / this module.
625
723
static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -713,7 +811,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
713
811
}
714
812
function->setSerializedKind (getRightSerializedKind (M));
715
813
716
- InstructionVisitor visitor (*function, *this );
814
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
717
815
for (SILBasicBlock &block : *function) {
718
816
for (SILInstruction &inst : block) {
719
817
visitor.getBuilder ().setInsertionPoint (&inst);
@@ -912,21 +1010,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
912
1010
});
913
1011
}
914
1012
915
- // / Ensure that all replacement types of \p substs are usable from serialized
916
- // / functions.
917
- void CrossModuleOptimization::makeSubstUsableFromInline (
918
- const SubstitutionMap &substs) {
919
- for (Type replType : substs.getReplacementTypes ()) {
920
- makeTypeUsableFromInline (replType->getCanonicalType ());
921
- }
922
- for (ProtocolConformanceRef pref : substs.getConformances ()) {
923
- if (pref.isConcrete ()) {
924
- ProtocolConformance *concrete = pref.getConcrete ();
925
- makeDeclUsableFromInline (concrete->getProtocol ());
926
- }
927
- }
928
- }
929
-
930
1013
class CrossModuleOptimizationPass : public SILModuleTransform {
931
1014
void run () override {
932
1015
auto &M = *getModule ();
0 commit comments