grokos created this revision.
grokos added reviewers: hfinkel, jdoerfert, ABataev, mdtoguchi, kbobrovs, 
sdmitriev.
grokos added a project: clang.
Herald added a subscriber: Anastasia.

This is the bundler-side patch for enabling static library support in clang. 
The scheme has been discussed extensively in the past and is described in this 
document prepared by @sdmitriev: F11310194: offload from static libs.pdf 
<https://reviews.llvm.org/F11310194>.

Patch was developed in collaboration with @kbobrovs and a similar version has 
been merged with Intel's SYCL compiler 
(https://github.com/intel/llvm/tree/sycl).

When a fat object is created, for each bundle the bundler also creates a 
corresponding "size" section consisting of a single 64-bit integer storing the 
size of the bundle. When linking from static objects, the host linker will 
fetch all dependencies and do a partial linking on them; this action 
concatenates all sections with the same name across fetched dependencies into a 
new aggregate section, so for each target there will be an aggregate section 
containing the concatenated bundles and another aggregate section containing 
the concatenated sizes. By visiting the aggregate sizes section the unbundler 
can then split the aggregate bundle into separate output device objects.

The patch introduces a new type "oo" which is used when unbundling 
partially-linked fat objects. When "oo" is specified, the output file is not an 
object file itself; instead it is a text file containing the paths to the 
actual outputs (because we may have multiple device objects as outputs - one 
for each dependency that was fetched).

Invocation of the host linker (to do partial-linking) and cleanup of temporary 
files will be done by the Driver. Once the bundler patch lands, the Driver 
patch will follow.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D74262

Files:
  clang/test/Driver/clang-offload-bundler-missing-size-section.cpp
  clang/test/Driver/clang-offload-bundler-oo.cpp
  clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp

Index: clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp
===================================================================
--- clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp
+++ clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp
@@ -26,6 +26,7 @@
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Endian.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorOr.h"
@@ -80,6 +81,7 @@
                        "  bc  - llvm-bc\n"
                        "  s   - assembler\n"
                        "  o   - object\n"
+                       "  oo  - object; output file is a list of unbundled objects\n"
                        "  gch - precompiled-header\n"
                        "  ast - clang AST file"),
               cl::cat(ClangOffloadBundlerCategory));
@@ -97,6 +99,9 @@
 /// Magic string that marks the existence of offloading data.
 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
 
+/// Prefix of an added section name with bundle size.
+#define SIZE_SECTION_PREFIX "__CLANG_OFFLOAD_BUNDLE_SIZE__"
+
 /// The index of the host input in the list of inputs.
 static unsigned HostInputIndex = ~0u;
 
@@ -141,6 +146,18 @@
   /// Read the current bundle and write the result into the stream \a OS.
   virtual Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
 
+  /// Read the current bundle and write the result into the file \a FileName.
+  /// The meaning of \a FileName depends on unbundling type - in some
+  /// cases (type="oo") it will contain a list of actual outputs.
+  virtual Error ReadBundle(StringRef FileName, MemoryBuffer &Input) {
+    std::error_code EC;
+    raw_fd_ostream OS(FileName, EC, sys::fs::OF_None);
+
+    if (EC)
+      return createFileError(FileName, EC);
+    return ReadBundle(OS, Input);
+  }
+
   /// Write the header of the bundled file to \a OS based on the information
   /// gathered from \a Inputs.
   virtual Error WriteHeader(raw_fd_ostream &OS,
@@ -157,6 +174,13 @@
 
   /// Write the bundle from \a Input into \a OS.
   virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
+
+  /// Sets a base name for temporary filename generation.
+  void SetTempFileNameBase(StringRef Base) { TempFileNameBase = Base.data(); }
+
+protected:
+  /// Serves as a base name for temporary filename generation.
+  std::string TempFileNameBase;
 };
 
 /// Handler for binary files. The bundled file will have the following format
@@ -308,6 +332,8 @@
     return Error::success();
   }
 
+  using FileHandler::ReadBundle; // to avoid hiding via the overload below
+
   Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
     assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
     StringRef FC = Input.getBuffer();
@@ -403,27 +429,99 @@
 /// designated name.
 ///
 /// To unbundle, we just copy the contents of the designated section.
