Skip to content

Commit 87aac59

Browse files
authored
Merge pull request #28707 from eeckstein/cmo-fixes
Two fixes for cross module optimization
2 parents 4c365a7 + 71df54c commit 87aac59

File tree

6 files changed

+122
-39
lines changed

6 files changed

+122
-39
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-block
311311
"Utility pass. Removes all non-term insts from blocks with unreachable terms")
312312
PASS(SerializeSILPass, "serialize-sil",
313313
"Utility pass. Serializes the current SILModule")
314+
PASS(CMOSerializeSILPass, "cmo-serialize-sil",
315+
"Utility pass. Serializes the current SILModule for cross-module-optimization")
314316
PASS(NonInlinableFunctionSkippingChecker, "check-non-inlinable-function-skipping",
315317
"Utility pass to ensure -experimental-skip-non-inlinable-function-bodies "
316318
"skips everything it should")

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.

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,12 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
413413
P.addOutliner();
414414

415415
P.addCrossModuleSerializationSetup();
416+
417+
// In case of cross-module-optimization, we need to serialize right after
418+
// CrossModuleSerializationSetup. Eventually we want to serialize early
419+
// anyway, but for now keep the SerializeSILPass at the later stage of the
420+
// pipeline in case cross-module-optimization is not enabled.
421+
P.addCMOSerializeSILPass();
416422
}
417423

418424
static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) {

lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@ void updateOpaqueArchetypes(SILFunction &F) {
391391
/// A utility pass to serialize a SILModule at any place inside the optimization
392392
/// pipeline.
393393
class SerializeSILPass : public SILModuleTransform {
394+
395+
bool onlyForCrossModuleOptimization;
396+
394397
/// Removes [serialized] from all functions. This allows for more
395398
/// optimizations and for a better dead function elimination.
396399
void removeSerializedFlagFromAllFunctions(SILModule &M) {
@@ -422,12 +425,19 @@ class SerializeSILPass : public SILModuleTransform {
422425
}
423426

424427
public:
425-
SerializeSILPass() {}
428+
SerializeSILPass(bool onlyForCrossModuleOptimization)
429+
: onlyForCrossModuleOptimization(onlyForCrossModuleOptimization)
430+
{ }
431+
426432
void run() override {
427433
auto &M = *getModule();
428434
// Nothing to do if the module was serialized already.
429435
if (M.isSerialized())
430436
return;
437+
438+
if (onlyForCrossModuleOptimization &&
439+
!M.getOptions().CrossModuleOptimization)
440+
return;
431441

432442
// Mark all reachable functions as "anchors" so that they are not
433443
// removed later by the dead function elimination pass. This
@@ -449,5 +459,9 @@ class SerializeSILPass : public SILModuleTransform {
449459
};
450460

451461
SILTransform *swift::createSerializeSILPass() {
452-
return new SerializeSILPass();
462+
return new SerializeSILPass(/* onlyForCrossModuleOptimization */ false);
463+
}
464+
465+
SILTransform *swift::createCMOSerializeSILPass() {
466+
return new SerializeSILPass(/* onlyForCrossModuleOptimization */ true);
453467
}

test/SILOptimizer/Inputs/cross-module.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,14 @@ public func useClassKeypath<T>(_ t: T) -> Int {
236236
return c[keyPath: getClassKeypath(t)]
237237
}
238238

239+
@inline(never)
240+
func unrelated<U>(_ u: U) {
241+
print(u)
242+
}
243+
244+
@inline(never)
245+
public func callUnrelated<T>(_ t: T) -> T {
246+
unrelated(43)
247+
return t
248+
}
249+

test/SILOptimizer/cross-module-optimization.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,18 @@ func testKeypath() {
113113
print(useClassKeypath(0))
114114
}
115115

116+
func testMisc() {
117+
// CHECK-OUTPUT: 43
118+
// CHECK-OUTPUT: 42
119+
// CHECK-SIL-DAG: sil shared {{.*}} @$s4Test13callUnrelatedyxxlFSi_Tg5
120+
print(callUnrelated(42))
121+
}
122+
116123
testNestedTypes()
117124
testClass()
118125
testError()
119126
testProtocolsAndClasses()
120127
testSubModule()
121128
testClosures()
122129
testKeypath()
130+
testMisc()

0 commit comments

Comments
 (0)