Skip to content

Commit 32fda75

Browse files
committed
Serialize SIL witness-tables and v-tables and their entries if package cmo is
enabled. If two modules are in the same package and package cmo is enabled, v-table or witness-table calls should not be generated at the use site in the client module. If package cmo is enabled, allow serializing a definition with shared linkage so a function referencing such def can be serialized. Also modified to allow serializing witness thunks for conformance with package linkage. Added a step after serializing SIL functions to loop over v-tables and witness-tables and serialize entries and tables if serializable. rdar://124632670
1 parent 576a4ba commit 32fda75

File tree

7 files changed

+783
-20
lines changed

7 files changed

+783
-20
lines changed

include/swift/SIL/SILLinkage.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,12 +394,16 @@ inline SILLinkage effectiveLinkageForClassMember(SILLinkage linkage,
394394
// protocol requirement, even if the extended type is not public;
395395
// then SILGen gives the member private linkage, ignoring the more
396396
// visible access level it was given in the AST.
397-
inline bool
398-
fixmeWitnessHasLinkageThatNeedsToBePublic(SILDeclRef witness) {
397+
//
398+
// Despite the FIXME above, this is still used to determine the linkage
399+
// for witness thunks. In case package serialization is enabled, we need
400+
// to take the package linkage into account so we can set a proper final
401+
// linkage to the thunks in the witness table with a package linkage.
402+
inline bool fixmeWitnessHasLinkageThatNeedsToBePublic(SILDeclRef witness,
403+
bool isPackageVisible) {
399404
auto witnessLinkage = witness.getLinkage(ForDefinition);
400-
return !hasPublicVisibility(witnessLinkage)
401-
&& (!hasSharedVisibility(witnessLinkage)
402-
|| !witness.isSerialized());
405+
return !hasPublicOrPackageVisibility(witnessLinkage, isPackageVisible) &&
406+
(!hasSharedVisibility(witnessLinkage) || !witness.isSerialized());
403407
}
404408

405409
} // end swift namespace

lib/SIL/IR/SILSymbolVisitor.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
289289
return;
290290

291291
if (!isa<SelfProtocolConformance>(rootConformance) &&
292-
!fixmeWitnessHasLinkageThatNeedsToBePublic(witnessRef)) {
292+
!fixmeWitnessHasLinkageThatNeedsToBePublic(
293+
witnessRef,
294+
witnessRef.getASTContext().SILOpts.EnableSerializePackage)) {
293295
return;
294296
}
295297
}

lib/SIL/IR/SILWitnessTable.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,19 @@ bool SILWitnessTable::conformanceIsSerialized(
171171
if (normalConformance && normalConformance->isResilient())
172172
return false;
173173

174-
if (conformance->getProtocol()->getEffectiveAccess() < AccessLevel::Public)
174+
// Allow serializing conformance with package access level if
175+
// package serialization is enabled.
176+
auto optInPackage = conformance->getDeclContext()
177+
->getASTContext()
178+
.SILOpts.EnableSerializePackage;
179+
auto accessLevelToCheck =
180+
optInPackage ? AccessLevel::Package : AccessLevel::Public;
181+
182+
if (conformance->getProtocol()->getEffectiveAccess() < accessLevelToCheck)
175183
return false;
176184

177185
auto *nominal = conformance->getDeclContext()->getSelfNominalTypeDecl();
178-
return nominal->getEffectiveAccess() >= AccessLevel::Public;
186+
return nominal->getEffectiveAccess() >= accessLevelToCheck;
179187
}
180188

