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