+///
+/// The bundler produces object file in host target native format (e.g. ELF for
+/// Linux). The sections it creates are:
+///
+/// <OFFLOAD_BUNDLER_MAGIC_STR><target triple 1>
+/// |
+/// | binary data for the <target 1>'s bundle
+/// |
+/// <SIZE_SECTION_PREFIX><target triple 1>
+/// | size of the <target1>'s bundle (8 bytes)
+/// ...
+/// <OFFLOAD_BUNDLER_MAGIC_STR><target triple N>
+/// |
+/// | binary data for the <target N>'s bundle
+/// |
+/// <SIZE_SECTION_PREFIX><target triple N>
+/// | size of the <target N>'s bundle (8 bytes)
+/// ...
+/// <OFFLOAD_BUNDLER_MAGIC_STR><host target>
+/// | 0 (1 byte long)
+/// <SIZE_SECTION_PREFIX><host target>
+/// | 1 (8 bytes)
+/// ...
+///
+/// Further, these fat objects can be "partially" linked by a platform linker:
+/// 1) ld -r a_fat.o b_fat.o c_fat.o -o abc_fat.o
+/// 2) ld -r a_fat.o -L. -lbc -o abc_fat.o
+///   where libbc.a is a static library created from b_fat.o and c_fat.o.
+/// This will still result in a fat object. But this object will have bundle and
+/// size sections for the same triple concatenated:
+/// ...
+/// <OFFLOAD_BUNDLER_MAGIC_STR><target triple 1>
+/// | binary data for the <target 1>'s bundle (from a_fat.o)
+/// | binary data for the <target 1>'s bundle (from b_fat.o)
+/// | binary data for the <target 1>'s bundle (from c_fat.o)
+/// <SIZE_SECTION_PREFIX><target triple 1>
+/// | size of the <target1>'s bundle (8 bytes) (from a_fat.o)
+/// | size of the <target1>'s bundle (8 bytes) (from b_fat.o)
+/// | size of the <target1>'s bundle (8 bytes) (from c_fat.o)
+/// ...
+///
+/// The alignment of all the added sections is set to one to avoid padding
+/// between concatenated parts.
+///
+/// The unbundler is able to unbundle both kinds of fat objects. The first one
+/// (non-partially linked) can be handled either with -type=o or -type=oo,
+/// whereas the second one with -type=oo option only. In the latter case
+/// unbundling may result in multiple output files per target and the output
+/// file specified in the command line contains a list with the names of the
+/// actual outputs.
+///
 class ObjectFileHandler final : public FileHandler {
 
+  /// Keeps infomation about a bundle for a particular target.
+  struct BundleInfo final {
+    /// The section that contains bundle data, can be a concatenation of a
+    /// number of individual bundles if produced via partial linkage of multiple
+    /// fat objects.
+    section_iterator BundleSection;
+    /// The sizes (in correct order) of the individual bundles constituting
+    /// bundle data.
+    SmallVector<uint64_t, 4> ObjectSizes;
+
+    BundleInfo(section_iterator S) : BundleSection(S) {}
+  };
+
   /// The object file we are currently dealing with.
   std::unique_ptr<ObjectFile> Obj;
 
+  /// Maps triple string to its bundle information
+  StringMap<std::unique_ptr<BundleInfo>> TripleToBundleInfo;
+  /// The two iterators below are to support the
+  /// ReadBundleStart/ReadBundle/ReadBundleEnd iteration mechanism
+  StringMap<std::unique_ptr<BundleInfo>>::iterator CurBundle;
+  StringMap<std::unique_ptr<BundleInfo>>::iterator NextBundle;
+
   /// Return the input file contents.
   StringRef getInputFileContents() const { return Obj->getData(); }
 
   /// Return bundle name (<kind>-<triple>) if the provided section is an offload
   /// section.
-  static Expected<Optional<StringRef>> IsOffloadSection(SectionRef CurSection) {
+  static Expected<Optional<StringRef>> IsOffloadSection(SectionRef CurSection,
+                                                        StringRef NamePrefix) {
     Expected<StringRef> NameOrErr = CurSection.getName();
     if (!NameOrErr)
       return NameOrErr.takeError();
 
-    // If it does not start with the reserved suffix, just skip this section.
-    if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR))
+    // If it does not start with given prefix, just skip this section.
+    if (!NameOrErr->startswith(NamePrefix))
       return None;
 
-    // Return the triple that is right after the reserved prefix.
-    return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
+    // Return the suffix (the triple that is right after the reserved prefix).
+    return NameOrErr->substr(NamePrefix.size());
   }
 
   /// Total number of inputs.
