[llvm] Extend llvm objdump fatbin (PR #140286)

via llvm-commits llvm-commits at lists.llvm.org
Fri May 16 10:42:25 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-binary-utilities

Author: David Salinas (david-salinas)

<details>
<summary>Changes</summary>



---

Patch is 69.01 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140286.diff


10 Files Affected:

- (modified) llvm/docs/CommandGuide/llvm-objdump.rst (+1-1) 
- (added) llvm/include/llvm/Object/OffloadBundle.h (+212) 
- (modified) llvm/lib/Object/CMakeLists.txt (+1) 
- (added) llvm/lib/Object/OffloadBundle.cpp (+473) 
- (added) llvm/test/tools/llvm-objdump/Offloading/fatbin.test (+60) 
- (modified) llvm/tools/llvm-objdump/OffloadDump.cpp (+45-1) 
- (modified) llvm/tools/llvm-objdump/OffloadDump.h (+5-1) 
- (modified) llvm/tools/llvm-objdump/llvm-objdump.cpp (+2-1) 
- (modified) llvm/unittests/Object/CMakeLists.txt (+1) 
- (added) llvm/unittests/Object/OffloadingBundleTest.cpp (+89) 


``````````diff
diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst
index ab9f583e96ec6..5e5eaccecd2b7 100644
--- a/llvm/docs/CommandGuide/llvm-objdump.rst
+++ b/llvm/docs/CommandGuide/llvm-objdump.rst
@@ -217,7 +217,7 @@ OPTIONS
 
 .. option:: --offloading
 
-  Display the content of the LLVM offloading section.
+  Display the content of the LLVM offloading sections and HIP offload bundles.
 
 .. option:: --prefix=<prefix>
 
diff --git a/llvm/include/llvm/Object/OffloadBundle.h b/llvm/include/llvm/Object/OffloadBundle.h
new file mode 100644
index 0000000000000..b6be7c3296cc1
--- /dev/null
+++ b/llvm/include/llvm/Object/OffloadBundle.h
@@ -0,0 +1,212 @@
+//===- OffloadBundle.h - Utilities for offload bundles---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===-------------------------------------------------------------------------===//
+//
+// This file contains the binary format used for budingling device metadata with
+// an associated device image. The data can then be stored inside a host object
+// file to create a fat binary and read by the linker. This is intended to be a
+// thin wrapper around the image itself. If this format becomes sufficiently
+// complex it should be moved to a standard binary format like msgpack or ELF.
+//
+//===-------------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECT_OFFLOADBUNDLE_H
+#define LLVM_OBJECT_OFFLOADBUNDLE_H
+
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+
+namespace llvm {
+
+namespace object {
+
+class CompressedOffloadBundle {
+private:
+  static inline const size_t MagicSize = 4;
+  static inline const size_t VersionFieldSize = sizeof(uint16_t);
+  static inline const size_t MethodFieldSize = sizeof(uint16_t);
+  static inline const size_t FileSizeFieldSize = sizeof(uint32_t);
+  static inline const size_t UncompressedSizeFieldSize = sizeof(uint32_t);
+  static inline const size_t HashFieldSize = sizeof(uint64_t);
+  static inline const size_t V1HeaderSize =
+      MagicSize + VersionFieldSize + MethodFieldSize +
+      UncompressedSizeFieldSize + HashFieldSize;
+  static inline const size_t V2HeaderSize =
+      MagicSize + VersionFieldSize + FileSizeFieldSize + MethodFieldSize +
+      UncompressedSizeFieldSize + HashFieldSize;
+  static inline const llvm::StringRef MagicNumber = "CCOB";
+  static inline const uint16_t Version = 2;
+
+public:
+  static llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+  compress(llvm::compression::Params P, const llvm::MemoryBuffer &Input,
+           bool Verbose = false);
+  static llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+  decompress(llvm::MemoryBufferRef &Input, bool Verbose = false);
+};
+
+/// Bundle entry in binary clang-offload-bundler format.
+struct OffloadBundleEntry {
+  uint64_t Offset = 0u;
+  uint64_t Size = 0u;
+  uint64_t IDLength = 0u;
+  StringRef ID;
+  OffloadBundleEntry(uint64_t O, uint64_t S, uint64_t I, StringRef T)
+      : Offset(O), Size(S), IDLength(I), ID(T) {}
+  void dumpInfo(raw_ostream &OS) {
+    OS << "Offset = " << Offset << ", Size = " << Size
+       << ", ID Length = " << IDLength << ", ID = " << ID;
+  }
+  void dumpURI(raw_ostream &OS, StringRef FilePath) {
+    OS << ID.data() << "\tfile://" << FilePath << "#offset=" << Offset
+       << "&size=" << Size << "\n";
+  }
+};
+
+/// Fat binary embedded in object files in clang-offload-bundler format
+class OffloadBundleFatBin {
+
+  uint64_t Size = 0u;
+  StringRef FileName;
+  uint64_t NumberOfEntries;
+  SmallVector<OffloadBundleEntry> Entries;
+
+public:
+  SmallVector<OffloadBundleEntry> getEntries() { return Entries; }
+  uint64_t getSize() const { return Size; }
+  StringRef getFileName() const { return FileName; }
+  uint64_t getNumEntries() const { return NumberOfEntries; }
+
+  static Expected<std::unique_ptr<OffloadBundleFatBin>>
+  create(MemoryBufferRef, uint64_t SectionOffset, StringRef FileName);
+  Error extractBundle(const ObjectFile &Source);
+
+  Error dumpEntryToCodeObject();
+
+  Error readEntries(StringRef Section, uint64_t SectionOffset);
+  void dumpEntries() {
+    for (OffloadBundleEntry &Entry : Entries)
+      Entry.dumpInfo(outs());
+  }
+
+  void printEntriesAsURI() {
+    for (OffloadBundleEntry &Entry : Entries)
+      Entry.dumpURI(outs(), FileName);
+  }
+
+  OffloadBundleFatBin(MemoryBufferRef Source, StringRef File)
+      : FileName(File), NumberOfEntries(0),
+        Entries(SmallVector<OffloadBundleEntry>()) {}
+
+  SmallVector<OffloadBundleEntry> entryIDContains(StringRef Str) {
+
+    SmallVector<OffloadBundleEntry> Found = SmallVector<OffloadBundleEntry>();
+    llvm::transform(Entries, std::back_inserter(Found), [Str](auto &X) {
+      if (X.ID.contains(Str))
+        return X;
+    });
+    return Found;
+  }
+};
+
+enum UriTypeT { FILE_URI, MEMORY_URI };
+
+struct OffloadBundleURI {
+  int64_t Offset = 0;
+  int64_t Size = 0;
+  uint64_t ProcessID = 0;
+  StringRef FileName;
+  UriTypeT URIType;
+
+  // Constructors
+  // TODO: add a Copy ctor ?
+  OffloadBundleURI(StringRef File, int64_t Off, int64_t Size)
+      : Offset(Off), Size(Size), ProcessID(0), FileName(File),
+        URIType(FILE_URI) {}
+
+public:
+  static Expected<std::unique_ptr<OffloadBundleURI>>
+  createOffloadBundleURI(StringRef Str, UriTypeT Type) {
+    switch (Type) {
+    case FILE_URI:
+      return createFileURI(Str);
+      break;
+    case MEMORY_URI:
+      return createMemoryURI(Str);
+      break;
+    default:
+      return createStringError(object_error::parse_failed,
+                               "Unrecognized URI type");
+    }
+  }
+
+  static Expected<std::unique_ptr<OffloadBundleURI>>
+  createFileURI(StringRef Str) {
+    int64_t O = 0;
+    int64_t S = 0;
+
+    if (!Str.consume_front("file://"))
+      return createStringError(object_error::parse_failed,
+                               "Reading type of URI");
+
+    StringRef FilePathname =
+        Str.take_until([](char C) { return (C == '#') || (C == '?'); });
+    Str = Str.drop_front(FilePathname.size());
+
+    if (!Str.consume_front("#offset="))
+      return createStringError(object_error::parse_failed,
+                               "Reading 'offset' in URI");
+
+    StringRef OffsetStr = Str.take_until([](char C) { return C == '&'; });
+    OffsetStr.getAsInteger(10, O);
+    Str = Str.drop_front(OffsetStr.size());
+
+    if (Str.consume_front("&size="))
+      return createStringError(object_error::parse_failed,
+                               "Reading 'size' in URI");
+
+    Str.getAsInteger(10, S);
+    std::unique_ptr<OffloadBundleURI> OffloadingURI(
+        new OffloadBundleURI(FilePathname, O, S));
+    // SALINAS return OffloadingURI;
+    return std::move(OffloadingURI);
+  }
+
+  static Expected<std::unique_ptr<OffloadBundleURI>>
+  createMemoryURI(StringRef Str) {
+    // TODO: add parseMemoryURI type
+    return createStringError(object_error::parse_failed,
+                             "Memory Type URI is not currently supported.");
+  }
+
+  StringRef getFileName() const { return FileName; }
+};
+
+/// Extracts fat binary in binary clang-offload-bundler format from object \p
+/// Obj and return it in \p Bundles
+Error extractOffloadBundleFatBinary(
+    const ObjectFile &Obj, SmallVectorImpl<OffloadBundleFatBin> &Bundles);
+
+/// Extract code object memory from the given \p Source object file at \p Offset
+/// and of \p Size, and copy into \p OutputFileName.
+Error extractCodeObject(const ObjectFile &Source, int64_t Offset, int64_t Size,
+                        StringRef OutputFileName);
+
+/// Extracts an Offload Bundle Entry given by URI
+Error extractOffloadBundleByURI(StringRef URIstr);
+
+} // namespace object
+
+} // namespace llvm
+#endif
diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt
index bfb420e57a7f4..870169a83174f 100644
--- a/llvm/lib/Object/CMakeLists.txt
+++ b/llvm/lib/Object/CMakeLists.txt
@@ -22,6 +22,7 @@ add_llvm_component_library(LLVMObject
   Object.cpp
   ObjectFile.cpp
   OffloadBinary.cpp
+  OffloadBundle.cpp
   RecordStreamer.cpp
   RelocationResolver.cpp
   SymbolicFile.cpp
diff --git a/llvm/lib/Object/OffloadBundle.cpp b/llvm/lib/Object/OffloadBundle.cpp
new file mode 100644
index 0000000000000..5f087a5c84e8d
--- /dev/null
+++ b/llvm/lib/Object/OffloadBundle.cpp
@@ -0,0 +1,473 @@
+//===- OffloadBundle.cpp - Utilities for offload bundles---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------===//
+
+#include "llvm/Object/OffloadBundle.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/Timer.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+static llvm::TimerGroup
+    OffloadBundlerTimerGroup("Offload Bundler Timer Group",
+                             "Timer group for offload bundler");
+
+// Extract an Offload bundle (usually a Offload Bundle) from a fat_bin
+// section
+Error extractOffloadBundle(MemoryBufferRef Contents, uint64_t SectionOffset,
+                           StringRef FileName,
+                           SmallVectorImpl<OffloadBundleFatBin> &Bundles) {
+
+  uint64_t Offset = 0;
+  int64_t NextbundleStart = 0;
+
+  // There could be multiple offloading bundles stored at this section.
+  while (NextbundleStart >= 0) {
+
+    std::unique_ptr<MemoryBuffer> Buffer =
+        MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
+                                   /*RequiresNullTerminator=*/false);
+
+    // Create the FatBinBindle object. This will also create the Bundle Entry
+    // list info.
+    auto FatBundleOrErr =
+        OffloadBundleFatBin::create(*Buffer, SectionOffset + Offset, FileName);
+    if (!FatBundleOrErr)
+      return FatBundleOrErr.takeError();
+
+    // Add current Bundle to list.
+    Bundles.emplace_back(std::move(**FatBundleOrErr));
+
+    // Find the next bundle by searching for the magic string
+    StringRef Str = Buffer->getBuffer();
+    NextbundleStart =
+        (int64_t)Str.find(StringRef("__CLANG_OFFLOAD_BUNDLE__"), 24);
+
+    if (NextbundleStart >= 0)
+      Offset += NextbundleStart;
+  }
+
+  return Error::success();
+}
+
+Error OffloadBundleFatBin::readEntries(StringRef Buffer,
+                                       uint64_t SectionOffset) {
+  uint64_t NumOfEntries = 0;
+
+  BinaryStreamReader Reader(Buffer, llvm::endianness::little);
+
+  // Read the Magic String first.
+  StringRef Magic;
+  if (auto EC = Reader.readFixedString(Magic, 24))
+    return errorCodeToError(object_error::parse_failed);
+
+  // Read the number of Code Objects (Entries) in the current Bundle.
+  if (auto EC = Reader.readInteger(NumOfEntries))
+    return errorCodeToError(object_error::parse_failed);
+
+  NumberOfEntries = NumOfEntries;
+
+  // For each Bundle Entry (code object)
+  for (uint64_t I = 0; I < NumOfEntries; I++) {
+    uint64_t EntrySize;
+    uint64_t EntryOffset;
+    uint64_t EntryIDSize;
+    StringRef EntryID;
+
+    if (auto EC = Reader.readInteger(EntryOffset))
+      return errorCodeToError(object_error::parse_failed);
+
+    if (auto EC = Reader.readInteger(EntrySize))
+      return errorCodeToError(object_error::parse_failed);
+
+    if (auto EC = Reader.readInteger(EntryIDSize))
+      return errorCodeToError(object_error::parse_failed);
+
+    if (auto EC = Reader.readFixedString(EntryID, EntryIDSize))
+      return errorCodeToError(object_error::parse_failed);
+
+    auto Entry = std::make_unique<OffloadBundleEntry>(
+        EntryOffset + SectionOffset, EntrySize, EntryIDSize, EntryID);
+
+    Entries.push_back(*Entry);
+  }
+
+  return Error::success();
+}
+
+Expected<std::unique_ptr<OffloadBundleFatBin>>
+OffloadBundleFatBin::create(MemoryBufferRef Buf, uint64_t SectionOffset,
+                            StringRef FileName) {
+  if (Buf.getBufferSize() < 24)
+    return errorCodeToError(object_error::parse_failed);
+
+  // Check for magic bytes.
+  if (identify_magic(Buf.getBuffer()) != file_magic::offload_bundle)
+    return errorCodeToError(object_error::parse_failed);
+
+  OffloadBundleFatBin *TheBundle = new OffloadBundleFatBin(Buf, FileName);
+
+  // Read the Bundle Entries
+  Error Err = TheBundle->readEntries(Buf.getBuffer(), SectionOffset);
+  if (Err)
+    return errorCodeToError(object_error::parse_failed);
+
+  return std::unique_ptr<OffloadBundleFatBin>(TheBundle);
+}
+
+Error OffloadBundleFatBin::extractBundle(const ObjectFile &Source) {
+  // This will extract all entries in the Bundle
+  for (OffloadBundleEntry &Entry : Entries) {
+
+    if (Entry.Size == 0)
+      continue;
+
+    // create output file name. Which should be
+    // <fileName>-offset<Offset>-size<Size>.co"
+    std::string Str = getFileName().str() + "-offset" + itostr(Entry.Offset) +
+                      "-size" + itostr(Entry.Size) + ".co";
+    if (Error Err = object::extractCodeObject(Source, Entry.Offset, Entry.Size,
+                                              StringRef(Str)))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+Error object::extractOffloadBundleFatBinary(
+    const ObjectFile &Obj, SmallVectorImpl<OffloadBundleFatBin> &Bundles) {
+  assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
+
+  // Iterate through Sections until we find an offload_bundle section.
+  for (SectionRef Sec : Obj.sections()) {
+    Expected<StringRef> Buffer = Sec.getContents();
+    if (!Buffer)
+      return Buffer.takeError();
+
+    // If it does not start with the reserved suffix, just skip this section.
+    if ((llvm::identify_magic(*Buffer) == llvm::file_magic::offload_bundle) ||
+        (llvm::identify_magic(*Buffer) ==
+         llvm::file_magic::offload_bundle_compressed)) {
+
+      uint64_t SectionOffset = 0;
+      if (Obj.isELF()) {
+        SectionOffset = ELFSectionRef(Sec).getOffset();
+      } else if (Obj.isCOFF()) // TODO: add COFF Support
+        return createStringError(object_error::parse_failed,
+                                 "COFF object files not supported.\n");
+
+      MemoryBufferRef Contents(*Buffer, Obj.getFileName());
+
+      if (llvm::identify_magic(*Buffer) ==
+          llvm::file_magic::offload_bundle_compressed) {
+        // Decompress the input if necessary.
+        Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
+            CompressedOffloadBundle::decompress(Contents, false);
+
+        if (!DecompressedBufferOrErr)
+          return createStringError(
+              inconvertibleErrorCode(),
+              "Failed to decompress input: " +
+                  llvm::toString(DecompressedBufferOrErr.takeError()));
+
+        MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
+        if (Error Err = extractOffloadBundle(DecompressedInput, SectionOffset,
+                                             Obj.getFileName(), Bundles))
+          return Err;
+      } else {
+        if (Error Err = extractOffloadBundle(Contents, SectionOffset,
+                                             Obj.getFileName(), Bundles))
+          return Err;
+      }
+    }
+  }
+  return Error::success();
+}
+
+Error object::extractCodeObject(const ObjectFile &Source, int64_t Offset,
+                                int64_t Size, StringRef OutputFileName) {
+  Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+      FileOutputBuffer::create(OutputFileName, Size);
+
+  if (!BufferOrErr)
+    return BufferOrErr.takeError();
+
+  Expected<MemoryBufferRef> InputBuffOrErr = Source.getMemoryBufferRef();
+  if (Error Err = InputBuffOrErr.takeError())
+    return Err;
+
+  std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
+  std::copy(InputBuffOrErr->getBufferStart() + Offset,
+            InputBuffOrErr->getBufferStart() + Offset + Size,
+            Buf->getBufferStart());
+  if (Error E = Buf->commit())
+    return E;
+
+  return Error::success();
+}
+
+// given a file name, offset, and size, extract data into a code object file,
+// into file <SourceFile>-offset<Offset>-size<Size>.co
+Error object::extractOffloadBundleByURI(StringRef URIstr) {
+  // create a URI object
+  Expected<std::unique_ptr<OffloadBundleURI>> UriOrErr(
+      OffloadBundleURI::createOffloadBundleURI(URIstr, FILE_URI));
+  if (!UriOrErr)
+    return UriOrErr.takeError();
+
+  OffloadBundleURI &Uri = **UriOrErr;
+  std::string OutputFile = Uri.FileName.str();
+  OutputFile +=
+      "-offset" + itostr(Uri.Offset) + "-size" + itostr(Uri.Size) + ".co";
+
+  // Create an ObjectFile object from uri.file_uri
+  auto ObjOrErr = ObjectFile::createObjectFile(Uri.FileName);
+  if (!ObjOrErr)
+    return ObjOrErr.takeError();
+
+  auto Obj = ObjOrErr->getBinary();
+  if (Error Err =
+          object::extractCodeObject(*Obj, Uri.Offset, Uri.Size, OutputFile))
+    return Err;
+
+  return Error::success();
+}
+
+// Utility function to format numbers with commas
+static std::string formatWithCommas(unsigned long long Value) {
+  std::string Num = std::to_string(Value);
+  int InsertPosition = Num.length() - 3;
+  while (InsertPosition > 0) {
+    Num.insert(InsertPosition, ",");
+    InsertPosition -= 3;
+  }
+  return Num;
+}
+
+llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+CompressedOffloadBundle::decompress(llvm::MemoryBufferRef &Input,
+                                    bool Verbose) {
+  StringRef Blob = Input.getBuffer();
+
+  if (Blob.size() < V1HeaderSize)
+    return llvm::MemoryBuffer::getMemBufferCopy(Blob);
+
+  if (llvm::identify_magic(Blob) !=
+      llvm::file_magic::offload_bundle_compressed) {
+    if (Verbose)
+      llvm::errs() << "Uncompressed bundle.\n";
+    return llvm::MemoryBuffer::getMemBufferCopy(Blob);
+  }
+
+  size_t CurrentOffset = MagicSize;
+
+  uint16_t ThisVersion;
+  memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
+  CurrentOffset += VersionFieldSize;
+
+  uint16_t CompressionMethod;
+  memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
+  CurrentOffset += MethodFieldSize;
+
+  uint32_t TotalFileSize;
+  if (ThisVersion >= 2) {
+    if (Blob.size() < V2HeaderSize)
+      return createStringError(inconvertibleErrorCode(),
+                               "Compressed bundle header size too small");
+    memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
+    CurrentOffset += FileSizeFieldSize;
+  }
+
+  uint32_t UncompressedSize;
+  memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
+  CurrentOffset += UncompressedSizeFieldSize;
+
+  uint64_t StoredHash;
+  memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
+  CurrentOffset += HashFieldSize;
+
+  llvm::compression::Format CompressionFormat;
+  if (CompressionMethod ==
+      static_cast<uint16_t>(llvm::compression::Format::Zlib))
+    CompressionFormat = llvm::compression::Format::Zlib;
+  else if (CompressionMethod ==
+           static_cast<uint16_t>(llvm::compression::Format::Zstd))
+    CompressionFormat = llvm::compression::Format::Zstd;
+  else
+    return createStringError(inconvertibleErrorCode(),
+                             "Unknown compressing method");
+
+  llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
+                              OffloadBundlerTimerGroup);
+  if (Verbose)
+    DecompressTime...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/140286


More information about the llvm-commits mailing list