[llvm] c6f7ac0 - [llvm-lipo] Add support for bitcode files

Alexander Shaposhnikov via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 21:11:36 PDT 2020


Author: Adrien Guinet
Date: 2020-08-25T21:11:18-07:00
New Revision: c6f7ac0071a1849a9f8046e2045e1631e224f1bd

URL: https://github.com/llvm/llvm-project/commit/c6f7ac0071a1849a9f8046e2045e1631e224f1bd
DIFF: https://github.com/llvm/llvm-project/commit/c6f7ac0071a1849a9f8046e2045e1631e224f1bd.diff

LOG: [llvm-lipo] Add support for bitcode files

A Mach-O universal binary may contain bitcode as a slice.
This diff adds proper handling of such binaries to llvm-lipo.

Test plan: make check-all

Differential revision: https://reviews.llvm.org/D85740

Added: 
    llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll
    llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll
    llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll
    llvm/test/tools/llvm-lipo/archs-ir-binary.test
    llvm/test/tools/llvm-lipo/create-arch-ir.test
    llvm/test/tools/llvm-lipo/info-universal-binary-ir.test
    llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test

Modified: 
    llvm/include/llvm/Object/Binary.h
    llvm/include/llvm/Object/MachOUniversal.h
    llvm/include/llvm/Object/MachOUniversalWriter.h
    llvm/lib/Object/Binary.cpp
    llvm/lib/Object/MachOUniversal.cpp
    llvm/lib/Object/MachOUniversalWriter.cpp
    llvm/test/tools/llvm-lipo/create-archive-input.test
    llvm/tools/llvm-lipo/CMakeLists.txt
    llvm/tools/llvm-lipo/LLVMBuild.txt
    llvm/tools/llvm-lipo/llvm-lipo.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h
index e95516f30a40..de0f48e7f4f5 100644
--- a/llvm/include/llvm/Object/Binary.h
+++ b/llvm/include/llvm/Object/Binary.h
@@ -228,7 +228,8 @@ template <typename T> const T* OwningBinary<T>::getBinary() const {
   return Bin.get();
 }
 
-Expected<OwningBinary<Binary>> createBinary(StringRef Path);
+Expected<OwningBinary<Binary>> createBinary(StringRef Path,
+                                            LLVMContext *Context = nullptr);
 
 } // end namespace object
 

