clang 23.0.0git
OffloadBundler.cpp
Go to the documentation of this file.
1//===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===//
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///
9/// \file
10/// This file implements an offload bundling API that bundles different files
11/// that relate with the same source code but different targets into a single
12/// one. Also the implements the opposite functionality, i.e. unbundle files
13/// previous created by this API.
14///
15//===----------------------------------------------------------------------===//
16
18#include "clang/Basic/Cuda.h"
20#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/ADT/StringMap.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/BinaryFormat/Magic.h"
27#include "llvm/Object/Archive.h"
28#include "llvm/Object/ArchiveWriter.h"
29#include "llvm/Object/Binary.h"
30#include "llvm/Object/ObjectFile.h"
31#include "llvm/Support/Casting.h"
32#include "llvm/Support/Compiler.h"
33#include "llvm/Support/Compression.h"
34#include "llvm/Support/Debug.h"
35#include "llvm/Support/EndianStream.h"
36#include "llvm/Support/Errc.h"
37#include "llvm/Support/Error.h"
38#include "llvm/Support/ErrorOr.h"
39#include "llvm/Support/FileSystem.h"
40#include "llvm/Support/MD5.h"
41#include "llvm/Support/ManagedStatic.h"
42#include "llvm/Support/MemoryBuffer.h"
43#include "llvm/Support/Path.h"
44#include "llvm/Support/Program.h"
45#include "llvm/Support/Signals.h"
46#include "llvm/Support/StringSaver.h"
47#include "llvm/Support/Timer.h"
48#include "llvm/Support/WithColor.h"
49#include "llvm/Support/raw_ostream.h"
50#include "llvm/TargetParser/Host.h"
51#include "llvm/TargetParser/Triple.h"
52#include <algorithm>
53#include <cassert>
54#include <cstddef>
55#include <cstdint>
56#include <forward_list>
57#include <llvm/Support/Process.h>
58#include <memory>
59#include <set>
60#include <string>
61#include <system_error>
62#include <utility>
63
64using namespace llvm;
65using namespace llvm::object;
66using namespace clang;
67
68namespace {
69struct CreateClangOffloadBundlerTimerGroup {
70 static void *call() {
71 return new TimerGroup("Clang Offload Bundler Timer Group",
72 "Timer group for clang offload bundler");
73 }
74};
75} // namespace
76static llvm::ManagedStatic<llvm::TimerGroup,
77 CreateClangOffloadBundlerTimerGroup>
79
80/// Magic string that marks the existence of offloading data.
81#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
82
84 const OffloadBundlerConfig &BC)
85 : BundlerConfig(BC) {
86
87 // <kind>-<triple>[-<target id>[:target features]]
88 // <triple> := <arch>-<vendor>-<os>-<env>
90 Target.split(Components, '-', /*MaxSplit=*/5);
91 assert((Components.size() == 5 || Components.size() == 6) &&
92 "malformed target string");
93
94 StringRef TargetIdWithFeature =
95 Components.size() == 6 ? Components.back() : "";
96 StringRef TargetId = TargetIdWithFeature.split(':').first;
97 if (!TargetId.empty() &&
99 this->TargetID = TargetIdWithFeature;
100 else
101 this->TargetID = "";
102
103 this->OffloadKind = Components.front();
104 ArrayRef<StringRef> TripleSlice{&Components[1], /*length=*/4};
105 llvm::Triple T = llvm::Triple(llvm::join(TripleSlice, "-"));
106 this->Triple = llvm::Triple(T.getArchName(), T.getVendorName(), T.getOSName(),
107 T.getEnvironmentName());
108}
109
111 return this->OffloadKind == "host";
112}
113
115 return OffloadKind == "host" || OffloadKind == "openmp" ||
116 OffloadKind == "hip" || OffloadKind == "hipv4";
117}
118
120 const StringRef TargetOffloadKind) const {
121 if ((OffloadKind == TargetOffloadKind) ||
122 (OffloadKind == "hip" && TargetOffloadKind == "hipv4") ||
123 (OffloadKind == "hipv4" && TargetOffloadKind == "hip"))
124 return true;
125
126 if (BundlerConfig.HipOpenmpCompatible) {
127 bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") &&
128 TargetOffloadKind == "openmp";
129 bool OpenMPCompatibleWithHIP =
130 OffloadKind == "openmp" &&
131 TargetOffloadKind.starts_with_insensitive("hip");
132 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
133 }
134 return false;
135}
136
138 return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
139}
140
142 return OffloadKind == Target.OffloadKind &&
143 Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID;
144}
145
146std::string OffloadTargetInfo::str() const {
147 std::string NormalizedTriple;
148 // Unfortunately we need some special sauce for AMDHSA because all the runtime
149 // assumes the triple to be "amdgcn/spirv64-amd-amdhsa-" (empty environment)
150 // instead of "amdgcn/spirv64-amd-amdhsa-unknown". It's gonna be very tricky
151 // to patch different layers of runtime.
152 if (Triple.getOS() == Triple::OSType::AMDHSA) {
153 NormalizedTriple = Triple.normalize(Triple::CanonicalForm::THREE_IDENT);
154 NormalizedTriple.push_back('-');
155 } else {
156 NormalizedTriple = Triple.normalize(Triple::CanonicalForm::FOUR_IDENT);
157 }
158 return Twine(OffloadKind + "-" + NormalizedTriple + "-" + TargetID).str();
159}
160
161static StringRef getDeviceFileExtension(StringRef Device,
162 StringRef BundleFileName) {
163 if (Device.contains("gfx"))
164 return ".bc";
165 if (Device.contains("sm_"))
166 return ".cubin";
167 return sys::path::extension(BundleFileName);
168}
169
170static std::string getDeviceLibraryFileName(StringRef BundleFileName,
171 StringRef Device) {
172 StringRef LibName = sys::path::stem(BundleFileName);
173 StringRef Extension = getDeviceFileExtension(Device, BundleFileName);
174
175 std::string Result;
176 Result += LibName;
177 Result += Extension;
178 return Result;
179}
180
181namespace {
182/// Generic file handler interface.
183class FileHandler {
184public:
185 struct BundleInfo {
186 StringRef BundleID;
187 };
188
189 FileHandler() {}
190
191 virtual ~FileHandler() {}
192
193 /// Update the file handler with information from the header of the bundled
194 /// file.
195 virtual Error ReadHeader(StringRef FC) = 0;
196
197 /// Read the marker of the next bundled to be read in the file. The bundle
198 /// name is returned if there is one in the file, or `std::nullopt` if there
199 /// are no more bundles to be read.
200 virtual Expected<std::optional<StringRef>>
201 ReadBundleStart(StringRef Input) = 0;
202
203 /// Read the marker that closes the current bundle.
204 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
205
206 /// Read the current bundle and write the result into the stream \a OS.
207 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
208
209 /// Write the header of the bundled file to \a OS based on the information
210 /// gathered from \a Inputs.
211 virtual Error WriteHeader(raw_ostream &OS,
212 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
213
214 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
215 /// \a OS.
216 virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0;
217
218 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
219 /// OS.
220 virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0;
221
222 /// Write the bundle from \a Input into \a OS.
223 virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
224
225 /// Finalize output file.
226 virtual Error finalizeOutputFile() { return Error::success(); }
227
228 /// List bundle IDs in \a Input.
229 virtual Error listBundleIDs(MemoryBuffer &Input) {
230 size_t NextBundleStart = 0;
231 StringRef BufferString = Input.getBuffer();
232 while (NextBundleStart != StringRef::npos) {
233
234 // Drop the data that has already been processed/read.
235 BufferString = BufferString.drop_front(NextBundleStart);
236
237 // Read the header.
238 Error Err = ReadHeader(BufferString);
239 if (Err)
240 return Err;
241
242 Err = forEachBundle(BufferString, [&](const BundleInfo &Info) -> Error {
243 llvm::outs() << Info.BundleID << '\n';
244 Error Err = listBundleIDsCallback(Input, Info);
245 if (Err)
246 return Err;
247 return Error::success();
248 });
249
250 if (Err)
251 return Err;
252
253 // Find the beginning of the next Bundle, if it exists.
254 NextBundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
256 }
257 return Error::success();
258 }
259
260 /// Get bundle IDs in \a Input in \a BundleIds.
261 virtual Error getBundleIDs(MemoryBuffer &Input,
262 std::set<StringRef> &BundleIds) {
263
264 if (Error Err = ReadHeader(Input.getBuffer()))
265 return Err;
266 return forEachBundle(Input.getBuffer(),
267 [&](const BundleInfo &Info) -> Error {
268 BundleIds.insert(Info.BundleID);
269 Error Err = listBundleIDsCallback(Input, Info);
270 if (Err)
271 return Err;
272 return Error::success();
273 });
274 }
275
276 /// For each bundle in \a Input, do \a Func.
277 Error forEachBundle(StringRef Input,
278 std::function<Error(const BundleInfo &)> Func) {
279 while (true) {
280 Expected<std::optional<StringRef>> CurTripleOrErr =
281 ReadBundleStart(Input);
282 if (!CurTripleOrErr)
283 return CurTripleOrErr.takeError();
284
285 // No more bundles.
286 if (!*CurTripleOrErr)
287 break;
288
289 StringRef CurTriple = **CurTripleOrErr;
290 assert(!CurTriple.empty());
291
292 BundleInfo Info{CurTriple};
293 if (Error Err = Func(Info))
294 return Err;
295 }
296 return Error::success();
297 }
298
299protected:
300 virtual Error listBundleIDsCallback(MemoryBuffer &Input,
301 const BundleInfo &Info) {
302 return Error::success();
303 }
304};
305
306/// Handler for binary files. The bundled file will have the following format
307/// (all integers are stored in little-endian format):
308///
309/// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
310///
311/// NumberOfOffloadBundles (8-byte integer)
312///
313/// OffsetOfBundle1 (8-byte integer)
314/// SizeOfBundle1 (8-byte integer)
315/// NumberOfBytesInTripleOfBundle1 (8-byte integer)
316/// TripleOfBundle1 (byte length defined before)
317///
318/// ...
319///
320/// OffsetOfBundleN (8-byte integer)
321/// SizeOfBundleN (8-byte integer)
322/// NumberOfBytesInTripleOfBundleN (8-byte integer)
323/// TripleOfBundleN (byte length defined before)
324///
325/// Bundle1
326/// ...
327/// BundleN
328
329/// Read 8-byte integers from a buffer in little-endian format.
330static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
331 return llvm::support::endian::read64le(Buffer.data() + pos);
332}
333
334/// Write 8-byte integers to a buffer in little-endian format.
335static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
336 llvm::support::endian::write(OS, Val, llvm::endianness::little);
337}
338
339class BinaryFileHandler final : public FileHandler {
340 /// Information about the bundles extracted from the header.
341 struct BinaryBundleInfo final : public BundleInfo {
342 /// Size of the bundle.
343 uint64_t Size = 0u;
344 /// Offset at which the bundle starts in the bundled file.
345 uint64_t Offset = 0u;
346
347 BinaryBundleInfo() {}
348 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
349 : Size(Size), Offset(Offset) {}
350 };
351
352 /// Map between a triple and the corresponding bundle information.
353 StringMap<BinaryBundleInfo> BundlesInfo;
354
355 /// Iterator for the bundle information that is being read.
356 StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
357 StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
358
359 /// Current bundle target to be written.
360 std::string CurWriteBundleTarget;
361
362 /// Configuration options and arrays for this bundler job
363 const OffloadBundlerConfig &BundlerConfig;
364
365public:
366 // TODO: Add error checking from ClangOffloadBundler.cpp
367 BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
368
369 ~BinaryFileHandler() final {}
370
371 Error ReadHeader(StringRef FC) final {
372 // Initialize the current bundle with the end of the container.
373 CurBundleInfo = BundlesInfo.end();
374
375 // Check if buffer is smaller than magic string.
376 size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
377 if (ReadChars > FC.size())
378 return Error::success();
379
380 // Check if no magic was found.
381 if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
382 return Error::success();
383
384 // Read number of bundles.
385 if (ReadChars + 8 > FC.size())
386 return Error::success();
387
388 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
389 ReadChars += 8;
390
391 // Read bundle offsets, sizes and triples.
392 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
393
394 // Read offset.
395 if (ReadChars + 8 > FC.size())
396 return Error::success();
397
398 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
399 ReadChars += 8;
400
401 // Read size.
402 if (ReadChars + 8 > FC.size())
403 return Error::success();
404
405 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
406 ReadChars += 8;
407
408 // Read triple size.
409 if (ReadChars + 8 > FC.size())
410 return Error::success();
411
412 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
413 ReadChars += 8;
414
415 // Read triple.
416 if (ReadChars + TripleSize > FC.size())
417 return Error::success();
418
419 StringRef Triple(&FC.data()[ReadChars], TripleSize);
420 ReadChars += TripleSize;
421
422 // Check if the offset and size make sense.
423 if (!Offset || Offset + Size > FC.size())
424 return Error::success();
425
426 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
427 }
428 // Set the iterator to where we will start to read.
429 CurBundleInfo = BundlesInfo.end();
430 NextBundleInfo = BundlesInfo.begin();
431 return Error::success();
432 }
433
434 Expected<std::optional<StringRef>> ReadBundleStart(StringRef Input) final {
435 if (NextBundleInfo == BundlesInfo.end())
436 return std::nullopt;
437 CurBundleInfo = NextBundleInfo++;
438 return CurBundleInfo->first();
439 }
440
441 Error ReadBundleEnd(MemoryBuffer &Input) final {
442 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
443 return Error::success();
444 }
445
446 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
447 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
448 StringRef FC = Input.getBuffer();
449 OS.write(FC.data() + CurBundleInfo->second.Offset,
450 CurBundleInfo->second.Size);
451 return Error::success();
452 }
453
454 Error WriteHeader(raw_ostream &OS,
455 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
456
457 // Compute size of the header.
458 uint64_t HeaderSize = 0;
459
460 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
461 HeaderSize += 8; // Number of Bundles
462
463 for (auto &T : BundlerConfig.TargetNames) {
464 HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
465 HeaderSize += T.size(); // The triple.
466 }
467
468 // Write to the buffer the header.
470
471 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
472
473 unsigned Idx = 0;
474 for (auto &T : BundlerConfig.TargetNames) {
475 MemoryBuffer &MB = *Inputs[Idx++];
476 HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
477 // Bundle offset.
478 Write8byteIntegerToBuffer(OS, HeaderSize);
479 // Size of the bundle (adds to the next bundle's offset)
480 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
481 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
482 HeaderSize += MB.getBufferSize();
483 // Size of the triple
484 Write8byteIntegerToBuffer(OS, T.size());
485 // Triple
486 OS << T;
487 }
488 return Error::success();
489 }
490
491 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
492 CurWriteBundleTarget = TargetTriple.str();
493 return Error::success();
494 }
495
496 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
497 return Error::success();
498 }
499
500 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
501 auto BI = BundlesInfo[CurWriteBundleTarget];
502
503 // Pad with 0 to reach specified offset.
504 size_t CurrentPos = OS.tell();
505 size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0;
506 for (size_t I = 0; I < PaddingSize; ++I)
507 OS.write('\0');
508 assert(OS.tell() == BI.Offset);
509
510 OS.write(Input.getBufferStart(), Input.getBufferSize());
511
512 return Error::success();
513 }
514};
515
516// This class implements a list of temporary files that are removed upon
517// object destruction.
518class TempFileHandlerRAII {
519public:
520 ~TempFileHandlerRAII() {
521 for (const auto &File : Files)
522 sys::fs::remove(File);
523 }
524
525 // Creates temporary file with given contents.
526 Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) {
527 SmallString<128u> File;
528 if (std::error_code EC =
529 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
530 return createFileError(File, EC);
531 Files.push_front(File);
532
533 if (Contents) {
534 std::error_code EC;
535 raw_fd_ostream OS(File, EC);
536 if (EC)
537 return createFileError(File, EC);
538 OS.write(Contents->data(), Contents->size());
539 }
540 return Files.front().str();
541 }
542
543private:
544 std::forward_list<SmallString<128u>> Files;
545};
546
547/// Handler for object files. The bundles are organized by sections with a
548/// designated name.
549///
550/// To unbundle, we just copy the contents of the designated section.
551class ObjectFileHandler final : public FileHandler {
552
553 /// The object file we are currently dealing with.
554 std::unique_ptr<ObjectFile> Obj;
555
556 /// Return the input file contents.
557 StringRef getInputFileContents() const { return Obj->getData(); }
558
559 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
560 /// section.
561 static Expected<std::optional<StringRef>>
562 IsOffloadSection(SectionRef CurSection) {
563 Expected<StringRef> NameOrErr = CurSection.getName();
564 if (!NameOrErr)
565 return NameOrErr.takeError();
566
567 // If it does not start with the reserved suffix, just skip this section.
568 if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle)
569 return std::nullopt;
570
571 // Return the triple that is right after the reserved prefix.
572 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
573 }
574
575 /// Total number of inputs.
576 unsigned NumberOfInputs = 0;
577
578 /// Total number of processed inputs, i.e, inputs that were already
579 /// read from the buffers.
580 unsigned NumberOfProcessedInputs = 0;
581
582 /// Iterator of the current and next section.
583 section_iterator CurrentSection;
584 section_iterator NextSection;
585
586 /// Configuration options and arrays for this bundler job
587 const OffloadBundlerConfig &BundlerConfig;
588
589public:
590 // TODO: Add error checking from ClangOffloadBundler.cpp
591 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
592 const OffloadBundlerConfig &BC)
593 : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()),
594 NextSection(Obj->section_begin()), BundlerConfig(BC) {}
595
596 ~ObjectFileHandler() final {}
597
598 Error ReadHeader(StringRef Input) final { return Error::success(); }
599
600 Expected<std::optional<StringRef>> ReadBundleStart(StringRef Input) final {
601 while (NextSection != Obj->section_end()) {
602 CurrentSection = NextSection;
603 ++NextSection;
604
605 // Check if the current section name starts with the reserved prefix. If
606 // so, return the triple.
607 Expected<std::optional<StringRef>> TripleOrErr =
608 IsOffloadSection(*CurrentSection);
609 if (!TripleOrErr)
610 return TripleOrErr.takeError();
611 if (*TripleOrErr)
612 return **TripleOrErr;
613 }
614 return std::nullopt;
615 }
616
617 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
618
619 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
620 Expected<StringRef> ContentOrErr = CurrentSection->getContents();
621 if (!ContentOrErr)
622 return ContentOrErr.takeError();
623 StringRef Content = *ContentOrErr;
624
625 // Copy fat object contents to the output when extracting host bundle.
626 std::string ModifiedContent;
627 if (Content.size() == 1u && Content.front() == 0) {
628 auto HostBundleOrErr = getHostBundle(
629 StringRef(Input.getBufferStart(), Input.getBufferSize()));
630 if (!HostBundleOrErr)
631 return HostBundleOrErr.takeError();
632
633 ModifiedContent = std::move(*HostBundleOrErr);
634 Content = ModifiedContent;
635 }
636
637 OS.write(Content.data(), Content.size());
638 return Error::success();
639 }
640
641 Error WriteHeader(raw_ostream &OS,
642 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
643 assert(BundlerConfig.HostInputIndex != ~0u &&
644 "Host input index not defined.");
645
646 // Record number of inputs.
647 NumberOfInputs = Inputs.size();
648 return Error::success();
649 }
650
651 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
652 ++NumberOfProcessedInputs;
653 return Error::success();
654 }
655
656 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
657 return Error::success();
658 }
659
660 Error finalizeOutputFile() final {
661 assert(NumberOfProcessedInputs <= NumberOfInputs &&
662 "Processing more inputs that actually exist!");
663 assert(BundlerConfig.HostInputIndex != ~0u &&
664 "Host input index not defined.");
665
666 // If this is not the last output, we don't have to do anything.
667 if (NumberOfProcessedInputs != NumberOfInputs)
668 return Error::success();
669
670 // We will use llvm-objcopy to add target objects sections to the output
671 // fat object. These sections should have 'exclude' flag set which tells
672 // link editor to remove them from linker inputs when linking executable or
673 // shared library.
674
675 assert(BundlerConfig.ObjcopyPath != "" &&
676 "llvm-objcopy path not specified");
677
678 // Temporary files that need to be removed.
679 TempFileHandlerRAII TempFiles;
680
681 // Compose llvm-objcopy command line for add target objects' sections with
682 // appropriate flags.
683 BumpPtrAllocator Alloc;
684 StringSaver SS{Alloc};
685 SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
686
687 for (unsigned I = 0; I < NumberOfInputs; ++I) {
688 StringRef InputFile = BundlerConfig.InputFileNames[I];
689 if (I == BundlerConfig.HostInputIndex) {
690 // Special handling for the host bundle. We do not need to add a
691 // standard bundle for the host object since we are going to use fat
692 // object as a host object. Therefore use dummy contents (one zero byte)
693 // when creating section for the host bundle.
694 Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0));
695 if (!TempFileOrErr)
696 return TempFileOrErr.takeError();
697 InputFile = *TempFileOrErr;
698 }
699
700 ObjcopyArgs.push_back(
701 SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR +
702 BundlerConfig.TargetNames[I] + "=" + InputFile));
703 ObjcopyArgs.push_back(
704 SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR +
705 BundlerConfig.TargetNames[I] + "=readonly,exclude"));
706 }
707 ObjcopyArgs.push_back("--");
708 ObjcopyArgs.push_back(
709 BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
710 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
711
712 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
713 return Err;
714
715 return Error::success();
716 }
717
718 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
719 return Error::success();
720 }
721
722private:
723 Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) {
724 // If the user asked for the commands to be printed out, we do that
725 // instead of executing it.
726 if (BundlerConfig.PrintExternalCommands) {
727 errs() << "\"" << Objcopy << "\"";
728 for (StringRef Arg : drop_begin(Args, 1))
729 errs() << " \"" << Arg << "\"";
730 errs() << "\n";
731 } else {
732 if (sys::ExecuteAndWait(Objcopy, Args))
733 return createStringError(inconvertibleErrorCode(),
734 "'llvm-objcopy' tool failed");
735 }
736 return Error::success();
737 }
738
739 Expected<std::string> getHostBundle(StringRef Input) {
740 TempFileHandlerRAII TempFiles;
741
742 auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
743 if (!ModifiedObjPathOrErr)
744 return ModifiedObjPathOrErr.takeError();
745 StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
746
747 BumpPtrAllocator Alloc;
748 StringSaver SS{Alloc};
749 SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"};
750
751 ObjcopyArgs.push_back("--regex");
752 ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
753 ObjcopyArgs.push_back("--");
754
755 StringRef ObjcopyInputFileName;
756 // When unbundling an archive, the content of each object file in the
757 // archive is passed to this function by parameter Input, which is different
758 // from the content of the original input archive file, therefore it needs
759 // to be saved to a temporary file before passed to llvm-objcopy. Otherwise,
760 // Input is the same as the content of the original input file, therefore
761 // temporary file is not needed.
762 if (StringRef(BundlerConfig.FilesType).starts_with("a")) {
763 auto InputFileOrErr = TempFiles.Create(ArrayRef<char>(Input));
764 if (!InputFileOrErr)
765 return InputFileOrErr.takeError();
766 ObjcopyInputFileName = *InputFileOrErr;
767 } else
768 ObjcopyInputFileName = BundlerConfig.InputFileNames.front();
769
770 ObjcopyArgs.push_back(ObjcopyInputFileName);
771 ObjcopyArgs.push_back(ModifiedObjPath);
772
773 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
774 return std::move(Err);
775
776 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
777 if (!BufOrErr)
778 return createStringError(BufOrErr.getError(),
779 "Failed to read back the modified object file");
780
781 return BufOrErr->get()->getBuffer().str();
782 }
783};
784
785/// Handler for text files. The bundled file will have the following format.
786///
787/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
788/// Bundle 1
789/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
790/// ...
791/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
792/// Bundle N
793/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
794class TextFileHandler final : public FileHandler {
795 /// String that begins a line comment.
796 StringRef Comment;
797
798 /// String that initiates a bundle.
799 std::string BundleStartString;
800
801 /// String that closes a bundle.
802 std::string BundleEndString;
803
804 /// Number of chars read from input.
805 size_t ReadChars = 0u;
806
807protected:
808 Error ReadHeader(StringRef Input) final { return Error::success(); }
809
810 Expected<std::optional<StringRef>> ReadBundleStart(StringRef FC) final {
811
812 // Find start of the bundle.
813 ReadChars = FC.find(BundleStartString, ReadChars);
814 if (ReadChars == FC.npos)
815 return std::nullopt;
816
817 // Get position of the triple.
818 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
819
820 // Get position that closes the triple.
821 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
822 if (TripleEnd == FC.npos)
823 return std::nullopt;
824
825 // Next time we read after the new line.
826 ++ReadChars;
827
828 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
829 }
830
831 Error ReadBundleEnd(MemoryBuffer &Input) final {
832 StringRef FC = Input.getBuffer();
833
834 // Read up to the next new line.
835 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
836
837 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
838 if (TripleEnd != FC.npos)
839 // Next time we read after the new line.
840 ++ReadChars;
841
842 return Error::success();
843 }
844
845 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
846 StringRef FC = Input.getBuffer();
847 size_t BundleStart = ReadChars;
848
849 // Find end of the bundle.
850 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
851
852 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
853 OS << Bundle;
854
855 return Error::success();
856 }
857
858 Error WriteHeader(raw_ostream &OS,
859 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
860 return Error::success();
861 }
862
863 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
864 OS << BundleStartString << TargetTriple << "\n";
865 return Error::success();
866 }
867
868 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
869 OS << BundleEndString << TargetTriple << "\n";
870 return Error::success();
871 }
872
873 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
874 OS << Input.getBuffer();
875 return Error::success();
876 }
877
878public:
879 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
880 BundleStartString =
881 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
882 BundleEndString =
883 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
884 }
885
886 Error listBundleIDsCallback(MemoryBuffer &Input,
887 const BundleInfo &Info) final {
888 // TODO: To list bundle IDs in a bundled text file we need to go through
889 // all bundles. The format of bundled text file may need to include a
890 // header if the performance of listing bundle IDs of bundled text file is
891 // important.
892 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
893 if (Error Err = ReadBundleEnd(Input))
894 return Err;
895 return Error::success();
896 }
897};
898} // namespace
899
900/// Return an appropriate object file handler. We use the specific object
901/// handler if we know how to deal with that format, otherwise we use a default
902/// binary file handler.
903static std::unique_ptr<FileHandler>
904CreateObjectFileHandler(MemoryBuffer &FirstInput,
905 const OffloadBundlerConfig &BundlerConfig) {
906 // Check if the input file format is one that we know how to deal with.
907 Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
908
909 // We only support regular object files. If failed to open the input as a
910 // known binary or this is not an object file use the default binary handler.
911 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
912 return std::make_unique<BinaryFileHandler>(BundlerConfig);
913
914 // Otherwise create an object file handler. The handler will be owned by the
915 // client of this function.
916 return std::make_unique<ObjectFileHandler>(
917 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
918 BundlerConfig);
919}
920
921/// Return an appropriate handler given the input files and options.
923CreateFileHandler(MemoryBuffer &FirstInput,
924 const OffloadBundlerConfig &BundlerConfig) {
925 std::string FilesType = BundlerConfig.FilesType;
926
927 if (FilesType == "i")
928 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
929 if (FilesType == "ii")
930 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
931 if (FilesType == "cui")
932 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
933 if (FilesType == "hipi")
934 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
935 // TODO: `.d` should be eventually removed once `-M` and its variants are
936 // handled properly in offload compilation.
937 if (FilesType == "d")
938 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
939 if (FilesType == "ll")
940 return std::make_unique<TextFileHandler>(/*Comment=*/";");
941 if (FilesType == "bc")
942 return std::make_unique<BinaryFileHandler>(BundlerConfig);
943 if (FilesType == "s")
944 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
945 if (FilesType == "o")
946 return CreateObjectFileHandler(FirstInput, BundlerConfig);
947 if (FilesType == "a")
948 return CreateObjectFileHandler(FirstInput, BundlerConfig);
949 if (FilesType == "gch")
950 return std::make_unique<BinaryFileHandler>(BundlerConfig);
951 if (FilesType == "ast")
952 return std::make_unique<BinaryFileHandler>(BundlerConfig);
953
954 return createStringError(errc::invalid_argument,
955 "'" + FilesType + "': invalid file type specified");
956}
957
960 if (llvm::compression::zstd::isAvailable()) {
961 CompressionFormat = llvm::compression::Format::Zstd;
962 // Compression level 3 is usually sufficient for zstd since long distance
963 // matching is enabled.
965 } else if (llvm::compression::zlib::isAvailable()) {
966 CompressionFormat = llvm::compression::Format::Zlib;
967 // Use default level for zlib since higher level does not have significant
968 // improvement.
969 CompressionLevel = llvm::compression::zlib::DefaultCompression;
970 }
971 auto IgnoreEnvVarOpt =
972 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
973 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1")
974 return;
975 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
976 if (VerboseEnvVarOpt.has_value())
977 Verbose = VerboseEnvVarOpt.value() == "1";
978 auto CompressEnvVarOpt =
979 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
980 if (CompressEnvVarOpt.has_value())
981 Compress = CompressEnvVarOpt.value() == "1";
982 auto CompressionLevelEnvVarOpt =
983 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
984 if (CompressionLevelEnvVarOpt.has_value()) {
985 llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
986 int Level;
987 if (!CompressionLevelStr.getAsInteger(10, Level))
988 CompressionLevel = Level;
989 else
990 llvm::errs()
991 << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
992 << CompressionLevelStr.str() << ". Ignoring it.\n";
993 }
994 auto CompressedBundleFormatVersionOpt =
995 llvm::sys::Process::GetEnv("COMPRESSED_BUNDLE_FORMAT_VERSION");
996 if (CompressedBundleFormatVersionOpt.has_value()) {
997 llvm::StringRef VersionStr = CompressedBundleFormatVersionOpt.value();
998 uint16_t Version;
999 if (!VersionStr.getAsInteger(10, Version)) {
1000 if (Version >= 2 && Version <= 3)
1001 CompressedBundleVersion = Version;
1002 else
1003 llvm::errs()
1004 << "Warning: Invalid value for COMPRESSED_BUNDLE_FORMAT_VERSION: "
1005 << VersionStr.str()
1006 << ". Valid values are 2 or 3. Using default version "
1007 << CompressedBundleVersion << ".\n";
1008 } else
1009 llvm::errs()
1010 << "Warning: Invalid value for COMPRESSED_BUNDLE_FORMAT_VERSION: "
1011 << VersionStr.str() << ". Using default version "
1012 << CompressedBundleVersion << ".\n";
1013 }
1014}
1015
1016// Utility function to format numbers with commas
1017static std::string formatWithCommas(unsigned long long Value) {
1018 std::string Num = std::to_string(Value);
1019 int InsertPosition = Num.length() - 3;
1020 while (InsertPosition > 0) {
1021 Num.insert(InsertPosition, ",");
1022 InsertPosition -= 3;
1023 }
1024 return Num;
1025}
1026
1028CompressedOffloadBundle::compress(llvm::compression::Params P,
1029 const llvm::MemoryBuffer &Input,
1030 uint16_t Version, bool Verbose) {
1031 if (!llvm::compression::zstd::isAvailable() &&
1032 !llvm::compression::zlib::isAvailable())
1033 return createStringError(llvm::inconvertibleErrorCode(),
1034 "Compression not supported");
1035 llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
1037 if (Verbose)
1038 HashTimer.startTimer();
1039 llvm::MD5 Hash;
1040 llvm::MD5::MD5Result Result;
1041 Hash.update(Input.getBuffer());
1042 Hash.final(Result);
1043 uint64_t TruncatedHash = Result.low();
1044 if (Verbose)
1045 HashTimer.stopTimer();
1046
1047 SmallVector<uint8_t, 0> CompressedBuffer;
1048 auto BufferUint8 = llvm::ArrayRef<uint8_t>(
1049 reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
1050 Input.getBuffer().size());
1051 llvm::Timer CompressTimer("Compression Timer", "Compression time",
1053 if (Verbose)
1054 CompressTimer.startTimer();
1055 llvm::compression::compress(P, BufferUint8, CompressedBuffer);
1056 if (Verbose)
1057 CompressTimer.stopTimer();
1058
1059 uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
1060
1061 // Store sizes in 64-bit variables first
1062 uint64_t UncompressedSize64 = Input.getBuffer().size();
1063 uint64_t TotalFileSize64;
1064
1065 // Calculate total file size based on version
1066 if (Version == 2) {
1067 // For V2, ensure the sizes don't exceed 32-bit limit
1068 if (UncompressedSize64 > std::numeric_limits<uint32_t>::max())
1069 return createStringError(llvm::inconvertibleErrorCode(),
1070 "Uncompressed size exceeds version 2 limit");
1071 if ((MagicNumber.size() + sizeof(uint32_t) + sizeof(Version) +
1072 sizeof(CompressionMethod) + sizeof(uint32_t) + sizeof(TruncatedHash) +
1073 CompressedBuffer.size()) > std::numeric_limits<uint32_t>::max())
1074 return createStringError(llvm::inconvertibleErrorCode(),
1075 "Total file size exceeds version 2 limit");
1076
1077 TotalFileSize64 = MagicNumber.size() + sizeof(uint32_t) + sizeof(Version) +
1078 sizeof(CompressionMethod) + sizeof(uint32_t) +
1079 sizeof(TruncatedHash) + CompressedBuffer.size();
1080 } else { // Version 3
1081 TotalFileSize64 = MagicNumber.size() + sizeof(uint64_t) + sizeof(Version) +
1082 sizeof(CompressionMethod) + sizeof(uint64_t) +
1083 sizeof(TruncatedHash) + CompressedBuffer.size();
1084 }
1085
1086 SmallVector<char, 0> FinalBuffer;
1087 llvm::raw_svector_ostream OS(FinalBuffer);
1088 OS << MagicNumber;
1089 OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
1090 OS.write(reinterpret_cast<const char *>(&CompressionMethod),
1091 sizeof(CompressionMethod));
1092
1093 // Write size fields according to version
1094 if (Version == 2) {
1095 uint32_t TotalFileSize32 = static_cast<uint32_t>(TotalFileSize64);
1096 uint32_t UncompressedSize32 = static_cast<uint32_t>(UncompressedSize64);
1097 OS.write(reinterpret_cast<const char *>(&TotalFileSize32),
1098 sizeof(TotalFileSize32));
1099 OS.write(reinterpret_cast<const char *>(&UncompressedSize32),
1100 sizeof(UncompressedSize32));
1101 } else { // Version 3
1102 OS.write(reinterpret_cast<const char *>(&TotalFileSize64),
1103 sizeof(TotalFileSize64));
1104 OS.write(reinterpret_cast<const char *>(&UncompressedSize64),
1105 sizeof(UncompressedSize64));
1106 }
1107
1108 OS.write(reinterpret_cast<const char *>(&TruncatedHash),
1109 sizeof(TruncatedHash));
1110 OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
1111 CompressedBuffer.size());
1112
1113 if (Verbose) {
1114 auto MethodUsed =
1115 P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
1116 double CompressionRate =
1117 static_cast<double>(UncompressedSize64) / CompressedBuffer.size();
1118 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1119 double CompressionSpeedMBs =
1120 (UncompressedSize64 / (1024.0 * 1024.0)) / CompressionTimeSeconds;
1121 llvm::errs() << "Compressed bundle format version: " << Version << "\n"
1122 << "Total file size (including headers): "
1123 << formatWithCommas(TotalFileSize64) << " bytes\n"
1124 << "Compression method used: " << MethodUsed << "\n"
1125 << "Compression level: " << P.level << "\n"
1126 << "Binary size before compression: "
1127 << formatWithCommas(UncompressedSize64) << " bytes\n"
1128 << "Binary size after compression: "
1129 << formatWithCommas(CompressedBuffer.size()) << " bytes\n"
1130 << "Compression rate: "
1131 << llvm::format("%.2lf", CompressionRate) << "\n"
1132 << "Compression ratio: "
1133 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1134 << "Compression speed: "
1135 << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
1136 << "Truncated MD5 hash: "
1137 << llvm::format_hex(TruncatedHash, 16) << "\n";
1138 }
1139
1140 return llvm::MemoryBuffer::getMemBufferCopy(
1141 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1142}
1143
1144// Use packed structs to avoid padding, such that the structs map the serialized
1145// format.
1146LLVM_PACKED_START
1179LLVM_PACKED_END
1180
1181// Helper method to get header size based on version
1182static size_t getHeaderSize(uint16_t Version) {
1183 switch (Version) {
1184 case 1:
1186 case 2:
1188 case 3:
1190 default:
1191 llvm_unreachable("Unsupported version");
1192 }
1193}
1194
1197 assert(Blob.size() >= sizeof(RawCompressedBundleHeader::CommonFields));
1198 assert(llvm::identify_magic(Blob) ==
1199 llvm::file_magic::offload_bundle_compressed);
1200
1202 memcpy(&Header, Blob.data(), std::min(Blob.size(), sizeof(Header)));
1203
1204 CompressedBundleHeader Normalized;
1205 Normalized.Version = Header.Common.Version;
1206
1207 size_t RequiredSize = getHeaderSize(Normalized.Version);
1208 if (Blob.size() < RequiredSize)
1209 return createStringError(inconvertibleErrorCode(),
1210 "Compressed bundle header size too small");
1211
1212 switch (Normalized.Version) {
1213 case 1:
1214 Normalized.UncompressedFileSize = Header.V1.UncompressedFileSize;
1215 Normalized.Hash = Header.V1.Hash;
1216 break;
1217 case 2:
1218 Normalized.FileSize = Header.V2.FileSize;
1219 Normalized.UncompressedFileSize = Header.V2.UncompressedFileSize;
1220 Normalized.Hash = Header.V2.Hash;
1221 break;
1222 case 3:
1223 Normalized.FileSize = Header.V3.FileSize;
1224 Normalized.UncompressedFileSize = Header.V3.UncompressedFileSize;
1225 Normalized.Hash = Header.V3.Hash;
1226 break;
1227 default:
1228 return createStringError(inconvertibleErrorCode(),
1229 "Unknown compressed bundle version");
1230 }
1231
1232 // Determine compression format
1233 switch (Header.Common.Method) {
1234 case static_cast<uint16_t>(compression::Format::Zlib):
1235 case static_cast<uint16_t>(compression::Format::Zstd):
1236 Normalized.CompressionFormat =
1237 static_cast<compression::Format>(Header.Common.Method);
1238 break;
1239 default:
1240 return createStringError(inconvertibleErrorCode(),
1241 "Unknown compressing method");
1242 }
1243
1244 return Normalized;
1245}
1246
1248CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
1249 bool Verbose) {
1250 StringRef Blob = Input.getBuffer();
1251
1252 // Check minimum header size (using V1 as it's the smallest)
1253 if (Blob.size() < sizeof(RawCompressedBundleHeader::CommonFields))
1254 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1255
1256 if (llvm::identify_magic(Blob) !=
1257 llvm::file_magic::offload_bundle_compressed) {
1258 if (Verbose)
1259 llvm::errs() << "Uncompressed bundle.\n";
1260 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1261 }
1262
1265 if (!HeaderOrErr)
1266 return HeaderOrErr.takeError();
1267
1268 const CompressedBundleHeader &Normalized = *HeaderOrErr;
1269 unsigned ThisVersion = Normalized.Version;
1270 size_t HeaderSize = getHeaderSize(ThisVersion);
1271
1272 llvm::compression::Format CompressionFormat = Normalized.CompressionFormat;
1273
1274 size_t TotalFileSize = Normalized.FileSize.value_or(0);
1275 size_t UncompressedSize = Normalized.UncompressedFileSize;
1276 auto StoredHash = Normalized.Hash;
1277
1278 llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
1280 if (Verbose)
1281 DecompressTimer.startTimer();
1282
1283 SmallVector<uint8_t, 0> DecompressedData;
1284 StringRef CompressedData =
1285 Blob.substr(HeaderSize, TotalFileSize - HeaderSize);
1286 if (llvm::Error DecompressionError = llvm::compression::decompress(
1287 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
1288 DecompressedData, UncompressedSize))
1289 return createStringError(inconvertibleErrorCode(),
1290 "Could not decompress embedded file contents: " +
1291 llvm::toString(std::move(DecompressionError)));
1292
1293 if (Verbose) {
1294 DecompressTimer.stopTimer();
1295
1296 double DecompressionTimeSeconds =
1297 DecompressTimer.getTotalTime().getWallTime();
1298
1299 // Recalculate MD5 hash for integrity check
1300 llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
1301 "Hash recalculation time",
1303 HashRecalcTimer.startTimer();
1304 llvm::MD5 Hash;
1305 llvm::MD5::MD5Result Result;
1306 Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData));
1307 Hash.final(Result);
1308 uint64_t RecalculatedHash = Result.low();
1309 HashRecalcTimer.stopTimer();
1310 bool HashMatch = (StoredHash == RecalculatedHash);
1311
1312 double CompressionRate =
1313 static_cast<double>(UncompressedSize) / CompressedData.size();
1314 double DecompressionSpeedMBs =
1315 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1316
1317 llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
1318 if (ThisVersion >= 2)
1319 llvm::errs() << "Total file size (from header): "
1320 << formatWithCommas(TotalFileSize) << " bytes\n";
1321 llvm::errs() << "Decompression method: "
1322 << (CompressionFormat == llvm::compression::Format::Zlib
1323 ? "zlib"
1324 : "zstd")
1325 << "\n"
1326 << "Size before decompression: "
1327 << formatWithCommas(CompressedData.size()) << " bytes\n"
1328 << "Size after decompression: "
1329 << formatWithCommas(UncompressedSize) << " bytes\n"
1330 << "Compression rate: "
1331 << llvm::format("%.2lf", CompressionRate) << "\n"
1332 << "Compression ratio: "
1333 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1334 << "Decompression speed: "
1335 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
1336 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
1337 << "Recalculated hash: "
1338 << llvm::format_hex(RecalculatedHash, 16) << "\n"
1339 << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
1340 }
1341
1342 return llvm::MemoryBuffer::getMemBufferCopy(
1343 llvm::toStringRef(DecompressedData));
1344}
1345
1346// List bundle IDs. Return true if an error was found.
1348 StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
1349
1350 size_t Offset = 0;
1351 size_t NextBundleStart = 0;
1352 std::unique_ptr<MemoryBuffer> Buffer;
1353
1354 // Open Input file.
1355 ErrorOr<std::unique_ptr<MemoryBuffer>> Contents =
1356 MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
1357 if (std::error_code EC = Contents.getError())
1358 return createFileError(InputFileName, EC);
1359
1360 // There may be multiple bundles.
1361 while ((NextBundleStart != StringRef::npos) &&
1362 (Offset < (**Contents).getBufferSize())) {
1363 Buffer = MemoryBuffer::getMemBuffer(
1364 (**Contents).getBuffer().drop_front(Offset), "",
1365 /*RequiresNullTerminator=*/false);
1366
1367 if (identify_magic((*Buffer).getBuffer()) ==
1368 file_magic::offload_bundle_compressed) {
1369 NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
1370 } else
1371 NextBundleStart = StringRef::npos;
1372
1373 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1374 MemoryBuffer::getMemBuffer(
1375 (*Buffer).getBuffer().take_front(NextBundleStart),
1376 InputFileName, // FileName,
1377 false);
1378 if (std::error_code EC = CodeOrErr.getError())
1379 return createFileError(InputFileName, EC);
1380
1381 // Decompress the input if necessary.
1382 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1384 if (!DecompressedBufferOrErr)
1385 return createStringError(
1386 inconvertibleErrorCode(),
1387 "Failed to decompress input: " +
1388 llvm::toString(DecompressedBufferOrErr.takeError()));
1389
1390 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1391
1392 // Select the right files handler.
1393 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1394 CreateFileHandler(DecompressedInput, BundlerConfig);
1395 if (!FileHandlerOrErr)
1396 return FileHandlerOrErr.takeError();
1397 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1398 assert(FH);
1399 Error E = FH->listBundleIDs(DecompressedInput);
1400 if (E)
1401 return E;
1402
1403 if (NextBundleStart != StringRef::npos)
1404 Offset += NextBundleStart;
1405 }
1406 return Error::success();
1407}
1408
1409/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
1410/// target \p TargetInfo.
1411/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
1414
1415 // Compatible in case of exact match.
1416 if (CodeObjectInfo == TargetInfo) {
1417 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1418 dbgs() << "Compatible: Exact match: \t[CodeObject: "
1419 << CodeObjectInfo.str()
1420 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1421 return true;
1422 }
1423
1424 // Incompatible if Kinds or Triples mismatch.
1425 if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) ||
1426 !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
1427 DEBUG_WITH_TYPE(
1428 "CodeObjectCompatibility",
1429 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
1430 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1431 << "]\n");
1432 return false;
1433 }
1434
1435 // Incompatible if Processors mismatch.
1436 llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
1437 std::optional<StringRef> CodeObjectProc = clang::parseTargetID(
1438 CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap);
1439 std::optional<StringRef> TargetProc = clang::parseTargetID(
1440 TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap);
1441
1442 // Both TargetProc and CodeObjectProc can't be empty here.
1443 if (!TargetProc || !CodeObjectProc ||
1444 CodeObjectProc.value() != TargetProc.value()) {
1445 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1446 dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
1447 << CodeObjectInfo.str()
1448 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1449 return false;
1450 }
1451
1452 // Incompatible if CodeObject has more features than Target, irrespective of
1453 // type or sign of features.
1454 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1455 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1456 dbgs() << "Incompatible: CodeObject has more features "
1457 "than target \t[CodeObject: "
1458 << CodeObjectInfo.str()
1459 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1460 return false;
1461 }
1462
1463 // Compatible if each target feature specified by target is compatible with
1464 // target feature of code object. The target feature is compatible if the
1465 // code object does not specify it (meaning Any), or if it specifies it
1466 // with the same value (meaning On or Off).
1467 for (const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1468 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1469 if (TargetFeature == TargetFeatureMap.end()) {
1470 DEBUG_WITH_TYPE(
1471 "CodeObjectCompatibility",
1472 dbgs()
1473 << "Incompatible: Value of CodeObject's non-ANY feature is "
1474 "not matching with Target feature's ANY value \t[CodeObject: "
1475 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1476 << "]\n");
1477 return false;
1478 } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1479 DEBUG_WITH_TYPE(
1480 "CodeObjectCompatibility",
1481 dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
1482 "not matching with Target feature's non-ANY value "
1483 "\t[CodeObject: "
1484 << CodeObjectInfo.str()
1485 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1486 return false;
1487 }
1488 }
1489
1490 // CodeObject is compatible if all features of Target are:
1491 // - either, present in the Code Object's features map with the same sign,
1492 // - or, the feature is missing from CodeObjects's features map i.e. it is
1493 // set to ANY
1494 DEBUG_WITH_TYPE(
1495 "CodeObjectCompatibility",
1496 dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
1497 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1498 << "]\n");
1499 return true;
1500}
1501
1502/// Bundle the files. Return true if an error was found.
1504 std::error_code EC;
1505
1506 // Create a buffer to hold the content before compressing.
1507 SmallVector<char, 0> Buffer;
1508 llvm::raw_svector_ostream BufferStream(Buffer);
1509
1510 // Open input files.
1512 InputBuffers.reserve(BundlerConfig.InputFileNames.size());
1513 for (auto &I : BundlerConfig.InputFileNames) {
1514 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1515 MemoryBuffer::getFileOrSTDIN(I, /*IsText=*/true);
1516 if (std::error_code EC = CodeOrErr.getError())
1517 return createFileError(I, EC);
1518 InputBuffers.emplace_back(std::move(*CodeOrErr));
1519 }
1520
1521 // Get the file handler. We use the host buffer as reference.
1522 assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) &&
1523 "Host input index undefined??");
1525 *InputBuffers[BundlerConfig.AllowNoHost ? 0
1526 : BundlerConfig.HostInputIndex],
1528 if (!FileHandlerOrErr)
1529 return FileHandlerOrErr.takeError();
1530
1531 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1532 assert(FH);
1533
1534 // Write header.
1535 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1536 return Err;
1537
1538 // Write all bundles along with the start/end markers. If an error was found
1539 // writing the end of the bundle component, abort the bundle writing.
1540 auto Input = InputBuffers.begin();
1541 for (auto &Triple : BundlerConfig.TargetNames) {
1542 if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1543 return Err;
1544 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1545 return Err;
1546 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1547 return Err;
1548 ++Input;
1549 }
1550
1551 raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
1552 sys::fs::OF_None);
1553 if (EC)
1554 return createFileError(BundlerConfig.OutputFileNames.front(), EC);
1555
1556 SmallVector<char, 0> CompressedBuffer;
1557 if (BundlerConfig.Compress) {
1558 std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1559 llvm::MemoryBuffer::getMemBufferCopy(
1560 llvm::StringRef(Buffer.data(), Buffer.size()));
1561 auto CompressionResult = CompressedOffloadBundle::compress(
1562 {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel,
1563 /*zstdEnableLdm=*/true},
1564 *BufferMemory, BundlerConfig.CompressedBundleVersion,
1565 BundlerConfig.Verbose);
1566 if (auto Error = CompressionResult.takeError())
1567 return Error;
1568
1569 auto CompressedMemBuffer = std::move(CompressionResult.get());
1570 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1571 CompressedMemBuffer->getBufferEnd());
1572 } else
1573 CompressedBuffer = std::move(Buffer);
1574
1575 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1576
1577 return FH->finalizeOutputFile();
1578}
1579
1580// Unbundle the files. Return true if an error was found.
1582 // Open Input file.
1583 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1584 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front(),
1585 /*IsText=*/true);
1586 if (std::error_code EC = CodeOrErr.getError())
1587 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1588
1589 // Create a work list that consist of the map triple/output file.
1590 StringMap<StringRef> Worklist;
1591 auto Output = BundlerConfig.OutputFileNames.begin();
1592 for (auto &Triple : BundlerConfig.TargetNames) {
1593 if (!checkOffloadBundleID(Triple))
1594 return createStringError(errc::invalid_argument,
1595 "invalid bundle id from bundle config");
1596 Worklist[Triple] = *Output;
1597 ++Output;
1598 }
1599
1600 // The input may contain multiple concatenated fat binary blobs (e.g. when
1601 // the linker merges .hip_fatbin sections from multiple TUs into one). Walk
1602 // through each blob exactly as ListBundleIDsInFile does, draining worklist
1603 // entries as matching targets are found.
1604 bool FoundHostBundle = false;
1605 size_t Offset = 0;
1606 size_t NextBundleStart = 0;
1607 std::unique_ptr<MemoryBuffer> Buffer;
1608
1609 while ((NextBundleStart != StringRef::npos) &&
1610 (Offset < (**CodeOrErr).getBufferSize())) {
1611
1612 Buffer = MemoryBuffer::getMemBuffer(
1613 (**CodeOrErr).getBuffer().drop_front(Offset), "",
1614 /*RequiresNullTerminator=*/false);
1615
1616 if (identify_magic((*Buffer).getBuffer()) ==
1617 file_magic::offload_bundle_compressed) {
1618 NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
1619 } else if (identify_magic((*Buffer).getBuffer()) ==
1620 file_magic::offload_bundle) {
1621 NextBundleStart = (*Buffer).getBuffer().find(
1623 } else
1624 NextBundleStart = StringRef::npos;
1625
1626 ErrorOr<std::unique_ptr<MemoryBuffer>> BlobOrErr =
1627 MemoryBuffer::getMemBuffer(
1628 (*Buffer).getBuffer().take_front(NextBundleStart),
1629 BundlerConfig.InputFileNames.front(),
1630 /*RequiresNullTerminator=*/false);
1631 if (std::error_code EC = BlobOrErr.getError())
1632 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1633
1634 // Decompress the blob if necessary.
1635 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1637 if (!DecompressedBufferOrErr)
1638 return createStringError(
1639 inconvertibleErrorCode(),
1640 "Failed to decompress input: " +
1641 llvm::toString(DecompressedBufferOrErr.takeError()));
1642
1643 MemoryBuffer &Input = **DecompressedBufferOrErr;
1644
1645 // Select the right file handler for this blob.
1646 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1648 if (!FileHandlerOrErr)
1649 return FileHandlerOrErr.takeError();
1650
1651 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1652 assert(FH);
1653
1654 // Read the header of this blob.
1655 if (Error Err = FH->ReadHeader(Input.getBuffer()))
1656 return Err;
1657
1658 // Drain worklist entries satisfied by this blob.
1659 while (!Worklist.empty()) {
1660 Expected<std::optional<StringRef>> CurTripleOrErr =
1661 FH->ReadBundleStart(Input.getBuffer());
1662 if (!CurTripleOrErr)
1663 return CurTripleOrErr.takeError();
1664
1665 // No more bundles in this blob.
1666 if (!*CurTripleOrErr)
1667 break;
1668
1669 StringRef CurTriple = **CurTripleOrErr;
1670 assert(!CurTriple.empty());
1671 if (!checkOffloadBundleID(CurTriple))
1672 return createStringError(errc::invalid_argument,
1673 "invalid bundle id read from the bundle");
1674
1675 auto Output = Worklist.begin();
1676 for (auto E = Worklist.end(); Output != E; Output++) {
1678 OffloadTargetInfo(CurTriple, BundlerConfig),
1679 OffloadTargetInfo((*Output).first(), BundlerConfig)))
1680 break;
1681 }
1682
1683 if (Output == Worklist.end())
1684 continue;
1685
1686 // Check if the output file can be opened and copy the bundle to it.
1687 std::error_code EC;
1688 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1689 if (EC)
1690 return createFileError((*Output).second, EC);
1691 if (Error Err = FH->ReadBundle(OutputFile, Input))
1692 return Err;
1693 if (Error Err = FH->ReadBundleEnd(Input))
1694 return Err;
1695 Worklist.erase(Output);
1696
1697 // Record if we found the host bundle.
1698 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
1699 if (OffloadInfo.hasHostKind())
1700 FoundHostBundle = true;
1701 }
1702
1703 if (NextBundleStart != StringRef::npos)
1704 Offset += NextBundleStart;
1705 }
1706
1707 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
1708 std::string ErrMsg = "Can't find bundles for";
1709 std::set<StringRef> Sorted;
1710 for (auto &E : Worklist)
1711 Sorted.insert(E.first());
1712 unsigned I = 0;
1713 unsigned Last = Sorted.size() - 1;
1714 for (auto &E : Sorted) {
1715 if (I != 0 && Last > 1)
1716 ErrMsg += ",";
1717 ErrMsg += " ";
1718 if (I == Last && I != 0)
1719 ErrMsg += "and ";
1720 ErrMsg += E.str();
1721 ++I;
1722 }
1723 return createStringError(inconvertibleErrorCode(), ErrMsg);
1724 }
1725
1726 // If no bundles were found, assume the input file is the host bundle and
1727 // create empty files for the remaining targets.
1728 if (Worklist.size() == BundlerConfig.TargetNames.size()) {
1729 for (auto &E : Worklist) {
1730 std::error_code EC;
1731 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1732 if (EC)
1733 return createFileError(E.second, EC);
1734
1735 // If this entry has a host kind, copy the input file to the output file.
1736 // We don't need to check E.getKey() here through checkOffloadBundleID
1737 // because the entire WorkList has been checked above.
1738 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
1739 if (OffloadInfo.hasHostKind())
1740 OutputFile.write((**CodeOrErr).getBufferStart(),
1741 (**CodeOrErr).getBufferSize());
1742 }
1743 return Error::success();
1744 }
1745
1746 // If we found elements, we emit an error if none of those were for the host
1747 // in case host bundle name was provided in command line.
1748 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
1749 BundlerConfig.AllowMissingBundles))
1750 return createStringError(inconvertibleErrorCode(),
1751 "Can't find bundle for the host target");
1752
1753 // If we still have any elements in the worklist, create empty files for them.
1754 for (auto &E : Worklist) {
1755 std::error_code EC;
1756 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1757 if (EC)
1758 return createFileError(E.second, EC);
1759 }
1760
1761 return Error::success();
1762}
1763
1764static Archive::Kind getDefaultArchiveKindForHost() {
1765 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1766 : Archive::K_GNU;
1767}
1768
1769/// @brief Computes a list of targets among all given targets which are
1770/// compatible with this code object
1771/// @param [in] CodeObjectInfo Code Object
1772/// @param [out] CompatibleTargets List of all compatible targets among all
1773/// given targets
1774/// @return false, if no compatible target is found.
1775static bool
1777 SmallVectorImpl<StringRef> &CompatibleTargets,
1778 const OffloadBundlerConfig &BundlerConfig) {
1779 if (!CompatibleTargets.empty()) {
1780 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1781 dbgs() << "CompatibleTargets list should be empty\n");
1782 return false;
1783 }
1784 for (auto &Target : BundlerConfig.TargetNames) {
1785 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
1786 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
1787 CompatibleTargets.push_back(Target);
1788 }
1789 return !CompatibleTargets.empty();
1790}
1791
1792// Check that each code object file in the input archive conforms to following
1793// rule: for a specific processor, a feature either shows up in all target IDs,
1794// or does not show up in any target IDs. Otherwise the target ID combination is
1795// invalid.
1796static Error
1797CheckHeterogeneousArchive(StringRef ArchiveName,
1798 const OffloadBundlerConfig &BundlerConfig) {
1799 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1800 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1801 MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
1802 if (std::error_code EC = BufOrErr.getError())
1803 return createFileError(ArchiveName, EC);
1804
1805 ArchiveBuffers.push_back(std::move(*BufOrErr));
1807 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1808 if (!LibOrErr)
1809 return LibOrErr.takeError();
1810
1811 auto Archive = std::move(*LibOrErr);
1812
1813 Error ArchiveErr = Error::success();
1814 auto ChildEnd = Archive->child_end();
1815
1816 /// Iterate over all bundled code object files in the input archive.
1817 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1818 ArchiveIter != ChildEnd; ++ArchiveIter) {
1819 if (ArchiveErr)
1820 return ArchiveErr;
1821 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1822 if (!ArchiveChildNameOrErr)
1823 return ArchiveChildNameOrErr.takeError();
1824
1825 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1826 if (!CodeObjectBufferRefOrErr)
1827 return CodeObjectBufferRefOrErr.takeError();
1828
1829 auto CodeObjectBuffer =
1830 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1831
1832 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1833 CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
1834 if (!FileHandlerOrErr)
1835 return FileHandlerOrErr.takeError();
1836
1837 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1838 assert(FileHandler);
1839
1840 std::set<StringRef> BundleIds;
1841 auto CodeObjectFileError =
1842 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1843 if (CodeObjectFileError)
1844 return CodeObjectFileError;
1845
1846 auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds);
1847 if (ConflictingArchs) {
1848 std::string ErrMsg =
1849 Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
1850 ", " + ConflictingArchs.value().second + "] found in " +
1851 ArchiveChildNameOrErr.get() + " of " + ArchiveName)
1852 .str();
1853 return createStringError(inconvertibleErrorCode(), ErrMsg);
1854 }
1855 }
1856
1857 return ArchiveErr;
1858}
1859
1860/// UnbundleArchive takes an archive file (".a") as input containing bundled
1861/// code object files, and a list of offload targets (not host), and extracts
1862/// the code objects into a new archive file for each offload target. Each
1863/// resulting archive file contains all code object files corresponding to that
1864/// particular offload target. The created archive file does not
1865/// contain an index of the symbols and code object files are named as
1866/// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
1868 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1869
1870 /// Map of target names with list of object files that will form the device
1871 /// specific archive for that target
1872 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1873
1874 // Map of target names and output archive filenames
1875 StringMap<StringRef> TargetOutputFileNameMap;
1876
1877 auto Output = BundlerConfig.OutputFileNames.begin();
1878 for (auto &Target : BundlerConfig.TargetNames) {
1879 TargetOutputFileNameMap[Target] = *Output;
1880 ++Output;
1881 }
1882
1883 StringRef IFName = BundlerConfig.InputFileNames.front();
1884
1885 if (BundlerConfig.CheckInputArchive) {
1886 // For a specific processor, a feature either shows up in all target IDs, or
1887 // does not show up in any target IDs. Otherwise the target ID combination
1888 // is invalid.
1889 auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig);
1890 if (ArchiveError) {
1891 return ArchiveError;
1892 }
1893 }
1894
1895 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1896 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1897 if (std::error_code EC = BufOrErr.getError())
1898 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1899
1900 ArchiveBuffers.push_back(std::move(*BufOrErr));
1902 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1903 if (!LibOrErr)
1904 return LibOrErr.takeError();
1905
1906 auto Archive = std::move(*LibOrErr);
1907
1908 Error ArchiveErr = Error::success();
1909 auto ChildEnd = Archive->child_end();
1910
1911 /// Iterate over all bundled code object files in the input archive.
1912 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1913 ArchiveIter != ChildEnd; ++ArchiveIter) {
1914 if (ArchiveErr)
1915 return ArchiveErr;
1916 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1917 if (!ArchiveChildNameOrErr)
1918 return ArchiveChildNameOrErr.takeError();
1919
1920 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1921
1922 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1923 if (!CodeObjectBufferRefOrErr)
1924 return CodeObjectBufferRefOrErr.takeError();
1925
1926 auto TempCodeObjectBuffer =
1927 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1928
1929 // Decompress the buffer if necessary.
1930 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1931 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer,
1932 BundlerConfig.Verbose);
1933 if (!DecompressedBufferOrErr)
1934 return createStringError(
1935 inconvertibleErrorCode(),
1936 "Failed to decompress code object: " +
1937 llvm::toString(DecompressedBufferOrErr.takeError()));
1938
1939 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1940
1941 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1942 CreateFileHandler(CodeObjectBuffer, BundlerConfig);
1943 if (!FileHandlerOrErr)
1944 return FileHandlerOrErr.takeError();
1945
1946 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1947 assert(FileHandler &&
1948 "FileHandle creation failed for file in the archive!");
1949
1950 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer.getBuffer()))
1951 return ReadErr;
1952
1953 Expected<std::optional<StringRef>> CurBundleIDOrErr =
1954 FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
1955 if (!CurBundleIDOrErr)
1956 return CurBundleIDOrErr.takeError();
1957
1958 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1959 // No device code in this child, skip.
1960 if (!OptionalCurBundleID)
1961 continue;
1962 StringRef CodeObject = *OptionalCurBundleID;
1963
1964 // Process all bundle entries (CodeObjects) found in this child of input
1965 // archive.
1966 while (!CodeObject.empty()) {
1967 SmallVector<StringRef> CompatibleTargets;
1968 if (!checkOffloadBundleID(CodeObject)) {
1969 return createStringError(errc::invalid_argument,
1970 "Invalid bundle id read from code object");
1971 }
1972 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
1973 if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
1974 BundlerConfig)) {
1975 std::string BundleData;
1976 raw_string_ostream DataStream(BundleData);
1977 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1978 return Err;
1979
1980 for (auto &CompatibleTarget : CompatibleTargets) {
1981 SmallString<128> BundledObjectFileName;
1982 BundledObjectFileName.assign(BundledObjectFile);
1983 auto OutputBundleName =
1984 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1985 CodeObject +
1986 getDeviceLibraryFileName(BundledObjectFileName,
1987 CodeObjectInfo.TargetID))
1988 .str();
1989 // Replace ':' in optional target feature list with '_' to ensure
1990 // cross-platform validity.
1991 llvm::replace(OutputBundleName, ':', '_');
1992
1993 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1994 DataStream.str(), OutputBundleName);
1995 ArchiveBuffers.push_back(std::move(MemBuf));
1996 llvm::MemoryBufferRef MemBufRef =
1997 MemoryBufferRef(*(ArchiveBuffers.back()));
1998
1999 // For inserting <CompatibleTarget, list<CodeObject>> entry in
2000 // OutputArchivesMap.
2001 OutputArchivesMap[CompatibleTarget].push_back(
2002 NewArchiveMember(MemBufRef));
2003 }
2004 }
2005
2006 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
2007 return Err;
2008
2009 Expected<std::optional<StringRef>> NextTripleOrErr =
2010 FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
2011 if (!NextTripleOrErr)
2012 return NextTripleOrErr.takeError();
2013
2014 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
2015 } // End of processing of all bundle entries of this child of input archive.
2016 } // End of while over children of input archive.
2017
2018 assert(!ArchiveErr && "Error occurred while reading archive!");
2019
2020 /// Write out an archive for each target
2021 for (auto &Target : BundlerConfig.TargetNames) {
2022 StringRef FileName = TargetOutputFileNameMap[Target];
2023 auto CurArchiveMembers = OutputArchivesMap.find(Target);
2024 if (CurArchiveMembers != OutputArchivesMap.end()) {
2025 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
2026 SymtabWritingMode::NormalSymtab,
2028 false, nullptr))
2029 return WriteErr;
2030 } else if (!BundlerConfig.AllowMissingBundles) {
2031 std::string ErrMsg =
2032 Twine("no compatible code object found for the target '" + Target +
2033 "' in heterogeneous archive library: " + IFName)
2034 .str();
2035 return createStringError(inconvertibleErrorCode(), ErrMsg);
2036 } else { // Create an empty archive file if no compatible code object is
2037 // found and "allow-missing-bundles" is enabled. It ensures that
2038 // the linker using output of this step doesn't complain about
2039 // the missing input file.
2040 std::vector<llvm::NewArchiveMember> EmptyArchive;
2041 EmptyArchive.clear();
2042 if (Error WriteErr = writeArchive(
2043 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
2044 getDefaultArchiveKindForHost(), true, false, nullptr))
2045 return WriteErr;
2046 }
2047 }
2048
2049 return Error::success();
2050}
2051
2052bool clang::checkOffloadBundleID(const llvm::StringRef Str) {
2053 // <kind>-<triple>[-<target id>[:target features]]
2054 // <triple> := <arch>-<vendor>-<os>-<env>
2055 SmallVector<StringRef, 6> Components;
2056 Str.split(Components, '-', /*MaxSplit=*/5);
2057 return Components.size() == 5 || Components.size() == 6;
2058}
Result
Implement __builtin_bit_cast and related operations.
llvm::MachO::Target Target
Definition MachO.h:51
static LLVM_PACKED_END size_t getHeaderSize(uint16_t Version)
static std::string getDeviceLibraryFileName(StringRef BundleFileName, StringRef Device)
static llvm::ManagedStatic< llvm::TimerGroup, CreateClangOffloadBundlerTimerGroup > ClangOffloadBundlerTimerGroup
static StringRef getDeviceFileExtension(StringRef Device, StringRef BundleFileName)
static Expected< std::unique_ptr< FileHandler > > CreateFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate handler given the input files and options.
#define OFFLOAD_BUNDLER_MAGIC_STR
Magic string that marks the existence of offloading data.
bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, const OffloadTargetInfo &TargetInfo)
Checks if a code object CodeObjectInfo is compatible with a given target TargetInfo.
static Error CheckHeterogeneousArchive(StringRef ArchiveName, const OffloadBundlerConfig &BundlerConfig)
static std::unique_ptr< FileHandler > CreateObjectFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate object file handler.
static Archive::Kind getDefaultArchiveKindForHost()
static std::string formatWithCommas(unsigned long long Value)
static bool getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, SmallVectorImpl< StringRef > &CompatibleTargets, const OffloadBundlerConfig &BundlerConfig)
Computes a list of targets among all given targets which are compatible with this code object.
This file defines an offload bundling API that bundles different files that relate with the same sour...
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > decompress(const llvm::MemoryBuffer &Input, bool Verbose=false)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > compress(llvm::compression::Params P, const llvm::MemoryBuffer &Input, uint16_t Version, bool Verbose=false)
llvm::compression::Format CompressionFormat
std::vector< std::string > OutputFileNames
std::vector< std::string > TargetNames
std::vector< std::string > InputFileNames
llvm::Error BundleFiles()
Bundle the files. Return true if an error was found.
llvm::Error UnbundleArchive()
UnbundleArchive takes an archive file (".a") as input containing bundled code object files,...
static llvm::Error ListBundleIDsInFile(llvm::StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig)
const OffloadBundlerConfig & BundlerConfig
Exposes information about the current target.
Definition TargetInfo.h:227
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
Definition Interp.h:3825
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
std::optional< llvm::StringRef > parseTargetID(const llvm::Triple &T, llvm::StringRef OffloadArch, llvm::StringMap< bool > *FeatureMap)
Parse a target ID to get processor and feature map.
Definition TargetID.cpp:108
@ Create
'create' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
std::optional< std::pair< llvm::StringRef, llvm::StringRef > > getConflictTargetIDCombination(const std::set< llvm::StringRef > &TargetIDs)
Get the conflicted pair of target IDs for a compilation or a bundled code object, assuming TargetIDs ...
Definition TargetID.cpp:147
@ Result
The result type of a method or function.
Definition TypeBase.h:905
OffloadArch StringToOffloadArch(llvm::StringRef S)
bool checkOffloadBundleID(const llvm::StringRef Str)
Check whether the bundle id is in the following format: <kind>-<triple>[-<target id>[:target features...
U cast(CodeGen::Address addr)
Definition Address.h:327
unsigned long uint64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
int const char * function
Definition c++config.h:31
static llvm::Expected< CompressedBundleHeader > tryParse(llvm::StringRef)
Obtain the offload kind, real machine triple, and an optional TargetID out of the target information ...
bool operator==(const OffloadTargetInfo &Target) const
bool isOffloadKindCompatible(const llvm::StringRef TargetOffloadKind) const
OffloadTargetInfo(const llvm::StringRef Target, const OffloadBundlerConfig &BC)
llvm::StringRef OffloadKind
std::string str() const
const OffloadBundlerConfig & BundlerConfig