Skip to content

Commit a7425c1

Browse files
committed
Improvements for cross-module-optimization
* Include small non-generic functions for serializaion * serialize initializer of global variables: so that global let variables can be constant propagated across modules rdar://problem/60696510
1 parent fce7fdc commit a7425c1

File tree

4 files changed

+100
-29
lines changed

4 files changed

+100
-29
lines changed

lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@
2323
#include "swift/SILOptimizer/PassManager/Passes.h"
2424
#include "swift/SILOptimizer/PassManager/Transforms.h"
2525
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
26+
#include "swift/SILOptimizer/Utils/SILInliner.h"
2627
#include "llvm/Support/CommandLine.h"
2728
#include "llvm/Support/Debug.h"
2829

2930
using namespace swift;
3031

32+
/// Functions up to this (abstract) size are serialized, even if they are not
33+
/// generic.
34+
static llvm::cl::opt<int> CMOFunctionSizeLimit("cmo-function-size-limit",
35+
llvm::cl::init(20));
36+
3137
namespace {
3238

3339
/// Scans a whole module and marks functions and types as inlinable or usable
@@ -54,6 +60,8 @@ class CrossModuleSerializationSetup {
5460

5561
bool canSerialize(SILFunction *F, bool lookIntoThunks);
5662

63+
bool canSerialize(SILInstruction *inst, bool lookIntoThunks);
64+
5765
void setUpForSerialization(SILFunction *F);
5866

5967
void prepareInstructionForSerialization(SILInstruction *inst);
@@ -193,18 +201,31 @@ static llvm::cl::opt<bool> SerializeEverything(
193201

194202
/// Decide whether to serialize a function.
195203
static bool shouldSerialize(SILFunction *F) {
196-
// The basic heursitic: serialize all generic functions, because it makes a
197-
// huge difference if generic functions can be specialized or not.
198-
if (!F->getLoweredFunctionType()->isPolymorphic() && !SerializeEverything)
199-
return false;
200-
201204
// Check if we already handled this function before.
202205
if (F->isSerialized() == IsSerialized)
203206
return false;
204207

205208
if (F->hasSemanticsAttr("optimize.no.crossmodule"))
206209
return false;
207210

211+
if (SerializeEverything)
212+
return true;
213+
214+
// The basic heursitic: serialize all generic functions, because it makes a
215+
// huge difference if generic functions can be specialized or not.
216+
if (F->getLoweredFunctionType()->isPolymorphic())
217+
return true;
218+
219+
// Also serialize "small" non-generic functions.
220+
int size = 0;
221+
for (SILBasicBlock &block : *F) {
222+
for (SILInstruction &inst : block) {
223+
size += (int)instructionInlineCost(inst);
224+
if (size >= CMOFunctionSizeLimit)
225+
return false;
226+
}
227+
}
228+
208229
return true;
209230
}
210231

@@ -227,6 +248,11 @@ prepareInstructionForSerialization(SILInstruction *inst) {
227248
handleReferencedFunction(callee);
228249
return;
229250
}
251+
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
252+
GAI->getReferencedGlobal()->setSerialized(IsSerialized);
253+
GAI->getReferencedGlobal()->setLinkage(SILLinkage::Public);
254+
return;
255+
}
230256
if (auto *MI = dyn_cast<MethodInst>(inst)) {
231257
handleReferencedMethod(MI->getMember());
232258
return;
@@ -285,26 +311,42 @@ bool CrossModuleSerializationSetup::canSerialize(SILFunction *F,
285311
// First step: check if serializing F is even possible.
286312
for (SILBasicBlock &block : *F) {
287313
for (SILInstruction &inst : block) {
288-
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(&inst)) {
289-
SILFunction *callee = FRI->getReferencedFunctionOrNull();
290-
if (!canUseFromInline(callee, lookIntoThunks))
291-
return false;
292-
} else if (auto *KPI = dyn_cast<KeyPathInst>(&inst)) {
293-
bool canUse = true;
294-
KPI->getPattern()->visitReferencedFunctionsAndMethods(
295-
[&](SILFunction *func) {
296-
if (!canUseFromInline(func, lookIntoThunks))
297-
canUse = false;
298-
},
299-
[](SILDeclRef method) { });
300-
if (!canUse)
301-
return false;
302-
}
314+
if (!canSerialize(&inst, lookIntoThunks))
315+
return false;
303316
}
304317
}
305318
return true;
306319
}
307320

321+
bool CrossModuleSerializationSetup::canSerialize(SILInstruction *inst,
322+
bool lookIntoThunks) {
323+
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(inst)) {
324+
SILFunction *callee = FRI->getReferencedFunctionOrNull();
325+
return canUseFromInline(callee, lookIntoThunks);
326+
}
327+
if (auto *KPI = dyn_cast<KeyPathInst>(inst)) {
328+
bool canUse = true;
329+
KPI->getPattern()->visitReferencedFunctionsAndMethods(
330+
[&](SILFunction *func) {
331+
if (!canUseFromInline(func, lookIntoThunks))
332+
canUse = false;
333+
},
334+
[&](SILDeclRef method) {
335+
if (method.isForeign)
336+
canUse = false;
337+
});
338+
return canUse;
339+
}
340+
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
341+
return !GAI->getReferencedGlobal()->getName().startswith("globalinit_");
342+
}
343+
if (auto *MI = dyn_cast<MethodInst>(inst)) {
344+
return !MI->getMember().isForeign;
345+
}
346+
347+
return true;
348+
}
349+
308350
/// Returns true if the function \p func can be used from a serialized function.
309351
///
310352
/// If \p lookIntoThunks is true, serializable shared thunks are also accepted.
@@ -351,12 +393,17 @@ void CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) {
351393
}
352394
F->setSerialized(IsSerialized);
353395

