Skip to content

Commit 1dfb3b1

Browse files
committed
cross-module-optimization: be more conservative when emitting a TBD file.
If we are emitting a TBD file, the TBD file only contains public symbols of this module. But not public symbols of imported modules which are statically linked to the current binary. This prevents referencing public symbols from other modules which could (potentially) linked statically. Unfortunately there is no way to find out if another module is linked statically or dynamically, so we have to be conservative. Fixes an unresolved-symbol linker error. rdar://89364148
1 parent adabdcb commit 1dfb3b1

File tree

6 files changed

+99
-4
lines changed

6 files changed

+99
-4
lines changed

include/swift/AST/SILOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ class SILOptions {
172172
/// Emit a mapping of profile counters for use in coverage.
173173
bool EmitProfileCoverageMapping = false;
174174

175+
bool emitTBD = false;
176+
175177
/// Should we use a pass pipeline passed in via a json file? Null by default.
176178
llvm::StringRef ExternalPassPipelineFilename;
177179

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,8 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
16701670
OPT_disable_previous_implementation_calls_in_dynamic_replacements);
16711671
Opts.ParseStdlib = FEOpts.ParseStdlib;
16721672

1673+
Opts.emitTBD = FEOpts.InputsAndOutputs.hasTBDPath();
1674+
16731675
if (const Arg *A = Args.getLastArg(OPT_save_optimization_record_EQ)) {
16741676
llvm::Expected<llvm::remarks::Format> formatOrErr =
16751677
llvm::remarks::parseFormat(A->getValue());

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,12 +338,40 @@ bool CrossModuleOptimization::canSerializeType(SILType type) {
338338
return success;
339339
}
340340

341+
/// Returns true if the function in \p funcCtxt could be linked statically to
342+
/// this module.
343+
static bool couldBeLinkedStatically(DeclContext *funcCtxt, SILModule &module) {
344+
if (!funcCtxt)
345+
return true;
346+
ModuleDecl *funcModule = funcCtxt->getParentModule();
347+
// If the function is in the same module, it's not in another module which
348+
// could be linked statically.
349+
if (module.getSwiftModule() == funcModule)
350+
return false;
351+
352+
// The stdlib module is always linked dynamically.
353+
if (funcModule == module.getASTContext().getStdlibModule())
354+
return false;
355+
356+
// Conservatively assume the function is in a statically linked module.
357+
return true;
358+
}
359+
341360
/// Returns true if the function \p func can be used from a serialized function.
342361
bool CrossModuleOptimization::canUseFromInline(SILFunction *function) {
343-
if (DeclContext *funcCtxt = function->getDeclContext()) {
344-
if (!M.getSwiftModule()->canBeUsedForCrossModuleOptimization(funcCtxt))
345-
return false;
346-
}
362+
DeclContext *funcCtxt = function->getDeclContext();
363+
if (funcCtxt && !M.getSwiftModule()->canBeUsedForCrossModuleOptimization(funcCtxt))
364+
return false;
365+
366+
/// If we are emitting a TBD file, the TBD file only contains public symbols
367+
/// of this module. But not public symbols of imported modules which are
368+
/// statically linked to the current binary.
369+
/// This prevents referencing public symbols from other modules which could
370+
/// (potentially) linked statically. Unfortunately there is no way to find out
371+
/// if another module is linked statically or dynamically, so we have to be
372+
/// conservative here.
373+
if (conservative && M.getOptions().emitTBD && couldBeLinkedStatically(funcCtxt, M))
374+
return false;
347375

348376
switch (function->getLinkage()) {
349377
case SILLinkage::PublicNonABI:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
import Submodule
3+
4+
public func incrementByThree(_ x: Int) -> Int {
5+
return incrementByOne(x) + 2
6+
}
7+
8+
public func incrementByThreeWithCall(_ x: Int) -> Int {
9+
return incrementByOneNoCMO(x) + 2
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
public func incrementByOne(_ x: Int) -> Int {
3+
return x + 1
4+
}
5+
6+
@_semantics("optimize.no.crossmodule")
7+
public func incrementByOneNoCMO(_ x: Int) -> Int {
8+
return x + 1
9+
}
10+

test/SILOptimizer/default-cmo.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
// RUN: %empty-directory(%t)
3+
4+
// RUN: %target-build-swift -O -wmo -parse-as-library -emit-module -emit-module-path=%t/Submodule.swiftmodule -module-name=Submodule %S/Inputs/cross-module/default-submodule.swift -c -o %t/submodule.o
5+
// RUN: %target-build-swift -O -wmo -parse-as-library -emit-module -emit-module-path=%t/Module.swiftmodule -module-name=Module -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/default-module.swift -c -o %t/module.o
6+
// RUN: %target-build-swift -O -wmo -parse-as-library -emit-tbd -emit-tbd-path %t/ModuleTBD.tbd -emit-module -emit-module-path=%t/ModuleTBD.swiftmodule -module-name=ModuleTBD -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/default-module.swift -c -o %t/moduletbd.o
7+
8+
// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -emit-sil | %FileCheck %s
9+
10+
11+
import Module
12+
import ModuleTBD
13+
14+
// CHECK-LABEL: sil @$s4Main11doIncrementyS2iF
15+
// CHECK-NOT: function_ref
16+
// CHECK-NOT: apply
17+
// CHECK: } // end sil function '$s4Main11doIncrementyS2iF'
18+
public func doIncrement(_ x: Int) -> Int {
19+
return Module.incrementByThree(x)
20+
}
21+
22+
// CHECK-LABEL: sil @$s4Main19doIncrementWithCallyS2iF
23+
// CHECK: function_ref @$s9Submodule19incrementByOneNoCMOyS2iF
24+
// CHECK: } // end sil function '$s4Main19doIncrementWithCallyS2iF'
25+
public func doIncrementWithCall(_ x: Int) -> Int {
26+
return Module.incrementByThreeWithCall(x)
27+
}
28+
29+
// CHECK-LABEL: sil @$s4Main14doIncrementTBDyS2iF
30+
// CHECK-NOT: function_ref
31+
// CHECK-NOT: apply
32+
// CHECK: } // end sil function '$s4Main14doIncrementTBDyS2iF'
33+
public func doIncrementTBD(_ x: Int) -> Int {
34+
return ModuleTBD.incrementByThree(x)
35+
}
36+
37+
// CHECK-LABEL: sil @$s4Main22doIncrementTBDWithCallyS2iF
38+
// CHECK: function_ref @$s9ModuleTBD24incrementByThreeWithCallyS2iF
39+
// CHECK: } // end sil function '$s4Main22doIncrementTBDWithCallyS2iF'
40+
public func doIncrementTBDWithCall(_ x: Int) -> Int {
41+
return ModuleTBD.incrementByThreeWithCall(x)
42+
}
43+

0 commit comments

Comments
 (0)