@@ -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,32 @@ 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+ 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+ }
175229 return Subs;
176230 }
177231
@@ -180,9 +234,36 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
180234 Cloned->eraseFromParent ();
181235 }
182236
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+ }
184261
185262 SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
263+
264+ bool canSerializeTypesInInst (SILInstruction *inst) {
265+ return isInstSerializable;
266+ }
186267};
187268
188269static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -456,32 +537,36 @@ bool CrossModuleOptimization::canSerializeFunction(
456537 }
457538
458539 // Check if any instruction prevents serializing the function.
540+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
541+
459542 for (SILBasicBlock &block : *function) {
460543 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 ();
462555 return false ;
463556 }
464557 }
465558 }
559+ M.reclaimUnresolvedLocalArchetypeDefinitions ();
560+
466561 canSerializeFlags[function] = true ;
467562 return true ;
468563}
469564
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 .
471566// /
472567// / If \p inst is a function_ref, recursively visits the referenced function.
473- bool CrossModuleOptimization::canSerializeInstruction (
568+ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
474569 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-
485570 if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
486571 SILFunction *callee = FRI->getReferencedFunctionOrNull ();
487572 if (!callee)
@@ -573,6 +658,45 @@ bool CrossModuleOptimization::canSerializeInstruction(
573658 return true ;
574659}
575660
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+
576700bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
577701 // Check for referenced functions in the initializer.
578702 for (const SILInstruction &initInst : *global) {
@@ -594,32 +718,6 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
594718 return true ;
595719}
596720
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-
623721// / Returns true if the function in \p funcCtxt could be linked statically to
624722// / this module.
625723static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -713,7 +811,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
713811 }
714812 function->setSerializedKind (getRightSerializedKind (M));
715813
716- InstructionVisitor visitor (*function, *this );
814+ InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
717815 for (SILBasicBlock &block : *function) {
718816 for (SILInstruction &inst : block) {
719817 visitor.getBuilder ().setInsertionPoint (&inst);
@@ -912,21 +1010,6 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
9121010 });
9131011}
9141012
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-
9301013class CrossModuleOptimizationPass : public SILModuleTransform {
9311014 void run () override {
9321015 auto &M = *getModule ();
0 commit comments