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

source code of clang/lib/Driver/OffloadBundler.cpp