@@ -433,50 +531,194 @@
   /// read from the buffers.
   unsigned NumberOfProcessedInputs = 0;
 
-  /// Iterator of the current and next section.
-  section_iterator CurrentSection;
-  section_iterator NextSection;
+  /// Input sizes.
+  SmallVector<uint64_t, 16u> InputSizes;
 
 public:
   ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn)
       : FileHandler(), Obj(std::move(ObjIn)),
-        CurrentSection(Obj->section_begin()),
-        NextSection(Obj->section_begin()) {}
+        CurBundle(TripleToBundleInfo.end()),
+        NextBundle(TripleToBundleInfo.end()) {}
 
   ~ObjectFileHandler() final {}
 
-  Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
+  // Iterate through sections and create a map from triple to relevant bundle
+  // information.
+  Error ReadHeader(MemoryBuffer &Input) final {
+    for (section_iterator Sec = Obj->section_begin(); Sec != Obj->section_end();
+         ++Sec) {
+      // Test if current section is an offload bundle section
+      Expected<Optional<StringRef>> BundleOrErr =
+          IsOffloadSection(*Sec, OFFLOAD_BUNDLER_MAGIC_STR);
+      if (!BundleOrErr)
+        return BundleOrErr.takeError();
+      if (*BundleOrErr) {
+        StringRef OffloadTriple = **BundleOrErr;
+        std::unique_ptr<BundleInfo> &BI = TripleToBundleInfo[OffloadTriple];
+        // A BundleInfo entry for this bundle should either:
+        // 1) not exist yet in which case we allocate it and initialize the
+        //    BundleSection iterator to Sec, while leaving ObjectSizes empty.
+        // 2) have been created if we previously encountered the size section
+        //    for this target in which case the BundleSection iterator should
+        //    point to Obj->section_end() and we initialize it now to Sec.
+        assert(!BI.get() || BI->BundleSection == Obj->section_end());
+
+        if (!BI.get()) {
+          BI.reset(new BundleInfo(Sec));
+        } else {
+          BI->BundleSection = Sec;
+        }
+        continue;
+      }
+      // Test if current section is an offload bundle size section
+      BundleOrErr = IsOffloadSection(*Sec, SIZE_SECTION_PREFIX);
+      if (!BundleOrErr)
+        return BundleOrErr.takeError();
+      if (*BundleOrErr) {
+        StringRef OffloadTriple = **BundleOrErr;
+
+        // yes, it is - parse object sizes
+        Expected<StringRef> Content = Sec->getContents();
+        if (!Content)
+          return Content.takeError();
+        unsigned int ElemSize = sizeof(uint64_t);
+
+        // the size of the size section must be a multiple of ElemSize
+        if (Content->size() % ElemSize != 0)
+          return createStringError(
+              errc::invalid_argument,
+              "invalid size of the bundle size section for triple " +
+                  OffloadTriple + ": " + Twine(Content->size()));
+        // read sizes
+        llvm::support::endianness E = Obj->isLittleEndian()
+                                          ? llvm::support::endianness::little
+                                          : llvm::support::endianness::big;
+        std::unique_ptr<BundleInfo> &BI = TripleToBundleInfo[OffloadTriple];
+        // A BundleInfo entry for this bundle should either:
+        // 1) not exist yet in which case we allocate it and initialize the
+        //    BundleSection iterator to Obj->section_end().
+        // 2) have been created if we previously encountered the data section
+        //    for this target in which case ObjectSizes should be empty.
+        assert(!BI.get() || BI->ObjectSizes.size() == 0);
+
+        if (!BI.get()) {
+          BI.reset(new BundleInfo(Obj->section_end()));
+        }
+        for (const char *Ptr = Content->data();
+             Ptr < Content->data() + Content->size(); Ptr += ElemSize) {
+          uint64_t Size = support::endian::read64(Ptr, E);
+          BI->ObjectSizes.push_back(Size);
+        }
+      }
+    }
+    NextBundle = TripleToBundleInfo.begin();
+    return Error::success();
+  }
 
   Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
-    while (NextSection != Obj->section_end()) {
-      CurrentSection = NextSection;
-      ++NextSection;
-
-      // Check if the current section name starts with the reserved prefix. If
-      // so, return the triple.
-      Expected<Optional<StringRef>> TripleOrErr =
-          IsOffloadSection(*CurrentSection);
-      if (!TripleOrErr)
-        return TripleOrErr.takeError();
-      if (*TripleOrErr)
-        return **TripleOrErr;
-    }
-    return None;
+    if (NextBundle == TripleToBundleInfo.end())
+      return None;
+    CurBundle = NextBundle;
+    NextBundle++;
+    return CurBundle->getKey();
   }
 
   Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
 
   Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