181189
bool SILWitnessTable::enumerateWitnessTableConditionalConformances(

lib/SILGen/SILGenType.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,12 @@ class SILGenConformance : public SILGenWitnessTable<SILGenConformance> {
635635
auto witnessLinkage = witnessRef.getLinkage(ForDefinition);
636636
auto witnessSerialized = Serialized;
637637
if (witnessSerialized &&
638-
fixmeWitnessHasLinkageThatNeedsToBePublic(witnessRef)) {
638+
// If package optimization is enabled, this is false;
639+
// witness thunk should get a `shared` linkage in the
640+
// else block below.
641+
fixmeWitnessHasLinkageThatNeedsToBePublic(
642+
witnessRef,
643+
witnessRef.getASTContext().SILOpts.EnableSerializePackage)) {
639644
witnessLinkage = SILLinkage::Public;
640645
witnessSerialized = IsNotSerialized;
641646
} else {

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class CrossModuleOptimization {
7171
: M(M), conservative(conservative), everything(everything) { }
7272

7373
void serializeFunctionsInModule();
74+
void serializeTablesInModule();
7475

7576
private:
7677
bool canSerializeFunction(SILFunction *function,
@@ -172,6 +173,14 @@ static bool isVisible(AccessLevel accessLevel, SILOptions options) {
172173
return accessLevel == AccessLevel::Public;
173174
}
174175

176+
static bool hasSerializableVisibility(SILLinkage linkage, bool isDefinition,
177+
SILOptions options) {
178+
if (options.EnableSerializePackage)
179+
return (hasPublicOrPackageVisibility(linkage, /*includePackage*/ true) ||
180+
(hasSharedVisibility(linkage) && isDefinition));
181+
return hasPublicVisibility(linkage);
182+
}
183+
175184
/// Select functions in the module which should be serialized.
176185
void CrossModuleOptimization::serializeFunctionsInModule() {
177186

@@ -188,6 +197,40 @@ void CrossModuleOptimization::serializeFunctionsInModule() {
188197
}
189198
}
190199

200+
void CrossModuleOptimization::serializeTablesInModule() {
201+
if (!M.getOptions().EnableSerializePackage)
202+
return;
203+
204+
for (const auto &vt : M.getVTables()) {
205+
if (!vt->isSerialized() &&
206+
vt->getClass()->getEffectiveAccess() >= AccessLevel::Package) {
207+
vt->setSerialized(IsSerialized);
208+
}
209+
}
210+
211+
for (auto &wt : M.getWitnessTables()) {
212+
if (!wt.isSerialized() && hasPublicOrPackageVisibility(
213+
wt.getLinkage(), /*includePackage*/ true)) {
214+
for (auto &entry : wt.getEntries()) {
215+
// Entries in witness table with package linkage are not serialized
216+
// (by default only entries in public witness table are; \see
217+
// SILWitnessTable::conformanceIsSerialized), so serialize them here
218+
// if package-cmo is enabled (otherwise nil).
219+
if (entry.getKind() == SILWitnessTable::Method &&
220+
!entry.getMethodWitness().Witness->isSerialized() &&
221+
hasSerializableVisibility(
222+
entry.getMethodWitness().Witness->getLinkage(),
223+
entry.getMethodWitness().Witness->isDefinition(),
224+
M.getOptions())) {
225+
entry.getMethodWitness().Witness->setSerialized(IsSerialized);
226+
}
227+
}
228+
// Then serialize the witness table itself.
229+
wt.setSerialized(IsSerialized);
230+
}
231+
}
232+
}
233+
191234
/// Recursively walk the call graph and select functions to be serialized.
192235
///
193236
/// The results are stored in \p canSerializeFlags and the result for \p
@@ -280,9 +323,16 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
280323
// public functions, because that can increase code size. E.g. if the
281324
// function is completely inlined afterwards.
282325
// Also, when emitting TBD files, we cannot introduce a new public symbol.
283-
if ((conservative || M.getOptions().emitTBD) &&
284-
!hasPublicOrPackageVisibility(callee->getLinkage(), M.getOptions().EnableSerializePackage)) {
285-
return false;
326+
if (conservative || M.getOptions().emitTBD) {
327+
// In package-cmo, allow serializing this inst if its referenced function
328+
// is a definition with a shared linkage or has a package/public linkage.
329+
// For example, `public func foo() { print("") }` is a function with
330+
// a public linkage which references `print`; the definition of `print`
331+
// should have a shared linkage and be allowed to be serialized, so that
332+
// `foo` could be serialized.
333+
if (!hasSerializableVisibility(callee->getLinkage(),
334+
callee->isDefinition(), M.getOptions()))
335+
return false;
286336
}
287337

288338
// In some project configurations imported C functions are not necessarily
@@ -306,7 +356,8 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
306356
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
307357
SILGlobalVariable *global = GAI->getReferencedGlobal();
308358
if ((conservative || M.getOptions().emitTBD) &&
309-
!hasPublicOrPackageVisibility(global->getLinkage(), M.getOptions().EnableSerializePackage)) {
359+
!hasSerializableVisibility(global->getLinkage(), global->isDefinition(),
360+
M.getOptions())) {
310361
return false;
311362
}
312363

@@ -354,7 +405,9 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
354405
// function is completely inlined afterwards.
355406
// Also, when emitting TBD files, we cannot introduce a new public symbol.
356407
if ((conservative || M.getOptions().emitTBD) &&
357-
!hasPublicOrPackageVisibility(referencedFunc->getLinkage(), M.getOptions().EnableSerializePackage)) {
408+
!hasSerializableVisibility(referencedFunc->getLinkage(),
409+
referencedFunc->isDefinition(),
410+
M.getOptions())) {
358411
return false;
359412
}
360413

@@ -374,7 +427,7 @@ bool CrossModuleOptimization::canSerializeType(SILType type) {
374427
[this](Type rawSubType) {
375428
CanType subType = rawSubType->getCanonicalType();
376429
if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal()) {
377-
430+
378431
if (conservative && subNT->getEffectiveAccess() < AccessLevel::Package) {
379432
return true;
380433
}
@@ -482,6 +535,12 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
482535

483536
if (function->getLinkage() == SILLinkage::Shared)
484537
return true;
538+
} else if (hasSerializableVisibility(function->getLinkage(),
539+
function->isDefinition(),
540+
function->getModule().getOptions())) {
541+
// If package-cmo is enabled, we should not limit the amount of inlining
542+
// as checked with CMOFunctionSizeLimit below.
543+
return true;
485544
}
486545

487546
// Also serialize "small" non-generic functions.
@@ -503,7 +562,7 @@ void CrossModuleOptimization::serializeFunction(SILFunction *function,
503562
const FunctionFlags &canSerializeFlags) {
504563
if (function->isSerialized())
505564
return;
506-
565+
507566
if (!canSerializeFlags.lookup(function))
508567
return;
509568

@@ -699,7 +758,10 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
699758
void run() override {
700759

701760
auto &M = *getModule();
702-
if (M.getSwiftModule()->isResilient())
761+
if (M.getSwiftModule()->isResilient() &&
762+
// FIXME: need to properly lower type when this is enabled
763+
// else leads to assert fail.
764+
!M.getOptions().EnableSerializePackage)
703765
return;
704766
if (!M.isWholeModule())
705767
return;
@@ -727,6 +789,7 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
727789

728790
CrossModuleOptimization CMO(M, conservative, everything);
729791
CMO.serializeFunctionsInModule();
792+
CMO.serializeTablesInModule();
730793
}
731794
};
732795

0 commit comments

Comments
 (0)