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