@@ -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,33 @@ 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
+ if (!CMS.canSerializeType (replType->getCanonicalType ()))
207
+ isInstSerializable = false ;
208
+ break ;
209
+ case VisitMode::SerializeInst:
210
+ // / Ensure that all replacement types of \p Subs are usable from serialized
211
+ // / functions.
212
+ CMS.makeTypeUsableFromInline (replType->getCanonicalType ());
213
+ break ;
214
+ }
215
+ }
216
+ for (ProtocolConformanceRef pref : Subs.getConformances ()) {
217
+ if (pref.isConcrete ()) {
218
+ ProtocolConformance *concrete = pref.getConcrete ();
219
+ switch (mode) {
220
+ case VisitMode::DetectSerializableInst:
221
+ if (!CMS.canSerializeDecl (concrete->getProtocol ()))
222
+ isInstSerializable = false ;
223
+ break ;
224
+ case VisitMode::SerializeInst:
225
+ CMS.makeDeclUsableFromInline (concrete->getProtocol ());
226
+ break ;
227
+ }
228
+ }
229
+ }
175
230
return Subs;
176
231
}
177
232
@@ -180,9 +235,36 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
180
235
Cloned->eraseFromParent ();
181
236
}
182
237
183
- SILValue getMappedValue (SILValue Value) { return Value; }
238
+ // This method retrieves the operand passed as \p Value as mapped
239
+ // in a previous instruction.
240
+ SILValue getMappedValue (SILValue Value) {
241
+ switch (mode) {
242
+ case VisitMode::DetectSerializableInst:
243
+ // Typically, the type of the operand (\p Value) is already checked
244
+ // and remapped as the resulting type of a previous instruction, so
245
+ // rechecking the type isn't necessary. However, certain instructions
246
+ // have operands that weren’t previously mapped, such as:
247
+ //
248
+ // ```
249
+ // bb0(%0 : $*Foo):
250
+ // %1 = struct_element_addr %0 : $*Foo, #Foo.bar
251
+ // ```
252
+ // where the operand of the first instruction is the argument of the
253
+ // basic block. In such case, an explicit check for the operand's type
254
+ // is required to ensure serializability.
255
+ remapType (Value->getType ());
256
+ break ;
257
+ case VisitMode::SerializeInst:
258
+ break ;
259
+ }
260
+ return Value;
261
+ }
184
262
185
263
SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
264
+
265
+ bool canSerializeTypesInInst (SILInstruction *inst) {
266
+ return isInstSerializable;
267
+ }
186
268
};
187
269
188
270
static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -456,32 +538,36 @@ bool CrossModuleOptimization::canSerializeFunction(
456
538
}
457
539
458
540
// Check if any instruction prevents serializing the function.
541
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
542
+
459
543
for (SILBasicBlock &block : *function) {
460
544
for (SILInstruction &inst : block) {
461
- if (!canSerializeInstruction (&inst, canSerializeFlags, maxDepth)) {
545
+ visitor.getBuilder ().setInsertionPoint (&inst);
546
+ // First, visit each instruction and see if its
547
+ // canonical or substituted types are serializalbe.
548
+ visitor.visit (&inst);
549
+ if (!visitor.canSerializeTypesInInst (&inst)) {
550
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
551
+ return false ;
552
+ }
553
+ // Next, check for any fields that weren't visited.
554
+ if (!canSerializeFieldsByInstructionKind (&inst, canSerializeFlags, maxDepth)) {
555
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
462
556
return false ;
463
557
}
464
558
}
465
559
}
560
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
561
+
466
562
canSerializeFlags[function] = true ;
467
563
return true ;
468
564
}
469
565
470
- // / Returns true if \p inst can be serialized.
566
+ // / Returns true if \p inst can be serialized by checking its fields per instruction kind .
471
567
// /
472
568
// / If \p inst is a function_ref, recursively visits the referenced function.
473
- bool CrossModuleOptimization::canSerializeInstruction (
569
+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
474
570
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
571
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
486
572
SILFunction *callee = FRI->getReferencedFunctionOrNull ();
487
573
if (!callee)
@@ -573,6 +659,46 @@ bool CrossModuleOptimization::canSerializeInstruction(
573
659
return true ;
574
660
}
575
661
662
+ bool CrossModuleOptimization::canSerializeType (SILType type) {
663
+ return canSerializeType (type.getASTType ());
664
+ }
665
+
666
+ bool CrossModuleOptimization::canSerializeType (CanType type) {
667
+ auto iter = canTypesChecked.find (type);
668
+ if (iter != canTypesChecked.end ())
669
+ return iter->getSecond ();
670
+
671
+ bool success = !type.findIf (
672
+ [this ](Type rawSubType) {
673
+ CanType subType = rawSubType->getCanonicalType ();
674
+ if (auto nominal = subType->getNominalOrBoundGenericNominal ()) {
675
+ return !canSerializeDecl (nominal);
676
+ }
677
+ // Types that might not have nominal include Builtin types (e.g. Builtin.Int64),
678
+ // generic parameter types (e.g. T as in Foo<T>), SIL function result types
679
+ // (e.g. @convention(method) (Int, @thin Hasher.Type) -> Hasher), etc.
680
+ return false ;
681
+ });
682
+
683
+ canTypesChecked[type] = success;
684
+ return success;
685
+ }
686
+
687
+ bool CrossModuleOptimization::canSerializeDecl (NominalTypeDecl *decl) {
688
+ assert (decl && " Decl should not be null when checking if it can be serialized" );
689
+
690
+ // In conservative mode we don't want to change the access level of types.
691
+ if (conservative && decl->getEffectiveAccess () < AccessLevel::Package) {
692
+ return false ;
693
+ }
694
+ // Exclude types which are defined in an @_implementationOnly imported
695
+ // module. Such modules are not transitively available.
696
+ if (!canUseFromInline (decl)) {
697
+ return false ;
698
+ }
699
+ return true ;
700
+ }
701
+
576
702
bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
577
703
// Check for referenced functions in the initializer.
578
704
for (const SILInstruction &initInst : *global) {
@@ -594,32 +720,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
594
720
return true ;
595
721
}
596
722
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
723
// / Returns true if the function in \p funcCtxt could be linked statically to
624
724
// / this module.
625
725
static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -713,7 +813,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
713
813
}
714
814
function->setSerializedKind (getRightSerializedKind (M));
715
815
716
- InstructionVisitor visitor (*function, *this );
816
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
717
817
for (SILBasicBlock &block : *function) {
718
818
for (SILInstruction &inst : block) {
719
819
visitor.getBuilder ().setInsertionPoint (&inst);
@@ -912,21 +1012,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
912
1012
});
913
1013
}
914
1014
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
1015
class CrossModuleOptimizationPass : public SILModuleTransform {
931
1016
void run () override {
932
1017
auto &M = *getModule ();
0 commit comments