diff  --git a/llvm/include/llvm/Object/MachOUniversal.h b/llvm/include/llvm/Object/MachOUniversal.h
index 5e006fd87318..9bcacb510108 100644
--- a/llvm/include/llvm/Object/MachOUniversal.h
+++ b/llvm/include/llvm/Object/MachOUniversal.h
@@ -22,8 +22,11 @@
 
 namespace llvm {
 class StringRef;
+class Module;
+class LLVMContext;
 
 namespace object {
+class IRObjectFile;
 
 class MachOUniversalBinary : public Binary {
   virtual void anchor();
@@ -101,6 +104,8 @@ class MachOUniversalBinary : public Binary {
     }
 
     Expected<std::unique_ptr<MachOObjectFile>> getAsObjectFile() const;
+    Expected<std::unique_ptr<IRObjectFile>>
+    getAsIRObject(LLVMContext &Ctx) const;
 
     Expected<std::unique_ptr<Archive>> getAsArchive() const;
   };
@@ -154,6 +159,9 @@ class MachOUniversalBinary : public Binary {
   Expected<std::unique_ptr<MachOObjectFile>>
   getMachOObjectForArch(StringRef ArchName) const;
 
+  Expected<std::unique_ptr<IRObjectFile>>
+  getIRObjectForArch(StringRef ArchName, LLVMContext &Ctx) const;
+
   Expected<std::unique_ptr<Archive>>
   getArchiveForArch(StringRef ArchName) const;
 };

diff  --git a/llvm/include/llvm/Object/MachOUniversalWriter.h b/llvm/include/llvm/Object/MachOUniversalWriter.h
index c860495ddd6f..643b70e8eaf1 100644
--- a/llvm/include/llvm/Object/MachOUniversalWriter.h
+++ b/llvm/include/llvm/Object/MachOUniversalWriter.h
@@ -19,7 +19,10 @@
 #include "llvm/Object/MachO.h"
 
 namespace llvm {
+class LLVMContext;
+
 namespace object {
+class IRObjectFile;
 
 class Slice {
   const Binary *B;
@@ -32,12 +35,18 @@ class Slice {
   // file size can be calculated before creating the output buffer.
   uint32_t P2Alignment;
 
+  Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType,
+        std::string ArchName, uint32_t Align);
+
 public:
   explicit Slice(const MachOObjectFile &O);
 
   Slice(const MachOObjectFile &O, uint32_t Align);
 
-  static Expected<Slice> create(const Archive *A);
+  static Expected<Slice> create(const Archive *A,
+                                LLVMContext *LLVMCtx = nullptr);
+
+  static Expected<Slice> create(const IRObjectFile *IRO, uint32_t Align);
 
   void setP2Alignment(uint32_t Align) { P2Alignment = Align; }
 

diff  --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp
index 944d2bc1bca7..384df4b84358 100644
--- a/llvm/lib/Object/Binary.cpp
+++ b/llvm/lib/Object/Binary.cpp
@@ -93,7 +93,8 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
   llvm_unreachable("Unexpected Binary File Type");
 }
 
-Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
+Expected<OwningBinary<Binary>> object::createBinary(StringRef Path,
+                                                    LLVMContext *Context) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
       MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
                                    /*RequiresNullTerminator=*/false);
@@ -102,7 +103,7 @@ Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
   std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
 
   Expected<std::unique_ptr<Binary>> BinOrErr =
-      createBinary(Buffer->getMemBufferRef());
+      createBinary(Buffer->getMemBufferRef(), Context);
   if (!BinOrErr)
     return BinOrErr.takeError();
   std::unique_ptr<Binary> &Bin = BinOrErr.get();

diff  --git a/llvm/lib/Object/MachOUniversal.cpp b/llvm/lib/Object/MachOUniversal.cpp
index a178ecde949e..f3ce005e6ef9 100644
--- a/llvm/lib/Object/MachOUniversal.cpp
+++ b/llvm/lib/Object/MachOUniversal.cpp
@@ -12,6 +12,7 @@
 
 #include "llvm/Object/MachOUniversal.h"
 #include "llvm/Object/Archive.h"
+#include "llvm/Object/IRObjectFile.h"
 #include "llvm/Object/MachO.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/Casting.h"
@@ -80,6 +81,25 @@ MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
   return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index);
 }
 
+Expected<std::unique_ptr<IRObjectFile>>
+MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext &Ctx) const {
+  if (!Parent)
+    report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() "
+                       "called when Parent is a nullptr");
+
+  StringRef ParentData = Parent->getData();
+  StringRef ObjectData;
+  if (Parent->getMagic() == MachO::FAT_MAGIC) {
+    ObjectData = ParentData.substr(Header.offset, Header.size);
+  } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
+    ObjectData = ParentData.substr(Header64.offset, Header64.size);
+  }
+  StringRef ObjectName = Parent->getFileName();
+  MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
+
+  return IRObjectFile::create(ObjBuffer, Ctx);
+}
+
 Expected<std::unique_ptr<Archive>>
 MachOUniversalBinary::ObjectForArch::getAsArchive() const {
   if (!Parent)
@@ -234,6 +254,15 @@ MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const {
   return O->getAsObjectFile();
 }
 
+Expected<std::unique_ptr<IRObjectFile>>
+MachOUniversalBinary::getIRObjectForArch(StringRef ArchName,
+                                         LLVMContext &Ctx) const {
+  Expected<ObjectForArch> O = getObjectForArch(ArchName);
+  if (!O)
+    return O.takeError();
+  return O->getAsIRObject(Ctx);
+}
+
 Expected<std::unique_ptr<Archive>>
 MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const {
   Expected<ObjectForArch> O = getObjectForArch(ArchName);

diff  --git a/llvm/lib/Object/MachOUniversalWriter.cpp b/llvm/lib/Object/MachOUniversalWriter.cpp
index 169d64430284..35db26aae632 100644
--- a/llvm/lib/Object/MachOUniversalWriter.cpp
+++ b/llvm/lib/Object/MachOUniversalWriter.cpp
@@ -12,9 +12,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Object/MachOUniversalWriter.h"
+#include "llvm/ADT/Triple.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/Error.h"
+#include "llvm/Object/IRObjectFile.h"
 #include "llvm/Object/MachO.h"
 #include "llvm/Object/MachOUniversal.h"
 #include "llvm/Support/FileOutputBuffer.h"
@@ -79,13 +81,36 @@ Slice::Slice(const MachOObjectFile &O, uint32_t Align)
       ArchName(std::string(O.getArchTriple().getArchName())),
       P2Alignment(Align) {}
 
+Slice::Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType,
+             std::string ArchName, uint32_t Align)
+    : B(IRO), CPUType(CPUType), CPUSubType(CPUSubType),
+      ArchName(std::move(ArchName)), P2Alignment(Align) {}
+
 Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {}
 
-Expected<Slice> Slice::create(const Archive *A) {
+using MachoCPUTy = std::pair<unsigned, unsigned>;
+
+static Expected<MachoCPUTy> getMachoCPUFromTriple(Triple TT) {
+  auto CPU = std::make_pair(MachO::getCPUType(TT), MachO::getCPUSubType(TT));
+  if (!CPU.first) {
+    return CPU.first.takeError();
+  }
+  if (!CPU.second) {
+    return CPU.second.takeError();
+  }
+  return std::make_pair(*CPU.first, *CPU.second);
+}
+
+static Expected<MachoCPUTy> getMachoCPUFromTriple(StringRef TT) {
+  return getMachoCPUFromTriple(Triple{TT});
+}
+
+Expected<Slice> Slice::create(const Archive *A, LLVMContext *LLVMCtx) {
   Error Err = Error::success();
-  std::unique_ptr<MachOObjectFile> FO = nullptr;
+  std::unique_ptr<MachOObjectFile> MFO = nullptr;
+  std::unique_ptr<IRObjectFile> IRFO = nullptr;
   for (const Archive::Child &Child : A->children(Err)) {
-    Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
+    Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(LLVMCtx);
     if (!ChildOrErr)
       return createFileError(A->getFileName(), ChildOrErr.takeError());
     Binary *Bin = ChildOrErr.get().get();
@@ -95,36 +120,79 @@ Expected<Slice> Slice::create(const Archive *A) {
                                 " is a fat file (not allowed in an archive)")
                                    .str()
                                    .c_str());
-    if (!Bin->isMachO())
-      return createStringError(
-          std::errc::invalid_argument,
-          ("archive member " + Bin->getFileName() +
-           " is not a MachO file (not allowed in an archive)")
-              .str()
-              .c_str());
-    MachOObjectFile *O = cast<MachOObjectFile>(Bin);
-    if (FO && std::tie(FO->getHeader().cputype, FO->getHeader().cpusubtype) !=
-                  std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
-      return createStringError(
-          std::errc::invalid_argument,
-          ("archive member " + O->getFileName() + " cputype (" +
-           Twine(O->getHeader().cputype) + ") and cpusubtype(" +
-           Twine(O->getHeader().cpusubtype) +
-           ") does not match previous archive members cputype (" +
-           Twine(FO->getHeader().cputype) + ") and cpusubtype(" +
-           Twine(FO->getHeader().cpusubtype) + ") (all members must match) " +
-           FO->getFileName())
-              .str()
-              .c_str());
-    }
-    if (!FO) {
-      ChildOrErr.get().release();
-      FO.reset(O);
-    }
+    if (Bin->isMachO()) {
+      MachOObjectFile *O = cast<MachOObjectFile>(Bin);
+      if (IRFO) {
+        return createStringError(
+            std::errc::invalid_argument,
+            "archive member %s is a MachO, while previous archive member "
+            "%s was an IR LLVM object",
+            O->getFileName().str().c_str(), IRFO->getFileName().str().c_str());
+      }
+      if (MFO &&
+          std::tie(MFO->getHeader().cputype, MFO->getHeader().cpusubtype) !=
+              std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
+        return createStringError(
+            std::errc::invalid_argument,
+            ("archive member " + O->getFileName() + " cputype (" +
+             Twine(O->getHeader().cputype) + ") and cpusubtype(" +
+             Twine(O->getHeader().cpusubtype) +
+             ") does not match previous archive members cputype (" +
+             Twine(MFO->getHeader().cputype) + ") and cpusubtype(" +
+             Twine(MFO->getHeader().cpusubtype) +
+             ") (all members must match) " + MFO->getFileName())
+                .str()
+                .c_str());
+      }
+      if (!MFO) {
+        ChildOrErr.get().release();
+        MFO.reset(O);
+      }
+    } else if (Bin->isIR()) {
+      IRObjectFile *O = cast<IRObjectFile>(Bin);
+      if (MFO) {
+        return createStringError(std::errc::invalid_argument,
+                                 "archive member '%s' is an LLVM IR object, "
+                                 "while previous archive member "
+                                 "'%s' was a MachO",
+                                 O->getFileName().str().c_str(),
+                                 MFO->getFileName().str().c_str());
+      }
+      if (IRFO) {
+        Expected<MachoCPUTy> CPUO = getMachoCPUFromTriple(O->getTargetTriple());
+        Expected<MachoCPUTy> CPUFO =
+            getMachoCPUFromTriple(IRFO->getTargetTriple());
+        if (!CPUO)
+          return CPUO.takeError();
+        if (!CPUFO)
+          return CPUFO.takeError();
+        if (*CPUO != *CPUFO) {
+          return createStringError(
+              std::errc::invalid_argument,
+              ("archive member " + O->getFileName() + " cputype (" +
+               Twine(CPUO->first) + ") and cpusubtype(" + Twine(CPUO->second) +
+               ") does not match previous archive members cputype (" +
+               Twine(CPUFO->first) + ") and cpusubtype(" +
+               Twine(CPUFO->second) + ") (all members must match) " +
+               IRFO->getFileName())
+                  .str()
+                  .c_str());
+        }
+      } else {
+        ChildOrErr.get().release();
+        IRFO.reset(O);
+      }
+    } else
+      return createStringError(std::errc::invalid_argument,
+                               ("archive member " + Bin->getFileName() +
+                                " is neither a MachO file or an LLVM IR file "
+                                "(not allowed in an archive)")
+                                   .str()
+                                   .c_str());
   }
   if (Err)
     return createFileError(A->getFileName(), std::move(Err));
-  if (!FO)
+  if (!MFO && !IRFO)
     return createStringError(
         std::errc::invalid_argument,
         ("empty archive with no architecture specification: " +
@@ -132,9 +200,32 @@ Expected<Slice> Slice::create(const Archive *A) {
             .str()
             .c_str());
 
-  Slice ArchiveSlice = Slice(*(FO.get()), FO->is64Bit() ? 3 : 2);
+  if (MFO) {
+    Slice ArchiveSlice(*(MFO.get()), MFO->is64Bit() ? 3 : 2);
+    ArchiveSlice.B = A;
+    return ArchiveSlice;
+  }
+
+  // For IR objects
+  Expected<Slice> ArchiveSliceOrErr = Slice::create(IRFO.get(), 0);
+  if (!ArchiveSliceOrErr)
+    return createFileError(A->getFileName(), ArchiveSliceOrErr.takeError());
+  auto &ArchiveSlice = ArchiveSliceOrErr.get();
   ArchiveSlice.B = A;
-  return ArchiveSlice;
+  return Slice{std::move(ArchiveSlice)};
+}
+
+Expected<Slice> Slice::create(const IRObjectFile *IRO, uint32_t Align) {
+  Expected<MachoCPUTy> CPUOrErr = getMachoCPUFromTriple(IRO->getTargetTriple());
+  if (!CPUOrErr)
+    return CPUOrErr.takeError();
+  unsigned CPUType, CPUSubType;
+  std::tie(CPUType, CPUSubType) = CPUOrErr.get();
+  // We don't directly use the architecture name of the target triple T, as,
+  // for instance, thumb is treated as ARM by the MachOUniversal object.
+  std::string ArchName(
+      MachOObjectFile::getArchTriple(CPUType, CPUSubType).getArchName());
+  return Slice{IRO, CPUType, CPUSubType, std::move(ArchName), Align};
 }
 
 static Expected<SmallVector<MachO::fat_arch, 2>>

diff  --git a/llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll b/llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll
new file mode 100644
index 000000000000..89d2d37e39a0
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll
@@ -0,0 +1 @@
+target triple = "arm64-apple-ios8.0.0"

diff  --git a/llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll b/llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll
new file mode 100644
index 000000000000..4356873db7e2
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll
@@ -0,0 +1 @@
+target triple = "thumbv7-apple-ios8.0.0"

diff  --git a/llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll b/llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll
new file mode 100644
index 000000000000..49bfad958c95
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll
@@ -0,0 +1 @@
+target triple = "x86_64-apple-macosx10.15.0"

diff  --git a/llvm/test/tools/llvm-lipo/archs-ir-binary.test b/llvm/test/tools/llvm-lipo/archs-ir-binary.test
new file mode 100644
index 000000000000..e62f2220fa53
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/archs-ir-binary.test
@@ -0,0 +1,4 @@
+# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o
+# RUN: llvm-lipo -archs %t-armv7.o | FileCheck %s
+
+# CHECK: armv7

diff  --git a/llvm/test/tools/llvm-lipo/create-arch-ir.test b/llvm/test/tools/llvm-lipo/create-arch-ir.test
new file mode 100644
index 000000000000..9c3c0785e1e7
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/create-arch-ir.test
@@ -0,0 +1,9 @@
+# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o
+# RUN: llvm-as %p/Inputs/arm64-ios.ll -o %t-arm64.o
+
+# RUN: llvm-lipo %t-armv7.o %t-arm64.o -create -output %t-universal.o
+# RUN: llvm-lipo -arch armv7 %t-armv7.o -arch arm64 %t-arm64.o -create -output %t-universal-1.o
+# RUN: cmp %t-universal.o %t-universal-1.o
+#
+# RUN: not llvm-lipo -arch armv7 %t-arm64.o -create -output /dev/null 2>&1 | FileCheck --check-prefix=ARCH_NOT_MATCHED %s
+# ARCH_NOT_MATCHED: error: specified architecture: armv7 for file: {{.*}} does not match the file's architecture (arm64)

diff  --git a/llvm/test/tools/llvm-lipo/create-archive-input.test b/llvm/test/tools/llvm-lipo/create-archive-input.test
index b40312528e05..5558797db87b 100644
--- a/llvm/test/tools/llvm-lipo/create-archive-input.test
+++ b/llvm/test/tools/llvm-lipo/create-archive-input.test
@@ -1,6 +1,8 @@
 # RUN: yaml2obj %p/Inputs/i386-slice.yaml -o %t-i386.o
 # RUN: yaml2obj %p/Inputs/x86_64-slice.yaml -o %t-x86_64.o
 # RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml -o %t-universal.o
+# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-ir-armv7.o
+# RUN: llvm-as %p/Inputs/x64-osx.ll -o %t-ir-x86_64.o
 
 # RUN: llvm-ar cr %t.empty.a
 # RUN: not llvm-lipo %t.empty.a -create -output /dev/null 2>&1 | FileCheck --check-prefix=EMPTY-ARCHIVE %s
@@ -18,9 +20,25 @@
 # RUN: cmp %t-extracted-i386-lib.a %t-i386-lib.a
 # RUN: llvm-lipo %t-i386-x86_64-universal.o -thin x86_64 -output %t-extracted-x86_64.o
 # RUN: cmp %t-extracted-x86_64.o %t-x86_64.o
-#
+
+# RUN: llvm-ar cr %t-ir-armv7-lib.a %t-ir-armv7.o
+# RUN: llvm-lipo %t-ir-armv7-lib.a %t-ir-x86_64.o -create -output %t-ir-armv7-x86_64-universal.o
+# RUN: llvm-lipo %t-ir-armv7-x86_64-universal.o -thin armv7 -output %t-ir-extracted-armv7-lib.a
+# RUN: cmp %t-ir-extracted-armv7-lib.a %t-ir-armv7-lib.a
+# RUN: llvm-lipo %t-ir-armv7-x86_64-universal.o -thin x86_64 -output %t-ir-extracted-x86_64.o
+# RUN: cmp %t-ir-extracted-x86_64.o %t-ir-x86_64.o
+
+# RUN: llvm-ar cr %t.
diff erent_types0.a %t-i386.o %t-ir-x86_64.o
+# RUN: not llvm-lipo -create %t.
diff erent_types0.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-MACHO-AND-IR %s
+# RUN: llvm-ar cr %t.
diff erent_types1.a %t-ir-x86_64.o %t-i386.o 
+# RUN: not llvm-lipo -create %t.
diff erent_types1.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-IR-AND-MACHO %s
+# RUN: llvm-ar cr %t.
diff erent_architectures_ir.a %t-ir-x86_64.o %t-ir-armv7.o
+# RUN: not llvm-lipo -create %t.
diff erent_architectures_ir.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-DIFFERENT-ARCHS %s
+
 # EMPTY-ARCHIVE: empty archive
 # ARCHIVE-WITH-DIFFERENT-ARCHS: all members must match
+# ARCHIVE-WITH-MACHO-AND-IR: is an LLVM IR object, while previous archive member {{.*}} was a MachO
+# ARCHIVE-WITH-IR-AND-MACHO: is a MachO, while previous archive member {{.*}} was an IR LLVM object
 # ARCHIVE-WITH-FAT-BINARY: fat file (not allowed in an archive)
 #
 # INFO-i386-x86_64: i386 x86_64

diff  --git a/llvm/test/tools/llvm-lipo/info-universal-binary-ir.test b/llvm/test/tools/llvm-lipo/info-universal-binary-ir.test
new file mode 100644
index 000000000000..4180220f8771
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/info-universal-binary-ir.test
@@ -0,0 +1,16 @@
+# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o
+# RUN: llvm-as %p/Inputs/arm64-ios.ll -o %t-arm64.o
+# RUN: llvm-as %p/Inputs/x64-osx.ll -o %t-x64.o
+
+# RUN: llvm-lipo %t-arm64.o %t-x64.o -create -output %t-universal0.o
+# RUN: llvm-lipo -info %t-universal0.o | FileCheck %s
+#
+# CHECK: Architectures in the fat file: {{.*}} are: x86_64 arm64
+
+# Test that, despites the "thumbv7" triple present in armv7-ios.ll,
+# architecture is still reported as armv7
+# RUN: llvm-lipo %t-armv7.o -create -output %t-universal1.o
+# RUN: llvm-lipo -info %t-universal1.o | FileCheck --check-prefix=ARMV7 %s
+#
+# ARMV7: armv7
+# ARMV7-NOT: thumbv7

diff  --git a/llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test b/llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test
new file mode 100644
index 000000000000..eff0fdc29439
--- /dev/null
+++ b/llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test
@@ -0,0 +1,11 @@
+# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o
+# RUN: llvm-as %p/Inputs/arm64-ios.ll -o %t-arm64.o
+
+# RUN: llvm-lipo %t-armv7.o %t-arm64.o -create -output %t-universal.o
+# RUN: llvm-lipo %t-universal.o -thin arm64 -output %t-arm64.out
+# RUN: opt -S %t-arm64.out | FileCheck --check-prefix=ARM64 %s
+# RUN: llvm-lipo %t-universal.o -thin armv7 -output %t-armv7.out
+# RUN: opt -S %t-armv7.out | FileCheck --check-prefix=ARMV7 %s
+
+# ARM64: arm64-apple-ios8.0.0
+# ARMV7: thumbv7-apple-ios8.0.0

diff  --git a/llvm/tools/llvm-lipo/CMakeLists.txt b/llvm/tools/llvm-lipo/CMakeLists.txt
index 335b286dd5f5..2f582cbb7418 100644
--- a/llvm/tools/llvm-lipo/CMakeLists.txt
+++ b/llvm/tools/llvm-lipo/CMakeLists.txt
@@ -4,6 +4,8 @@ set(LLVM_LINK_COMPONENTS
   Option
   Support
   TextAPI
+  Core
+  BinaryFormat
 )
 
 set(LLVM_TARGET_DEFINITIONS LipoOpts.td)

diff  --git a/llvm/tools/llvm-lipo/LLVMBuild.txt b/llvm/tools/llvm-lipo/LLVMBuild.txt
index fa807add933a..744c9add7e3a 100644
--- a/llvm/tools/llvm-lipo/LLVMBuild.txt
+++ b/llvm/tools/llvm-lipo/LLVMBuild.txt
@@ -17,4 +17,4 @@
 type = Tool
 name = llvm-lipo
 parent = Tools
-required_libraries = Object Option Support
+required_libraries = Object Option Support BinaryFormat

diff  --git a/llvm/tools/llvm-lipo/llvm-lipo.cpp b/llvm/tools/llvm-lipo/llvm-lipo.cpp
index f6d220257902..82d476f48738 100644
--- a/llvm/tools/llvm-lipo/llvm-lipo.cpp
+++ b/llvm/tools/llvm-lipo/llvm-lipo.cpp
@@ -12,7 +12,11 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Triple.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
 #include "llvm/Object/Binary.h"
+#include "llvm/Object/IRObjectFile.h"
 #include "llvm/Object/MachO.h"
 #include "llvm/Object/MachOUniversal.h"
 #include "llvm/Object/MachOUniversalWriter.h"
@@ -30,6 +34,7 @@ using namespace llvm;
 using namespace llvm::object;
 
 static const StringRef ToolName = "llvm-lipo";
+static LLVMContext LLVMCtx;
 
 LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) {
   WithColor::error(errs(), ToolName) << Message << "\n";
@@ -113,13 +118,20 @@ struct Config {
   LipoAction ActionToPerform;
 };
 
-static Slice archiveSlice(const Archive *A, StringRef File) {
-  Expected<Slice> ArchiveOrSlice = Slice::create(A);
+static Slice createSliceFromArchive(const Archive *A) {
+  Expected<Slice> ArchiveOrSlice = Slice::create(A, &LLVMCtx);
   if (!ArchiveOrSlice)
-    reportError(File, ArchiveOrSlice.takeError());
+    reportError(A->getFileName(), ArchiveOrSlice.takeError());
   return *ArchiveOrSlice;
 }
 
+static Slice createSliceFromIR(const IRObjectFile *IRO, unsigned Align) {
+  Expected<Slice> IROrErr = Slice::create(IRO, Align);
+  if (!IROrErr)
+    reportError(IRO->getFileName(), IROrErr.takeError());
+  return *IROrErr;
+}
+
 } // end namespace
 
 static void validateArchitectureName(StringRef ArchitectureName) {
@@ -307,15 +319,20 @@ static SmallVector<OwningBinary<Binary>, 1>
 readInputBinaries(ArrayRef<InputFile> InputFiles) {
   SmallVector<OwningBinary<Binary>, 1> InputBinaries;
   for (const InputFile &IF : InputFiles) {
-    Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(IF.FileName);
+    Expected<OwningBinary<Binary>> BinaryOrErr =
+        createBinary(IF.FileName, &LLVMCtx);
     if (!BinaryOrErr)
       reportError(IF.FileName, BinaryOrErr.takeError());
     const Binary *B = BinaryOrErr->getBinary();
-    if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary())
+    if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary() &&
+        !B->isIR())
       reportError("File " + IF.FileName + " has unsupported binary format");
-    if (IF.ArchType && (B->isMachO() || B->isArchive())) {
-      const auto S = B->isMachO() ? Slice(*cast<MachOObjectFile>(B))
-                                  : archiveSlice(cast<Archive>(B), IF.FileName);
+    if (IF.ArchType && (B->isMachO() || B->isArchive() || B->isIR())) {
+      const auto S = B->isMachO()
+                         ? Slice(*cast<MachOObjectFile>(B))
+                         : B->isArchive()
+                               ? createSliceFromArchive(cast<Archive>(B))
+                               : createSliceFromIR(cast<IRObjectFile>(B), 0);
       const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
                                         MachO::getArchitectureFromName(
                                             Triple(*IF.ArchType).getArchName()))
@@ -367,27 +384,55 @@ static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) {
   // Prints trailing space for compatibility with cctools lipo.
   if (auto UO = dyn_cast<MachOUniversalBinary>(Binary)) {
     for (const auto &O : UO->objects()) {
+      // Order here is important, because both MachOObjectFile and
+      // IRObjectFile can be created with a binary that has embedded bitcode.
       Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError =
           O.getAsObjectFile();
       if (MachOObjOrError) {
         OS << Slice(*(MachOObjOrError->get())).getArchString() << " ";
         continue;
       }
+      Expected<std::unique_ptr<IRObjectFile>> IROrError =
+          O.getAsIRObject(LLVMCtx);
+      if (IROrError) {
+        consumeError(MachOObjOrError.takeError());
+        Expected<Slice> SliceOrErr =
+            Slice::create(IROrError->get(), O.getAlign());
+        if (!SliceOrErr) {
+          reportError(Binary->getFileName(), SliceOrErr.takeError());
+          continue;
+        }
+        OS << SliceOrErr.get().getArchString() << " ";
+        continue;
+      }
       Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
       if (ArchiveOrError) {
         consumeError(MachOObjOrError.takeError());
-        OS << archiveSlice(ArchiveOrError->get(), Binary->getFileName())
-                  .getArchString()
+        consumeError(IROrError.takeError());
+        OS << createSliceFromArchive(ArchiveOrError->get()).getArchString()
            << " ";
         continue;
       }
       consumeError(ArchiveOrError.takeError());
       reportError(Binary->getFileName(), MachOObjOrError.takeError());
+      reportError(Binary->getFileName(), IROrError.takeError());
     }
     OS << "\n";
     return;
   }
-  OS << Slice(*cast<MachOObjectFile>(Binary)).getArchString() << " \n";
+
+  if (const auto *MachO = dyn_cast<MachOObjectFile>(Binary)) {
+    OS << Slice(*MachO).getArchString() << " \n";
+    return;
+  }
+
+  // This should be always the case, as this is tested in readInputBinaries
+  const auto *IR = cast<IRObjectFile>(Binary);
+  Expected<Slice> SliceOrErr = createSliceFromIR(IR, 0);
+  if (!SliceOrErr)
+    reportError(IR->getFileName(), SliceOrErr.takeError());
+  
+  OS << SliceOrErr->getArchString() << " \n";
 }
 
 LLVM_ATTRIBUTE_NORETURN
@@ -437,13 +482,23 @@ static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
   auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
   Expected<std::unique_ptr<MachOObjectFile>> Obj =
       UO->getMachOObjectForArch(ArchType);
+  Expected<std::unique_ptr<IRObjectFile>> IRObj =
+      UO->getIRObjectForArch(ArchType, LLVMCtx);
   Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType);
-  if (!Obj && !Ar)
+  if (!Obj && !IRObj && !Ar)
     reportError("fat input file " + UO->getFileName() +
                 " does not contain the specified architecture " + ArchType +
                 " to thin it to");
-  Binary *B = Obj ? static_cast<Binary *>(Obj->get())
-                  : static_cast<Binary *>(Ar->get());
+  Binary *B;
+  // Order here is important, because both Obj and IRObj will be valid with a
+  // binary that has embedded bitcode.
+  if (Obj)
+    B = Obj->get();
+  else if (IRObj)
+    B = IRObj->get();
+  else
+    B = Ar->get();
+
   Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
       FileOutputBuffer::create(OutputFileName,
                                B->getMemoryBufferRef().getBufferSize(),
@@ -498,26 +553,48 @@ static void checkUnusedAlignments(ArrayRef<Slice> Slices,
 
 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
 // Universal Binary files to transfer ownership.
-static SmallVector<Slice, 2> buildSlices(
-    ArrayRef<OwningBinary<Binary>> InputBinaries,
-    const StringMap<const uint32_t> &Alignments,
-    SmallVectorImpl<std::unique_ptr<MachOObjectFile>> &ExtractedObjects) {
+static SmallVector<Slice, 2>
+buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
+            const StringMap<const uint32_t> &Alignments,
+            SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) {
   SmallVector<Slice, 2> Slices;
   for (auto &IB : InputBinaries) {
     const Binary *InputBinary = IB.getBinary();
     if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
       for (const auto &O : UO->objects()) {
+        // Order here is important, because both MachOObjectFile and
+        // IRObjectFile can be created with a binary that has embedded bitcode.
         Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
             O.getAsObjectFile();
-        if (!BinaryOrError)
-          reportError(InputBinary->getFileName(), BinaryOrError.takeError());
-        ExtractedObjects.push_back(std::move(BinaryOrError.get()));
-        Slices.emplace_back(*(ExtractedObjects.back().get()), O.getAlign());
+        if (BinaryOrError) {
+          Slices.emplace_back(*(BinaryOrError.get()), O.getAlign());
+          ExtractedObjects.push_back(std::move(BinaryOrError.get()));
+          continue;
+        }
+        Expected<std::unique_ptr<IRObjectFile>> IROrError =
+            O.getAsIRObject(LLVMCtx);
+        if (IROrError) {
+          consumeError(BinaryOrError.takeError());
+          Slice S = createSliceFromIR(
+              static_cast<IRObjectFile *>(IROrError.get().get()), O.getAlign());
+          ExtractedObjects.emplace_back(std::move(IROrError.get()));
+          Slices.emplace_back(std::move(S));
+          continue;
+        }
+        reportError(InputBinary->getFileName(), BinaryOrError.takeError());
       }
     } else if (auto O = dyn_cast<MachOObjectFile>(InputBinary)) {
       Slices.emplace_back(*O);
     } else if (auto A = dyn_cast<Archive>(InputBinary)) {
-      Slices.push_back(archiveSlice(A, InputBinary->getFileName()));
+      Slices.push_back(createSliceFromArchive(A));
+    } else if (const auto *IRO = dyn_cast<IRObjectFile>(InputBinary)) {
+      // Original Apple's lipo set the alignment to 0
+      Expected<Slice> SliceOrErr = Slice::create(IRO, 0);
+      if (!SliceOrErr) {
+        reportError(InputBinary->getFileName(), SliceOrErr.takeError());
+        continue;
+      }
+      Slices.emplace_back(std::move(SliceOrErr.get()));
     } else {
       llvm_unreachable("Unexpected binary format");
     }
@@ -533,7 +610,7 @@ static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,
   assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
   assert(!OutputFileName.empty() && "Create expects a single output file");
 
-  SmallVector<std::unique_ptr<MachOObjectFile>, 1> ExtractedObjects;
+  SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects;
   SmallVector<Slice, 1> Slices =
       buildSlices(InputBinaries, Alignments, ExtractedObjects);
   checkArchDuplicates(Slices);
@@ -561,7 +638,7 @@ static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
                 " must be a fat file when the -extract option is specified");
   }
 
-  SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
+  SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
   SmallVector<Slice, 2> Slices =
       buildSlices(InputBinaries, Alignments, ExtractedObjects);
   erase_if(Slices, [ArchType](const Slice &S) {
@@ -623,7 +700,7 @@ static void replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
 
   StringMap<Slice> ReplacementSlices =
       buildReplacementSlices(ReplacementBinaries, Alignments);
-  SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
+  SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
   SmallVector<Slice, 2> Slices =
       buildSlices(InputBinaries, Alignments, ExtractedObjects);
 


        


More information about the llvm-commits mailing list