-    Expected<StringRef> ContentOrErr = CurrentSection->getContents();
-    if (!ContentOrErr)
-      return ContentOrErr.takeError();
-    StringRef Content = *ContentOrErr;
+    llvm_unreachable("must not be called for the ObjectFileHandler");
+  }
 
-    // Copy fat object contents to the output when extracting host bundle.
-    if (Content.size() == 1u && Content.front() == 0)
-      Content = StringRef(Input.getBufferStart(), Input.getBufferSize());
+  Error ReadBundle(StringRef OutName, MemoryBuffer &Input) final {
+    assert(CurBundle != TripleToBundleInfo.end() &&
+           "all bundles have been read already");
 
-    OS.write(Content.data(), Content.size());
+    // TODO: temporary workaround to copy the entire fat object to the host
+    // output until driver is fixed to correctly handle list file for the host
+    // bundle in 'oo' mode.
+    if (FilesType == "oo" && hasHostKind(CurBundle->getKey())) {
+      std::error_code EC;
+      raw_fd_ostream OS(OutName, EC);
+
+      if (EC)
+        return createFileError(OutName, EC);
+      OS.write(Input.getBufferStart(), Input.getBufferSize());
+      return Error::success();
+    }
+
+    // Read content of the section representing the bundle
+    Expected<StringRef> Content =
+        CurBundle->second->BundleSection->getContents();
+    if (!Content)
+      return Content.takeError();
+
+    // Backwards compatibility adjustment: object files created with older
+    // versions of clang-offload-bundler (before support for partially-linked
+    // objects) do not contain a sizes section and rightfully so because such a
+    // section was not necessary. However, the current version of the bundler
+    // expects the object to have a sizes section, even if it is a
+    // non-partially-linked one. Emulate the existence of a sizes section by
+    // adding the related number into ObjectSizes manually.
+    if (CurBundle->second->ObjectSizes.empty() && FilesType == "o") {
+      CurBundle->second->ObjectSizes.push_back(Content->size());
+    }
+
+    const char *ObjData = Content->data();
+    // Determine the number of "device objects" (or individual bundles
+    // concatenated by partial linkage) in the bundle:
+    const auto &SizeVec = CurBundle->second->ObjectSizes;
+    auto NumObjects = SizeVec.size();
+    bool FileListMode = FilesType == "oo";
+
+    if (NumObjects > 1 && !FileListMode)
+      return createStringError(
+          errc::invalid_argument,
+          "'o' file type is requested, but the fat object contains multiple "
+          "device objects; use 'oo' instead");
+    std::string FileList;
+
+    // Iterate through individual objects and extract them
+    for (size_t I = 0; I < NumObjects; ++I) {
+      uint64_t ObjSize = SizeVec[I];
+      StringRef ObjFileName = OutName;
+      SmallString<128> Path;
+
+      // If not in file list mode there is no need for a temporary file - output
+      // goes directly to what was specified in -outputs. The same is true for
+      // the host triple.
+      if (FileListMode) {
+        std::error_code EC =
+            sys::fs::createTemporaryFile(TempFileNameBase, "devo", Path);
+        ObjFileName = Path.data();
+
+        if (EC)
+          return createFileError(ObjFileName, EC);
+      }
+      std::error_code EC;
+      raw_fd_ostream OS(ObjFileName, EC);
+
+      if (EC)
+        return createFileError(ObjFileName, EC);
+      OS.write(ObjData, ObjSize);
+
+      if (FileListMode) {
+        // add the written file name to the output list of files
+        FileList = (Twine(FileList) + Twine(ObjFileName) + Twine("\n")).str();
+      }
+      // Move "object data" pointer to the next object within the concatenated
+      // bundle.
+      ObjData += ObjSize;
+    }
+    if (FileListMode) {
+      // dump the list of files into the file list specified in -outputs for the
+      // current target
+      std::error_code EC;
+      raw_fd_ostream OS1(OutName, EC);
+
+      if (EC)
+        return createFileError(OutName, EC);
+      OS1.write(FileList.data(), FileList.size());
+    }
     return Error::success();
   }
 
