@@ -53,7 +53,7 @@ namespace {
5353class CrossModuleOptimization {
5454 friend class InstructionVisitor ;
5555
56- llvm::DenseMap<SILType , bool > typesChecked ;
56+ llvm::DenseMap<CanType , bool > canTypesChecked ;
5757 llvm::SmallPtrSet<TypeBase *, 16 > typesHandled;
5858
5959 SILModule &M;
@@ -90,14 +90,18 @@ class CrossModuleOptimization {
9090 void trySerializeFunctions (ArrayRef<SILFunction *> functions);
9191
9292 bool canSerializeFunction (SILFunction *function,
93- FunctionFlags &canSerializeFlags, int maxDepth);
93+ FunctionFlags &canSerializeFlags,
94+ int maxDepth);
9495
95- bool canSerializeInstruction (SILInstruction *inst,
96- FunctionFlags &canSerializeFlags, int maxDepth);
96+ bool canSerializeFieldsByInstructionKind (SILInstruction *inst,
97+ FunctionFlags &canSerializeFlags,
98+ int maxDepth);
9799
98100 bool canSerializeGlobal (SILGlobalVariable *global);
99101
100102 bool canSerializeType (SILType type);
103+ bool canSerializeType (CanType type);
104+ bool canSerializeDecl (NominalTypeDecl *decl);
101105
102106 bool canUseFromInline (DeclContext *declCtxt);
103107
@@ -120,11 +124,10 @@ class CrossModuleOptimization {
120124 void makeDeclUsableFromInline (ValueDecl *decl);
121125
122126 void makeTypeUsableFromInline (CanType type);
123-
124- void makeSubstUsableFromInline (const SubstitutionMap &substs);
125127};
126128
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.
128131// /
129132// / We use the SILCloner for visiting types, though it sucks that we allocate
130133// / instructions just to delete them immediately. But it's better than to
@@ -136,12 +139,22 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
136139 friend class SILInstructionVisitor <InstructionVisitor>;
137140 friend class CrossModuleOptimization ;
138141
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+
139150private:
140151 CrossModuleOptimization &CMS;
152+ VisitMode mode;
153+ bool isInstSerializable = true ;
141154
142155public:
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) {}
145158
146159 SILType remapType (SILType Ty) {
147160 if (Ty.hasLocalArchetype ()) {
@@ -151,7 +164,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
151164 SubstFlags::SubstituteLocalArchetypes);
152165 }
153166
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+ }
155176 return Ty;
156177 }
157178
@@ -162,7 +183,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
162183 SubstFlags::SubstituteLocalArchetypes)->getCanonicalType ();
163184 }
164185
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+ }
166195 return Ty;
167196 }
168197
@@ -173,7 +202,32 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
173202 SubstFlags::SubstituteLocalArchetypes);
174203 }
175204
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+ }
177231 return Subs;
178232 }
179233
@@ -185,6 +239,10 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
185239 SILValue getMappedValue (SILValue Value) { return Value; }
186240
187241 SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
242+
243+ bool canSerializeTypesInInst (SILInstruction *inst) {
244+ return isInstSerializable;
245+ }
188246};
189247
190248static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -434,32 +492,46 @@ bool CrossModuleOptimization::canSerializeFunction(
434492 return false ;
435493
436494 // Check if any instruction prevents serializing the function.
495+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
437496 for (SILBasicBlock &block : *function) {
438497 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 ();
440520 return false ;
441521 }
442522 }
443523 }
524+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
525+
444526 canSerializeFlags[function] = true ;
445527 return true ;
446528}
447529
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 .
449531// /
450532// / If \p inst is a function_ref, recursively visits the referenced function.
451- bool CrossModuleOptimization::canSerializeInstruction (
533+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
452534 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-
463535 if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
464536 SILFunction *callee = FRI->getReferencedFunctionOrNull ();
465537 if (!callee)
@@ -551,6 +623,45 @@ bool CrossModuleOptimization::canSerializeInstruction(
551623 return true ;
552624}
553625
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+
554665bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
555666 // Check for referenced functions in the initializer.
556667 for (const SILInstruction &initInst : *global) {
@@ -572,32 +683,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
572683 return true ;
573684}
574685
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-
601686// / Returns true if the function in \p funcCtxt could be linked statically to
602687// / this module.
603688static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -730,7 +815,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
730815 }
731816 function->setSerializedKind (getRightSerializedKind (M));
732817
733- InstructionVisitor visitor (*function, *this );
818+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
734819 for (SILBasicBlock &block : *function) {
735820 for (SILInstruction &inst : block) {
736821 visitor.getBuilder ().setInsertionPoint (&inst);
@@ -929,21 +1014,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
9291014 });
9301015}
9311016
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-
9471017class CrossModuleOptimizationPass : public SILModuleTransform {
9481018 void run () override {
9491019 auto &M = *getModule ();
0 commit comments