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