LLVM 22.0.0git
OffloadBundle.cpp
Go to the documentation of this file.
1//===- OffloadBundle.cpp - Utilities for offload bundles---*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------===//
8
11#include "llvm/IR/Module.h"
14#include "llvm/Object/Archive.h"
15#include "llvm/Object/Binary.h"
16#include "llvm/Object/COFF.h"
18#include "llvm/Object/Error.h"
23#include "llvm/Support/Timer.h"
24
25using namespace llvm;
26using namespace llvm::object;
27
29 OffloadBundlerTimerGroup("Offload Bundler Timer Group",
30 "Timer group for offload bundler");
31
32// Extract an Offload bundle (usually a Offload Bundle) from a fat_bin
33// section
35 StringRef FileName,
37
38 size_t Offset = 0;
39 size_t NextbundleStart = 0;
40
41 // There could be multiple offloading bundles stored at this section.
42 while (NextbundleStart != StringRef::npos) {
43 std::unique_ptr<MemoryBuffer> Buffer =
45 /*RequiresNullTerminator=*/false);
46
47 // Create the FatBinBindle object. This will also create the Bundle Entry
48 // list info.
49 auto FatBundleOrErr =
50 OffloadBundleFatBin::create(*Buffer, SectionOffset + Offset, FileName);
51 if (!FatBundleOrErr)
52 return FatBundleOrErr.takeError();
53
54 // Add current Bundle to list.
55 Bundles.emplace_back(std::move(**FatBundleOrErr));
56
57 // Find the next bundle by searching for the magic string
58 StringRef Str = Buffer->getBuffer();
59 NextbundleStart = Str.find(StringRef("__CLANG_OFFLOAD_BUNDLE__"), 24);
60
61 if (NextbundleStart != StringRef::npos)
62 Offset += NextbundleStart;
63 }
64
65 return Error::success();
66}
67
69 uint64_t SectionOffset) {
70 uint64_t NumOfEntries = 0;
71
73
74 // Read the Magic String first.
75 StringRef Magic;
76 if (auto EC = Reader.readFixedString(Magic, 24))
78
79 // Read the number of Code Objects (Entries) in the current Bundle.
80 if (auto EC = Reader.readInteger(NumOfEntries))
82
83 NumberOfEntries = NumOfEntries;
84
85 // For each Bundle Entry (code object)
86 for (uint64_t I = 0; I < NumOfEntries; I++) {
87 uint64_t EntrySize;
88 uint64_t EntryOffset;
89 uint64_t EntryIDSize;
90 StringRef EntryID;
91
92 if (auto EC = Reader.readInteger(EntryOffset))
94
95 if (auto EC = Reader.readInteger(EntrySize))
97
98 if (auto EC = Reader.readInteger(EntryIDSize))
100
101 if (auto EC = Reader.readFixedString(EntryID, EntryIDSize))
103
104 auto Entry = std::make_unique<OffloadBundleEntry>(
105 EntryOffset + SectionOffset, EntrySize, EntryIDSize, EntryID);
106
107 Entries.push_back(*Entry);
108 }
109
110 return Error::success();
111}
112
115 StringRef FileName) {
116 if (Buf.getBufferSize() < 24)
118
119 // Check for magic bytes.
122
123 OffloadBundleFatBin *TheBundle = new OffloadBundleFatBin(Buf, FileName);
124
125 // Read the Bundle Entries
126 Error Err = TheBundle->readEntries(Buf.getBuffer(), SectionOffset);
127 if (Err)
129
130 return std::unique_ptr<OffloadBundleFatBin>(TheBundle);
131}
132
134 // This will extract all entries in the Bundle
135 for (OffloadBundleEntry &Entry : Entries) {
136
137 if (Entry.Size == 0)
138 continue;
139
140 // create output file name. Which should be
141 // <fileName>-offset<Offset>-size<Size>.co"
142 std::string Str = getFileName().str() + "-offset" + itostr(Entry.Offset) +
143 "-size" + itostr(Entry.Size) + ".co";
144 if (Error Err = object::extractCodeObject(Source, Entry.Offset, Entry.Size,
145 StringRef(Str)))
146 return Err;
147 }
148
149 return Error::success();
150}
151
154 assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
155
156 // Iterate through Sections until we find an offload_bundle section.
157 for (SectionRef Sec : Obj.sections()) {
158 Expected<StringRef> Buffer = Sec.getContents();
159 if (!Buffer)
160 return Buffer.takeError();
161
162 // If it does not start with the reserved suffix, just skip this section.
164 (llvm::identify_magic(*Buffer) ==
166
167 uint64_t SectionOffset = 0;
168 if (Obj.isELF()) {
169 SectionOffset = ELFSectionRef(Sec).getOffset();
170 } else if (Obj.isCOFF()) // TODO: add COFF Support
172 "COFF object files not supported.\n");
173
174 MemoryBufferRef Contents(*Buffer, Obj.getFileName());
175
176 if (llvm::identify_magic(*Buffer) ==
178 // Decompress the input if necessary.
179 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
181
182 if (!DecompressedBufferOrErr)
183 return createStringError(
185 "Failed to decompress input: " +
186 llvm::toString(DecompressedBufferOrErr.takeError()));
187
188 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
189 if (Error Err = extractOffloadBundle(DecompressedInput, SectionOffset,
190 Obj.getFileName(), Bundles))
191 return Err;
192 } else {
193 if (Error Err = extractOffloadBundle(Contents, SectionOffset,
194 Obj.getFileName(), Bundles))
195 return Err;
196 }
197 }
198 }
199 return Error::success();
200}
201
203 int64_t Size, StringRef OutputFileName) {
205 FileOutputBuffer::create(OutputFileName, Size);
206
207 if (!BufferOrErr)
208 return BufferOrErr.takeError();
209
210 Expected<MemoryBufferRef> InputBuffOrErr = Source.getMemoryBufferRef();
211 if (Error Err = InputBuffOrErr.takeError())
212 return Err;
213
214 std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
215 std::copy(InputBuffOrErr->getBufferStart() + Offset,
216 InputBuffOrErr->getBufferStart() + Offset + Size,
217 Buf->getBufferStart());
218 if (Error E = Buf->commit())
219 return E;
220
221 return Error::success();
222}
223
224// given a file name, offset, and size, extract data into a code object file,
225// into file <SourceFile>-offset<Offset>-size<Size>.co
227 // create a URI object
230 if (!UriOrErr)
231 return UriOrErr.takeError();
232
233 OffloadBundleURI &Uri = **UriOrErr;
234 std::string OutputFile = Uri.FileName.str();
235 OutputFile +=
236 "-offset" + itostr(Uri.Offset) + "-size" + itostr(Uri.Size) + ".co";
237
238 // Create an ObjectFile object from uri.file_uri
239 auto ObjOrErr = ObjectFile::createObjectFile(Uri.FileName);
240 if (!ObjOrErr)
241 return ObjOrErr.takeError();
242
243 auto Obj = ObjOrErr->getBinary();
244 if (Error Err =
245 object::extractCodeObject(*Obj, Uri.Offset, Uri.Size, OutputFile))
246 return Err;
247
248 return Error::success();
249}
250
251// Utility function to format numbers with commas
252static std::string formatWithCommas(unsigned long long Value) {
253 std::string Num = std::to_string(Value);
254 int InsertPosition = Num.length() - 3;
255 while (InsertPosition > 0) {
256 Num.insert(InsertPosition, ",");
257 InsertPosition -= 3;
258 }
259 return Num;
260}
261
264 bool Verbose) {
265 StringRef Blob = Input.getBuffer();
266
267 if (Blob.size() < V1HeaderSize)
269
270 if (llvm::identify_magic(Blob) !=
272 if (Verbose)
273 llvm::errs() << "Uncompressed bundle.\n";
275 }
276
277 size_t CurrentOffset = MagicSize;
278
279 uint16_t ThisVersion;
280 memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
281 CurrentOffset += VersionFieldSize;
282
283 uint16_t CompressionMethod;
284 memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
285 CurrentOffset += MethodFieldSize;
286
287 uint32_t TotalFileSize;
288 if (ThisVersion >= 2) {
289 if (Blob.size() < V2HeaderSize)
291 "Compressed bundle header size too small");
292 memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
293 CurrentOffset += FileSizeFieldSize;
294 }
295
296 uint32_t UncompressedSize;
297 memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
298 CurrentOffset += UncompressedSizeFieldSize;
299
300 uint64_t StoredHash;
301 memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
302 CurrentOffset += HashFieldSize;
303
304 llvm::compression::Format CompressionFormat;
305 if (CompressionMethod ==
307 CompressionFormat = llvm::compression::Format::Zlib;
308 else if (CompressionMethod ==
310 CompressionFormat = llvm::compression::Format::Zstd;
311 else
313 "Unknown compressing method");
314
315 llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
317 if (Verbose)
318 DecompressTimer.startTimer();
319
320 SmallVector<uint8_t, 0> DecompressedData;
321 StringRef CompressedData = Blob.substr(CurrentOffset);
322 if (llvm::Error DecompressionError = llvm::compression::decompress(
323 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
324 DecompressedData, UncompressedSize))
326 "Could not decompress embedded file contents: " +
327 llvm::toString(std::move(DecompressionError)));
328
329 if (Verbose) {
330 DecompressTimer.stopTimer();
331
332 double DecompressionTimeSeconds =
333 DecompressTimer.getTotalTime().getWallTime();
334
335 // Recalculate MD5 hash for integrity check.
336 llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
337 "Hash recalculation time",
339 HashRecalcTimer.startTimer();
340 llvm::MD5 Hash;
342 Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData));
343 Hash.final(Result);
344 uint64_t RecalculatedHash = Result.low();
345 HashRecalcTimer.stopTimer();
346 bool HashMatch = (StoredHash == RecalculatedHash);
347
348 double CompressionRate =
349 static_cast<double>(UncompressedSize) / CompressedData.size();
350 double DecompressionSpeedMBs =
351 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
352
353 llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
354 if (ThisVersion >= 2)
355 llvm::errs() << "Total file size (from header): "
356 << formatWithCommas(TotalFileSize) << " bytes\n";
357 llvm::errs() << "Decompression method: "
358 << (CompressionFormat == llvm::compression::Format::Zlib
359 ? "zlib"
360 : "zstd")
361 << "\n"
362 << "Size before decompression: "
363 << formatWithCommas(CompressedData.size()) << " bytes\n"
364 << "Size after decompression: "
365 << formatWithCommas(UncompressedSize) << " bytes\n"
366 << "Compression rate: "
367 << llvm::format("%.2lf", CompressionRate) << "\n"
368 << "Compression ratio: "
369 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
370 << "Decompression speed: "
371 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
372 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
373 << "Recalculated hash: "
374 << llvm::format_hex(RecalculatedHash, 16) << "\n"
375 << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
376 }
377
379 llvm::toStringRef(DecompressedData));
380}
381
384 const llvm::MemoryBuffer &Input,
385 bool Verbose) {
389 "Compression not supported");
390
391 llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
393 if (Verbose)
394 HashTimer.startTimer();
395 llvm::MD5 Hash;
397 Hash.update(Input.getBuffer());
398 Hash.final(Result);
399 uint64_t TruncatedHash = Result.low();
400 if (Verbose)
401 HashTimer.stopTimer();
402
403 SmallVector<uint8_t, 0> CompressedBuffer;
404 auto BufferUint8 = llvm::ArrayRef<uint8_t>(
405 reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
406 Input.getBuffer().size());
407
408 llvm::Timer CompressTimer("Compression Timer", "Compression time",
410 if (Verbose)
411 CompressTimer.startTimer();
412 llvm::compression::compress(P, BufferUint8, CompressedBuffer);
413 if (Verbose)
414 CompressTimer.stopTimer();
415
416 uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
417 uint32_t UncompressedSize = Input.getBuffer().size();
418 uint32_t TotalFileSize = MagicNumber.size() + sizeof(TotalFileSize) +
419 sizeof(Version) + sizeof(CompressionMethod) +
420 sizeof(UncompressedSize) + sizeof(TruncatedHash) +
421 CompressedBuffer.size();
422
423 SmallVector<char, 0> FinalBuffer;
424 llvm::raw_svector_ostream OS(FinalBuffer);
425 OS << MagicNumber;
426 OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
427 OS.write(reinterpret_cast<const char *>(&CompressionMethod),
428 sizeof(CompressionMethod));
429 OS.write(reinterpret_cast<const char *>(&TotalFileSize),
430 sizeof(TotalFileSize));
431 OS.write(reinterpret_cast<const char *>(&UncompressedSize),
432 sizeof(UncompressedSize));
433 OS.write(reinterpret_cast<const char *>(&TruncatedHash),
434 sizeof(TruncatedHash));
435 OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
436 CompressedBuffer.size());
437
438 if (Verbose) {
439 auto MethodUsed =
440 P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
441 double CompressionRate =
442 static_cast<double>(UncompressedSize) / CompressedBuffer.size();
443 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
444 double CompressionSpeedMBs =
445 (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
446
447 llvm::errs() << "Compressed bundle format version: " << Version << "\n"
448 << "Total file size (including headers): "
449 << formatWithCommas(TotalFileSize) << " bytes\n"
450 << "Compression method used: " << MethodUsed << "\n"
451 << "Compression level: " << P.level << "\n"
452 << "Binary size before compression: "
453 << formatWithCommas(UncompressedSize) << " bytes\n"
454 << "Binary size after compression: "
455 << formatWithCommas(CompressedBuffer.size()) << " bytes\n"
456 << "Compression rate: "
457 << llvm::format("%.2lf", CompressionRate) << "\n"
458 << "Compression ratio: "
459 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
460 << "Compression speed: "
461 << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
462 << "Truncated MD5 hash: "
463 << llvm::format_hex(TruncatedHash, 16) << "\n";
464 }
466 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
467}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t Size
Module.h This file contains the declarations for the Module class.
#define I(x, y, z)
Definition: MD5.cpp:58
Error extractOffloadBundle(MemoryBufferRef Contents, uint64_t SectionOffset, StringRef FileName, SmallVectorImpl< OffloadBundleFatBin > &Bundles)
static llvm::TimerGroup OffloadBundlerTimerGroup("Offload Bundler Timer Group", "Timer group for offload bundler")
static std::string formatWithCommas(unsigned long long Value)
#define P(N)
raw_pwrite_stream & OS
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
Provides read only access to a subclass of BinaryStream.
Error readInteger(T &Dest)
Read an integer of the specified endianness into Dest and update the stream's offset.
LLVM_ABI Error readFixedString(StringRef &Dest, uint32_t Length)
Read a Length byte string into Dest.
Lightweight error class with error context and mandatory checking.
Definition: Error.h:159
static ErrorSuccess success()
Create a success value.
Definition: Error.h:336
Tagged union holding either a T or a Error.
Definition: Error.h:485
Error takeError()
Take ownership of the stored error.
Definition: Error.h:612
static LLVM_ABI Expected< std::unique_ptr< FileOutputBuffer > > create(StringRef FilePath, size_t Size, unsigned Flags=0)
Factory method to create an OutputBuffer object which manages a read/write buffer of the specified si...
Definition: MD5.h:42
LLVM_ABI void update(ArrayRef< uint8_t > Data)
Updates the hash for the byte stream provided.
Definition: MD5.cpp:189
LLVM_ABI void final(MD5Result &Result)
Finishes off the hash and puts the result in result.
Definition: MD5.cpp:234
size_t getBufferSize() const
StringRef getBuffer() const
This interface provides simple read-only access to a block of memory, and provides simple methods for...
Definition: MemoryBuffer.h:52
static std::unique_ptr< MemoryBuffer > getMemBuffer(StringRef InputData, StringRef BufferName="", bool RequiresNullTerminator=true)
Open the specified memory range as a MemoryBuffer.
static std::unique_ptr< MemoryBuffer > getMemBufferCopy(StringRef InputData, const Twine &BufferName="")
Open the specified memory range as a MemoryBuffer, copying the contents and taking ownership of it.
StringRef getBuffer() const
Definition: MemoryBuffer.h:71
size_t size() const
Definition: SmallVector.h:79
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:574
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:938
pointer data()
Return a pointer to the vector's buffer, even if empty().
Definition: SmallVector.h:287
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:233
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:581
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition: StringRef.h:619
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:154
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:148
static constexpr size_t npos
Definition: StringRef.h:57
double getWallTime() const
Definition: Timer.h:46
The TimerGroup class is used to group together related timers into a single report that is printed wh...
Definition: Timer.h:186
This class is used to track the amount of time spent between invocations of its startTimer()/stopTime...
Definition: Timer.h:82
LLVM_ABI void stopTimer()
Stop the timer.
Definition: Timer.cpp:158
LLVM_ABI void startTimer()
Start the timer running.
Definition: Timer.cpp:149
TimeRecord getTotalTime() const
Return the duration for which this timer has been running.
Definition: Timer.h:140
LLVM Value Representation.
Definition: Value.h:75
bool isCOFF() const
Definition: Binary.h:133
StringRef getFileName() const
Definition: Binary.cpp:41
bool isELF() const
Definition: Binary.h:125
static LLVM_ABI llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > compress(llvm::compression::Params P, const llvm::MemoryBuffer &Input, bool Verbose=false)
static LLVM_ABI llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > decompress(llvm::MemoryBufferRef &Input, bool Verbose=false)
uint64_t getOffset() const
This class is the base class for all object file types.
Definition: ObjectFile.h:231
section_iterator_range sections() const
Definition: ObjectFile.h:331
static Expected< OwningBinary< ObjectFile > > createObjectFile(StringRef ObjectPath)
Definition: ObjectFile.cpp:211
Fat binary embedded in object files in clang-offload-bundler format.
Definition: OffloadBundle.h:79
static LLVM_ABI Expected< std::unique_ptr< OffloadBundleFatBin > > create(MemoryBufferRef, uint64_t SectionOffset, StringRef FileName)
LLVM_ABI Error readEntries(StringRef Section, uint64_t SectionOffset)
LLVM_ABI Error extractBundle(const ObjectFile &Source)
This is a value type class that represents a single section in the list of sections in the object fil...
Definition: ObjectFile.h:83
raw_ostream & write(unsigned char C)
A raw_ostream that writes to an SmallVector or SmallString.
Definition: raw_ostream.h:692
MagicNumber
Definition: XCOFF.h:49
LLVM_ABI bool isAvailable()
LLVM_ABI bool isAvailable()
LLVM_ABI Error decompress(DebugCompressionType T, ArrayRef< uint8_t > Input, uint8_t *Output, size_t UncompressedSize)
Definition: Compression.cpp:58
LLVM_ABI void compress(Params P, ArrayRef< uint8_t > Input, SmallVectorImpl< uint8_t > &Output)
Definition: Compression.cpp:46
LLVM_ABI Error extractCodeObject(const ObjectFile &Source, int64_t Offset, int64_t Size, StringRef OutputFileName)
Extract code object memory from the given Source object file at Offset and of Size,...
LLVM_ABI Error extractOffloadBundleByURI(StringRef URIstr)
Extracts an Offload Bundle Entry given by URI.
LLVM_ABI Error extractOffloadBundleFatBinary(const ObjectFile &Obj, SmallVectorImpl< OffloadBundleFatBin > &Bundles)
Extracts fat binary in binary clang-offload-bundler format from object Obj and return it in Bundles.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
LLVM_ABI file_magic identify_magic(StringRef magic)
Identify the type of a binary file based on how magical it is.
Definition: Magic.cpp:33
@ Offset
Definition: DWP.cpp:477
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:98
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition: Error.h:1305
FormattedNumber format_hex(uint64_t N, unsigned Width, bool Upper=false)
format_hex - Output N as a fixed width hexadecimal.
Definition: Format.h:188
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition: Format.h:126
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
const char * toString(DWARFSectionKind Kind)
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:111
@ offload_bundle
Clang offload bundle file.
Definition: Magic.h:60
@ offload_bundle_compressed
Compressed clang offload bundle file.
Definition: Magic.h:61
Bundle entry in binary clang-offload-bundler format.
Definition: OffloadBundle.h:61
static Expected< std::unique_ptr< OffloadBundleURI > > createOffloadBundleURI(StringRef Str, UriTypeT Type)