From 1e6d1b7612df9e73f281979b2af39e5172056cec Mon Sep 17 00:00:00 2001 From: Jan Sjodin Date: Mon, 22 Jun 2020 13:04:12 -0400 Subject: [PATCH] llvm-link: Add support for archive files as inputs This patch adds support for archive files as inputs to llvm-link. One of the use-cases is for OpenMP, where device specific libraries need to be extracted from libraries containing bundled object files. The clang-offload-bundler will support extracting these archives, which will be passed into llvm-link, see https://reviews.llvm.org/D80816. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D81109 --- test/tools/llvm-link/Inputs/f.ll | 6 +++ test/tools/llvm-link/Inputs/g.ll | 6 +++ test/tools/llvm-link/Inputs/h.ll | 6 +++ test/tools/llvm-link/archive-bad.ll | 7 +++ test/tools/llvm-link/archive.ll | 17 +++++++ test/tools/llvm-link/archivell.ll | 7 +++ tools/llvm-link/llvm-link.cpp | 73 ++++++++++++++++++++++++++++- 7 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 test/tools/llvm-link/Inputs/f.ll create mode 100644 test/tools/llvm-link/Inputs/g.ll create mode 100644 test/tools/llvm-link/Inputs/h.ll create mode 100644 test/tools/llvm-link/archive-bad.ll create mode 100644 test/tools/llvm-link/archive.ll create mode 100644 test/tools/llvm-link/archivell.ll diff --git a/test/tools/llvm-link/Inputs/f.ll b/test/tools/llvm-link/Inputs/f.ll new file mode 100644 index 00000000000..a7cdacea82f --- /dev/null +++ b/test/tools/llvm-link/Inputs/f.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @f() { +entry: + ret void +} diff --git a/test/tools/llvm-link/Inputs/g.ll b/test/tools/llvm-link/Inputs/g.ll new file mode 100644 index 00000000000..b81de922b4d --- /dev/null +++ b/test/tools/llvm-link/Inputs/g.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @g() { +entry: + ret void +} diff --git a/test/tools/llvm-link/Inputs/h.ll b/test/tools/llvm-link/Inputs/h.ll new file mode 100644 index 00000000000..c2bda1712a4 --- /dev/null +++ b/test/tools/llvm-link/Inputs/h.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @h() { +entry: + ret void +} diff --git a/test/tools/llvm-link/archive-bad.ll b/test/tools/llvm-link/archive-bad.ll new file mode 100644 index 00000000000..80ce6fc1fe0 --- /dev/null +++ b/test/tools/llvm-link/archive-bad.ll @@ -0,0 +1,7 @@ +# RUN: cp %S/Inputs/f.ll %t.fg.a +# RUN: not llvm-link %S/Inputs/h.ll %t.fg.a -o %t.linked.bc 2>&1 | FileCheck %s + +# RUN: rm -f %t.fg.a +# RUN: rm -f %t.linked.bc + +# CHECK: file too small to be an archive diff --git a/test/tools/llvm-link/archive.ll b/test/tools/llvm-link/archive.ll new file mode 100644 index 00000000000..10ab83a3d5b --- /dev/null +++ b/test/tools/llvm-link/archive.ll @@ -0,0 +1,17 @@ +# RUN: llvm-as %S/Inputs/f.ll -o %t.f.bc +# RUN: llvm-as %S/Inputs/g.ll -o %t.g.bc +# RUN: llvm-ar cr %t.fg.a %t.f.bc %t.g.bc +# RUN: llvm-ar cr %t.empty.a +# RUN: llvm-link %S/Inputs/h.ll %t.fg.a %t.empty.a -o %t.linked.bc + +# RUN: llvm-nm %t.linked.bc | FileCheck %s + +# RUN: rm -f %t.f.bc +# RUN: rm -f %t.g.bc +# RUN: rm -f %t.fg.a +# RUN: rm -f %t.empty.a +# RUN: rm -f %t.linked.bc + +# CHECK: -------- T f +# CHECK: -------- T g +# CHECK: -------- T h diff --git a/test/tools/llvm-link/archivell.ll b/test/tools/llvm-link/archivell.ll new file mode 100644 index 00000000000..7474df14e90 --- /dev/null +++ b/test/tools/llvm-link/archivell.ll @@ -0,0 +1,7 @@ +# RUN: llvm-ar cr %t.fg.a %S/Inputs/f.ll llvm-as %S/Inputs/g.ll +# RUN: not llvm-link %S/Inputs/h.ll %t.fg.a -o %t.linked.bc 2>&1 | FileCheck %s + +# RUN: rm -f %t.fg.a +# RUN: rm -f %t.linked.bc + +# CHECK: error: member of archive is not a bitcode file diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index a7cda24bbe0..7141bd1ca7a 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Object/Archive.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" @@ -139,6 +140,73 @@ static std::unique_ptr loadFile(const char *argv0, return Result; } +static std::unique_ptr loadArFile(const char *Argv0, + const std::string &ArchiveName, + LLVMContext &Context, Linker &L, + unsigned OrigFlags, + unsigned ApplicableFlags) { + std::unique_ptr Result(new Module("ArchiveModule", Context)); + if (Verbose) + errs() << "Reading library archive file '" << ArchiveName + << "' to memory\n"; + ErrorOr> Buf = + MemoryBuffer::getFile(ArchiveName, -1, false); + ExitOnErr(errorCodeToError(Buf.getError())); + Error Err = Error::success(); + object::Archive Archive(Buf.get()->getMemBufferRef(), Err); + ExitOnErr(std::move(Err)); + for (const object::Archive::Child &C : Archive.children(Err)) { + Expected Ename = C.getName(); + if (Error E = Ename.takeError()) { + errs() << Argv0 << ": "; + WithColor::error() + << " failed to read name of archive member" + << ArchiveName << "'\n"; + return nullptr; + }; + std::string ChildName = Ename.get().str(); + if (Verbose) + errs() << "Parsing member '" << ChildName + << "' of archive library to module.\n"; + SMDiagnostic ParseErr; + Expected MemBuf = C.getMemoryBufferRef(); + if (Error E = MemBuf.takeError()) { + errs() << Argv0 << ": "; + WithColor::error() << " loading memory for member '" << ChildName + << "' of archive library failed'" << ArchiveName + << "'\n"; + return nullptr; + }; + + if (!isBitcode(reinterpret_cast + (MemBuf.get().getBufferStart()), + reinterpret_cast + (MemBuf.get().getBufferEnd()))) { + errs() << Argv0 << ": "; + WithColor::error() << " member of archive is not a bitcode file: '" + << ChildName << "'\n"; + return nullptr; + } + + std::unique_ptr M = parseIR(MemBuf.get(), ParseErr, Context); + + if (!M.get()) { + errs() << Argv0 << ": "; + WithColor::error() << " parsing member '" << ChildName + << "' of archive library failed'" << ArchiveName + << "'\n"; + return nullptr; + } + if (Verbose) + errs() << "Linking member '" << ChildName << "' of archive library.\n"; + if (L.linkModules(*Result, std::move(M), ApplicableFlags)) + return nullptr; + ApplicableFlags = OrigFlags; + } // end for each child + ExitOnErr(std::move(Err)); + return Result; +} + namespace { /// Helper to load on demand a Module from file and cache it for subsequent @@ -281,7 +349,10 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, // Similar to some flags, internalization doesn't apply to the first file. bool InternalizeLinkedSymbols = false; for (const auto &File : Files) { - std::unique_ptr M = loadFile(argv0, File, Context); + std::unique_ptr M = + (llvm::sys::path::extension(File) == ".a") + ? loadArFile(argv0, File, Context, L, Flags, ApplicableFlags) + : loadFile(argv0, File, Context); if (!M.get()) { errs() << argv0 << ": "; WithColor::error() << " loading file '" << File << "'\n";