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