diff --git a/lld/test/wasm/lto/Inputs/libcall-archive.ll b/lld/test/wasm/lto/Inputs/libcall-archive.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/lto/Inputs/libcall-archive.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +define void @memcpy() { + ret void +} diff --git a/lld/test/wasm/lto/libcall-archive.ll b/lld/test/wasm/lto/libcall-archive.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/lto/libcall-archive.ll @@ -0,0 +1,25 @@ +; RUN: rm -f %t.a +; RUN: llvm-as -o %t.o %s +; RUN: llvm-as -o %t2.o %S/Inputs/libcall-archive.ll +; RUN: llvm-ar rcs %t.a %t2.o +; RUN: wasm-ld -o %t %t.o %t.a +; RUN: obj2yaml %t | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +define void @_start(i8* %a, i8* %b) { +entry: + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 1024, i1 false) + ret void +} + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i1) + +; CHECK: - Type: CUSTOM +; CHECK-NEXT: Name: name +; CHECK-NEXT: FunctionNames: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: Name: _start +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: Name: memcpy diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -437,6 +437,18 @@ return sym; } +static void handleLibcall(StringRef name) { + Symbol *sym = symtab->find(name); + if (!sym) + return; + + if (auto *lazySym = dyn_cast(sym)) { + MemoryBufferRef mb = lazySym->getMemberBuffer(); + if (isBitcode(mb)) + lazySym->fetch(); + } +} + static UndefinedGlobal * createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) { auto *sym = cast(symtab->addUndefinedGlobal( @@ -742,6 +754,21 @@ // Create wrapped symbols for -wrap option. std::vector wrapped = addWrappedSymbols(args); + // If any of our inputs are bitcode files, the LTO code generator may create + // references to certain library functions that might not be explicit in the + // bitcode file's symbol table. If any of those library functions are defined + // in a bitcode file in an archive member, we need to arrange to use LTO to + // compile those archive members by adding them to the link beforehand. + // + // We only need to add libcall symbols to the link before LTO if the symbol's + // definition is in bitcode. Any other required libcall symbols will be added + // to the link after LTO when we add the LTO object file to the link. + if (!symtab->bitcodeFiles.empty()) + for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) + handleLibcall(s); + if (errorCount()) + return; + // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. symtab->addCombinedLTOObject(); diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -162,6 +162,10 @@ std::unique_ptr obj; }; +inline bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; +} + // Will report a fatal() error if the input buffer is not a valid bitcode // or wasm object file. InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = ""); diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -410,6 +410,7 @@ static bool classof(const Symbol *s) { return s->kind() == LazyKind; } void fetch(); + MemoryBufferRef getMemberBuffer(); // Lazy symbols can have a signature because they can replace an // UndefinedFunction which which case we need to be able to preserve the diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -20,6 +20,7 @@ #define DEBUG_TYPE "lld" using namespace llvm; +using namespace llvm::object; using namespace llvm::wasm; namespace lld { @@ -334,6 +335,16 @@ void LazySymbol::fetch() { cast(file)->addMember(&archiveSymbol); } +MemoryBufferRef LazySymbol::getMemberBuffer() { + Archive::Child c = + CHECK(archiveSymbol.getMember(), + "could not get the member for symbol " + toString(*this)); + + return CHECK(c.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + toString(*this)); +} + void printTraceSymbolUndefined(StringRef name, const InputFile* file) { message(toString(file) + ": reference to " + name); }