Skip to content

Commit c70c521

Browse files
authored
Merge pull request swiftlang#74070 from apple/elsh/pcmo-fixes
[SIL][PackageCMO] Fix dispatch thunk linker error and update table serialization
2 parents 23915e8 + af9e5e6 commit c70c521

8 files changed

+754
-423
lines changed

lib/SIL/IR/Linker.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ void SILLinkerVisitor::maybeAddFunctionToWorklist(
105105
F->hasValidLinkageForFragileRef(callerSerializedKind) ||
106106
hasSharedVisibility(linkage) || F->isExternForwardDeclaration()) &&
107107
"called function has wrong linkage for serialized function");
108-
109108
if (!F->isExternalDeclaration()) {
110109
// The function is already in the module, so no need to de-serialized it.
111110
// But check if we need to set the IsSerialized flag.

lib/SIL/IR/SILWitnessTable.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,7 @@ SerializedKind_t SILWitnessTable::conformanceSerializedKind(
184184

185185
auto *nominal = conformance->getDeclContext()->getSelfNominalTypeDecl();
186186
if (nominal->getEffectiveAccess() >= accessLevelToCheck)
187-
return optInPackage &&
188-
conformance->getDeclContext()->getParentModule()->isResilient() ?
189-
IsSerializedForPackage : IsSerialized;
187+
return IsSerialized;
190188

191189
return IsNotSerialized;
192190
}

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 112 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,14 @@ class CrossModuleOptimization {
6767
bool everything;
6868

6969
typedef llvm::DenseMap<SILFunction *, bool> FunctionFlags;
70+
FunctionFlags canSerializeFlags;
7071

7172
public:
7273
CrossModuleOptimization(SILModule &M, bool conservative, bool everything)
7374
: M(M), conservative(conservative), everything(everything) { }
7475

75-
void serializeFunctionsInModule(ArrayRef<SILFunction *> functions);
76+
void trySerializeFunctions(ArrayRef<SILFunction *> functions);
77+
void serializeFunctionsInModule(SILPassManager *manager);
7678
void serializeTablesInModule();
7779

7880
private:
@@ -187,6 +189,10 @@ static bool isPackageOrPublic(AccessLevel accessLevel, SILOptions options) {
187189
return accessLevel == AccessLevel::Public;
188190
}
189191

192+
static bool isPackageCMOEnabled(ModuleDecl *mod) {
193+
return mod->isResilient() && mod->serializePackageEnabled();
194+
}
195+
190196
/// Checks wither this function is [serialized_for_package] due to Package CMO
191197
/// or [serialized] with non-package CMO. The [serialized_for_package] attribute
192198
/// is used to indicate that a function is serialized because of Package CMO, which
@@ -201,22 +207,19 @@ static bool isSerializedWithRightKind(const SILModule &mod,
201207
SILFunction *f) {
202208
// If Package CMO is enabled in resilient mode, return
203209
// true if the function is [serialized] due to @inlinable
204-
// (or similar) or [serialized_for_pkg] due to this
210+
// (or similar) or [serialized_for_pkg] due to this
205211
// optimization.
206-
return mod.getSwiftModule()->serializePackageEnabled() &&
207-
mod.getSwiftModule()->isResilient() ?
208-
f->isAnySerialized() : f->isSerialized();
212+
return isPackageCMOEnabled(mod.getSwiftModule()) ? f->isAnySerialized()
213+
: f->isSerialized();
209214
}
210215
static bool isSerializedWithRightKind(const SILModule &mod,
211216
SILGlobalVariable *g) {
212-
return mod.getSwiftModule()->serializePackageEnabled() &&
213-
mod.getSwiftModule()->isResilient() ?
214-
g->isAnySerialized() : g->isSerialized();
217+
return isPackageCMOEnabled(mod.getSwiftModule()) ? g->isAnySerialized()
218+
: g->isSerialized();
215219
}
216220
static SerializedKind_t getRightSerializedKind(const SILModule &mod) {
217-
return mod.getSwiftModule()->serializePackageEnabled() &&
218-
mod.getSwiftModule()->isResilient() ?
219-
IsSerializedForPackage : IsSerialized;
221+
return isPackageCMOEnabled(mod.getSwiftModule()) ? IsSerializedForPackage
222+
: IsSerialized;
220223
}
221224

222225
static bool isSerializeCandidate(SILFunction *F, SILOptions options) {
@@ -255,12 +258,8 @@ static bool isReferenceSerializeCandidate(SILGlobalVariable *G,
255258
}
256259

257260
/// Select functions in the module which should be serialized.
258-
void CrossModuleOptimization::serializeFunctionsInModule(
261+
void CrossModuleOptimization::trySerializeFunctions(
259262
ArrayRef<SILFunction *> functions) {
260-
FunctionFlags canSerializeFlags;
261-
262-
// The passed functions are already ordered bottom-up so the most
263-
// nested referenced function is checked first.
264263
for (SILFunction *F : functions) {
265264
if (isSerializeCandidate(F, M.getOptions()) || everything) {
266265
if (canSerializeFunction(F, canSerializeFlags, /*maxDepth*/ 64)) {
@@ -270,31 +269,91 @@ void CrossModuleOptimization::serializeFunctionsInModule(
270269
}
271270
}
272271

272+
void CrossModuleOptimization::serializeFunctionsInModule(SILPassManager *manager) {
273+
// Reorder SIL funtions in the module bottom up so we can serialize
274+
// the most nested referenced functions first and avoid unnecessary
275+
// recursive checks.
276+
BasicCalleeAnalysis *BCA = manager->getAnalysis<BasicCalleeAnalysis>();
277+
BottomUpFunctionOrder BottomUpOrder(M, BCA);
278+
auto bottomUpFunctions = BottomUpOrder.getFunctions();
279+
trySerializeFunctions(bottomUpFunctions);
280+
}
281+
273282
void CrossModuleOptimization::serializeTablesInModule() {
274283
if (!M.getSwiftModule()->serializePackageEnabled())
275284
return;
276285

277286
for (const auto &vt : M.getVTables()) {
278-
if (!vt->isAnySerialized() &&
287+
if (vt->getSerializedKind() != getRightSerializedKind(M) &&
279288
vt->getClass()->getEffectiveAccess() >= AccessLevel::Package) {
280-
vt->setSerializedKind(getRightSerializedKind(M));
289+
// This checks if a vtable entry is not serialized and attempts to
290+
// serialize (and its references) if they have the right visibility.
291+
// This should not be necessary but is added to ensure all applicable
292+
// symbols are serialized. Whether serialized or not is cached so
293+
// this check shouldn't be expensive.
294+
auto unserializedClassMethodRange = llvm::make_filter_range(
295+
vt->getEntries(), [&](const SILVTableEntry &entry) {
296+
return entry.getImplementation()->getSerializedKind() !=
297+
getRightSerializedKind(M);
298+
});
299+
std::vector<SILFunction *> classMethodsToSerialize;
300+
llvm::transform(unserializedClassMethodRange,
301+
std::back_inserter(classMethodsToSerialize),
302+
[&](const SILVTableEntry &entry) {
303+
return entry.getImplementation();
304+
});
305+
trySerializeFunctions(classMethodsToSerialize);
306+
307+
bool containsInternal =
308+
llvm::any_of(vt->getEntries(), [&](const SILVTableEntry &entry) {
309+
// If the entry is internal, vtable should not be serialized.
310+
// However, if the entry is not serialized but has the right
311+
// visibility, it can still be referenced, thus the vtable
312+
// should serialized.
313+
return !entry.getImplementation()->hasValidLinkageForFragileRef(
314+
getRightSerializedKind(M));
315+
});
316+
if (!containsInternal)
317+
vt->setSerializedKind(getRightSerializedKind(M));
281318
}
282319
}
283320

321+
// Witness thunks are not serialized, so serialize them here.
284322
for (auto &wt : M.getWitnessTables()) {
285-
if (!wt.isAnySerialized() &&
323+
if (wt.getSerializedKind() != getRightSerializedKind(M) &&
286324
hasPublicOrPackageVisibility(wt.getLinkage(), /*includePackage*/ true)) {
287-
for (auto &entry : wt.getEntries()) {
288-
// Witness thunks are not serialized, so serialize them here.
289-
if (entry.getKind() == SILWitnessTable::Method &&
290-
!entry.getMethodWitness().Witness->isAnySerialized() &&
291-
isSerializeCandidate(entry.getMethodWitness().Witness,
292-
M.getOptions())) {
293-
entry.getMethodWitness().Witness->setSerializedKind(getRightSerializedKind(M));
294-
}
295-
}
296-
// Then serialize the witness table itself.
297-
wt.setSerializedKind(getRightSerializedKind(M));
325+
// This checks if a wtable entry is not serialized and attempts to
326+
// serialize (and its references) if they have the right visibility.
327+
// This should not be necessary but is added to ensure all applicable
328+
// symbols are serialized. Whether serialized or not is cached so
329+
// this check shouldn't be expensive.
330+
auto unserializedWTMethodRange = llvm::make_filter_range(
331+
wt.getEntries(), [&](const SILWitnessTable::Entry &entry) {
332+
return entry.getKind() == SILWitnessTable::Method &&
333+
entry.getMethodWitness().Witness->getSerializedKind() !=
334+
getRightSerializedKind(M);
335+
});
336+
std::vector<SILFunction *> wtMethodsToSerialize;
337+
llvm::transform(unserializedWTMethodRange,
338+
std::back_inserter(wtMethodsToSerialize),
339+
[&](const SILWitnessTable::Entry &entry) {
340+
return entry.getMethodWitness().Witness;
341+
});
342+
trySerializeFunctions(wtMethodsToSerialize);
343+
344+
bool containsInternal = llvm::any_of(
345+
wt.getEntries(), [&](const SILWitnessTable::Entry &entry) {
346+
// If the entry is internal, wtable should not be serialized.
347+
// However, if the entry is not serialized but has the right
348+
// visibility, it can still be referenced, thus the vtable
349+
// should serialized.
350+
return entry.getKind() == SILWitnessTable::Method &&
351+
!entry.getMethodWitness()
352+
.Witness->hasValidLinkageForFragileRef(
353+
getRightSerializedKind(M));
354+
});
355+
if (!containsInternal)
356+
wt.setSerializedKind(getRightSerializedKind(M));
298357
}
299358
}
300359
}
@@ -440,11 +499,29 @@ bool CrossModuleOptimization::canSerializeInstruction(
440499
[&](SILDeclRef method) {
441500
if (method.isForeign)
442501
canUse = false;
502+
else if (isPackageCMOEnabled(method.getModuleContext())) {
503+
// If the referenced keypath is internal, do not
504+
// serialize.
505+
auto methodScope = method.getDecl()->getFormalAccessScope(
506+
nullptr,
507+
/*treatUsableFromInlineAsPublic*/ true);
508+
canUse = methodScope.isPublicOrPackage();
509+
}
443510
});
444511
return canUse;
445512
}
446513
if (auto *MI = dyn_cast<MethodInst>(inst)) {
447-
return !MI->getMember().isForeign;
514+
// If a class_method or witness_method is internal,
515+
// it can't be serialized.
516+
auto member = MI->getMember();
517+
auto canUse = !member.isForeign;
518+
if (canUse && isPackageCMOEnabled(member.getModuleContext())) {
519+
auto methodScope = member.getDecl()->getFormalAccessScope(
520+
nullptr,
521+
/*treatUsableFromInlineAsPublic*/ true);
522+
canUse = methodScope.isPublicOrPackage();
523+
}
524+
return canUse;
448525
}
449526
if (auto *REAI = dyn_cast<RefElementAddrInst>(inst)) {
450527
// In conservative mode, we don't support class field accesses of non-public
@@ -679,7 +756,9 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
679756
if (canSerializeGlobal(global)) {
680757
serializeGlobal(global);
681758
}
682-
if (!hasPublicOrPackageVisibility(global->getLinkage(), M.getSwiftModule()->serializePackageEnabled())) {
759+
if (!hasPublicOrPackageVisibility(
760+
global->getLinkage(),
761+
M.getSwiftModule()->serializePackageEnabled())) {
683762
global->setLinkage(SILLinkage::Public);
684763
}
685764
return;
@@ -851,14 +930,7 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
851930
}
852931

853932
CrossModuleOptimization CMO(M, conservative, everything);
854-
855-
// Reorder SIL funtions in the module bottom up so we can serialize
856-
// the most nested referenced functions first and avoid unnecessary
857-
// recursive checks.
858-
BasicCalleeAnalysis *BCA = PM->getAnalysis<BasicCalleeAnalysis>();
859-
BottomUpFunctionOrder BottomUpOrder(M, BCA);
860-
auto BottomUpFunctions = BottomUpOrder.getFunctions();
861-
CMO.serializeFunctionsInModule(BottomUpFunctions);
933+
CMO.serializeFunctionsInModule(PM);
862934

863935
// Serialize SIL v-tables and witness-tables if package-cmo is enabled.
864936
CMO.serializeTablesInModule();

0 commit comments

Comments
 (0)