@@ -53,7 +53,7 @@ namespace {
5353class CrossModuleOptimization {
5454 friend class InstructionVisitor ;
5555
56- llvm::DenseMap<CanType , bool > canTypesChecked ;
56+ llvm::DenseMap<SILType , bool > typesChecked ;
5757 llvm::SmallPtrSet<TypeBase *, 16 > typesHandled;
5858
5959 SILModule &M;
@@ -90,18 +90,14 @@ class CrossModuleOptimization {
9090 void trySerializeFunctions (ArrayRef<SILFunction *> functions);
9191
9292 bool canSerializeFunction (SILFunction *function,
93- FunctionFlags &canSerializeFlags,
94- int maxDepth);
93+ FunctionFlags &canSerializeFlags, int maxDepth);
9594
96- bool canSerializeFieldsByInstructionKind (SILInstruction *inst,
97- FunctionFlags &canSerializeFlags,
98- int maxDepth);
95+ bool canSerializeInstruction (SILInstruction *inst,
96+ FunctionFlags &canSerializeFlags, int maxDepth);
9997
10098 bool canSerializeGlobal (SILGlobalVariable *global);
10199
102100 bool canSerializeType (SILType type);
103- bool canSerializeType (CanType type);
104- bool canSerializeDecl (NominalTypeDecl *decl);
105101
106102 bool canUseFromInline (DeclContext *declCtxt);
107103
@@ -122,10 +118,11 @@ class CrossModuleOptimization {
122118 void makeDeclUsableFromInline (ValueDecl *decl);
123119
124120 void makeTypeUsableFromInline (CanType type);
121+
122+ void makeSubstUsableFromInline (const SubstitutionMap &substs);
125123};
126124
127- // / Visitor for detecting if an instruction can be serialized and also making used
128- // / types of an instruction inlinable if so.
125+ // / Visitor for making used types of an instruction inlinable.
129126// /
130127// / We use the SILCloner for visiting types, though it sucks that we allocate
131128// / instructions just to delete them immediately. But it's better than to
@@ -137,22 +134,12 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
137134 friend class SILInstructionVisitor <InstructionVisitor>;
138135 friend class CrossModuleOptimization ;
139136
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-
148137private:
149138 CrossModuleOptimization &CMS;
150- VisitMode mode;
151- bool isInstSerializable = true ;
152139
153140public:
154- InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS, VisitMode visitMode ) :
155- SILCloner (F), CMS(CMS), mode(visitMode) {}
141+ InstructionVisitor (SILFunction &F, CrossModuleOptimization &CMS) :
142+ SILCloner (F), CMS(CMS) {}
156143
157144 SILType remapType (SILType Ty) {
158145 if (Ty.hasLocalArchetype ()) {
@@ -162,15 +149,7 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
162149 SubstFlags::SubstituteLocalArchetypes);
163150 }
164151
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- }
152+ CMS.makeTypeUsableFromInline (Ty.getASTType ());
174153 return Ty;
175154 }
176155
@@ -181,15 +160,7 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
181160 SubstFlags::SubstituteLocalArchetypes)->getCanonicalType ();
182161 }
183162
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- }
163+ CMS.makeTypeUsableFromInline (Ty);
193164 return Ty;
194165 }
195166
@@ -200,32 +171,7 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
200171 SubstFlags::SubstituteLocalArchetypes);
201172 }
202173
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- }
174+ CMS.makeSubstUsableFromInline (Subs);
229175 return Subs;
230176 }
231177
@@ -234,36 +180,9 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
234180 Cloned->eraseFromParent ();
235181 }
236182
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- }
183+ SILValue getMappedValue (SILValue Value) { return Value; }
261184
262185 SILBasicBlock *remapBasicBlock (SILBasicBlock *BB) { return BB; }
263-
264- bool canSerializeTypesInInst (SILInstruction *inst) {
265- return isInstSerializable;
266- }
267186};
268187
269188static bool isPackageCMOEnabled (ModuleDecl *mod) {
@@ -537,36 +456,32 @@ bool CrossModuleOptimization::canSerializeFunction(
537456 }
538457
539458 // Check if any instruction prevents serializing the function.
540- InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::DetectSerializableInst);
541-
542459 for (SILBasicBlock &block : *function) {
543460 for (SILInstruction &inst : block) {
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 ();
461+ if (!canSerializeInstruction (&inst, canSerializeFlags, maxDepth)) {
555462 return false ;
556463 }
557464 }
558465 }
559- M.reclaimUnresolvedLocalArchetypeDefinitions ();
560-
561466 canSerializeFlags[function] = true ;
562467 return true ;
563468}
564469
565- // / Returns true if \p inst can be serialized by checking its fields per instruction kind .
470+ // / Returns true if \p inst can be serialized.
566471// /
567472// / If \p inst is a function_ref, recursively visits the referenced function.
568- bool CrossModuleOptimization::canSerializeFieldsByInstructionKind (
473+ bool CrossModuleOptimization::canSerializeInstruction (
569474 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+
570485 if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
571486 SILFunction *callee = FRI->getReferencedFunctionOrNull ();
572487 if (!callee)
@@ -658,45 +573,6 @@ bool CrossModuleOptimization::canSerializeFieldsByInstructionKind(
658573 return true ;
659574}
660575
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-
700576bool CrossModuleOptimization::canSerializeGlobal (SILGlobalVariable *global) {
701577 // Check for referenced functions in the initializer.
702578 for (const SILInstruction &initInst : *global) {
@@ -718,6 +594,32 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
718594 return true ;
719595}
720596
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+
721623// / Returns true if the function in \p funcCtxt could be linked statically to
722624// / this module.
723625static bool couldBeLinkedStatically (DeclContext *funcCtxt, SILModule &module ) {
@@ -811,7 +713,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
811713 }
812714 function->setSerializedKind (getRightSerializedKind (M));
813715
814- InstructionVisitor visitor (*function, *this , InstructionVisitor::VisitMode::SerializeInst );
716+ InstructionVisitor visitor (*function, *this );
815717 for (SILBasicBlock &block : *function) {
816718 for (SILInstruction &inst : block) {
817719 visitor.getBuilder ().setInsertionPoint (&inst);
@@ -1010,6 +912,21 @@ void CrossModuleOptimization::makeTypeUsableFromInline(CanType type) {
1010912 });
1011913}
1012914
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+
1013930class CrossModuleOptimizationPass : public SILModuleTransform {
1014931 void run () override {
1015932 auto &M = *getModule ();
0 commit comments