Skip to content

Commit 2ea8a3a

Browse files
committed
[lld][WebAssembly] Process stub libraries before performing LTO
There are cases where stub library processing can trigger new exports which might require them to be included at LTO time. Specifically `processStubLibraries` marks symbols as `forceExports` which even effect the LTO process. And since the LTO process can generate new undefined symbols (specifically libcall function) we need to also process the stub libraries after LTO. Differential Revision: https://reviews.llvm.org/D147190
1 parent bc73ef0 commit 2ea8a3a

File tree

7 files changed

+114
-4
lines changed

7 files changed

+114
-4
lines changed

lld/test/wasm/lto/Inputs/foo.ll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128"
2+
target triple = "wasm32-unknown-unknown"
3+
4+
define void @foo() local_unnamed_addr {
5+
entry:
6+
ret void
7+
}

lld/test/wasm/lto/Inputs/libcall.ll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128"
2+
target triple = "wasm32-unknown-unknown"
3+
4+
; This function, when compiled will generate a new undefined reference to
5+
; memcpy, that is not present in the bitcode object
6+
define void @func_with_libcall(ptr %a, ptr %b) {
7+
entry:
8+
call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %b, i64 1024, i1 false)
9+
ret void
10+
}
11+
12+
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)

lld/test/wasm/lto/Inputs/stub.so

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#STUB
2+
bar: foo
3+
memcpy: foo
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# RUN: split-file %s %t
2+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t_main.o %t/main.s
3+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t_foo.o %t/foo.s
4+
# RUN: llvm-as %S/Inputs/libcall.ll -o %t_libcall.o
5+
# RUN: wasm-ld %t_main.o %t_libcall.o %t_foo.o %p/Inputs/stub.so -o %t.wasm
6+
# RUN: obj2yaml %t.wasm | FileCheck %s
7+
8+
# The function `func_with_libcall` will generate an undefined reference to
9+
# `memcpy` at LTO time. `memcpy` itself also declared in stub.so and depends
10+
# on `foo`
11+
12+
# If %t_foo.o is not included in the link we get an undefined symbol reported
13+
# to the dependency of memcpy on the foo export:
14+
15+
# RUN: not wasm-ld %t_main.o %t_libcall.o %p/Inputs/stub.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING %s
16+
# MISSING: stub.so: undefined symbol: foo. Required by memcpy
17+
18+
#--- main.s
19+
.functype func_with_libcall (i32, i32) -> ()
20+
.globl _start
21+
_start:
22+
.functype _start () -> ()
23+
i32.const 0
24+
i32.const 0
25+
call func_with_libcall
26+
end_function
27+
28+
#--- foo.s
29+
.globl foo
30+
foo:
31+
.functype foo () -> ()
32+
end_function
33+
34+
# CHECK: Imports:
35+
# CHECK-NEXT: - Module: env
36+
# CHECK-NEXT: Field: memcpy
37+
# CHECK-NEXT: Kind: FUNCTION
38+
# CHECK-NEXT: SigIndex: 0
39+
40+
# CHECK: Exports:
41+
# CHECK-NEXT: - Name: memory
42+
# CHECK-NEXT: Kind: MEMORY
43+
# CHECK-NEXT: Index: 0
44+
# CHECK-NEXT: - Name: _start
45+
# CHECK-NEXT: Kind: FUNCTION
46+
# CHECK-NEXT: Index: 1
47+
# CHECK-NEXT: - Name: foo
48+
# CHECK-NEXT: Kind: FUNCTION
49+
# CHECK-NEXT: Index: 2

lld/test/wasm/lto/stub-library.s

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
2+
# RUN: llvm-as %S/Inputs/foo.ll -o %t1.o
3+
# RUN: wasm-ld %t.o %t1.o %p/Inputs/stub.so -o %t.wasm
4+
# RUN: obj2yaml %t.wasm | FileCheck %s
5+
6+
# The function `bar` is declared in stub.so and depends on `foo`, which happens
7+
# be in an LTO object.
8+
# This verifies that stub library dependencies (required exports) can be defined
9+
# in LTO objects.
10+
.functype bar () -> ()
11+
12+
.globl _start
13+
_start:
14+
.functype _start () -> ()
15+
call bar
16+
end_function
17+
18+
# CHECK: Imports:
19+
# CHECK-NEXT: - Module: env
20+
# CHECK-NEXT: Field: bar
21+
# CHECK-NEXT: Kind: FUNCTION
22+
# CHECK-NEXT: SigIndex: 0
23+
24+
# CHECK: Exports:
25+
# CHECK-NEXT: - Name: memory
26+
# CHECK-NEXT: Kind: MEMORY
27+
# CHECK-NEXT: Index: 0
28+
# CHECK-NEXT: - Name: _start
29+
# CHECK-NEXT: Kind: FUNCTION
30+
# CHECK-NEXT: Index: 1
31+
# CHECK-NEXT: - Name: foo
32+
# CHECK-NEXT: Kind: FUNCTION
33+
# CHECK-NEXT: Index: 2
File renamed without changes.

lld/wasm/Driver.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -881,8 +881,7 @@ static void processStubLibraries() {
881881
<< "processing stub file: " << stub_file->getName() << "\n");
882882
for (auto [name, deps]: stub_file->symbolDependencies) {
883883
auto* sym = symtab->find(name);
884-
if (!sym || !sym->isUndefined() || !sym->isUsedInRegularObj ||
885-
sym->forceImport) {
884+
if (!sym || !sym->isUndefined() || sym->forceImport) {
886885
LLVM_DEBUG(llvm::dbgs() << "stub not in needed: " << name << "\n");
887886
continue;
888887
}
@@ -907,7 +906,6 @@ static void processStubLibraries() {
907906
LLVM_DEBUG(llvm::dbgs()
908907
<< "force export: " << toString(*needed) << "\n");
909908
needed->forceExport = true;
910-
needed->isUsedInRegularObj = true;
911909
if (auto *lazy = dyn_cast<LazySymbol>(needed)) {
912910
lazy->fetch();
913911
if (!config->whyExtract.empty())
@@ -1211,16 +1209,24 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
12111209
if (errorCount())
12121210
return;
12131211

1214-
writeWhyExtract();
1212+
// processStubLibraries must happen before LTO because it can trigger the
1213+
// export of arbirary symbols that might themselves be defined in LTO objects.
1214+
processStubLibraries();
12151215

12161216
// Do link-time optimization if given files are LLVM bitcode files.
12171217
// This compiles bitcode files into real object files.
12181218
symtab->compileBitcodeFiles();
12191219
if (errorCount())
12201220
return;
12211221

1222+
// The LTO process can generate new undefined symbols, specifically libcall
1223+
// functions. Because those symbols might be declared in a stub library we
1224+
// need the process the stub libraries once again after LTO to handle any
1225+
// newly undefined symbols.
12221226
processStubLibraries();
12231227

1228+
writeWhyExtract();
1229+
12241230
createOptionalSymbols();
12251231

12261232
// Resolve any variant symbols that were created due to signature

0 commit comments

Comments
 (0)