https://github.com/david-salinas updated 
https://github.com/llvm/llvm-project/pull/182579

>From 2ed86ba4294fbf589cc603d62f7ea24b2ee33fd1 Mon Sep 17 00:00:00 2001
From: dsalinas_amdeng <[email protected]>
Date: Thu, 19 Feb 2026 23:44:28 +0000
Subject: [PATCH 1/9] clang-offload-bundler incorrectly errors on multi-CCOB
 binaries

---
 clang/lib/Driver/OffloadBundler.cpp | 156 ++++++++++++++++++----------
 1 file changed, 102 insertions(+), 54 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index 8dd57c5c3b4a5..61b2b68aa4e4b 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -192,13 +192,13 @@ class FileHandler {
 
   /// Update the file handler with information from the header of the bundled
   /// file.
-  virtual Error ReadHeader(MemoryBuffer &Input) = 0;
+  virtual Error ReadHeader(StringRef FC) = 0;
 
   /// Read the marker of the next bundled to be read in the file. The bundle
   /// name is returned if there is one in the file, or `std::nullopt` if there
   /// are no more bundles to be read.
   virtual Expected<std::optional<StringRef>>
-  ReadBundleStart(MemoryBuffer &Input) = 0;
+  ReadBundleStart(StringRef Input) = 0;
 
   /// Read the marker that closes the current bundle.
   virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
@@ -227,33 +227,52 @@ class FileHandler {
 
   /// List bundle IDs in \a Input.
   virtual Error listBundleIDs(MemoryBuffer &Input) {
-    if (Error Err = ReadHeader(Input))
-      return Err;
-    return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
-      llvm::outs() << Info.BundleID << '\n';
-      Error Err = listBundleIDsCallback(Input, Info);
+    size_t NextbundleStart = 0;
+    StringRef BufferString = Input.getBuffer();
+    while ((NextbundleStart != StringRef::npos)) {
+      BufferString = BufferString.drop_front(NextbundleStart);
+
+      // Read the header.
+      Error Err = ReadHeader(BufferString);
       if (Err)
         return Err;
-      return Error::success();
-    });
+
+      Err = forEachBundle(BufferString, [&](const BundleInfo &Info) -> Error {
+        llvm::outs() << Info.BundleID << '\n';
+        Error Err = listBundleIDsCallback(Input, Info);
+        if (Err)
+          return Err;
+        return Error::success();
+      });
+
+      if (Err)
+        return Err;
+
+      // Find the beginning of the next Bundle, if it exists.
+      NextbundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
+                                          sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
+    }
+    return Error::success();
   }
 
   /// Get bundle IDs in \a Input in \a BundleIds.
   virtual Error getBundleIDs(MemoryBuffer &Input,
                              std::set<StringRef> &BundleIds) {
-    if (Error Err = ReadHeader(Input))
+
+    if (Error Err = ReadHeader(Input.getBuffer()))
       return Err;
-    return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
-      BundleIds.insert(Info.BundleID);
-      Error Err = listBundleIDsCallback(Input, Info);
-      if (Err)
-        return Err;
-      return Error::success();
-    });
+    return forEachBundle(Input.getBuffer(),
+                         [&](const BundleInfo &Info) -> Error {
+                           BundleIds.insert(Info.BundleID);
+                           Error Err = listBundleIDsCallback(Input, Info);
+                           if (Err)
+                             return Err;
+                           return Error::success();
+                         });
   }
 
   /// For each bundle in \a Input, do \a Func.
