clang 24.0.0git
OffloadBundler.cpp
Go to the documentation of this file.
1//===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements an offload bundling API that bundles different files
11/// that relate with the same source code but different targets into a single
12/// one. Also the implements the opposite functionality, i.e. unbundle files
13/// previous created by this API.
14///
15//===----------------------------------------------------------------------===//
16
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
73 const OffloadBundlerConfig &BC)
74 : BundlerConfig(BC) {
75
76 // <kind>-<triple>[-<target id>[:target features]]
77 // <triple> := <arch>-<vendor>-<os>-<env>
79 Target.split(Components, '-', /*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(':').first;
86 if (!TargetId.empty() && !clang::StringToOffloadArch(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(TripleSlice, "-"));
94 this->Triple = llvm::Triple(T.getArchName(), T.getVendorName(), T.getOSName(),
95 T.getEnvironmentName());
96}
97
99 return this->OffloadKind == "host";
100}
101
103 return OffloadKind == "host" || OffloadKind == "openmp" ||
104 OffloadKind == "hip" || OffloadKind == "hipv4";
105}
106
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("hip") &&
116 TargetOffloadKind == "openmp";
117 bool OpenMPCompatibleWithHIP =
118 OffloadKind == "openmp" &&
119 TargetOffloadKind.starts_with_insensitive("hip");
120 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
121 }
122 return false;
123}
124
126 return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
127}
128
130 return OffloadKind == Target.OffloadKind &&
131 Triple.isCompatibleWith(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(Triple::CanonicalForm::THREE_IDENT);
142 NormalizedTriple.push_back('-');
143 } else {
144 NormalizedTriple = Triple.normalize(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("gfx"))
152 return ".bc";
153 if (Device.contains("sm_"))
154 return ".cubin";
155 return sys::path::extension(BundleFileName);
156}
157
158static std::string getDeviceLibraryFileName(StringRef BundleFileName,
159 StringRef Device) {
160 StringRef LibName = sys::path::stem(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(NextBundleStart);
224
225 // Read the header.
226 Error Err = ReadHeader(BufferString);
227 if (Err)
228 return Err;
229
230 Err = forEachBundle(BufferString, [&](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(StringRef(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(Input.getBuffer()))
253 return Err;
254 return forEachBundle(Input.getBuffer(),
255 [&](const BundleInfo &Info) -> Error {
256 BundleIds.insert(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{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(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, Val, 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(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(FC, 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(FC, ReadChars);
389 ReadChars += 8;
390
391 // Read size.
392 if (ReadChars + 8 > FC.size())
393 return Error::success();
394
395 uint64_t Size = Read8byteIntegerFromBuffer(FC, 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(FC, 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(FC.data() + CurBundleInfo->second.Offset,
440 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.
460
461 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
462
463 unsigned Idx = 0;
464 for (auto &T : BundlerConfig.TargetNames) {
465 MemoryBuffer &MB = *Inputs[Idx++];
466 HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
467 // Bundle offset.
468 Write8byteIntegerToBuffer(OS, HeaderSize);
469 // Size of the bundle (adds to the next bundle's offset)
470 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
471 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
472 HeaderSize += MB.getBufferSize();
473 // Size of the triple
474 Write8byteIntegerToBuffer(OS, 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('\0');
498 assert(OS.tell() == BI.Offset);
499
500 OS.write(Input.getBufferStart(), 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(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("clang-offload-bundler", "tmp", File))
520 return createFileError(File, EC);
521 Files.push_front(File);
522
523 if (Contents) {
524 std::error_code EC;
525 raw_fd_ostream OS(File, EC);
526 if (EC)
527 return createFileError(File, EC);
528 OS.write(Contents->data(), 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(*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(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(*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 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(Content.data(), 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(ArrayRef<char>(0));
685 if (!TempFileOrErr)
686 return TempFileOrErr.takeError();
687 InputFile = *TempFileOrErr;
688 }
689
690 ObjcopyArgs.push_back(
691 SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR +
692 BundlerConfig.TargetNames[I] + "=" + InputFile));
693 ObjcopyArgs.push_back(
694 SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR +
695 BundlerConfig.TargetNames[I] + "=readonly,exclude"));
696 }
697 ObjcopyArgs.push_back("--");
698 ObjcopyArgs.push_back(
699 BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
700 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
701
702 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, 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(Args, 1))
719 errs() << " \"" << Arg << "\"";
720 errs() << "\n";
721 } else {
722 if (sys::ExecuteAndWait(Objcopy, Args))
723 return createStringError(inconvertibleErrorCode(),
724 "'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(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("--regex");
742 ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
743 ObjcopyArgs.push_back("--");
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("a")) {
753 auto InputFileOrErr = TempFiles.Create(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(ObjcopyInputFileName);
761 ObjcopyArgs.push_back(ModifiedObjPath);
762
763 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
764 return std::move(Err);
765
766 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
767 if (!BufOrErr)
768 return createStringError(BufOrErr.getError(),
769 "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(BundleStartString, 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("\n", 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("\n", 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(BundleEndString, 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(BundleEndString, 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(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(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
902 return std::make_unique<BinaryFileHandler>(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 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
908 BundlerConfig);
909}
910
911/// Return an appropriate handler given the input files and options.
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=*/"//");
919 if (FilesType == "ii")
920 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
921 if (FilesType == "cui")
922 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
923 if (FilesType == "hipi")
924 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
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=*/"#");
929 if (FilesType == "ll")
930 return std::make_unique<TextFileHandler>(/*Comment=*/";");
931 if (FilesType == "bc")
932 return std::make_unique<BinaryFileHandler>(BundlerConfig);
933 if (FilesType == "s")
934 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
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>(BundlerConfig);
941 if (FilesType == "ast")
942 return std::make_unique<BinaryFileHandler>(BundlerConfig);
943
944 return createStringError(errc::invalid_argument,
945 "'" + FilesType + "': invalid file type specified");
946}
947
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.
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("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
963 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1")
964 return;
965 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
966 if (VerboseEnvVarOpt.has_value())
967 Verbose = VerboseEnvVarOpt.value() == "1";
968 auto CompressEnvVarOpt =
969 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
970 if (CompressEnvVarOpt.has_value())
971 Compress = CompressEnvVarOpt.value() == "1";
972 auto CompressionLevelEnvVarOpt =
973 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
974 if (CompressionLevelEnvVarOpt.has_value()) {
975 llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
976 int Level;
977 if (!CompressionLevelStr.getAsInteger(10, 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("COMPRESSED_BUNDLE_FORMAT_VERSION");
986 if (CompressedBundleFormatVersionOpt.has_value()) {
987 llvm::StringRef VersionStr = CompressedBundleFormatVersionOpt.value();
988 uint16_t Version;
989 if (!VersionStr.getAsInteger(10, 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) {
1010 CompressedOffloadBundle::CompressedBundleHeader::tryParse(Blob);
1011 if (!HeaderOrErr) {
1012 consumeError(HeaderOrErr.takeError());
1013 return std::nullopt;
1014 }
1015 return HeaderOrErr->FileSize;
1016}
1017
1018// List bundle IDs. Return true if an error was found.
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(InputFileName, /*IsText=*/true);
1029 if (std::error_code EC = Contents.getError())
1030 return createFileError(InputFileName, EC);
1031
1032 // There may be multiple bundles.
1033 while ((NextBundleStart != StringRef::npos) &&
1034 (Offset < (**Contents).getBufferSize())) {
1035 Buffer = MemoryBuffer::getMemBuffer(
1036 (**Contents).getBuffer().drop_front(Offset), "",
1037 /*RequiresNullTerminator=*/false);
1038
1039 size_t CurBundleEnd = StringRef::npos;
1040 if (identify_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((*Buffer).getBuffer())) {
1045 CurBundleEnd = *Size;
1046 NextBundleStart = (*Buffer).getBuffer().find("CCOB", *Size);
1047 } else {
1048 // Legacy bundle without a recorded size: fall back to magic scanning.
1049 NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
1050 CurBundleEnd = NextBundleStart;
1051 }
1052 } else
1053 NextBundleStart = StringRef::npos;
1054
1055 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1056 MemoryBuffer::getMemBuffer(
1057 (*Buffer).getBuffer().take_front(CurBundleEnd),
1058 InputFileName, // FileName,
1059 false);
1060 if (std::error_code EC = CodeOrErr.getError())
1061 return createFileError(InputFileName, EC);
1062
1063 // Decompress the input if necessary.
1064 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1065 CompressedOffloadBundle::decompress(
1066 **CodeOrErr, BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1067 if (!DecompressedBufferOrErr)
1068 return createStringError(
1069 inconvertibleErrorCode(),
1070 "Failed to decompress input: " +
1071 llvm::toString(DecompressedBufferOrErr.takeError()));
1072
1073 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1074
1075 // Select the right files handler.
1076 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1077 CreateFileHandler(DecompressedInput, BundlerConfig);
1078 if (!FileHandlerOrErr)
1079 return FileHandlerOrErr.takeError();
1080 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1081 assert(FH);
1082 Error E = FH->listBundleIDs(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
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(TargetInfo.OffloadKind) ||
1109 !CodeObjectInfo.Triple.isCompatibleWith(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 CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap);
1122 std::optional<StringRef> TargetProc = clang::parseTargetID(
1123 TargetInfo.Triple, TargetInfo.TargetID, &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(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.
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.
1195 InputBuffers.reserve(BundlerConfig.InputFileNames.size());
1196 for (auto &I : BundlerConfig.InputFileNames) {
1197 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1198 MemoryBuffer::getFileOrSTDIN(I, /*IsText=*/true);
1199 if (std::error_code EC = CodeOrErr.getError())
1200 return createFileError(I, EC);
1201 InputBuffers.emplace_back(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??");
1208 *InputBuffers[BundlerConfig.AllowNoHost ? 0
1209 : BundlerConfig.HostInputIndex],
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(BufferStream, 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(BufferStream, Triple))
1226 return Err;
1227 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1228 return Err;
1229 if (Error Err = FH->WriteBundleEnd(BufferStream, 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(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 llvm::StringRef(Buffer.data(), Buffer.size()));
1244 auto CompressionResult = CompressedOffloadBundle::compress(
1245 {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel,
1246 /*zstdEnableLdm=*/true},
1247 *BufferMemory, BundlerConfig.CompressedBundleVersion,
1248 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(CompressedMemBuffer->getBufferStart(),
1254 CompressedMemBuffer->getBufferEnd());
1255 } else
1256 CompressedBuffer = std::move(Buffer);
1257
1258 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1259
1260 return FH->finalizeOutputFile();
1261}
1262
1263// Unbundle the files. Return true if an error was found.
1265 // Open Input file.
1266 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1267 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front(),
1268 /*IsText=*/true);
1269 if (std::error_code EC = CodeOrErr.getError())
1270 return createFileError(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(Triple))
1277 return createStringError(errc::invalid_argument,
1278 "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 (**CodeOrErr).getBuffer().drop_front(Offset), "",
1297 /*RequiresNullTerminator=*/false);
1298
1299 size_t CurBundleEnd = StringRef::npos;
1300 if (identify_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((*Buffer).getBuffer())) {
1305 CurBundleEnd = *Size;
1306 NextBundleStart = (*Buffer).getBuffer().find("CCOB", *Size);
1307 } else {
1308 // Legacy bundle without a recorded size: fall back to magic scanning.
1309 NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
1310 CurBundleEnd = NextBundleStart;
1311 }
1312 } else if (identify_magic((*Buffer).getBuffer()) ==
1313 file_magic::offload_bundle) {
1314 NextBundleStart = (*Buffer).getBuffer().find(
1316 CurBundleEnd = NextBundleStart;
1317 } else
1318 NextBundleStart = StringRef::npos;
1319
1320 ErrorOr<std::unique_ptr<MemoryBuffer>> BlobOrErr =
1321 MemoryBuffer::getMemBuffer(
1322 (*Buffer).getBuffer().take_front(CurBundleEnd),
1323 BundlerConfig.InputFileNames.front(),
1324 /*RequiresNullTerminator=*/false);
1325 if (std::error_code EC = BlobOrErr.getError())
1326 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1327
1328 // Decompress the blob if necessary.
1329 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1330 CompressedOffloadBundle::decompress(
1331 **BlobOrErr, BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1332 if (!DecompressedBufferOrErr)
1333 return createStringError(
1334 inconvertibleErrorCode(),
1335 "Failed to decompress input: " +
1336 llvm::toString(DecompressedBufferOrErr.takeError()));
1337
1338 MemoryBuffer &Input = **DecompressedBufferOrErr;
1339
1340 // Select the right file handler for this blob.
1341 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
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(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.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(CurTriple))
1367 return createStringError(errc::invalid_argument,
1368 "invalid bundle id read from the bundle");
1369
1370 auto Output = Worklist.begin();
1371 for (auto E = Worklist.end(); Output != E; Output++) {
1373 OffloadTargetInfo(CurTriple, BundlerConfig),
1374 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((*Output).second, EC);
1386 if (Error Err = FH->ReadBundle(OutputFile, Input))
1387 return Err;
1388 if (Error Err = FH->ReadBundleEnd(Input))
1389 return Err;
1390 Worklist.erase(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(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(inconvertibleErrorCode(), 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(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((**CodeOrErr).getBufferStart(),
1436 (**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(inconvertibleErrorCode(),
1446 "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(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
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(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(ArchiveName, true, false);
1497 if (std::error_code EC = BufOrErr.getError())
1498 return createFileError(ArchiveName, EC);
1499
1500 ArchiveBuffers.push_back(std::move(*BufOrErr));
1502 Archive::create(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(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(*CodeObjectBufferRefOrErr, false);
1526
1527 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1528 CreateFileHandler(*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(*CodeObjectBuffer, BundleIds);
1538 if (CodeObjectFileError)
1539 return CodeObjectFileError;
1540
1541 auto &&ConflictingArchs = clang::getConflictTargetIDCombination(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(inconvertibleErrorCode(), 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 '_'.
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(IFName, BundlerConfig);
1585 if (ArchiveError) {
1586 return ArchiveError;
1587 }
1588 }
1589
1590 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1591 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1592 if (std::error_code EC = BufOrErr.getError())
1593 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1594
1595 ArchiveBuffers.push_back(std::move(*BufOrErr));
1597 Archive::create(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(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(*ArchiveChildNameOrErr);
1616
1617 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1618 if (!CodeObjectBufferRefOrErr)
1619 return CodeObjectBufferRefOrErr.takeError();
1620
1621 auto TempCodeObjectBuffer =
1622 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1623
1624 // Decompress the buffer if necessary.
1625 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1626 CompressedOffloadBundle::decompress(
1627 *TempCodeObjectBuffer,
1628 BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1629 if (!DecompressedBufferOrErr)
1630 return createStringError(
1631 inconvertibleErrorCode(),
1632 "Failed to decompress code object: " +
1633 llvm::toString(DecompressedBufferOrErr.takeError()));
1634
1635 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1636
1637 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1638 CreateFileHandler(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(CodeObjectBuffer.getBuffer()))
1647 return ReadErr;
1648
1649 Expected<std::optional<StringRef>> CurBundleIDOrErr =
1650 FileHandler->ReadBundleStart(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(CodeObject)) {
1665 return createStringError(errc::invalid_argument,
1666 "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(DataStream, CodeObjectBuffer))
1674 return Err;
1675
1676 for (auto &CompatibleTarget : CompatibleTargets) {
1677 SmallString<128> BundledObjectFileName;
1678 BundledObjectFileName.assign(BundledObjectFile);
1679 auto OutputBundleName =
1680 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1681 CodeObject +
1682 getDeviceLibraryFileName(BundledObjectFileName,
1683 CodeObjectInfo.TargetID))
1684 .str();
1685 // Replace ':' in optional target feature list with '_' to ensure
1686 // cross-platform validity.
1687 llvm::replace(OutputBundleName, ':', '_');
1688
1689 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1690 DataStream.str(), OutputBundleName);
1691 ArchiveBuffers.push_back(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 NewArchiveMember(MemBufRef));
1699 }
1700 }
1701
1702 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1703 return Err;
1704
1705 Expected<std::optional<StringRef>> NextTripleOrErr =
1706 FileHandler->ReadBundleStart(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(Target);
1720 if (CurArchiveMembers != OutputArchivesMap.end()) {
1721 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1722 SymtabWritingMode::NormalSymtab,
1724 false, 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(inconvertibleErrorCode(), 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 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1740 getDefaultArchiveKindForHost(), true, false, 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(Components, '-', /*MaxSplit=*/5);
1753 return Components.size() == 5 || Components.size() == 6;
1754}
Result
Implement __builtin_bit_cast and related operations.
llvm::MachO::Target Target
Definition MachO.h:51
static std::string getDeviceLibraryFileName(StringRef BundleFileName, StringRef Device)
static StringRef getDeviceFileExtension(StringRef Device, StringRef BundleFileName)
static Expected< std::unique_ptr< FileHandler > > CreateFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate handler given the input files and options.
#define OFFLOAD_BUNDLER_MAGIC_STR
Magic string that marks the existence of offloading data.
bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, const OffloadTargetInfo &TargetInfo)
Checks if a code object CodeObjectInfo is compatible with a given target TargetInfo.
static Error CheckHeterogeneousArchive(StringRef ArchiveName, const OffloadBundlerConfig &BundlerConfig)
static std::unique_ptr< FileHandler > CreateObjectFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate object file handler.
static Archive::Kind getDefaultArchiveKindForHost()
static std::optional< size_t > getCompressedBundleSize(StringRef Blob)
static bool getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, SmallVectorImpl< StringRef > &CompatibleTargets, const OffloadBundlerConfig &BundlerConfig)
Computes a list of targets among all given targets which are compatible with this code object.
This file defines an offload bundling API that bundles different files that relate with the same sour...
llvm::SmallVector< std::string, 4 > TargetNames
llvm::compression::Format CompressionFormat
llvm::SmallVector< std::string, 4 > OutputFileNames
llvm::SmallVector< std::string, 4 > InputFileNames
llvm::Error BundleFiles()
Bundle the files. Return true if an error was found.
llvm::Error UnbundleArchive()
UnbundleArchive takes an archive file (".a") as input containing bundled code object files,...
static llvm::Error ListBundleIDsInFile(llvm::StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig)
const OffloadBundlerConfig & BundlerConfig
Exposes information about the current target.
Definition TargetInfo.h:226
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
Definition Interp.h:3945
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
std::optional< llvm::StringRef > parseTargetID(const llvm::Triple &T, llvm::StringRef OffloadArch, llvm::StringMap< bool > *FeatureMap)
Parse a target ID to get processor and feature map.
Definition TargetID.cpp:122
@ Create
'create' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
std::optional< std::pair< llvm::StringRef, llvm::StringRef > > getConflictTargetIDCombination(const std::set< llvm::StringRef > &TargetIDs)
Get the conflicted pair of target IDs for a compilation or a bundled code object, assuming TargetIDs ...
Definition TargetID.cpp:161
const FunctionProtoType * T
OffloadArch StringToOffloadArch(llvm::StringRef S)
bool checkOffloadBundleID(const llvm::StringRef Str)
Check whether the bundle id is in the following format: <kind>-<triple>[-<target id>[:target features...
U cast(CodeGen::Address addr)
Definition Address.h:327
unsigned long uint64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
int const char * function
Definition c++config.h:31
Obtain the offload kind, real machine triple, and an optional TargetID out of the target information ...
bool operator==(const OffloadTargetInfo &Target) const
bool isOffloadKindCompatible(const llvm::StringRef TargetOffloadKind) const
OffloadTargetInfo(const llvm::StringRef Target, const OffloadBundlerConfig &BC)
llvm::StringRef OffloadKind
std::string str() const
const OffloadBundlerConfig & BundlerConfig