Reapply: [llvm-objdump] Add support for HIP offload bundles (#140128)

Utilize the new extensions to the LLVM Offloading API to extend to
llvm-objdump to handle dumping fatbin offload bundles generated by HIP.
This extension to llvm-objdump adds the option --offload-fatbin.
Specifying this option will take the input object/executable and extract
all offload fatbin bundle entries into distinct code object files with
names reflecting the source file name combined with the Bundle Entry ID.
Users can also use the --arch-name option to filter offload fatbin
bundle entries by their target triple.

---------

Co-authored-by: dsalinas <dsalinas@MKM-L1-DSALINAS.amd.com>
This commit is contained in:
David Salinas
2025-05-16 09:39:15 -04:00
committed by GitHub
parent 689a960b69
commit 910220b84f
10 changed files with 888 additions and 4 deletions

View File

@@ -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>

View File

@@ -0,0 +1,211 @@
//===- 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));
return 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

View File

@@ -22,6 +22,7 @@ add_llvm_component_library(LLVMObject
Object.cpp
ObjectFile.cpp
OffloadBinary.cpp
OffloadBundle.cpp
RecordStreamer.cpp
RelocationResolver.cpp
SymbolicFile.cpp

View File

@@ -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)
DecompressTimer.startTimer();
SmallVector<uint8_t, 0> DecompressedData;
StringRef CompressedData = Blob.substr(CurrentOffset);
if (llvm::Error DecompressionError = llvm::compression::decompress(
CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
DecompressedData, UncompressedSize))
return createStringError(inconvertibleErrorCode(),
"Could not decompress embedded file contents: " +
llvm::toString(std::move(DecompressionError)));
if (Verbose) {
DecompressTimer.stopTimer();
double DecompressionTimeSeconds =
DecompressTimer.getTotalTime().getWallTime();
// Recalculate MD5 hash for integrity check.
llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
"Hash recalculation time",
OffloadBundlerTimerGroup);
HashRecalcTimer.startTimer();
llvm::MD5 Hash;
llvm::MD5::MD5Result Result;
Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData.data(),
DecompressedData.size()));
Hash.final(Result);
uint64_t RecalculatedHash = Result.low();
HashRecalcTimer.stopTimer();
bool HashMatch = (StoredHash == RecalculatedHash);
double CompressionRate =
static_cast<double>(UncompressedSize) / CompressedData.size();
double DecompressionSpeedMBs =
(UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
if (ThisVersion >= 2)
llvm::errs() << "Total file size (from header): "
<< formatWithCommas(TotalFileSize) << " bytes\n";
llvm::errs() << "Decompression method: "
<< (CompressionFormat == llvm::compression::Format::Zlib
? "zlib"
: "zstd")
<< "\n"
<< "Size before decompression: "
<< formatWithCommas(CompressedData.size()) << " bytes\n"
<< "Size after decompression: "
<< formatWithCommas(UncompressedSize) << " bytes\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"
<< "Recalculated hash: "
<< llvm::format_hex(RecalculatedHash, 16) << "\n"
<< "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
}
return llvm::MemoryBuffer::getMemBufferCopy(
llvm::toStringRef(DecompressedData));
}
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
CompressedOffloadBundle::compress(llvm::compression::Params P,
const llvm::MemoryBuffer &Input,
bool Verbose) {
if (!llvm::compression::zstd::isAvailable() &&
!llvm::compression::zlib::isAvailable())
return createStringError(llvm::inconvertibleErrorCode(),
"Compression not supported");
llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
OffloadBundlerTimerGroup);
if (Verbose)
HashTimer.startTimer();
llvm::MD5 Hash;
llvm::MD5::MD5Result Result;
Hash.update(Input.getBuffer());
Hash.final(Result);
uint64_t TruncatedHash = Result.low();
if (Verbose)
HashTimer.stopTimer();
SmallVector<uint8_t, 0> CompressedBuffer;
auto BufferUint8 = llvm::ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
Input.getBuffer().size());
llvm::Timer CompressTimer("Compression Timer", "Compression time",
OffloadBundlerTimerGroup);
if (Verbose)
CompressTimer.startTimer();
llvm::compression::compress(P, BufferUint8, CompressedBuffer);
if (Verbose)
CompressTimer.stopTimer();
uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
uint32_t UncompressedSize = Input.getBuffer().size();
uint32_t TotalFileSize = MagicNumber.size() + sizeof(TotalFileSize) +
sizeof(Version) + sizeof(CompressionMethod) +
sizeof(UncompressedSize) + sizeof(TruncatedHash) +
CompressedBuffer.size();
SmallVector<char, 0> FinalBuffer;
llvm::raw_svector_ostream OS(FinalBuffer);
OS << MagicNumber;
OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
OS.write(reinterpret_cast<const char *>(&CompressionMethod),
sizeof(CompressionMethod));
OS.write(reinterpret_cast<const char *>(&TotalFileSize),
sizeof(TotalFileSize));
OS.write(reinterpret_cast<const char *>(&UncompressedSize),
sizeof(UncompressedSize));
OS.write(reinterpret_cast<const char *>(&TruncatedHash),
sizeof(TruncatedHash));
OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
CompressedBuffer.size());
if (Verbose) {
auto MethodUsed =
P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
double CompressionRate =
static_cast<double>(UncompressedSize) / CompressedBuffer.size();
double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
double CompressionSpeedMBs =
(UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
llvm::errs() << "Compressed bundle format version: " << Version << "\n"
<< "Total file size (including headers): "
<< formatWithCommas(TotalFileSize) << " bytes\n"
<< "Compression method used: " << MethodUsed << "\n"
<< "Compression level: " << P.level << "\n"
<< "Binary size before compression: "
<< formatWithCommas(UncompressedSize) << " bytes\n"
<< "Binary size after compression: "
<< formatWithCommas(CompressedBuffer.size()) << " bytes\n"
<< "Compression rate: "
<< llvm::format("%.2lf", CompressionRate) << "\n"
<< "Compression ratio: "
<< llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
<< "Compression speed: "
<< llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
<< "Truncated MD5 hash: "
<< llvm::format_hex(TruncatedHash, 16) << "\n";
}
return llvm::MemoryBuffer::getMemBufferCopy(
llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
}

File diff suppressed because one or more lines are too long

View File

@@ -14,12 +14,16 @@
#include "OffloadDump.h"
#include "llvm-objdump.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/OffloadBinary.h"
#include "llvm/Object/OffloadBundle.h"
#include "llvm/Support/Alignment.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
void disassembleObject(llvm::object::ObjectFile *, bool InlineRelocs);
/// Get the printable name of the image kind.
static StringRef getImageName(const OffloadBinary &OB) {
switch (OB.getImageKind()) {
@@ -48,7 +52,7 @@ static void printBinary(const OffloadBinary &OB, uint64_t Index) {
}
/// Print the embedded offloading contents of an ObjectFile \p O.
void llvm::dumpOffloadBinary(const ObjectFile &O) {
void llvm::dumpOffloadBinary(const ObjectFile &O, StringRef ArchName) {
if (!O.isELF() && !O.isCOFF()) {
reportWarning(
"--offloading is currently only supported for COFF and ELF targets",
@@ -64,6 +68,46 @@ void llvm::dumpOffloadBinary(const ObjectFile &O) {
// Print out all the binaries that are contained in this buffer.
for (uint64_t I = 0, E = Binaries.size(); I != E; ++I)
printBinary(*Binaries[I].getBinary(), I);
dumpOffloadBundleFatBinary(O, ArchName);
}
// Given an Object file, collect all Bundles of FatBin Binaries
// and dump them into Code Object files
// if -arch=-name is specified, only dump the Entries that match the target arch
void llvm::dumpOffloadBundleFatBinary(const ObjectFile &O, StringRef ArchName) {
if (!O.isELF() && !O.isCOFF()) {
reportWarning(
"--offloading is currently only supported for COFF and ELF targets",
O.getFileName());
return;
}
SmallVector<llvm::object::OffloadBundleFatBin> FoundBundles;
SmallVector<llvm::object::OffloadBundleEntry> FoundEntries;
if (Error Err = llvm::object::extractOffloadBundleFatBinary(O, FoundBundles))
reportError(O.getFileName(), "while extracting offload FatBin bundles: " +
toString(std::move(Err)));
for (const auto &[BundleNum, Bundle] : llvm::enumerate(FoundBundles)) {
if (!ArchName.empty())
FoundEntries = Bundle.entryIDContains(ArchName);
else
FoundEntries = Bundle.getEntries();
for (OffloadBundleEntry &Entry : FoundEntries) {
// create file name for this object file: <source-filename>:<Bundle
// Number>.<EntryID>
std::string str = Bundle.getFileName().str() + "." + itostr(BundleNum) +
"." + Entry.ID.str();
if (Error Err = object::extractCodeObject(O, Entry.Offset, Entry.Size,
StringRef(str)))
reportError(O.getFileName(),
"while extracting offload Bundle Entries: " +
toString(std::move(Err)));
}
}
}
/// Print the contents of an offload binary file \p OB. This may contain

View File

@@ -11,12 +11,16 @@
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/OffloadBinary.h"
#include "llvm/Object/OffloadBundle.h"
namespace llvm {
void dumpOffloadSections(const object::OffloadBinary &OB);
void dumpOffloadBinary(const object::ObjectFile &O);
void dumpOffloadBinary(const object::ObjectFile &O, StringRef ArchName);
/// Dump fat binary in binary clang-offload-bundler format
void dumpOffloadBundleFatBinary(const object::ObjectFile &O,
StringRef ArchName);
} // namespace llvm
#endif

View File

@@ -56,6 +56,7 @@
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/OffloadBinary.h"
#include "llvm/Object/OffloadBundle.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
@@ -3361,7 +3362,7 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
if (FaultMapSection)
printFaultMaps(O);
if (Offloading)
dumpOffloadBinary(*O);
dumpOffloadBinary(*O, StringRef(ArchName));
}
static void dumpObject(const COFFImportFile *I, const Archive *A,

View File

@@ -16,6 +16,7 @@ add_llvm_unittest(ObjectTests
MinidumpTest.cpp
ObjectFileTest.cpp
OffloadingTest.cpp
OffloadingBundleTest.cpp
SymbolSizeTest.cpp
SymbolicFileTest.cpp
XCOFFObjectFileTest.cpp

File diff suppressed because one or more lines are too long