diff --git a/lld/test/wasm/lto/Inputs/foo.ll b/lld/test/wasm/lto/Inputs/foo.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/lto/Inputs/foo.ll @@ -0,0 +1,7 @@ +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +define void @foo() local_unnamed_addr { +entry: + ret void +} diff --git a/lld/test/wasm/lto/Inputs/stub.so b/lld/test/wasm/lto/Inputs/stub.so new file mode 100644 --- /dev/null +++ b/lld/test/wasm/lto/Inputs/stub.so @@ -0,0 +1,2 @@ +#STUB +bar: foo diff --git a/lld/test/wasm/lto/stub-library.s b/lld/test/wasm/lto/stub-library.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/lto/stub-library.s @@ -0,0 +1,33 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: llvm-as %S/Inputs/foo.ll -o %t1.o +# RUN: wasm-ld %t.o %t1.o %p/Inputs/stub.so -o %t.wasm +# RUN: obj2yaml %t.wasm | FileCheck %s + +# The function `foo` is defined in stub.so but depend on `foo` which happens +# be in an lto object. +# This verifies that stub library dependencies (exports) can be defined in LTO +# objects. +.functype bar () -> () + +.globl _start +_start: + .functype _start () -> () + call bar + end_function + +# CHECK: Imports: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: bar +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: SigIndex: 0 + +# CHECK: Exports: +# CHECK-NEXT: - Name: memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: - Name: _start +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: - Name: foo +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 2 diff --git a/lld/test/wasm/stub_library.s b/lld/test/wasm/stub-library.s rename from lld/test/wasm/stub_library.s rename to lld/test/wasm/stub-library.s diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -881,8 +881,7 @@ << "processing stub file: " << stub_file->getName() << "\n"); for (auto [name, deps]: stub_file->symbolDependencies) { auto* sym = symtab->find(name); - if (!sym || !sym->isUndefined() || !sym->isUsedInRegularObj || - sym->forceImport) { + if (!sym || !sym->isUndefined() || sym->forceImport) { LLVM_DEBUG(llvm::dbgs() << "stub not in needed: " << name << "\n"); continue; } @@ -907,7 +906,6 @@ LLVM_DEBUG(llvm::dbgs() << "force export: " << toString(*needed) << "\n"); needed->forceExport = true; - needed->isUsedInRegularObj = true; if (auto *lazy = dyn_cast(needed)) { lazy->fetch(); if (!config->whyExtract.empty()) @@ -1211,7 +1209,9 @@ if (errorCount()) return; - writeWhyExtract(); + // processStubLibraries must happen before LTO because it can trigger the + // export of arbirary symbols that might themselves be defined in LTO objects. + processStubLibraries(); // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. @@ -1219,8 +1219,14 @@ if (errorCount()) return; + // The LTO process can generate new undefined symbols, specifically libcall + // functions. Because those symbols might be declared in a stub library we + // need the process the stub libraries once again after LTO to handle any + // newly undefined symbols. processStubLibraries(); + writeWhyExtract(); + createOptionalSymbols(); // Resolve any variant symbols that were created due to signature