<div dir="ltr"><div dir="ltr">On Tue, 14 Jul 2020 at 12:32, Jan Sjodin via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Jan Sjodin<br>
Date: 2020-07-14T15:30:59-04:00<br>
New Revision: 66b409582a1d349a3ce5480237aeab92dd5ebde1<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/66b409582a1d349a3ce5480237aeab92dd5ebde1" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/66b409582a1d349a3ce5480237aeab92dd5ebde1</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/66b409582a1d349a3ce5480237aeab92dd5ebde1.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/66b409582a1d349a3ce5480237aeab92dd5ebde1.diff</a><br>
<br>
LOG: llvm-link: Add support for archive files as inputs<br>
<br>
This patch adds support for archive files as inputs to llvm-link. One<br>
of the use-cases is for OpenMP, where device specific libraries need<br>
to be extracted from libraries containing bundled object files. The<br>
clang-offload-bundler will support extracting these archives, which<br>
will be passed into llvm-link, see <a href="https://reviews.llvm.org/D80816" rel="noreferrer" target="_blank">https://reviews.llvm.org/D80816</a>.<br>
<br>
Reviewed By: jdoerfert<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D81109" rel="noreferrer" target="_blank">https://reviews.llvm.org/D81109</a><br>
<br>
Added: <br>
    llvm/test/tools/llvm-link/Inputs/f.ll<br>
    llvm/test/tools/llvm-link/Inputs/g.ll<br>
    llvm/test/tools/llvm-link/Inputs/h.ll<br>
    llvm/test/tools/llvm-link/archive-bad.ll<br>
    llvm/test/tools/llvm-link/archive.ll<br>
    llvm/test/tools/llvm-link/archivell.ll<br>
<br>
Modified: <br>
    llvm/tools/llvm-link/llvm-link.cpp<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/llvm/test/tools/llvm-link/Inputs/f.ll b/llvm/test/tools/llvm-link/Inputs/f.ll<br>
new file mode 100644<br>
index 000000000000..a7cdacea82fb<br>
--- /dev/null<br>
+++ b/llvm/test/tools/llvm-link/Inputs/f.ll<br>
@@ -0,0 +1,6 @@<br>
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"<br>
+<br>
+define void @f() {<br>
+entry:<br>
+  ret void<br>
+}<br>
<br>
diff  --git a/llvm/test/tools/llvm-link/Inputs/g.ll b/llvm/test/tools/llvm-link/Inputs/g.ll<br>
new file mode 100644<br>
index 000000000000..b81de922b4da<br>
--- /dev/null<br>
+++ b/llvm/test/tools/llvm-link/Inputs/g.ll<br>
@@ -0,0 +1,6 @@<br>
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"<br>
+<br>
+define void @g() {<br>
+entry:<br>
+  ret void<br>
+}<br>
<br>
diff  --git a/llvm/test/tools/llvm-link/Inputs/h.ll b/llvm/test/tools/llvm-link/Inputs/h.ll<br>
new file mode 100644<br>
index 000000000000..c2bda1712a40<br>
--- /dev/null<br>
+++ b/llvm/test/tools/llvm-link/Inputs/h.ll<br>
@@ -0,0 +1,6 @@<br>
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"<br>
+<br>
+define void @h() {<br>
+entry:<br>
+  ret void<br>
+}<br>
<br>
diff  --git a/llvm/test/tools/llvm-link/archive-bad.ll b/llvm/test/tools/llvm-link/archive-bad.ll<br>
new file mode 100644<br>
index 000000000000..80ce6fc1fe0d<br>
--- /dev/null<br>
+++ b/llvm/test/tools/llvm-link/archive-bad.ll<br>
@@ -0,0 +1,7 @@<br>
+# RUN: cp %S/Inputs/f.ll %t.fg.a<br>
+# RUN: not llvm-link %S/Inputs/h.ll %t.fg.a -o %t.linked.bc 2>&1 | FileCheck %s<br>
+<br>
+# RUN: rm -f %t.fg.a<br>
+# RUN: rm -f %t.linked.bc<br>
+<br>
+# CHECK: file too small to be an archive<br>
<br>
diff  --git a/llvm/test/tools/llvm-link/archive.ll b/llvm/test/tools/llvm-link/archive.ll<br>
new file mode 100644<br>
index 000000000000..10ab83a3d5be<br>
--- /dev/null<br>
+++ b/llvm/test/tools/llvm-link/archive.ll<br>
@@ -0,0 +1,17 @@<br>
+# RUN: llvm-as %S/Inputs/f.ll -o %t.f.bc<br>
+# RUN: llvm-as %S/Inputs/g.ll -o %t.g.bc<br>
+# RUN: llvm-ar cr %t.fg.a %t.f.bc %t.g.bc<br>
+# RUN: llvm-ar cr %t.empty.a<br>
+# RUN: llvm-link %S/Inputs/h.ll %t.fg.a %t.empty.a -o %t.linked.bc<br>
+<br>
+# RUN: llvm-nm %t.linked.bc | FileCheck %s<br>
+<br>
+# RUN: rm -f %t.f.bc<br>
+# RUN: rm -f %t.g.bc<br>
+# RUN: rm -f %t.fg.a<br>
+# RUN: rm -f %t.empty.a<br>
+# RUN: rm -f %t.linked.bc<br>
+<br>
+# CHECK: -------- T f<br>
+# CHECK: -------- T g<br>
+# CHECK: -------- T h<br>
<br>
diff  --git a/llvm/test/tools/llvm-link/archivell.ll b/llvm/test/tools/llvm-link/archivell.ll<br>
new file mode 100644<br>
index 000000000000..7474df14e907<br>
--- /dev/null<br>
+++ b/llvm/test/tools/llvm-link/archivell.ll<br>
@@ -0,0 +1,7 @@<br>
+# RUN: llvm-ar cr %t.fg.a %S/Inputs/f.ll llvm-as %S/Inputs/g.ll<br></blockquote><div><br></div><div>Did you really mean to include the 'llvm-as' binary in the archive here? This is causing this test to break in our test environment.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+# RUN: not llvm-link %S/Inputs/h.ll %t.fg.a -o %t.linked.bc 2>&1 | FileCheck %s<br>
+<br>
+# RUN: rm -f %t.fg.a<br>
+# RUN: rm -f %t.linked.bc<br>
+<br>
+# CHECK: error: member of archive is not a bitcode file<br>
<br>
diff  --git a/llvm/tools/llvm-link/llvm-link.cpp b/llvm/tools/llvm-link/llvm-link.cpp<br>
index a7cda24bbe0a..7141bd1ca7a1 100644<br>
--- a/llvm/tools/llvm-link/llvm-link.cpp<br>
+++ b/llvm/tools/llvm-link/llvm-link.cpp<br>
@@ -11,6 +11,7 @@<br>
 //<br>
 //===----------------------------------------------------------------------===//<br>