354-
// As a code size optimization, make serialized functions
355-
// @alwaysEmitIntoClient.
356-
// Also, for shared thunks it's required to make them @alwaysEmitIntoClient.
357-
// SILLinkage::Public would not work for shared functions, because it could
358-
// result in duplicate-symbol linker errors.
359-
F->setLinkage(SILLinkage::PublicNonABI);
396+
if (F->getLoweredFunctionType()->isPolymorphic() ||
397+
F->getLinkage() != SILLinkage::Public) {
398+
// As a code size optimization, make serialized functions
399+
// @alwaysEmitIntoClient.
400+
// Also, for shared thunks it's required to make them @alwaysEmitIntoClient.
401+
// SILLinkage::Public would not work for shared functions, because it could
402+
// result in duplicate-symbol linker errors.
403+
F->setLinkage(SILLinkage::PublicNonABI);
404+
} else {
405+
F->setLinkage(SILLinkage::Public);
406+
}
360407
}
361408

362409
/// Select functions in the module which should be serialized.

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,10 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
447447
// is linked in from the stdlib.
448448
P.addTempRValueOpt();
449449

450+
// Needed to serialize static initializers of globals for cross-module
451+
// optimization.
452+
P.addGlobalOpt();
453+
450454
// Add the outliner pass (Osize).
451455
P.addOutliner();
452456

test/SILOptimizer/Inputs/cross-module.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ extension Int : PrivateProtocol {
130130
}
131131

132132
@inline(never)
133+
@_semantics("optimize.no.crossmodule")
133134
private func printFooExistential(_ p: PrivateProtocol) {
134135
print(p.foo())
135136
}
@@ -266,3 +267,4 @@ public func callUnrelated<T>(_ t: T) -> T {
266267
return t
267268
}
268269

270+
public let globalLet = 529387

test/SILOptimizer/cross-module-optimization.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
// Second test: check if CMO really imports the SIL of functions in other modules.
1919

20-
// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil | %FileCheck %s -check-prefix=CHECK-SIL
20+
// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil -o %t/out.sil
21+
// RUN: %FileCheck %s -check-prefix=CHECK-SIL < %t/out.sil
22+
// RUN: %FileCheck %s -check-prefix=CHECK-SIL2 < %t/out.sil
2123

2224
import Test
2325

@@ -54,13 +56,18 @@ func testClass() {
5456
print(createClass_gen(0))
5557
}
5658

59+
// CHECK-SIL2-LABEL: sil hidden [noinline] @$s4Main9testErroryyF
60+
@inline(never)
5761
func testError() {
5862
// CHECK-OUTPUT: PrivateError()
59-
// CHECK-SIL-DAG: sil @$s4Test12PrivateError33_{{.*}} : $@convention(method) (@thin PrivateError.Type) -> PrivateError{{$}}
63+
// CHECK-SIL2: struct $PrivateError ()
64+
// CHECK-SIL2: alloc_existential_box $Error, $PrivateError
6065
print(returnPrivateError(27))
6166
// CHECK-OUTPUT: InternalError()
62-
// CHECK-SIL-DAG: sil @$s4Test13InternalErrorVACycfC : $@convention(method) (@thin InternalError.Type) -> InternalError{{$}}
67+
// CHECK-SIL2: struct $InternalError ()
68+
// CHECK-SIL2: alloc_existential_box $Error, $InternalError
6369
print(returnInternalError(27))
70+
// CHECK-SIL2: } // end sil function '$s4Main9testErroryyF'
6471
}
6572

6673
class DerivedFromOpen<T> : OpenClass<T> { }
@@ -123,6 +130,15 @@ func testMisc() {
123130
print(classWithPublicProperty(33))
124131
}
125132

133+
// CHECK-SIL2-LABEL: sil hidden [noinline] @$s4Main10testGlobalyyF
134+
@inline(never)
135+
func testGlobal() {
136+
// CHECK-OUTPUT: 529387
137+
// CHECK-SIL2: integer_literal $Builtin.Int{{[0-9]+}}, 529387
138+
print(globalLet)
139+
// CHECK-SIL2: } // end sil function '$s4Main10testGlobalyyF'
140+
}
141+
126142
testNestedTypes()
127143
testClass()
128144
testError()
@@ -131,3 +147,5 @@ testSubModule()
131147
testClosures()
132148
testKeypath()
133149
testMisc()
150+
testGlobal()
151+

0 commit comments

Comments
 (0)