@@ -486,6 +728,10 @@
 
     // Record number of inputs.
     NumberOfInputs = Inputs.size();
+
+    // And input sizes.
+    for (unsigned I = 0; I < NumberOfInputs; ++I)
+      InputSizes.push_back(Inputs[I]->getBufferSize());
     return Error::success();
   }
 
@@ -555,6 +801,17 @@
       ObjcopyArgs.push_back(SS.save(Twine("--add-section=") +
                                     OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] +
                                     "=" + InputFile));
+
+      // Create temporary file with the section size contents.
+      Expected<StringRef> SizeFileOrErr = TempFiles.Create(makeArrayRef(
+          reinterpret_cast<char *>(&InputSizes[I]), sizeof(InputSizes[I])));
+      if (!SizeFileOrErr)
+        return SizeFileOrErr.takeError();
+
+      // And add one more section with target object size.
+      ObjcopyArgs.push_back(SS.save(Twine("--add-section=") +
+                                    SIZE_SECTION_PREFIX + TargetNames[I] + "=" +
+                                    *SizeFileOrErr));
     }
     ObjcopyArgs.push_back(InputFileNames[HostInputIndex]);
     ObjcopyArgs.push_back(IntermediateObj);
@@ -564,10 +821,14 @@
 
     // And run llvm-objcopy for the second time to update section flags.
     ObjcopyArgs.resize(1);
-    for (unsigned I = 0; I < NumberOfInputs; ++I)
+    for (unsigned I = 0; I < NumberOfInputs; ++I) {
       ObjcopyArgs.push_back(SS.save(Twine("--set-section-flags=") +
                                     OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] +
                                     "=readonly,exclude"));
+      ObjcopyArgs.push_back(SS.save(Twine("--set-section-flags=") +
+                                    SIZE_SECTION_PREFIX + TargetNames[I] +
+                                    "=readonly,exclude"));
+    }
     ObjcopyArgs.push_back(IntermediateObj);
     ObjcopyArgs.push_back(OutputFileNames.front());
 
@@ -660,6 +921,8 @@
     return Error::success();
   }
 
+  using FileHandler::ReadBundle; // to avoid hiding via the overload below
+
   Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
     StringRef FC = Input.getBuffer();
     size_t BundleStart = ReadChars;
@@ -741,7 +1004,7 @@
     return std::make_unique<BinaryFileHandler>();
   if (FilesType == "s")
     return std::make_unique<TextFileHandler>(/*Comment=*/"#");
-  if (FilesType == "o")
+  if (FilesType == "o" || FilesType == "oo")
     return CreateObjectFileHandler(FirstInput);
   if (FilesType == "gch")
     return std::make_unique<BinaryFileHandler>();
@@ -820,6 +1083,9 @@
   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
   assert(FH);
 
+  // Seed temporary filename generation with the stem of the input file.
+  FH->SetTempFileNameBase(llvm::sys::path::stem(InputFileNames.front()));
+
   // Read the header of the bundled file.
   if (Error Err = FH->ReadHeader(Input))
     return Err;
@@ -854,11 +1120,7 @@
       continue;
 
     // Check if the output file can be opened and copy the bundle to it.
-    std::error_code EC;
-    raw_fd_ostream OutputFile(Output->second, EC, sys::fs::OF_None);
-    if (EC)
-      return createFileError(Output->second, EC);
-    if (Error Err = FH->ReadBundle(OutputFile, Input))
+    if (Error Err = FH->ReadBundle(Output->second, Input))
       return Err;
     if (Error Err = FH->ReadBundleEnd(Input))
       return Err;
@@ -940,6 +1202,10 @@
       reportError(createStringError(errc::invalid_argument,
                                     "number of output files and targets should "
                                     "match in unbundling mode"));