-  Error forEachBundle(MemoryBuffer &Input,
+  Error forEachBundle(StringRef Input,
                       std::function<Error(const BundleInfo &)> Func) {
     while (true) {
       Expected<std::optional<StringRef>> CurTripleOrErr =
@@ -347,9 +366,7 @@ class BinaryFileHandler final : public FileHandler {
 
   ~BinaryFileHandler() final {}
 
-  Error ReadHeader(MemoryBuffer &Input) final {
-    StringRef FC = Input.getBuffer();
-
+  Error ReadHeader(StringRef FC) final {
     // Initialize the current bundle with the end of the container.
     CurBundleInfo = BundlesInfo.end();
 
@@ -404,7 +421,6 @@ class BinaryFileHandler final : public FileHandler {
       if (!Offset || Offset + Size > FC.size())
         return Error::success();
 
-      assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??");
       BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
     }
     // Set the iterator to where we will start to read.
@@ -413,8 +429,7 @@ class BinaryFileHandler final : public FileHandler {
     return Error::success();
   }
 
-  Expected<std::optional<StringRef>>
-  ReadBundleStart(MemoryBuffer &Input) final {
+  Expected<std::optional<StringRef>> ReadBundleStart(StringRef Input) final {
     if (NextBundleInfo == BundlesInfo.end())
       return std::nullopt;
     CurBundleInfo = NextBundleInfo++;
@@ -578,10 +593,9 @@ class ObjectFileHandler final : public FileHandler {
 
   ~ObjectFileHandler() final {}
 
-  Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
+  Error ReadHeader(StringRef Input) final { return Error::success(); }
 
-  Expected<std::optional<StringRef>>
-  ReadBundleStart(MemoryBuffer &Input) final {
+  Expected<std::optional<StringRef>> ReadBundleStart(StringRef Input) final {
     while (NextSection != Obj->section_end()) {
       CurrentSection = NextSection;
       ++NextSection;
@@ -789,11 +803,9 @@ class TextFileHandler final : public FileHandler {
   size_t ReadChars = 0u;
 
 protected:
-  Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
+  Error ReadHeader(StringRef Input) final { return Error::success(); }
 
-  Expected<std::optional<StringRef>>
-  ReadBundleStart(MemoryBuffer &Input) final {
-    StringRef FC = Input.getBuffer();
+  Expected<std::optional<StringRef>> ReadBundleStart(StringRef FC) final {
 
     // Find start of the bundle.
     ReadChars = FC.find(BundleStartString, ReadChars);
@@ -1267,7 +1279,8 @@ CompressedOffloadBundle::decompress(const 
llvm::MemoryBuffer &Input,
     DecompressTimer.startTimer();
 
   SmallVector<uint8_t, 0> DecompressedData;
-  StringRef CompressedData = Blob.substr(HeaderSize);
+  StringRef CompressedData =
+      Blob.substr(HeaderSize, TotalFileSize - HeaderSize);
   if (llvm::Error DecompressionError = llvm::compression::decompress(
           CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
           DecompressedData, UncompressedSize))
@@ -1319,6 +1332,17 @@ CompressedOffloadBundle::decompress(const 
llvm::MemoryBuffer &Input,
                  << "Decompression speed: "
                  << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
                  << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+                 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
+                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+                 << "Compression rate: "
+                 << llvm::format("%.2lf", CompressionRate) << "\n"
+                 << "Compression ratio: "
+                 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
+                 << "Decompression speed: "
+                 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
+                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
                  << "Recalculated hash: "
                  << llvm::format_hex(RecalculatedHash, 16) << "\n"
                  << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
@@ -1331,32 +1355,56 @@ CompressedOffloadBundle::decompress(const 
llvm::MemoryBuffer &Input,
 // List bundle IDs. Return true if an error was found.
 Error OffloadBundler::ListBundleIDsInFile(
     StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
+
+  size_t Offset = 0;
+  size_t NextbundleStart = 0;
+  std::unique_ptr<MemoryBuffer> Buffer;
+
   // Open Input file.
   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
       MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
   if (std::error_code EC = CodeOrErr.getError())
     return createFileError(InputFileName, EC);
 
-  // Decompress the input if necessary.
-  Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
-      CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
-  if (!DecompressedBufferOrErr)
-    return createStringError(
-        inconvertibleErrorCode(),
-        "Failed to decompress input: " +
-            llvm::toString(DecompressedBufferOrErr.takeError()));
+  StringRef Buf = (**CodeOrErr).getBuffer();
 
-  MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
+  // There may be multiple bundles.
+  while ((NextbundleStart != StringRef::npos) && (Offset < Buf.size())) {
+    Buf = Buf.drop_front(Offset);
 
-  // Select the right files handler.
-  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
-      CreateFileHandler(DecompressedInput, BundlerConfig);
-  if (!FileHandlerOrErr)
-    return FileHandlerOrErr.takeError();
+    if (identify_magic(Buf) == file_magic::offload_bundle_compressed)
+      // Decompress this bundle first.
+      NextbundleStart = Buf.find("CCOB", sizeof("CCOB"));
+    if (NextbundleStart == StringRef::npos)
+      NextbundleStart = Buf.size();
 
-  std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
-  assert(FH);
-  return FH->listBundleIDs(DecompressedInput);
+    // Decompress the input if necessary.
+    Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
+        CompressedOffloadBundle::decompress(**CodeOrErr, 
BundlerConfig.Verbose);
+    if (!DecompressedBufferOrErr)
+      return createStringError(
+          inconvertibleErrorCode(),
+          "Failed to decompress input: " +
+              llvm::toString(DecompressedBufferOrErr.takeError()));
+
+    MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
+
+    // Select the right files handler.
+    Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
+        CreateFileHandler(DecompressedInput, BundlerConfig);
+    if (!FileHandlerOrErr)
+      return FileHandlerOrErr.takeError();
+
+    std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
+    assert(FH);
+    Error E = FH->listBundleIDs(DecompressedInput);
+    if (E)
+      return E;
+
+    if (NextbundleStart != StringRef::npos)
+      Offset += NextbundleStart;
+  }
+  return Error::success();
 }
 
 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
@@ -1560,7 +1608,7 @@ Error OffloadBundler::UnbundleFiles() {
   assert(FH);
 
   // Read the header of the bundled file.
-  if (Error Err = FH->ReadHeader(Input))
+  if (Error Err = FH->ReadHeader(Input.getBuffer()))
     return Err;
 
   // Create a work list that consist of the map triple/output file.
@@ -1579,7 +1627,7 @@ Error OffloadBundler::UnbundleFiles() {
   bool FoundHostBundle = false;
   while (!Worklist.empty()) {
     Expected<std::optional<StringRef>> CurTripleOrErr =
-        FH->ReadBundleStart(Input);
+        FH->ReadBundleStart(Input.getBuffer());
     if (!CurTripleOrErr)
       return CurTripleOrErr.takeError();
 
@@ -1863,11 +1911,11 @@ Error OffloadBundler::UnbundleArchive() {
     assert(FileHandler &&
            "FileHandle creation failed for file in the archive!");
 
-    if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
+    if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer.getBuffer()))
       return ReadErr;
 
     Expected<std::optional<StringRef>> CurBundleIDOrErr =
-        FileHandler->ReadBundleStart(CodeObjectBuffer);
+        FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
     if (!CurBundleIDOrErr)
       return CurBundleIDOrErr.takeError();
 
@@ -1923,7 +1971,7 @@ Error OffloadBundler::UnbundleArchive() {
         return Err;
 
       Expected<std::optional<StringRef>> NextTripleOrErr =
-          FileHandler->ReadBundleStart(CodeObjectBuffer);
+          FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
       if (!NextTripleOrErr)
         return NextTripleOrErr.takeError();
 

>From 46e333837aca6ed77ddfd59ed275ab68b3dad2c0 Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Fri, 27 Feb 2026 21:08:35 +0000
Subject: [PATCH 2/9] Fix clang-offload-bundler handling of compressed offload
 bundles

---
 clang/lib/Driver/OffloadBundler.cpp | 44 +++++++++++++++++++++--------
 1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index 61b2b68aa4e4b..67422aaaa6456 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -228,8 +228,12 @@ class FileHandler {
   /// List bundle IDs in \a Input.
   virtual Error listBundleIDs(MemoryBuffer &Input) {
     size_t NextbundleStart = 0;
+    size_t Offset = 0;
     StringRef BufferString = Input.getBuffer();
-    while ((NextbundleStart != StringRef::npos)) {
+    while ((NextbundleStart != StringRef::npos) &&
+           (Offset < BufferString.size())) {
+
+      // Drop the data that has already been processed/read.
       BufferString = BufferString.drop_front(NextbundleStart);
 
       // Read the header.
@@ -251,6 +255,9 @@ class FileHandler {
       // Find the beginning of the next Bundle, if it exists.
       NextbundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
                                           sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
+
+      if (NextbundleStart != StringRef::npos)
+      Offset += NextbundleStart;
     }
     return Error::success();
   }
@@ -1358,25 +1365,39 @@ Error OffloadBundler::ListBundleIDsInFile(
 
   size_t Offset = 0;
   size_t NextbundleStart = 0;
+  StringRef Magic; // = OFFLOAD_BUNDLER_MAGIC_STR;
   std::unique_ptr<MemoryBuffer> Buffer;
 
   // Open Input file.
-  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+  // ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Contents =
       MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
-  if (std::error_code EC = CodeOrErr.getError())
+  if (std::error_code EC = Contents.getError())
     return createFileError(InputFileName, EC);
 
-  StringRef Buf = (**CodeOrErr).getBuffer();
+  StringRef Buf = (**Contents).getBuffer();
 
   // There may be multiple bundles.
-  while ((NextbundleStart != StringRef::npos) && (Offset < Buf.size())) {
-    Buf = Buf.drop_front(Offset);
+  while ((NextbundleStart != StringRef::npos) &&
+         (Offset < (**Contents).getBufferSize())) {
+    Buffer = MemoryBuffer::getMemBuffer(
+        (**Contents).getBuffer().drop_front(Offset), "",
+        /*RequiresNullTerminator=*/false);
+
+    if (identify_magic((*Buffer).getBuffer()) ==
+        file_magic::offload_bundle_compressed) {
+      Magic = "CCOB";
+      NextbundleStart = (*Buffer).getBuffer().find(Magic, Magic.size());
+    } else
+      NextbundleStart = StringRef::npos;
 
-    if (identify_magic(Buf) == file_magic::offload_bundle_compressed)
-      // Decompress this bundle first.
-      NextbundleStart = Buf.find("CCOB", sizeof("CCOB"));
-    if (NextbundleStart == StringRef::npos)
-      NextbundleStart = Buf.size();
+    ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+        MemoryBuffer::getMemBuffer(
+            (*Buffer).getBuffer().take_front(NextbundleStart),
+            InputFileName, // FileName,
+            false);
+    if (std::error_code EC = CodeOrErr.getError())
+      return createFileError(InputFileName, EC);
 
     // Decompress the input if necessary.
     Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
@@ -1394,7 +1415,6 @@ Error OffloadBundler::ListBundleIDsInFile(
         CreateFileHandler(DecompressedInput, BundlerConfig);
     if (!FileHandlerOrErr)
       return FileHandlerOrErr.takeError();
-
     std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
     assert(FH);
     Error E = FH->listBundleIDs(DecompressedInput);

>From 57b6393c8cde2c5175f59821dc459bfcb595f461 Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Thu, 5 Mar 2026 18:32:01 +0000
Subject: [PATCH 3/9] Clean Up formatting and build errors

---
 clang/lib/Driver/OffloadBundler.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index 67422aaaa6456..bb726fdd537c9 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -257,7 +257,7 @@ class FileHandler {
                                           sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
 
       if (NextbundleStart != StringRef::npos)
-      Offset += NextbundleStart;
+        Offset += NextbundleStart;
     }
     return Error::success();
   }
@@ -1375,8 +1375,6 @@ Error OffloadBundler::ListBundleIDsInFile(
   if (std::error_code EC = Contents.getError())
     return createFileError(InputFileName, EC);
 
-  StringRef Buf = (**Contents).getBuffer();
-
   // There may be multiple bundles.
   while ((NextbundleStart != StringRef::npos) &&
          (Offset < (**Contents).getBufferSize())) {

>From 79aaf1d272cef2cfb0569bd56d5e1b64b398704d Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Fri, 27 Mar 2026 16:26:18 +0000
Subject: [PATCH 4/9] clean up.

---
 clang/lib/Driver/OffloadBundler.cpp | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index bb726fdd537c9..4d409d709a61c 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1339,17 +1339,6 @@ CompressedOffloadBundle::decompress(const 
llvm::MemoryBuffer &Input,
                  << "Decompression speed: "
                  << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
                  << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
-                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
-                 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
-                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
-                 << "Compression rate: "
-                 << llvm::format("%.2lf", CompressionRate) << "\n"
-                 << "Compression ratio: "
-                 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
-                 << "Decompression speed: "
-                 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
-                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
-                 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
                  << "Recalculated hash: "
                  << llvm::format_hex(RecalculatedHash, 16) << "\n"
                  << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";

>From 86d0f7babc81a063bcb5e74ecb609ac91860a564 Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Wed, 1 Apr 2026 19:00:17 +0000
Subject: [PATCH 5/9] Address PR comments.

---
 clang/lib/Driver/OffloadBundler.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index 4d409d709a61c..7e34e5fde6f4b 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1358,7 +1358,6 @@ Error OffloadBundler::ListBundleIDsInFile(
   std::unique_ptr<MemoryBuffer> Buffer;
 
   // Open Input file.
-  // ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
   ErrorOr<std::unique_ptr<MemoryBuffer>> Contents =
       MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
   if (std::error_code EC = Contents.getError())

>From 000390203c87508e7d1bbf2002bb9ce819cef1b8 Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Wed, 8 Apr 2026 19:58:35 +0000
Subject: [PATCH 6/9] Adress PR comments.

---
 clang/lib/Driver/OffloadBundler.cpp | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index 7e34e5fde6f4b..420639b2fe1f5 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -227,14 +227,14 @@ class FileHandler {
 
   /// List bundle IDs in \a Input.
   virtual Error listBundleIDs(MemoryBuffer &Input) {
-    size_t NextbundleStart = 0;
+    size_t NextBundleStart = 0;
     size_t Offset = 0;
     StringRef BufferString = Input.getBuffer();
-    while ((NextbundleStart != StringRef::npos) &&
+    while ((NextBundleStart != StringRef::npos) &&
            (Offset < BufferString.size())) {
 
       // Drop the data that has already been processed/read.
-      BufferString = BufferString.drop_front(NextbundleStart);
+      BufferString = BufferString.drop_front(NextBundleStart);
 
       // Read the header.
       Error Err = ReadHeader(BufferString);
@@ -253,11 +253,11 @@ class FileHandler {
         return Err;
 
       // Find the beginning of the next Bundle, if it exists.
-      NextbundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
+      NextBundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
                                           sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
 
-      if (NextbundleStart != StringRef::npos)
-        Offset += NextbundleStart;
+      if (NextBundleStart != StringRef::npos)
+        Offset += NextBundleStart;
     }
     return Error::success();
   }
@@ -1353,8 +1353,7 @@ Error OffloadBundler::ListBundleIDsInFile(
     StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
 
   size_t Offset = 0;
-  size_t NextbundleStart = 0;
-  StringRef Magic; // = OFFLOAD_BUNDLER_MAGIC_STR;
+  size_t NextBundleStart = 0;
   std::unique_ptr<MemoryBuffer> Buffer;
 
   // Open Input file.
@@ -1364,7 +1363,7 @@ Error OffloadBundler::ListBundleIDsInFile(
     return createFileError(InputFileName, EC);
 
   // There may be multiple bundles.
-  while ((NextbundleStart != StringRef::npos) &&
+  while ((NextBundleStart != StringRef::npos) &&
          (Offset < (**Contents).getBufferSize())) {
     Buffer = MemoryBuffer::getMemBuffer(
         (**Contents).getBuffer().drop_front(Offset), "",
@@ -1372,14 +1371,13 @@ Error OffloadBundler::ListBundleIDsInFile(
 
     if (identify_magic((*Buffer).getBuffer()) ==
         file_magic::offload_bundle_compressed) {
-      Magic = "CCOB";
-      NextbundleStart = (*Buffer).getBuffer().find(Magic, Magic.size());
+      NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
     } else
-      NextbundleStart = StringRef::npos;
+      NextBundleStart = StringRef::npos;
 
     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
         MemoryBuffer::getMemBuffer(
-            (*Buffer).getBuffer().take_front(NextbundleStart),
+            (*Buffer).getBuffer().take_front(NextBundleStart),
             InputFileName, // FileName,
             false);
     if (std::error_code EC = CodeOrErr.getError())
@@ -1407,8 +1405,8 @@ Error OffloadBundler::ListBundleIDsInFile(
     if (E)
       return E;
 
-    if (NextbundleStart != StringRef::npos)
-      Offset += NextbundleStart;
+    if (NextBundleStart != StringRef::npos)
+      Offset += NextBundleStart;
   }
   return Error::success();
 }

>From 40b72feac0b783c598b838270665d509570a0ee1 Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Fri, 17 Apr 2026 15:14:31 +0000
Subject: [PATCH 7/9] Add a clang-offload-bundler test

---
 .../clang-offload-bundler-multi-compress.c    | 77 +++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 clang/test/Driver/clang-offload-bundler-multi-compress.c

diff --git a/clang/test/Driver/clang-offload-bundler-multi-compress.c 
b/clang/test/Driver/clang-offload-bundler-multi-compress.c
new file mode 100644
index 0000000000000..4cfdd0e2fce8a
--- /dev/null
+++ b/clang/test/Driver/clang-offload-bundler-multi-compress.c
@@ -0,0 +1,77 @@
+// REQUIRES: x86-registered-target
+// REQUIRES: zlib || zstd
+// UNSUPPORTED: target={{.*}}-darwin{{.*}}, target={{.*}}-aix{{.*}}, 
target={{.*}}-zos{{.*}}
+
+// Tests that clang-offload-bundler --list correctly enumerates all bundle IDs
+// from multiple concatenated compressed (CCOB) fat binary blobs stored in the
+// .hip_fatbin section of an ELF object. This models the layout produced when
+// a shared library or relocatable object is linked from multiple HIP
+// translation units, each of which contributes its own CCOB blob to the
+// .hip_fatbin section.
+
+//
+// Create device content files for two simulated translation units.
+//
+// RUN: echo 'Content of device file 1' > %t.dev1
+// RUN: echo 'Content of device file 2' > %t.dev2
+
+//
+// Produce two compressed fat binary blobs with distinct GPU targets so that
+// the FileCheck assertions below are unambiguous.
+//
+// Bundle 1: gfx906 + gfx908
+// RUN: clang-offload-bundler -compress -type=bc \
+// RUN:   -targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx908 
\
+// RUN:   -input=%t.dev1 -input=%t.dev2 \
+// RUN:   -output=%t.bundle1.ccob
+
+// Bundle 2: gfx1030 + gfx1100
+// RUN: clang-offload-bundler -compress -type=bc \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx1030,hip-amdgcn-amd-amdhsa--gfx1100 \
+// RUN:   -input=%t.dev1 -input=%t.dev2 \
+// RUN:   -output=%t.bundle2.ccob
+
+//
+// Baseline: --list on each individual compressed bundle must work.
+//
+// RUN: clang-offload-bundler -type=bc -list -input=%t.bundle1.ccob \
+// RUN:   | FileCheck %s --check-prefix=SINGLE1
+// SINGLE1-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// SINGLE1-DAG: hip-amdgcn-amd-amdhsa--gfx908
+
+// RUN: clang-offload-bundler -type=bc -list -input=%t.bundle2.ccob \
+// RUN:   | FileCheck %s --check-prefix=SINGLE2
+// SINGLE2-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// SINGLE2-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+
+//
+// Concatenate the two CCOB blobs. This mirrors what the linker does when it
+// merges the .hip_fatbin input sections contributed by multiple TUs.
+//
+// RUN: cat %t.bundle1.ccob %t.bundle2.ccob > %t.multi.fatbin
+
+//
+// Build a host object and inject the concatenated blob as its .hip_fatbin
+// section, replicating the ELF layout of a fat shared library.
+//
+// RUN: %clang -O0 -target %itanium_abi_triple %s -c -o %t.host.o
+// RUN: llvm-objcopy \
+// RUN:   --add-section=.hip_fatbin=%t.multi.fatbin \
+// RUN:   --set-section-flags=.hip_fatbin=alloc \
+// RUN:   %t.host.o %t.multi.o
+
+//
+// --list on an object whose .hip_fatbin section contains two concatenated
+// CCOB blobs must enumerate all bundle IDs from both fat binaries.
+//
+// RUN: clang-offload-bundler -type=o -list -input=%t.multi.fatbin \
+// RUN:   | FileCheck %s --check-prefix=MULTI
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx908
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+
+// Some code so that we can compile this file as a host object.
+int A = 0;
+void test_func(void) { ++A; }
+

>From d4cbad06077645939059bd8e42ba6c947c4401d3 Mon Sep 17 00:00:00 2001
From: david-salinas <[email protected]>
Date: Wed, 22 Apr 2026 17:58:16 +0000
Subject: [PATCH 8/9] Correct clang-offload-bundler --unbundle on multi bundle
 fatbins.

---
 clang/lib/Driver/OffloadBundler.cpp           | 165 +++++++++++-------
 .../clang-offload-bundler-multi-compress.c    |  50 ++++++
 2 files changed, 151 insertions(+), 64 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index 420639b2fe1f5..c5589c3c6626c 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1591,30 +1591,6 @@ Error OffloadBundler::UnbundleFiles() {
   if (std::error_code EC = CodeOrErr.getError())
     return createFileError(BundlerConfig.InputFileNames.front(), EC);
 
-  // Decompress the input if necessary.
-  Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
-      CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
-  if (!DecompressedBufferOrErr)
-    return createStringError(
-        inconvertibleErrorCode(),
-        "Failed to decompress input: " +
-            llvm::toString(DecompressedBufferOrErr.takeError()));
-
-  MemoryBuffer &Input = **DecompressedBufferOrErr;
-
-  // Select the right files handler.
-  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
-      CreateFileHandler(Input, BundlerConfig);
-  if (!FileHandlerOrErr)
-    return FileHandlerOrErr.takeError();
-
-  std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
-  assert(FH);
-
-  // Read the header of the bundled file.
-  if (Error Err = FH->ReadHeader(Input.getBuffer()))
-    return Err;
-
   // Create a work list that consist of the map triple/output file.
   StringMap<StringRef> Worklist;
   auto Output = BundlerConfig.OutputFileNames.begin();
@@ -1626,51 +1602,111 @@ Error OffloadBundler::UnbundleFiles() {
     ++Output;
   }
 
-  // Read all the bundles that are in the work list. If we find no bundles we
-  // assume the file is meant for the host target.
+  // The input may contain multiple concatenated fat binary blobs (e.g. when
+  // the linker merges .hip_fatbin sections from multiple TUs into one). Walk
+  // through each blob exactly as ListBundleIDsInFile does, draining worklist
+  // entries as matching targets are found.
   bool FoundHostBundle = false;
-  while (!Worklist.empty()) {
-    Expected<std::optional<StringRef>> CurTripleOrErr =
-        FH->ReadBundleStart(Input.getBuffer());
-    if (!CurTripleOrErr)
-      return CurTripleOrErr.takeError();
-
-    // We don't have more bundles.
-    if (!*CurTripleOrErr)
-      break;
-
-    StringRef CurTriple = **CurTripleOrErr;
-    assert(!CurTriple.empty());
-    if (!checkOffloadBundleID(CurTriple))
-      return createStringError(errc::invalid_argument,
-                               "invalid bundle id read from the bundle");
+  size_t Offset = 0;
+  size_t NextBundleStart = 0;
+  std::unique_ptr<MemoryBuffer> Buffer;
+
+  while ((NextBundleStart != StringRef::npos) &&
+         (Offset < (**CodeOrErr).getBufferSize())) {
+
+    Buffer = MemoryBuffer::getMemBuffer(
+        (**CodeOrErr).getBuffer().drop_front(Offset), "",
+        /*RequiresNullTerminator=*/false);
+
+    if (identify_magic((*Buffer).getBuffer()) ==
+        file_magic::offload_bundle_compressed) {
+      NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
+    } else if (identify_magic((*Buffer).getBuffer()) ==
+               file_magic::offload_bundle) {
+      NextBundleStart = (*Buffer).getBuffer().find(
+          OFFLOAD_BUNDLER_MAGIC_STR, sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
+    } else
+      NextBundleStart = StringRef::npos;
+
+    ErrorOr<std::unique_ptr<MemoryBuffer>> BlobOrErr =
+        MemoryBuffer::getMemBuffer(
+            (*Buffer).getBuffer().take_front(NextBundleStart),
+            BundlerConfig.InputFileNames.front(),
+            /*RequiresNullTerminator=*/false);
+    if (std::error_code EC = BlobOrErr.getError())
+      return createFileError(BundlerConfig.InputFileNames.front(), EC);
+
+    // Decompress the blob if necessary.
+    Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
+        CompressedOffloadBundle::decompress(**BlobOrErr, 
BundlerConfig.Verbose);
+    if (!DecompressedBufferOrErr)
+      return createStringError(
+          inconvertibleErrorCode(),
+          "Failed to decompress input: " +
+              llvm::toString(DecompressedBufferOrErr.takeError()));
+
+    MemoryBuffer &Input = **DecompressedBufferOrErr;
 
-    auto Output = Worklist.begin();
-    for (auto E = Worklist.end(); Output != E; Output++) {
-      if (isCodeObjectCompatible(
-              OffloadTargetInfo(CurTriple, BundlerConfig),
-              OffloadTargetInfo((*Output).first(), BundlerConfig))) {
+    // Select the right file handler for this blob.
+    Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
+        CreateFileHandler(Input, BundlerConfig);
+    if (!FileHandlerOrErr)
+      return FileHandlerOrErr.takeError();
+
+    std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
+    assert(FH);
+
+    // Read the header of this blob.
+    if (Error Err = FH->ReadHeader(Input.getBuffer()))
+      return Err;
+
+    // Drain worklist entries satisfied by this blob.
+    while (!Worklist.empty()) {
+      Expected<std::optional<StringRef>> CurTripleOrErr =
+          FH->ReadBundleStart(Input.getBuffer());
+      if (!CurTripleOrErr)
+        return CurTripleOrErr.takeError();
+
+      // No more bundles in this blob.
+      if (!*CurTripleOrErr)
         break;
+
+      StringRef CurTriple = **CurTripleOrErr;
+      assert(!CurTriple.empty());
+      if (!checkOffloadBundleID(CurTriple))
+        return createStringError(errc::invalid_argument,
+                                 "invalid bundle id read from the bundle");
+
+      auto Output = Worklist.begin();
+      for (auto E = Worklist.end(); Output != E; Output++) {
+        if (isCodeObjectCompatible(
+                OffloadTargetInfo(CurTriple, BundlerConfig),
+                OffloadTargetInfo((*Output).first(), BundlerConfig)))
+          break;
       }
-    }
 
-    if (Output == Worklist.end())
-      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))
-      return Err;
-    if (Error Err = FH->ReadBundleEnd(Input))
-      return Err;
-    Worklist.erase(Output);
+      if (Output == Worklist.end())
+        continue;
 
-    // Record if we found the host bundle.
-    auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
-    if (OffloadInfo.hasHostKind())
-      FoundHostBundle = true;
+      // 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))
+        return Err;
+      if (Error Err = FH->ReadBundleEnd(Input))
+        return Err;
+      Worklist.erase(Output);
+
+      // Record if we found the host bundle.
+      auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
+      if (OffloadInfo.hasHostKind())
+        FoundHostBundle = true;
+    }
+
+    if (NextBundleStart != StringRef::npos)
+      Offset += NextBundleStart;
   }
 
   if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
@@ -1706,7 +1742,8 @@ Error OffloadBundler::UnbundleFiles() {
       // because the entire WorkList has been checked above.
       auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
       if (OffloadInfo.hasHostKind())
-        OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
+        OutputFile.write((**CodeOrErr).getBufferStart(),
+                         (**CodeOrErr).getBufferSize());
     }
     return Error::success();
   }
diff --git a/clang/test/Driver/clang-offload-bundler-multi-compress.c 
b/clang/test/Driver/clang-offload-bundler-multi-compress.c
index 4cfdd0e2fce8a..fd2be0552d415 100644
--- a/clang/test/Driver/clang-offload-bundler-multi-compress.c
+++ b/clang/test/Driver/clang-offload-bundler-multi-compress.c
@@ -71,6 +71,56 @@
 // MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1030
 // MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1100
 
+//
+// ===--- Uncompressed multi-bundle tests ---===
+//
+// Repeat the same --list and --unbundle tests using uncompressed fat binary
+// blobs (__CLANG_OFFLOAD_BUNDLE__ binary format without CCOB).
+//
+
+// Bundle 1 (uncompressed): gfx906 + gfx908
+// RUN: clang-offload-bundler -type=bc \
+// RUN:   -targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx908 
\
+// RUN:   -input=%t.dev1 -input=%t.dev2 \
+// RUN:   -output=%t.unc.bundle1.bc
+
+// Bundle 2 (uncompressed): gfx1030 + gfx1100
+// RUN: clang-offload-bundler -type=bc \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx1030,hip-amdgcn-amd-amdhsa--gfx1100 \
+// RUN:   -input=%t.dev1 -input=%t.dev2 \
+// RUN:   -output=%t.unc.bundle2.bc
+
+// Concatenate the two uncompressed blobs.
+// RUN: cat %t.unc.bundle1.bc %t.unc.bundle2.bc > %t.unc.multi.fatbin
+
+// --list must enumerate all bundle IDs from both uncompressed blobs.
+// RUN: clang-offload-bundler -type=o -list -input=%t.unc.multi.fatbin \
+// RUN:   | FileCheck %s --check-prefix=UNC-MULTI
+// UNC-MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// UNC-MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx908
+// UNC-MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// UNC-MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+
+// --unbundle must extract targets spanning both uncompressed blobs.
+// RUN: clang-offload-bundler -type=o -unbundle \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx1100 \
+// RUN:   -output=%t.unc.res.gfx906 -output=%t.unc.res.gfx1100 \
+// RUN:   -input=%t.unc.multi.fatbin
+// RUN: diff %t.dev1 %t.unc.res.gfx906
+// RUN: diff %t.dev2 %t.unc.res.gfx1100
+
+//
+// --unbundle on the same concatenated CCOB file must correctly extract targets
+// that span both blobs in a single call. gfx906 comes from bundle1 and gfx1100
+// comes from bundle2, so this exercises cross-blob extraction.
+//
+// RUN: clang-offload-bundler -type=o -unbundle \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx1100 \
+// RUN:   -output=%t.res.gfx906 -output=%t.res.gfx1100 \
+// RUN:   -input=%t.multi.fatbin
+// RUN: diff %t.dev1 %t.res.gfx906
+// RUN: diff %t.dev2 %t.res.gfx1100
+
 // Some code so that we can compile this file as a host object.
 int A = 0;
 void test_func(void) { ++A; }

>From fdb60d995f2f9cdb33c1db92fdcf3a4ce29133f5 Mon Sep 17 00:00:00 2001
From: dsalinas <[email protected]>
Date: Sun, 17 May 2026 17:18:06 -0500
Subject: [PATCH 9/9] [clang-offload-bundler] Address PR review: fix loop
 termination in listBundleIDs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Remove the redundant and incorrect `Offset < BufferString.size()`
condition from the while loop in listBundleIDs(). This condition was
buggy because `Offset` accumulates from the original buffer start while
`BufferString.size()` shrinks each iteration via drop_front(), causing
the loop to exit prematurely when 3+ CCOB blobs are concatenated.

The `NextBundleStart != StringRef::npos` check is the correct and
sufficient termination condition — when find() returns npos there are
no more magic strings to find.

Addresses review comment from yxsamliu:
https://github.com/llvm/llvm-project/pull/182579
---
 clang/lib/Driver/OffloadBundler.cpp           |  7 +--
 .../clang-offload-bundler-multi-compress.c    | 60 +++++++++++++++++++
 2 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index c5589c3c6626c..9d6f32c4a4e8f 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -228,10 +228,8 @@ class FileHandler {
   /// List bundle IDs in \a Input.
   virtual Error listBundleIDs(MemoryBuffer &Input) {
     size_t NextBundleStart = 0;
-    size_t Offset = 0;
     StringRef BufferString = Input.getBuffer();
-    while ((NextBundleStart != StringRef::npos) &&
-           (Offset < BufferString.size())) {
+    while (NextBundleStart != StringRef::npos) {
 
       // Drop the data that has already been processed/read.
       BufferString = BufferString.drop_front(NextBundleStart);
@@ -255,9 +253,6 @@ class FileHandler {
       // Find the beginning of the next Bundle, if it exists.
       NextBundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
                                           sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
-
-      if (NextBundleStart != StringRef::npos)
-        Offset += NextBundleStart;
     }
     return Error::success();
   }
diff --git a/clang/test/Driver/clang-offload-bundler-multi-compress.c 
b/clang/test/Driver/clang-offload-bundler-multi-compress.c
index fd2be0552d415..859d3e5773e6b 100644
--- a/clang/test/Driver/clang-offload-bundler-multi-compress.c
+++ b/clang/test/Driver/clang-offload-bundler-multi-compress.c
@@ -14,6 +14,7 @@
 //
 // RUN: echo 'Content of device file 1' > %t.dev1
 // RUN: echo 'Content of device file 2' > %t.dev2
+// RUN: echo 'Content of device file 3' > %t.dev3
 
 //
 // Produce two compressed fat binary blobs with distinct GPU targets so that
@@ -31,6 +32,12 @@
 // RUN:   -input=%t.dev1 -input=%t.dev2 \
 // RUN:   -output=%t.bundle2.ccob
 
+// Bundle 3: gfx942 + gfx1201
+// RUN: clang-offload-bundler -compress -type=bc \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx942,hip-amdgcn-amd-amdhsa--gfx1201 \
+// RUN:   -input=%t.dev1 -input=%t.dev3 \
+// RUN:   -output=%t.bundle3.ccob
+
 //
 // Baseline: --list on each individual compressed bundle must work.
 //
@@ -71,6 +78,31 @@
 // MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1030
 // MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1100
 
+//
+// Concatenate three CCOB blobs to verify that the loop correctly processes
+// 3+ blobs without premature termination.
+//
+// RUN: cat %t.bundle1.ccob %t.bundle2.ccob %t.bundle3.ccob > %t.triple.fatbin
+
+// --list on three concatenated CCOB blobs must enumerate all bundle IDs.
+// RUN: clang-offload-bundler -type=o -list -input=%t.triple.fatbin \
+// RUN:   | FileCheck %s --check-prefix=TRIPLE
+// TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx908
+// TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+// TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx942
+// TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx1201
+
+// --unbundle must extract targets spanning all three CCOB blobs.
+// RUN: clang-offload-bundler -type=o -unbundle \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx1030,hip-amdgcn-amd-amdhsa--gfx942
 \
+// RUN:   -output=%t.tri.res.gfx906 -output=%t.tri.res.gfx1030 
-output=%t.tri.res.gfx942 \
+// RUN:   -input=%t.triple.fatbin
+// RUN: diff %t.dev1 %t.tri.res.gfx906
+// RUN: diff %t.dev1 %t.tri.res.gfx1030
+// RUN: diff %t.dev1 %t.tri.res.gfx942
+
 //
 // ===--- Uncompressed multi-bundle tests ---===
 //
@@ -90,6 +122,12 @@
 // RUN:   -input=%t.dev1 -input=%t.dev2 \
 // RUN:   -output=%t.unc.bundle2.bc
 
+// Bundle 3 (uncompressed): gfx942 + gfx1201
+// RUN: clang-offload-bundler -type=bc \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx942,hip-amdgcn-amd-amdhsa--gfx1201 \
+// RUN:   -input=%t.dev1 -input=%t.dev3 \
+// RUN:   -output=%t.unc.bundle3.bc
+
 // Concatenate the two uncompressed blobs.
 // RUN: cat %t.unc.bundle1.bc %t.unc.bundle2.bc > %t.unc.multi.fatbin
 
@@ -109,6 +147,28 @@
 // RUN: diff %t.dev1 %t.unc.res.gfx906
 // RUN: diff %t.dev2 %t.unc.res.gfx1100
 
+// Concatenate three uncompressed blobs.
+// RUN: cat %t.unc.bundle1.bc %t.unc.bundle2.bc %t.unc.bundle3.bc > 
%t.unc.triple.fatbin
+
+// --list on three concatenated uncompressed blobs must enumerate all bundle 
IDs.
+// RUN: clang-offload-bundler -type=o -list -input=%t.unc.triple.fatbin \
+// RUN:   | FileCheck %s --check-prefix=UNC-TRIPLE
+// UNC-TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// UNC-TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx908
+// UNC-TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// UNC-TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+// UNC-TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx942
+// UNC-TRIPLE-DAG: hip-amdgcn-amd-amdhsa--gfx1201
+
+// --unbundle must extract targets spanning all three uncompressed blobs.
+// RUN: clang-offload-bundler -type=o -unbundle \
+// RUN:   
-targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx1030,hip-amdgcn-amd-amdhsa--gfx942
 \
+// RUN:   -output=%t.unc.tri.res.gfx906 -output=%t.unc.tri.res.gfx1030 
-output=%t.unc.tri.res.gfx942 \
+// RUN:   -input=%t.unc.triple.fatbin
+// RUN: diff %t.dev1 %t.unc.tri.res.gfx906
+// RUN: diff %t.dev1 %t.unc.tri.res.gfx1030
+// RUN: diff %t.dev1 %t.unc.tri.res.gfx942
+
 //
 // --unbundle on the same concatenated CCOB file must correctly extract targets
 // that span both blobs in a single call. gfx906 comes from bundle1 and gfx1100

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to