Skip to content

Commit 033f667

Browse files
authored
Merge pull request #80185 from kubamracek/embedded/used
[embedded] Link in @_used declarations from other modules in SILLinker
2 parents 05084ac + f036837 commit 033f667

File tree

10 files changed

+504
-303
lines changed

10 files changed

+504
-303
lines changed

include/swift/Runtime/RuntimeFunctions.def

+291-291
Large diffs are not rendered by default.

include/swift/Serialization/SerializedSILLoader.h

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class SerializedSILLoader {
6363
SILFunction *lookupSILFunction(SILFunction *Callee, bool onlyUpdateLinkage);
6464
SILFunction *lookupSILFunction(StringRef Name,
6565
std::optional<SILLinkage> linkage);
66+
SILGlobalVariable *lookupSILGlobalVariable(StringRef Name);
6667
bool hasSILFunction(StringRef Name,
6768
std::optional<SILLinkage> linkage = std::nullopt);
6869
SILVTable *lookupVTable(const ClassDecl *C);

lib/SIL/IR/Linker.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,9 @@ void SILLinkerVisitor::visitGlobalAddrInst(GlobalAddrInst *GAI) {
470470
if (!Mod.getOptions().EmbeddedSwift)
471471
return;
472472

473+
// In Embedded Swift, we want to actually link globals from other modules too,
474+
// so strip "external" from the linkage.
473475
SILGlobalVariable *G = GAI->getReferencedGlobal();
474-
G->setDeclaration(false);
475476
G->setLinkage(stripExternalFromLinkage(G->getLinkage()));
476477
}
477478

lib/SIL/IR/SILModule.cpp

+10-5
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,16 @@ class SILModule::SerializationCallback final
4949

5050
void didDeserialize(ModuleDecl *M, SILGlobalVariable *var) override {
5151
updateLinkage(var);
52-
53-
// For globals we currently do not support available_externally.
54-
// In the interpreter it would result in two instances for a single global:
55-
// one in the imported module and one in the main module.
56-
var->setDeclaration(true);
52+
53+
if (!M->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
54+
// For globals we currently do not support available_externally.
55+
// In the interpreter it would result in two instances for a single
56+
// global: one in the imported module and one in the main module.
57+
//
58+
// We avoid that in Embedded Swift where we do actually link globals from
59+
// other modules into the client module.
60+
var->setDeclaration(true);
61+
}
5762
}
5863

5964
void didDeserialize(ModuleDecl *M, SILVTable *vtable) override {

lib/SILOptimizer/UtilityPasses/Link.cpp

+96-6
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@
1414
#include "swift/SILOptimizer/PassManager/Transforms.h"
1515
#include "swift/SIL/SILModule.h"
1616
#include "swift/Serialization/SerializedSILLoader.h"
17+
#include "swift/Serialization/SerializedModuleLoader.h"
1718

1819
using namespace swift;
1920

2021
static llvm::cl::opt<bool> LinkEmbeddedRuntime("link-embedded-runtime",
2122
llvm::cl::init(true));
2223

24+
static llvm::cl::opt<bool> LinkUsedFunctions("link-used-functions",
25+
llvm::cl::init(true));
26+
2327
//===----------------------------------------------------------------------===//
2428
// Top Level Driver
2529
//===----------------------------------------------------------------------===//
@@ -45,6 +49,12 @@ class SILLinker : public SILModuleTransform {
4549
if (M.getOptions().EmbeddedSwift && LinkEmbeddedRuntime) {
4650
linkEmbeddedRuntimeFromStdlib();
4751
}
52+
53+
// In embedded Swift, we need to explicitly link any @_used globals and
54+
// functions from imported modules.
55+
if (M.getOptions().EmbeddedSwift && LinkUsedFunctions) {
56+
linkUsedGlobalsAndFunctions();
57+
}
4858
}
4959

5060
void linkEmbeddedRuntimeFromStdlib() {
@@ -82,23 +92,103 @@ class SILLinker : public SILModuleTransform {
8292
// Don't link allocating runtime functions in -no-allocations mode.
8393
if (M.getOptions().NoAllocations && allocating) return;
8494

85-
// Bail if runtime function is already loaded.
86-
if (M.lookUpFunction(name)) return;
95+
// Swift Runtime functions are all expected to be SILLinkage::PublicExternal
96+
linkUsedFunctionByName(name, SILLinkage::PublicExternal);
97+
}
98+
99+
SILFunction *linkUsedFunctionByName(StringRef name,
100+
std::optional<SILLinkage> Linkage) {
101+
SILModule &M = *getModule();
87102

88-
SILFunction *Fn =
89-
M.getSILLoader()->lookupSILFunction(name, SILLinkage::PublicExternal);
90-
if (!Fn) return;
103+
// Bail if function is already loaded.
104+
if (auto *Fn = M.lookUpFunction(name)) return Fn;
105+
106+
SILFunction *Fn = M.getSILLoader()->lookupSILFunction(name, Linkage);
107+
if (!Fn) return nullptr;
91108

92109
if (M.linkFunction(Fn, LinkMode))
93110
invalidateAnalysis(Fn, SILAnalysis::InvalidationKind::Everything);
94111

95-
// Make sure that dead-function-elimination doesn't remove runtime functions.
112+
// Make sure that dead-function-elimination doesn't remove the explicitly
113+
// linked functions.
114+
//
96115
// TODO: lazily emit runtime functions in IRGen so that we don't have to
97116
// rely on dead-stripping in the linker to remove unused runtime
98117
// functions.
99118
if (Fn->isDefinition())
100119
Fn->setLinkage(SILLinkage::Public);
120+
121+
return Fn;
122+
}
123+
124+
SILGlobalVariable *linkUsedGlobalVariableByName(StringRef name) {
125+
SILModule &M = *getModule();
126+
127+
// Bail if runtime function is already loaded.
128+
if (auto *GV = M.lookUpGlobalVariable(name)) return GV;
129+
130+
SILGlobalVariable *GV = M.getSILLoader()->lookupSILGlobalVariable(name);
131+
if (!GV) return nullptr;
132+
133+
// Make sure that dead-function-elimination doesn't remove the explicitly
134+
// linked global variable.
135+
if (GV->isDefinition())
136+
GV->setLinkage(SILLinkage::Public);
137+
138+
return GV;
101139
}
140+
141+
void linkUsedGlobalsAndFunctions() {
142+
SmallVector<VarDecl *, 32> Globals;
143+
SmallVector<AbstractFunctionDecl *, 32> Functions;
144+
collectUsedDeclsFromLoadedModules(Globals, Functions);
145+
146+
for (auto *G : Globals) {
147+
auto declRef = SILDeclRef(G, SILDeclRef::Kind::Func);
148+
linkUsedGlobalVariableByName(declRef.mangle());
149+
}
150+
151+
for (auto *F : Functions) {
152+
auto declRef = SILDeclRef(F, SILDeclRef::Kind::Func);
153+
auto *Fn = linkUsedFunctionByName(declRef.mangle(), /*Linkage*/{});
154+
155+
// If we have @_cdecl or @_silgen_name, also link the foreign thunk
156+
if (Fn->hasCReferences()) {
157+
auto declRef = SILDeclRef(F, SILDeclRef::Kind::Func, /*isForeign*/true);
158+
linkUsedFunctionByName(declRef.mangle(), /*Linkage*/{});
159+
}
160+
}
161+
}
162+
163+
void collectUsedDeclsFromLoadedModules(
164+
SmallVectorImpl<VarDecl *> &Globals,
165+
SmallVectorImpl<AbstractFunctionDecl *> &Functions) {
166+
SILModule &M = *getModule();
167+
168+
for (const auto &Entry : M.getASTContext().getLoadedModules()) {
169+
for (auto File : Entry.second->getFiles()) {
170+
if (auto LoadedAST = dyn_cast<SerializedASTFile>(File)) {
171+
auto matcher = [](const DeclAttributes &attrs) -> bool {
172+
return attrs.hasAttribute<UsedAttr>();
173+
};
174+
175+
SmallVector<Decl *, 32> Decls;
176+
LoadedAST->getTopLevelDeclsWhereAttributesMatch(Decls, matcher);
177+
178+
for (Decl *D : Decls) {
179+
if (AbstractFunctionDecl *F = dyn_cast<AbstractFunctionDecl>(D)) {
180+
Functions.push_back(F);
181+
} else if (VarDecl *G = dyn_cast<VarDecl>(D)) {
182+
Globals.push_back(G);
183+
} else {
184+
assert(false && "only funcs and globals can be @_used");
185+
}
186+
}
187+
}
188+
}
189+
}
190+
}
191+
102192
};
103193
} // end anonymous namespace
104194

lib/Serialization/DeserializeSIL.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3977,6 +3977,10 @@ SILFunction *SILDeserializer::lookupSILFunction(StringRef name,
39773977
return maybeFunc.get();
39783978
}
39793979

3980+
SILGlobalVariable *SILDeserializer::lookupSILGlobalVariable(StringRef name) {
3981+
return getGlobalForReference(name);
3982+
}
3983+
39803984
SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) {
39813985
if (!GlobalVarList)
39823986
return nullptr;

lib/Serialization/DeserializeSIL.h

+1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ namespace swift {
197197
SILFunction *lookupSILFunction(SILFunction *InFunc, bool onlyUpdateLinkage);
198198
SILFunction *lookupSILFunction(StringRef Name,
199199
bool declarationOnly = false);
200+
SILGlobalVariable *lookupSILGlobalVariable(StringRef Name);
200201
bool hasSILFunction(StringRef Name,
201202
std::optional<SILLinkage> Linkage = std::nullopt);
202203
SILVTable *lookupVTable(StringRef MangledClassName);

lib/Serialization/SerializedSILLoader.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ SerializedSILLoader::lookupSILFunction(StringRef Name,
8585
return nullptr;
8686
}
8787

88+
SILGlobalVariable *SerializedSILLoader::lookupSILGlobalVariable(StringRef Name) {
89+
for (auto &Des : LoadedSILSections) {
90+
if (auto *G = Des->lookupSILGlobalVariable(Name)) {
91+
return G;
92+
}
93+
}
94+
return nullptr;
95+
}
96+
8897
bool SerializedSILLoader::hasSILFunction(StringRef Name,
8998
std::optional<SILLinkage> Linkage) {
9099
// It is possible that one module has a declaration of a SILFunction, while

test/embedded/modules-used.swift

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %{python} %utils/split_file.py -o %t %s
3+
4+
// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Embedded -parse-as-library -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift
5+
// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Embedded -parse-as-library -I %t %t/Main.swift -emit-sil | %FileCheck %s --check-prefix CHECK-SIL
6+
// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Embedded -parse-as-library -I %t %t/Main.swift -c -o %t/a.o
7+
// RUN: %target-clang %t/a.o -o %t/a.out
8+
// RUN: %target-run %t/a.out | %FileCheck %s
9+
10+
// REQUIRES: swift_in_compiler
11+
// REQUIRES: executable_test
12+
// REQUIRES: OS=macosx
13+
// REQUIRES: swift_feature_Embedded
14+
// REQUIRES: swift_feature_SymbolLinkageMarkers
15+
16+
// BEGIN MyModule.swift
17+
18+
@_used
19+
@_section("__DATA,__mysection")
20+
let i_am_not_referenced = 42
21+
22+
// BEGIN Main.swift
23+
24+
import MyModule
25+
26+
@_silgen_name(raw: "section$start$__DATA$__mysection")
27+
var mysection_start: Int
28+
29+
@_silgen_name(raw: "section$end$__DATA$__mysection")
30+
var mysection_end: Int
31+
32+
@main
33+
struct Main {
34+
static func main() {
35+
let start = UnsafeRawPointer(&mysection_start)
36+
let end = UnsafeRawPointer(&mysection_end)
37+
let size = end - start
38+
let count = size / (Int.bitWidth / 8)
39+
print("count: \(count)")
40+
let linker_set = UnsafeBufferPointer(start: start.bindMemory(to: Int.self, capacity: count), count: count)
41+
for i in 0 ..< linker_set.count {
42+
print("mysection[\(i)]: \(linker_set[i])")
43+
}
44+
}
45+
}
46+
47+
// CHECK-SIL: // i_am_not_referenced
48+
// CHECK-SIL-NEXT: sil_global [serialized] [let] @$e8MyModule19i_am_not_referencedSivp : $Int = {
49+
50+
// CHECK: count: 1
51+
// CHECK: mysection[0]: 42

test/embedded/modules-used2.swift

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %{python} %utils/split_file.py -o %t %s
3+
4+
// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Embedded -parse-as-library -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift
5+
// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Embedded -parse-as-library -I %t %t/Main.swift -emit-sil | %FileCheck %s --check-prefix CHECK-SIL
6+
// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Embedded -parse-as-library -I %t %t/Main.swift -c -o %t/a.o
7+
// RUN: %target-clang %t/a.o -o %t/a.out
8+
// RUN: %target-run %t/a.out | %FileCheck %s
9+
10+
// REQUIRES: swift_in_compiler
11+
// REQUIRES: executable_test
12+
// REQUIRES: swift_feature_Embedded
13+
// REQUIRES: swift_feature_SymbolLinkageMarkers
14+
15+
// BEGIN MyModule.swift
16+
17+
@_used
18+
@_cdecl("main")
19+
func main() -> CInt {
20+
print("main in a submodule")
21+
return 0
22+
}
23+
24+
@_used
25+
func foo() {
26+
}
27+
28+
// BEGIN Main.swift
29+
30+
import MyModule
31+
32+
// CHECK-SIL: // main()
33+
// CHECK-SIL-NEXT: sil @$e8MyModule4mains5Int32VyF : $@convention(thin) () -> Int32 {
34+
// CHECK-SIL: // main
35+
// CHECK-SIL-NEXT: sil [thunk] @main : $@convention(c) () -> Int32
36+
// CHECK-SIL: // foo()
37+
// CHECK-SIL-NEXT: sil @$e8MyModule3fooyyF : $@convention(thin) () -> () {
38+
39+
// CHECK: main in a submodule

0 commit comments

Comments
 (0)