[clang] clang-offload-bundler incorrectly errors on multi-CCOB binaries (PR #182579)
David Salinas via cfe-commits
cfe-commits at lists.llvm.org
Sun May 17 17:41:16 PDT 2026
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 <david.salinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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 <dsalinas at amd.com>
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
More information about the cfe-commits
mailing list