@@ -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
@@ -118,11 +122,10 @@ class CrossModuleOptimization {
118122 void makeDeclUsableFromInline (ValueDecl *decl);
119123
120124 void makeTypeUsableFromInline (CanType type);
121-
122- void makeSubstUsableFromInline (const SubstitutionMap &substs);
123125};
124126
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.
126129// /
127130// / We use the SILCloner for visiting types, though it sucks that we allocate
128131// / instructions just to delete them immediately. But it's better than to
@@ -134,12 +137,22 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
134137 friend class SILInstructionVisitor <InstructionVisitor>;
135138 friend class CrossModuleOptimization ;
136139
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+
137148private:
138149 CrossModuleOptimization &CMS;
150+ VisitMode mode;
151+ bool isInstSerializable = true ;
139152
140153public:
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) {}
143156
144157 SILType remapType (SILType Ty) {
145158 if (Ty.hasLocalArchetype ()) {
@@ -149,7 +162,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
149162 SubstFlags::SubstituteLocalArchetypes);
150163 }
151164
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+ }
153174 return Ty;
154175 }
155176
@@ -160,7 +181,15 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
160181 SubstFlags::SubstituteLocalArchetypes)->getCanonicalType ();
161182 }
162183
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+ }
164193 return Ty;
165194 }
166195
@@ -171,7 +200,33 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
171200 SubstFlags::SubstituteLocalArchetypes);
172201 }
173202
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+ }
175230 return Subs;
176231 }
177232
@@ -180,9 +235,36 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
180235 Cloned->eraseFromParent ();
181236 }
182237
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+ }
184262
185263 SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
264+
265+ bool canSerializeTypesInInst (SILInstruction *inst) {
266+ return isInstSerializable;
267+ }
186268};
187269
188270static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -456,32 +538,36 @@ bool CrossModuleOptimization::canSerializeFunction(
456538 }
457539
458540 // Check if any instruction prevents serializing the function.
541+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
542+
459543 for (SILBasicBlock &block : *function) {
460544 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 ();
462556 return false ;
463557 }
464558 }
465559 }
560+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
561+
466562 canSerializeFlags[function] = true ;
467563 return true ;
468564}
469565
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 .
471567// /
472568// / If \p inst is a function_ref, recursively visits the referenced function.
473- bool CrossModuleOptimization::canSerializeInstruction (
569+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
474570 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-
485571 if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
486572 SILFunction *callee = FRI->getReferencedFunctionOrNull ();
487573 if (!callee)
@@ -573,6 +659,46 @@ bool CrossModuleOptimization::canSerializeInstruction(
573659 return true ;
574660}
575661
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+
576702bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
577703 // Check for referenced functions in the initializer.
578704 for (const SILInstruction &initInst : *global) {
@@ -594,32 +720,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
594720 return true ;
595721}
596722
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-
623723// / Returns true if the function in \p funcCtxt could be linked statically to
624724// / this module.
625725static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -713,7 +813,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
713813 }
714814 function->setSerializedKind (getRightSerializedKind (M));
715815
716- InstructionVisitor visitor (*function, *this );
816+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
717817 for (SILBasicBlock &block : *function) {
718818 for (SILInstruction &inst : block) {
719819 visitor.getBuilder ().setInsertionPoint (&inst);
@@ -912,21 +1012,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
9121012 });
9131013}
9141014
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-
9301015class CrossModuleOptimizationPass : public SILModuleTransform {
9311016 void run () override {
9321017 auto &M = *getModule ();
0 commit comments