[lld] [lld][WebAssembly] Preserve LTO stub deps for bitcode symbols (PR #173235)
Heejin Ahn via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 22 13:54:08 PST 2025
https://github.com/aheejin updated https://github.com/llvm/llvm-project/pull/173235
>From 5ef9c3fb5b674e9692d9f14a5628f9053e547d46 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Sun, 21 Dec 2025 03:36:12 +0000
Subject: [PATCH 1/3] [lld][WebAssembly] Preserve LTO stub deps for bitcode
symbols
When a stub .so file contains
```
A: B
```
And `A` is defined in bitcode that's pulled in for LTO, but both `A` and
`B` are removed in `LTO::linkRegularLTO` due to not being dead:
https://github.com/llvm/llvm-project/blob/24297bea9672722d8fbaaff137b301b0becaae9c/llvm/lib/LTO/LTO.cpp#L1042-L1054
Then the symbol `A` becomes undefined after LTO, `processStubLibraries`
tries to import `A` from JS, and tries to export its dependency `B`:
https://github.com/llvm/llvm-project/blob/24297bea9672722d8fbaaff137b301b0becaae9c/lld/wasm/Driver.cpp#L1108-L1109
But `B` is gone, causing this error:
```console
wasm-ld: error: ....: undefined symbol: B. Required by A
```
This PR marks `B` as required not only when `A` is null or undefined but
also `A` is in bitcode, because bitcode functions can be DCE'd, causing
the linker to import the symbol and export its dependencies.
---
.../wasm/lto/Inputs/stub-dependency-main.s | 4 +++
lld/test/wasm/lto/stub-dependency.ll | 33 +++++++++++++++++++
lld/wasm/Driver.cpp | 6 +++-
3 files changed, 42 insertions(+), 1 deletion(-)
create mode 100644 lld/test/wasm/lto/Inputs/stub-dependency-main.s
create mode 100644 lld/test/wasm/lto/stub-dependency.ll
diff --git a/lld/test/wasm/lto/Inputs/stub-dependency-main.s b/lld/test/wasm/lto/Inputs/stub-dependency-main.s
new file mode 100644
index 0000000000000..9e84901a6fe80
--- /dev/null
+++ b/lld/test/wasm/lto/Inputs/stub-dependency-main.s
@@ -0,0 +1,4 @@
+.globl _start
+_start:
+ .functype _start () -> ()
+ end_function
diff --git a/lld/test/wasm/lto/stub-dependency.ll b/lld/test/wasm/lto/stub-dependency.ll
new file mode 100644
index 0000000000000..3b2daf39cc03a
--- /dev/null
+++ b/lld/test/wasm/lto/stub-dependency.ll
@@ -0,0 +1,33 @@
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/stub-dependency-main.s -o %t.o
+; RUN: echo "#STUB" > %t.so
+; RUN: echo "A: B" >> %t.so
+; RUN: wasm-ld %t.o %t.bc %t.so -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test if LTO stub dependencies are preserved if a symbol they depend on is
+; defined in bitcode and DCE'd and become undefined in the LTO process. Here 'B'
+; should be preserved and exported.
+
+target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-unknown"
+
+define void @A() {
+ ret void
+}
+
+define void @B() {
+ ret void
+}
+
+; CHECK: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: B
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 97e50783985a8..71daa30f1dec9 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1028,7 +1028,11 @@ static void processStubLibrariesPreLTO() {
// If the symbol is not present at all (yet), or if it is present but
// undefined, then mark the dependent symbols as used by a regular
// object so they will be preserved and exported by the LTO process.
- if (!sym || sym->isUndefined()) {
+ // If the symbol is defined and in bitcode, it can be DCE'd during LTO and
+ // become undefined, so mark the dependent symbols as used by a regular
+ // object as well.
+ if (!sym || sym->isUndefined() ||
+ (sym->isDefined() && isa_and_nonnull<BitcodeFile>(sym->getFile()))) {
for (const auto dep : deps) {
auto* needed = symtab->find(dep);
if (needed ) {
>From b16180f6c511dd18f02d6427555d858ccded03e3 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Mon, 22 Dec 2025 21:14:43 +0000
Subject: [PATCH 2/3] Use existing start.s
---
lld/test/wasm/lto/Inputs/stub-dependency-main.s | 4 ----
lld/test/wasm/lto/stub-dependency.ll | 2 +-
2 files changed, 1 insertion(+), 5 deletions(-)
delete mode 100644 lld/test/wasm/lto/Inputs/stub-dependency-main.s
diff --git a/lld/test/wasm/lto/Inputs/stub-dependency-main.s b/lld/test/wasm/lto/Inputs/stub-dependency-main.s
deleted file mode 100644
index 9e84901a6fe80..0000000000000
--- a/lld/test/wasm/lto/Inputs/stub-dependency-main.s
+++ /dev/null
@@ -1,4 +0,0 @@
-.globl _start
-_start:
- .functype _start () -> ()
- end_function
diff --git a/lld/test/wasm/lto/stub-dependency.ll b/lld/test/wasm/lto/stub-dependency.ll
index 3b2daf39cc03a..d88cb9e187327 100644
--- a/lld/test/wasm/lto/stub-dependency.ll
+++ b/lld/test/wasm/lto/stub-dependency.ll
@@ -1,5 +1,5 @@
; RUN: llvm-as %s -o %t.bc
-; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/stub-dependency-main.s -o %t.o
+; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/../Inputs/start.s -o %t.o
; RUN: echo "#STUB" > %t.so
; RUN: echo "A: B" >> %t.so
; RUN: wasm-ld %t.o %t.bc %t.so -o %t.wasm
>From a02b3f014933a78874e17a1d85691790cafb64e4 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Mon, 22 Dec 2025 21:52:49 +0000
Subject: [PATCH 3/3] isa_and_nonnull -> isa
---
lld/wasm/Driver.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 71daa30f1dec9..6d5447be812aa 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1032,7 +1032,7 @@ static void processStubLibrariesPreLTO() {
// become undefined, so mark the dependent symbols as used by a regular
// object as well.
if (!sym || sym->isUndefined() ||
- (sym->isDefined() && isa_and_nonnull<BitcodeFile>(sym->getFile()))) {
+ (sym->isDefined() && isa<BitcodeFile>(sym->getFile()))) {
for (const auto dep : deps) {
auto* needed = symtab->find(dep);
if (needed ) {
More information about the llvm-commits
mailing list