@@ -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,25 @@ 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 ;
154
+ // Tracks whether an instruction can be serialized by
155
+ // checking its contained types and fields.
156
+ llvm::DenseMap<SILInstruction *, bool > instToSerializeMap;
141
157
142
158
public:
143
- InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS) :
144
- SILCloner (F), CMS(CMS) {}
159
+ InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS, VisitMode visitMode ) :
160
+ SILCloner (F), CMS(CMS), mode(visitMode) {}
145
161
146
162
SILType remapType (SILType Ty) {
147
163
if (Ty.hasLocalArchetype ()) {
@@ -151,7 +167,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
151
167
SubstFlags::SubstituteLocalArchetypes);
152
168
}
153
169
154
- CMS.makeTypeUsableFromInline (Ty.getASTType ());
170
+ switch (mode) {
171
+ case VisitMode::DetectSerializableInst:
172
+ if (!CMS.canSerializeType (Ty))
173
+ isInstSerializable = false ;
174
+ break ;
175
+ case VisitMode::SerializeInst:
176
+ CMS.makeTypeUsableFromInline (Ty.getASTType ());
177
+ break ;
178
+ }
155
179
return Ty;
156
180
}
157
181
@@ -162,7 +186,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
162
186
SubstFlags::SubstituteLocalArchetypes)->getCanonicalType ();
163
187
}
164
188
165
- CMS.makeTypeUsableFromInline (Ty);
189
+ switch (mode) {
190
+ case VisitMode::DetectSerializableInst:
191
+ if (!CMS.canSerializeType (Ty))
192
+ isInstSerializable = false ;
193
+ break ;
194
+ case VisitMode::SerializeInst:
195
+ CMS.makeTypeUsableFromInline (Ty);
196
+ break ;
197
+ }
166
198
return Ty;
167
199
}
168
200
@@ -173,18 +205,57 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
173
205
SubstFlags::SubstituteLocalArchetypes);
174
206
}
175
207
176
- CMS.makeSubstUsableFromInline (Subs);
208
+ for (Type replType : Subs.getReplacementTypes ()) {
209
+ switch (mode) {
210
+ case VisitMode::DetectSerializableInst:
211
+ CMS.canSerializeType (replType->getCanonicalType ());
212
+ break ;
213
+ case VisitMode::SerializeInst:
214
+ // / Ensure that all replacement types of \p Subs are usable from serialized
215
+ // / functions.
216
+ CMS.makeTypeUsableFromInline (replType->getCanonicalType ());
217
+ break ;
218
+ }
219
+ }
220
+ for (ProtocolConformanceRef pref : Subs.getConformances ()) {
221
+ if (pref.isConcrete ()) {
222
+ ProtocolConformance *concrete = pref.getConcrete ();
223
+ switch (mode) {
224
+ case VisitMode::DetectSerializableInst:
225
+ if (!CMS.canSerializeDecl (concrete->getProtocol ()))
226
+ isInstSerializable = false ;
227
+ break ;
228
+ case VisitMode::SerializeInst:
229
+ CMS.makeDeclUsableFromInline (concrete->getProtocol ());
230
+ break ;
231
+ }
232
+ }
233
+ }
177
234
return Subs;
178
235
}
179
236
180
237
void postProcess (SILInstruction *Orig, SILInstruction *Cloned) {
181
238
SILCloner<InstructionVisitor>::postProcess (Orig, Cloned);
239
+ switch (mode) {
240
+ case VisitMode::DetectSerializableInst:
241
+ instToSerializeMap[Orig] = isInstSerializable;
242
+ break ;
243
+ case VisitMode::SerializeInst:
244
+ break ;
245
+ }
182
246
Cloned->eraseFromParent ();
183
247
}
184
248
185
249
SILValue getMappedValue (SILValue Value) { return Value; }
186
250
187
251
SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
252
+
253
+ bool canSerializeTypesInInst (SILInstruction *inst) {
254
+ auto iter = instToSerializeMap.find (inst);
255
+ if (iter != instToSerializeMap.end ())
256
+ return iter->getSecond ();
257
+ return false ;
258
+ }
188
259
};
189
260
190
261
static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -434,32 +505,32 @@ bool CrossModuleOptimization::canSerializeFunction(
434
505
return false ;
435
506
436
507
// Check if any instruction prevents serializing the function.
508
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
437
509
for (SILBasicBlock &block : *function) {
438
510
for (SILInstruction &inst : block) {
439
- if (!canSerializeInstruction (&inst, canSerializeFlags, maxDepth)) {
511
+ visitor.getBuilder ().setInsertionPoint (&inst);
512
+ // First, visit each instruction and see if its lowered types
513
+ // are serializalbe and track them in a map.
514
+ visitor.visit (&inst);
515
+ if (!visitor.canSerializeTypesInInst (&inst))
516
+ return false ;
517
+ // If serializable, check if any fields in the instruction (that
518
+ // are not covered in the visitor) can be serialized.
519
+ if (!canSerializeFieldsByInstructionKind (&inst, canSerializeFlags, maxDepth))
440
520
return false ;
441
- }
442
521
}
443
522
}
523
+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
524
+
444
525
canSerializeFlags[function] = true ;
445
526
return true ;
446
527
}
447
528
448
- // / Returns true if \p inst can be serialized.
529
+ // / Returns true if \p inst can be serialized by checking its fields per instruction kind .
449
530
// /
450
531
// / If \p inst is a function_ref, recursively visits the referenced function.
451
- bool CrossModuleOptimization::canSerializeInstruction (
532
+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
452
533
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
534
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
464
535
SILFunction *callee = FRI->getReferencedFunctionOrNull ();
465
536
if (!callee)
@@ -551,6 +622,45 @@ bool CrossModuleOptimization::canSerializeInstruction(
551
622
return true ;
552
623
}
553
624
625
+ bool CrossModuleOptimization::canSerializeType (SILType type) {
626
+ return canSerializeType (type.getASTType ());
627
+ }
628
+
629
+ bool CrossModuleOptimization::canSerializeType (CanType type) {
630
+ auto iter = canTypesChecked.find (type);
631
+ if (iter != canTypesChecked.end ())
632
+ return iter->getSecond ();
633
+
634
+ bool success = type.findIf (
635
+ [this ](Type rawSubType) {
636
+ CanType subType = rawSubType->getCanonicalType ();
637
+ if (auto nominal = subType->getNominalOrBoundGenericNominal ()) {
638
+ return canSerializeDecl (nominal);
639
+ }
640
+ // If reached here, the type is a Builtin type or similar,
641
+ // e.g. Builtin.Int64, Builtin.Word, Builtin.NativeObject, etc.
642
+ return true ;
643
+ });
644
+
645
+ canTypesChecked[type] = success;
646
+ return success;
647
+ }
648
+
649
+ bool CrossModuleOptimization::canSerializeDecl (NominalTypeDecl *decl) {
650
+ assert (decl && " Decl should not be null when checking if it can be serialized" );
651
+
652
+ // In conservative mode we don't want to change the access level of types.
653
+ if (conservative && decl->getEffectiveAccess () < AccessLevel::Package) {
654
+ return false ;
655
+ }
656
+ // Exclude types which are defined in an @_implementationOnly imported
657
+ // module. Such modules are not transitively available.
658
+ if (!canUseFromInline (decl)) {
659
+ return false ;
660
+ }
661
+ return true ;
662
+ }
663
+
554
664
bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
555
665
// Check for referenced functions in the initializer.
556
666
for (const SILInstruction &initInst : *global) {
@@ -572,32 +682,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
572
682
return true ;
573
683
}
574
684
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
685
// / Returns true if the function in \p funcCtxt could be linked statically to
602
686
// / this module.
603
687
static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -730,7 +814,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
730
814
}
731
815
function->setSerializedKind (getRightSerializedKind (M));
732
816
733
- InstructionVisitor visitor (*function, *this );
817
+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
734
818
for (SILBasicBlock &block : *function) {
735
819
for (SILInstruction &inst : block) {
736
820
visitor.getBuilder ().setInsertionPoint (&inst);
@@ -929,21 +1013,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
929
1013
});
930
1014
}
931
1015
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
1016
class CrossModuleOptimizationPass : public SILModuleTransform {
948
1017
void run () override {
949
1018
auto &M = *getModule ();
0 commit comments