<br>
+#include "llvm/Object/Archive.h"<br>
 #include "llvm/ADT/STLExtras.h"<br>
 #include "llvm/Bitcode/BitcodeReader.h"<br>
 #include "llvm/Bitcode/BitcodeWriter.h"<br>
@@ -139,6 +140,73 @@ static std::unique_ptr<Module> loadFile(const char *argv0,<br>
   return Result;<br>
 }<br>
<br>
+static std::unique_ptr<Module> loadArFile(const char *Argv0,<br>
+                                          const std::string &ArchiveName,<br>
+                                          LLVMContext &Context, Linker &L,<br>
+                                          unsigned OrigFlags,<br>
+                                          unsigned ApplicableFlags) {<br>
+  std::unique_ptr<Module> Result(new Module("ArchiveModule", Context));<br>
+  if (Verbose)<br>
+    errs() << "Reading library archive file '" << ArchiveName<br>
+           << "' to memory\n";<br>
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =<br>
+    MemoryBuffer::getFile(ArchiveName, -1, false);<br>
+  ExitOnErr(errorCodeToError(Buf.getError()));<br>
+  Error Err = Error::success();<br>
+  object::Archive Archive(Buf.get()->getMemBufferRef(), Err);<br>
+  ExitOnErr(std::move(Err));<br>
+  for (const object::Archive::Child &C : Archive.children(Err)) {<br>
+    Expected<StringRef> Ename = C.getName();<br>
+    if (Error E = Ename.takeError()) {<br>
+      errs() << Argv0 << ": ";<br>
+      WithColor::error()<br>
+          << " failed to read name of archive member"<br>
+          << ArchiveName << "'\n";<br>
+      return nullptr;<br>
+    };<br>
+    std::string ChildName = Ename.get().str();<br>
+    if (Verbose)<br>
+      errs() << "Parsing member '" << ChildName<br>
+             << "' of archive library to module.\n";<br>
+    SMDiagnostic ParseErr;<br>
+    Expected<MemoryBufferRef> MemBuf = C.getMemoryBufferRef();<br>
+    if (Error E = MemBuf.takeError()) {<br>
+      errs() << Argv0 << ": ";<br>
+      WithColor::error() << " loading memory for member '" << ChildName<br>
+                         << "' of archive library failed'" << ArchiveName<br>
+                         << "'\n";<br>
+      return nullptr;<br>
+    };<br>
+<br>
+    if (!isBitcode(reinterpret_cast<const unsigned char *><br>
+                   (MemBuf.get().getBufferStart()),<br>
+                   reinterpret_cast<const unsigned char *><br>
+                   (MemBuf.get().getBufferEnd()))) {<br>
+      errs() << Argv0 << ": ";<br>
+      WithColor::error() << "  member of archive is not a bitcode file: '"<br>
+                         << ChildName << "'\n";<br>
+      return nullptr;<br>
+    }<br>
+<br>
+    std::unique_ptr<Module> M = parseIR(MemBuf.get(), ParseErr, Context);<br>
+<br>
+    if (!M.get()) {<br>
+      errs() << Argv0 << ": ";<br>
+      WithColor::error() << " parsing member '" << ChildName<br>
+                         << "' of archive library failed'" << ArchiveName<br>
+                         << "'\n";<br>
+      return nullptr;<br>
+    }<br>
+    if (Verbose)<br>
+      errs() << "Linking member '" << ChildName << "' of archive library.\n";<br>
+    if (L.linkModules(*Result, std::move(M), ApplicableFlags))<br>
+      return nullptr;<br>
+    ApplicableFlags = OrigFlags;<br>
+  } // end for each child<br>
+  ExitOnErr(std::move(Err));<br>
+  return Result;<br>
+}<br>
+<br>
 namespace {<br>
<br>
 /// Helper to load on demand a Module from file and cache it for subsequent<br>
@@ -281,7 +349,10 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,<br>
   // Similar to some flags, internalization doesn't apply to the first file.<br>
   bool InternalizeLinkedSymbols = false;<br>
   for (const auto &File : Files) {<br>
-    std::unique_ptr<Module> M = loadFile(argv0, File, Context);<br>
+    std::unique_ptr<Module> M =<br>
+      (llvm::sys::path::extension(File) == ".a")<br>
+          ? loadArFile(argv0, File, Context, L, Flags, ApplicableFlags)<br>
+          : loadFile(argv0, File, Context);<br>
     if (!M.get()) {<br>
       errs() << argv0 << ": ";<br>
       WithColor::error() << " loading file '" << File << "'\n";<br>
<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div></div>