Skip to content

Commit 71df54c

Browse files
committed
CrossModuleSerializationSetup: don't make shared functions usableFromInline
To avoid duplicate-symbol linker errors. Instead make them alwaysEmitIntoClient. But only do that for thunks to limit the code size impact. Anyway, it's only important for thunks because thunks are generated at SILGen, i.e. before CrossModuleSerializationSetup. Other shared functions, e.g. specializations are created after CrossModuleSerializationSetup, so we don't have to deal with them.
1 parent f03956b commit 71df54c

File tree

1 file changed

+79
-37
lines changed

1 file changed

+79
-37
lines changed

lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ class CrossModuleSerializationSetup {
4949
}
5050
}
5151

52-
bool setUpForSerialization(SILFunction *F);
52+
bool canUseFromInline(SILFunction *F, bool lookIntoThunks);
53+
54+
bool canSerialize(SILFunction *F, bool lookIntoThunks);
55+
56+
void setUpForSerialization(SILFunction *F);
5357

5458
void prepareInstructionForSerialization(SILInstruction *inst);
5559

@@ -67,27 +71,6 @@ class CrossModuleSerializationSetup {
6771
void scanModule();
6872
};
6973

70-
static bool canUseFromInline(SILFunction *F) {
71-
if (!F)
72-
return false;
73-
74-
switch (F->getLinkage()) {
75-
case SILLinkage::PublicNonABI:
76-
case SILLinkage::Shared:
77-
return F->isSerialized() != IsNotSerialized;
78-
case SILLinkage::Public:
79-
case SILLinkage::Hidden:
80-
case SILLinkage::Private:
81-
case SILLinkage::PublicExternal:
82-
case SILLinkage::SharedExternal:
83-
case SILLinkage::PrivateExternal:
84-
case SILLinkage::HiddenExternal:
85-
break;
86-
}
87-
88-
return true;
89-
}
90-
9174
/// Visitor for making used types of an intruction inlinable.
9275
///
9376
/// We use the SILCloner for visiting types, though it sucks that we allocate
@@ -253,11 +236,27 @@ prepareInstructionForSerialization(SILInstruction *inst) {
253236
void CrossModuleSerializationSetup::handleReferencedFunction(SILFunction *func) {
254237
if (!func->isDefinition() || func->isAvailableExternally())
255238
return;
239+
if (func->getLinkage() == SILLinkage::Shared) {
240+
assert(func->isThunk() != IsNotThunk &&
241+
"only thunks are accepted to have shared linkage");
242+
assert(canSerialize(func, /*lookIntoThunks*/ false) &&
243+
"we should already have checked that the thunk is serializable");
244+
245+
if (func->isSerialized() == IsSerialized)
246+
return;
247+
248+
// We cannot make shared functions "usableFromInline", i.e. make them Public
249+
// because this could result in duplicate-symbol errors. Instead we make
250+
// them "@alwaysEmitIntoClient"
251+
setUpForSerialization(func);
252+
return;
253+
}
256254
if (shouldSerialize(func)) {
257255
addToWorklistIfNotHandled(func);
258-
} else {
259-
makeFunctionUsableFromInline(func);
256+
return;
260257
}
258+
makeFunctionUsableFromInline(func);
259+
return;
261260
}
262261

263262
void CrossModuleSerializationSetup::handleReferencedMethod(SILDeclRef method) {
@@ -268,23 +267,24 @@ void CrossModuleSerializationSetup::handleReferencedMethod(SILDeclRef method) {
268267
M.addExternallyVisibleDecl(getBaseMethod(methodDecl));
269268
}
270269

271-
/// Setup the function \p param F for serialization and put callees onto the
272-
/// worklist for further processing.
270+
/// Check if the function \p F can be serialized.
273271
///
274-
/// Returns false in case this is not possible for some reason.
275-
bool CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) {
272+
/// If \p lookIntoThunks is true, function_ref instructions of shared
273+
/// thunks are also accepted.
274+
bool CrossModuleSerializationSetup::canSerialize(SILFunction *F,
275+
bool lookIntoThunks) {
276276
// First step: check if serializing F is even possible.
277277
for (SILBasicBlock &block : *F) {
278278
for (SILInstruction &inst : block) {
279279
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(&inst)) {
280280
SILFunction *callee = FRI->getReferencedFunctionOrNull();
281-
if (!canUseFromInline(callee))
281+
if (!canUseFromInline(callee, lookIntoThunks))
282282
return false;
283283
} else if (auto *KPI = dyn_cast<KeyPathInst>(&inst)) {
284284
bool canUse = true;
285285
KPI->getPattern()->visitReferencedFunctionsAndMethods(
286286
[&](SILFunction *func) {
287-
if (!canUseFromInline(func))
287+
if (!canUseFromInline(func, lookIntoThunks))
288288
canUse = false;
289289
},
290290
[](SILDeclRef method) { });
@@ -293,6 +293,45 @@ bool CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) {
293293
}
294294
}
295295
}
296+
return true;
297+
}
298+
299+
/// Returns true if the function \p func can be used from a serialized function.
300+
///
301+
/// If \p lookIntoThunks is true, serializable shared thunks are also accepted.
302+
bool CrossModuleSerializationSetup::canUseFromInline(SILFunction *func,
303+
bool lookIntoThunks) {
304+
if (!func)
305+
return false;
306+
307+
switch (func->getLinkage()) {
308+
case SILLinkage::PublicNonABI:
309+
return func->isSerialized() != IsNotSerialized;
310+
case SILLinkage::Shared:
311+
if (func->isThunk() != IsNotThunk && lookIntoThunks &&
312+
// Don't recursively lookIntoThunks to avoid infinite loops.
313+
canSerialize(func, /*lookIntoThunks*/ false)) {
314+
return true;
315+
}
316+
return false;
317+
case SILLinkage::Public:
318+
case SILLinkage::Hidden:
319+
case SILLinkage::Private:
320+
case SILLinkage::PublicExternal:
321+
case SILLinkage::SharedExternal:
322+
case SILLinkage::PrivateExternal:
323+
case SILLinkage::HiddenExternal:
324+
break;
325+
}
326+
return true;
327+
}
328+
329+
/// Setup the function \p param F for serialization and put callees onto the
330+
/// worklist for further processing.
331+
///
332+
/// Returns false in case this is not possible for some reason.
333+
void CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) {
334+
assert(F->isSerialized() != IsSerialized);
296335

297336
// Second step: go through all instructions and prepare them for
298337
// for serialization.
@@ -301,7 +340,14 @@ bool CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) {
301340
prepareInstructionForSerialization(&inst);
302341
}
303342
}
304-
return true;
343+
F->setSerialized(IsSerialized);
344+
345+
// As a code size optimization, make serialized functions
346+
// @alwaysEmitIntoClient.
347+
// Also, for shared thunks it's required to make them @alwaysEmitIntoClient.
348+
// SILLinkage::Public would not work for shared functions, because it could
349+
// result in duplicate-symbol linker errors.
350+
F->setLinkage(SILLinkage::PublicNonABI);
305351
}
306352

307353
/// Select functions in the module which should be serialized.
@@ -319,12 +365,8 @@ void CrossModuleSerializationSetup::scanModule() {
319365
// Decide whether we want to serialize the function.
320366
if (shouldSerialize(F)) {
321367
// Try to serialize.
322-
if (setUpForSerialization(F)) {
323-
F->setSerialized(IsSerialized);
324-
325-
// As a code size optimization, make serialized functions
326-
// @alwaysEmitIntoClient.
327-
F->setLinkage(SILLinkage::PublicNonABI);
368+
if (canSerialize(F, /*lookIntoThunks*/ true)) {
369+
setUpForSerialization(F);
328370
} else {
329371
// If for some reason the function cannot be serialized, we mark it as
330372
// usable-from-inline.

0 commit comments

Comments
 (0)