+      if (FilesType == "oo") {
+        Error = true;
+        errs() << "error: type \"oo\" cannot be used in bundling mode\n.";
+      }
     }
   } else {
     if (OutputFileNames.size() != 1) {
Index: clang/test/Driver/clang-offload-bundler-oo.cpp
===================================================================
--- /dev/null
+++ clang/test/Driver/clang-offload-bundler-oo.cpp
@@ -0,0 +1,69 @@
+// REQUIRES: x86-registered-target
+// RUN: %clangxx -c %s -o %t_fat.o
+// RUN: %clangxx %t_fat.o -o %t.exe
+// RUN: clang-offload-bundler -type=oo -targets=host-x86_64-unknown-linux-gnu,openmp-x86_64-pc-linux-gnu -outputs=%t.o,%t_list.txt -inputs=%t_fat.o -unbundle
+// RUN: %t.exe %t_list.txt | FileCheck %s
+// CHECK:11
+// CHECK:222
+// CHECK:3333
+
+#include <cstdint>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+using namespace std;
+
+#define BUNDLE_SECTION_PREFIX "__CLANG_OFFLOAD_BUNDLE__"
+#define BUNDLE_SIZE_SECTION_PREFIX "__CLANG_OFFLOAD_BUNDLE_SIZE__"
+
+#define TARGET0 "host-x86_64-unknown-linux-gnu"
+#define TARGET1 "openmp-x86_64-pc-linux-gnu"
+
+// Populate section with special names recognized by the bundler;
+// this emulates fat object partially linked from 3 other fat objects.
+// The test uses the bundler to split the bundle into 3 objects and then prints
+// their contents to stdout.
+char str0[] __attribute__((section(BUNDLE_SECTION_PREFIX TARGET0))) = { 0, 0, 0 };
+int64_t size0[] __attribute__((section(BUNDLE_SIZE_SECTION_PREFIX TARGET0))) = { 1, 1, 1 };
+
+char str1[] __attribute__((section(BUNDLE_SECTION_PREFIX TARGET1))) = { "11\n222\n3333\n" };
+int64_t size1[] __attribute__((section(BUNDLE_SIZE_SECTION_PREFIX TARGET1))) = { 3, 4, 5 };
+
+void cat(const string& File) {
+  string Line;
+  ifstream F(File);
+  if (F.is_open()) {
+    while (getline(F, Line)) {
+      cout << Line << '\n';
+    }
+    F.close();
+  }
+  else cout << "Unable to open file " << File;
+}
+
+// main is invoked with the bundler output file as argument -
+// read this file and print their contents to stdout.
+int main(int argc, char **argv) {
+  string ListFile(argv[1]);
+  string Line;
+  ifstream F(ListFile);
+  vector<string> OutFiles;
+
+  if (F.is_open()) {
+    while (getline(F, Line)) {
+      OutFiles.push_back(Line);
+    }
+    F.close();
+  }
+  else {
+    cout << "Unable to open file " << ListFile;
+    return 1;
+  }
+
+  for (const auto &File : OutFiles) {
+    cat(File);
+  }
+  return 0;
+}
+
Index: clang/test/Driver/clang-offload-bundler-missing-size-section.cpp
===================================================================
--- /dev/null
+++ clang/test/Driver/clang-offload-bundler-missing-size-section.cpp
@@ -0,0 +1,44 @@
+// REQUIRES: x86-registered-target
+// RUN: %clangxx -c %s -o %t_fat.o
+// RUN: %clangxx %t_fat.o -o %t.exe
+// RUN: clang-offload-bundler -type=o -targets=host-x86_64-unknown-linux-gnu,openmp-x86_64-pc-linux-gnu -outputs=%t_host.o,%t_device.o -inputs=%t_fat.o -unbundle
+// RUN: %t.exe %t_device.o | FileCheck %s
+// CHECK:11
+
+#include <iostream>
+#include <fstream>
+#include <string>
+using namespace std;
+
+#define BUNDLE_SECTION_PREFIX "__CLANG_OFFLOAD_BUNDLE__"
+
+#define TARGET0 "host-x86_64-unknown-linux-gnu"
+#define TARGET1 "openmp-x86_64-pc-linux-gnu"
+
+// Populate section with special names recognized by the bundler;
+// this emulates fat object with one host and one device section.
+// The size sections are missing, emulating the old fat object format.
+char str0[] __attribute__((section(BUNDLE_SECTION_PREFIX TARGET0))) = { 0 };
+char str1[] __attribute__((section(BUNDLE_SECTION_PREFIX TARGET1))) = { "11\n" };
+
+// main is invoked with the bundler output file as argument -
+// read this file and print their contents to stdout.
+int main(int argc, char **argv) {
+  string DeviceObj(argv[1]);
+  string Line;
+  ifstream F(DeviceObj);
+
+  if (F.is_open()) {
+    while (getline(F, Line)) {
+      cout << Line;
+    }
+    F.close();
+  }
+  else {
+    cout << "Unable to open file " << DeviceObj;
+    return 1;
+  }
+
+  return 0;
+}
+
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to