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() &&
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// Returns the on-disk size recorded in the compressed offload bundle header at
1006// the start of \p Blob, or std::nullopt if the header carries no size field.
1007static std::optional<size_t> getCompressedBundleSize(StringRef Blob) {
1009 CompressedOffloadBundle::CompressedBundleHeader::tryParse(Blob);
1010 if (!HeaderOrErr) {
1011 consumeError(HeaderOrErr.takeError());
1012 return std::nullopt;
1013 }
1014 return HeaderOrErr->FileSize;
1015}
1016
1017// List bundle IDs. Return true if an error was found.
1019 StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
1020
1021 size_t Offset = 0;
1022 size_t NextBundleStart = 0;
1023 std::unique_ptr<MemoryBuffer> Buffer;
1024
1025 // Open Input file.
1026 ErrorOr<std::unique_ptr<MemoryBuffer>> Contents =
1027 MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
1028 if (std::error_code EC = Contents.getError())
1029 return createFileError(InputFileName, EC);
1030
1031 // There may be multiple bundles.
1032 while ((NextBundleStart != StringRef::npos) &&
1033 (Offset < (**Contents).getBufferSize())) {
1034 Buffer = MemoryBuffer::getMemBuffer(
1035 (**Contents).getBuffer().drop_front(Offset), "",
1036 /*RequiresNullTerminator=*/false);
1037
1038 size_t CurBundleEnd = StringRef::npos;
1039 if (identify_magic((*Buffer).getBuffer()) ==
1040 file_magic::offload_bundle_compressed) {
1041 // Locate this bundle's end and the next bundle from the header size.
1042 if (std::optional<size_t> Size =
1043 getCompressedBundleSize((*Buffer).getBuffer())) {
1044 CurBundleEnd = *Size;
1045 NextBundleStart = (*Buffer).getBuffer().find("CCOB", *Size);
1046 } else {
1047 // Legacy bundle without a recorded size: fall back to magic scanning.
1048 NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
1049 CurBundleEnd = NextBundleStart;
1050 }
1051 } else
1052 NextBundleStart = StringRef::npos;
1053
1054 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1055 MemoryBuffer::getMemBuffer(
1056 (*Buffer).getBuffer().take_front(CurBundleEnd),
1057 InputFileName, // FileName,
1058 false);
1059 if (std::error_code EC = CodeOrErr.getError())
1060 return createFileError(InputFileName, EC);
1061
1062 // Decompress the input if necessary.
1063 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1064 CompressedOffloadBundle::decompress(
1065 **CodeOrErr, BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1066 if (!DecompressedBufferOrErr)
1067 return createStringError(
1068 inconvertibleErrorCode(),
1069 "Failed to decompress input: " +
1070 llvm::toString(DecompressedBufferOrErr.takeError()));
1071
1072 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1073
1074 // Select the right files handler.
1075 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1076 CreateFileHandler(DecompressedInput, BundlerConfig);
1077 if (!FileHandlerOrErr)
1078 return FileHandlerOrErr.takeError();
1079 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1080 assert(FH);
1081 Error E = FH->listBundleIDs(DecompressedInput);
1082 if (E)
1083 return E;
1084
1085 if (NextBundleStart != StringRef::npos)
1086 Offset += NextBundleStart;
1087 }
1088 return Error::success();
1089}
1090
1091/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
1092/// target \p TargetInfo.
1093/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
1096
1097 // Compatible in case of exact match.
1098 if (CodeObjectInfo == TargetInfo) {
1099 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1100 dbgs() << "Compatible: Exact match: \t[CodeObject: "
1101 << CodeObjectInfo.str()
1102 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1103 return true;
1104 }
1105
1106 // Incompatible if Kinds or Triples mismatch.
1107 if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) ||
1108 !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
1109 DEBUG_WITH_TYPE(
1110 "CodeObjectCompatibility",
1111 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
1112 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1113 << "]\n");
1114 return false;
1115 }
1116
1117 // Incompatible if Processors mismatch.
1118 llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
1119 std::optional<StringRef> CodeObjectProc = clang::parseTargetID(
1120 CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap);
1121 std::optional<StringRef> TargetProc = clang::parseTargetID(
1122 TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap);
1123
1124 // Both TargetProc and CodeObjectProc can't be empty here.
1125 if (!TargetProc || !CodeObjectProc ||
1126 CodeObjectProc.value() != TargetProc.value()) {
1127 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1128 dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
1129 << CodeObjectInfo.str()
1130 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1131 return false;
1132 }
1133
1134 // Incompatible if CodeObject has more features than Target, irrespective of
1135 // type or sign of features.
1136 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1137 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1138 dbgs() << "Incompatible: CodeObject has more features "
1139 "than target \t[CodeObject: "
1140 << CodeObjectInfo.str()
1141 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1142 return false;
1143 }
1144
1145 // Compatible if each target feature specified by target is compatible with
1146 // target feature of code object. The target feature is compatible if the
1147 // code object does not specify it (meaning Any), or if it specifies it
1148 // with the same value (meaning On or Off).
1149 for (const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1150 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1151 if (TargetFeature == TargetFeatureMap.end()) {
1152 DEBUG_WITH_TYPE(
1153 "CodeObjectCompatibility",
1154 dbgs()
1155 << "Incompatible: Value of CodeObject's non-ANY feature is "
1156 "not matching with Target feature's ANY value \t[CodeObject: "
1157 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1158 << "]\n");
1159 return false;
1160 } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1161 DEBUG_WITH_TYPE(
1162 "CodeObjectCompatibility",
1163 dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
1164 "not matching with Target feature's non-ANY value "
1165 "\t[CodeObject: "
1166 << CodeObjectInfo.str()
1167 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1168 return false;
1169 }
1170 }
1171
1172 // CodeObject is compatible if all features of Target are:
1173 // - either, present in the Code Object's features map with the same sign,
1174 // - or, the feature is missing from CodeObjects's features map i.e. it is
1175 // set to ANY
1176 DEBUG_WITH_TYPE(
1177 "CodeObjectCompatibility",
1178 dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
1179 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1180 << "]\n");
1181 return true;
1182}
1183
1184/// Bundle the files. Return true if an error was found.
1186 std::error_code EC;
1187
1188 // Create a buffer to hold the content before compressing.
1189 SmallVector<char, 0> Buffer;
1190 llvm::raw_svector_ostream BufferStream(Buffer);
1191
1192 // Open input files.
1194 InputBuffers.reserve(BundlerConfig.InputFileNames.size());
1195 for (auto &I : BundlerConfig.InputFileNames) {
1196 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1197 MemoryBuffer::getFileOrSTDIN(I, /*IsText=*/true);
1198 if (std::error_code EC = CodeOrErr.getError())
1199 return createFileError(I, EC);
1200 InputBuffers.emplace_back(std::move(*CodeOrErr));
1201 }
1202
1203 // Get the file handler. We use the host buffer as reference.
1204 assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) &&
1205 "Host input index undefined??");
1207 *InputBuffers[BundlerConfig.AllowNoHost ? 0
1208 : BundlerConfig.HostInputIndex],
1210 if (!FileHandlerOrErr)
1211 return FileHandlerOrErr.takeError();
1212
1213 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1214 assert(FH);
1215
1216 // Write header.
1217 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1218 return Err;
1219
1220 // Write all bundles along with the start/end markers. If an error was found
1221 // writing the end of the bundle component, abort the bundle writing.
1222 auto Input = InputBuffers.begin();
1223 for (auto &Triple : BundlerConfig.TargetNames) {
1224 if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1225 return Err;
1226 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1227 return Err;
1228 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1229 return Err;
1230 ++Input;
1231 }
1232
1233 raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
1234 sys::fs::OF_None);
1235 if (EC)
1236 return createFileError(BundlerConfig.OutputFileNames.front(), EC);
1237
1238 SmallVector<char, 0> CompressedBuffer;
1239 if (BundlerConfig.Compress) {
1240 std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1241 llvm::MemoryBuffer::getMemBufferCopy(
1242 llvm::StringRef(Buffer.data(), Buffer.size()));
1243 auto CompressionResult = CompressedOffloadBundle::compress(
1244 {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel,
1245 /*zstdEnableLdm=*/true},
1246 *BufferMemory, BundlerConfig.CompressedBundleVersion,
1247 BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1248 if (auto Error = CompressionResult.takeError())
1249 return Error;
1250
1251 auto CompressedMemBuffer = std::move(CompressionResult.get());
1252 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1253 CompressedMemBuffer->getBufferEnd());
1254 } else
1255 CompressedBuffer = std::move(Buffer);
1256
1257 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1258
1259 return FH->finalizeOutputFile();
1260}
1261
1262// Unbundle the files. Return true if an error was found.
1264 // Open Input file.
1265 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1266 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front(),
1267 /*IsText=*/true);
1268 if (std::error_code EC = CodeOrErr.getError())
1269 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1270
1271 // Create a work list that consist of the map triple/output file.
1272 StringMap<StringRef> Worklist;
1273 auto Output = BundlerConfig.OutputFileNames.begin();
1274 for (auto &Triple : BundlerConfig.TargetNames) {
1275 if (!checkOffloadBundleID(Triple))
1276 return createStringError(errc::invalid_argument,
1277 "invalid bundle id from bundle config");
1278 Worklist[Triple] = *Output;
1279 ++Output;
1280 }
1281
1282 // The input may contain multiple concatenated fat binary blobs (e.g. when
1283 // the linker merges .hip_fatbin sections from multiple TUs into one). Walk
1284 // through each blob exactly as ListBundleIDsInFile does, draining worklist
1285 // entries as matching targets are found.
1286 bool FoundHostBundle = false;
1287 size_t Offset = 0;
1288 size_t NextBundleStart = 0;
1289 std::unique_ptr<MemoryBuffer> Buffer;
1290
1291 while ((NextBundleStart != StringRef::npos) &&
1292 (Offset < (**CodeOrErr).getBufferSize())) {
1293
1294 Buffer = MemoryBuffer::getMemBuffer(
1295 (**CodeOrErr).getBuffer().drop_front(Offset), "",
1296 /*RequiresNullTerminator=*/false);
1297
1298 size_t CurBundleEnd = StringRef::npos;
1299 if (identify_magic((*Buffer).getBuffer()) ==
1300 file_magic::offload_bundle_compressed) {
1301 // Locate this bundle's end and the next bundle from the header size.
1302 if (std::optional<size_t> Size =
1303 getCompressedBundleSize((*Buffer).getBuffer())) {
1304 CurBundleEnd = *Size;
1305 NextBundleStart = (*Buffer).getBuffer().find("CCOB", *Size);
1306 } else {
1307 // Legacy bundle without a recorded size: fall back to magic scanning.
1308 NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
1309 CurBundleEnd = NextBundleStart;
1310 }
1311 } else if (identify_magic((*Buffer).getBuffer()) ==
1312 file_magic::offload_bundle) {
1313 NextBundleStart = (*Buffer).getBuffer().find(
1315 CurBundleEnd = NextBundleStart;
1316 } else
1317 NextBundleStart = StringRef::npos;
1318
1319 ErrorOr<std::unique_ptr<MemoryBuffer>> BlobOrErr =
1320 MemoryBuffer::getMemBuffer(
1321 (*Buffer).getBuffer().take_front(CurBundleEnd),
1322 BundlerConfig.InputFileNames.front(),
1323 /*RequiresNullTerminator=*/false);
1324 if (std::error_code EC = BlobOrErr.getError())
1325 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1326
1327 // Decompress the blob if necessary.
1328 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1329 CompressedOffloadBundle::decompress(
1330 **BlobOrErr, BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1331 if (!DecompressedBufferOrErr)
1332 return createStringError(
1333 inconvertibleErrorCode(),
1334 "Failed to decompress input: " +
1335 llvm::toString(DecompressedBufferOrErr.takeError()));
1336
1337 MemoryBuffer &Input = **DecompressedBufferOrErr;
1338
1339 // Select the right file handler for this blob.
1340 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1342 if (!FileHandlerOrErr)
1343 return FileHandlerOrErr.takeError();
1344
1345 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1346 assert(FH);
1347
1348 // Read the header of this blob.
1349 if (Error Err = FH->ReadHeader(Input.getBuffer()))
1350 return Err;
1351
1352 // Drain worklist entries satisfied by this blob.
1353 while (!Worklist.empty()) {
1354 Expected<std::optional<StringRef>> CurTripleOrErr =
1355 FH->ReadBundleStart(Input.getBuffer());
1356 if (!CurTripleOrErr)
1357 return CurTripleOrErr.takeError();
1358
1359 // No more bundles in this blob.
1360 if (!*CurTripleOrErr)
1361 break;
1362
1363 StringRef CurTriple = **CurTripleOrErr;
1364 assert(!CurTriple.empty());
1365 if (!checkOffloadBundleID(CurTriple))
1366 return createStringError(errc::invalid_argument,
1367 "invalid bundle id read from the bundle");
1368
1369 auto Output = Worklist.begin();
1370 for (auto E = Worklist.end(); Output != E; Output++) {
1372 OffloadTargetInfo(CurTriple, BundlerConfig),
1373 OffloadTargetInfo((*Output).first(), BundlerConfig)))
1374 break;
1375 }
1376
1377 if (Output == Worklist.end())
1378 continue;
1379
1380 // Check if the output file can be opened and copy the bundle to it.
1381 std::error_code EC;
1382 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1383 if (EC)
1384 return createFileError((*Output).second, EC);
1385 if (Error Err = FH->ReadBundle(OutputFile, Input))
1386 return Err;
1387 if (Error Err = FH->ReadBundleEnd(Input))
1388 return Err;
1389 Worklist.erase(Output);
1390
1391 // Record if we found the host bundle.
1392 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
1393 if (OffloadInfo.hasHostKind())
1394 FoundHostBundle = true;
1395 }
1396
1397 if (NextBundleStart != StringRef::npos)
1398 Offset += NextBundleStart;
1399 }
1400
1401 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
1402 std::string ErrMsg = "Can't find bundles for";
1403 std::set<StringRef> Sorted;
1404 for (auto &E : Worklist)
1405 Sorted.insert(E.first());
1406 unsigned I = 0;
1407 unsigned Last = Sorted.size() - 1;
1408 for (auto &E : Sorted) {
1409 if (I != 0 && Last > 1)
1410 ErrMsg += ",";
1411 ErrMsg += " ";
1412 if (I == Last && I != 0)
1413 ErrMsg += "and ";
1414 ErrMsg += E.str();
1415 ++I;
1416 }
1417 return createStringError(inconvertibleErrorCode(), ErrMsg);
1418 }
1419
1420 // If no bundles were found, assume the input file is the host bundle and
1421 // create empty files for the remaining targets.
1422 if (Worklist.size() == BundlerConfig.TargetNames.size()) {
1423 for (auto &E : Worklist) {
1424 std::error_code EC;
1425 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1426 if (EC)
1427 return createFileError(E.second, EC);
1428
1429 // If this entry has a host kind, copy the input file to the output file.
1430 // We don't need to check E.getKey() here through checkOffloadBundleID
1431 // because the entire WorkList has been checked above.
1432 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
1433 if (OffloadInfo.hasHostKind())
1434 OutputFile.write((**CodeOrErr).getBufferStart(),
1435 (**CodeOrErr).getBufferSize());
1436 }
1437 return Error::success();
1438 }
1439
1440 // If we found elements, we emit an error if none of those were for the host
1441 // in case host bundle name was provided in command line.
1442 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
1443 BundlerConfig.AllowMissingBundles))
1444 return createStringError(inconvertibleErrorCode(),
1445 "Can't find bundle for the host target");
1446
1447 // If we still have any elements in the worklist, create empty files for them.
1448 for (auto &E : Worklist) {
1449 std::error_code EC;
1450 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1451 if (EC)
1452 return createFileError(E.second, EC);
1453 }
1454
1455 return Error::success();
1456}
1457
1458static Archive::Kind getDefaultArchiveKindForHost() {
1459 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1460 : Archive::K_GNU;
1461}
1462
1463/// @brief Computes a list of targets among all given targets which are
1464/// compatible with this code object
1465/// @param [in] CodeObjectInfo Code Object
1466/// @param [out] CompatibleTargets List of all compatible targets among all
1467/// given targets
1468/// @return false, if no compatible target is found.
1469static bool
1471 SmallVectorImpl<StringRef> &CompatibleTargets,
1472 const OffloadBundlerConfig &BundlerConfig) {
1473 if (!CompatibleTargets.empty()) {
1474 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1475 dbgs() << "CompatibleTargets list should be empty\n");
1476 return false;
1477 }
1478 for (auto &Target : BundlerConfig.TargetNames) {
1479 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
1480 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
1481 CompatibleTargets.push_back(Target);
1482 }
1483 return !CompatibleTargets.empty();
1484}
1485
1486// Check that each code object file in the input archive conforms to following
1487// rule: for a specific processor, a feature either shows up in all target IDs,
1488// or does not show up in any target IDs. Otherwise the target ID combination is
1489// invalid.
1490static Error
1491CheckHeterogeneousArchive(StringRef ArchiveName,
1492 const OffloadBundlerConfig &BundlerConfig) {
1493 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1494 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1495 MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
1496 if (std::error_code EC = BufOrErr.getError())
1497 return createFileError(ArchiveName, EC);
1498
1499 ArchiveBuffers.push_back(std::move(*BufOrErr));
1501 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1502 if (!LibOrErr)
1503 return LibOrErr.takeError();
1504
1505 auto Archive = std::move(*LibOrErr);
1506
1507 Error ArchiveErr = Error::success();
1508 auto ChildEnd = Archive->child_end();
1509
1510 /// Iterate over all bundled code object files in the input archive.
1511 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1512 ArchiveIter != ChildEnd; ++ArchiveIter) {
1513 if (ArchiveErr)
1514 return ArchiveErr;
1515 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1516 if (!ArchiveChildNameOrErr)
1517 return ArchiveChildNameOrErr.takeError();
1518
1519 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1520 if (!CodeObjectBufferRefOrErr)
1521 return CodeObjectBufferRefOrErr.takeError();
1522
1523 auto CodeObjectBuffer =
1524 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1525
1526 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1527 CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
1528 if (!FileHandlerOrErr)
1529 return FileHandlerOrErr.takeError();
1530
1531 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1532 assert(FileHandler);
1533
1534 std::set<StringRef> BundleIds;
1535 auto CodeObjectFileError =
1536 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1537 if (CodeObjectFileError)
1538 return CodeObjectFileError;
1539
1540 auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds);
1541 if (ConflictingArchs) {
1542 std::string ErrMsg =
1543 Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
1544 ", " + ConflictingArchs.value().second + "] found in " +
1545 ArchiveChildNameOrErr.get() + " of " + ArchiveName)
1546 .str();
1547 return createStringError(inconvertibleErrorCode(), ErrMsg);
1548 }
1549 }
1550
1551 return ArchiveErr;
1552}
1553
1554/// UnbundleArchive takes an archive file (".a") as input containing bundled
1555/// code object files, and a list of offload targets (not host), and extracts
1556/// the code objects into a new archive file for each offload target. Each
1557/// resulting archive file contains all code object files corresponding to that
1558/// particular offload target. The created archive file does not
1559/// contain an index of the symbols and code object files are named as
1560/// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
1562 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1563
1564 /// Map of target names with list of object files that will form the device
1565 /// specific archive for that target
1566 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1567
1568 // Map of target names and output archive filenames
1569 StringMap<StringRef> TargetOutputFileNameMap;
1570
1571 auto Output = BundlerConfig.OutputFileNames.begin();
1572 for (auto &Target : BundlerConfig.TargetNames) {
1573 TargetOutputFileNameMap[Target] = *Output;
1574 ++Output;
1575 }
1576
1577 StringRef IFName = BundlerConfig.InputFileNames.front();
1578
1579 if (BundlerConfig.CheckInputArchive) {
1580 // For a specific processor, a feature either shows up in all target IDs, or
1581 // does not show up in any target IDs. Otherwise the target ID combination
1582 // is invalid.
1583 auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig);
1584 if (ArchiveError) {
1585 return ArchiveError;
1586 }
1587 }
1588
1589 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1590 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1591 if (std::error_code EC = BufOrErr.getError())
1592 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1593
1594 ArchiveBuffers.push_back(std::move(*BufOrErr));
1596 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1597 if (!LibOrErr)
1598 return LibOrErr.takeError();
1599
1600 auto Archive = std::move(*LibOrErr);
1601
1602 Error ArchiveErr = Error::success();
1603 auto ChildEnd = Archive->child_end();
1604
1605 /// Iterate over all bundled code object files in the input archive.
1606 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1607 ArchiveIter != ChildEnd; ++ArchiveIter) {
1608 if (ArchiveErr)
1609 return ArchiveErr;
1610 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1611 if (!ArchiveChildNameOrErr)
1612 return ArchiveChildNameOrErr.takeError();
1613
1614 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1615
1616 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1617 if (!CodeObjectBufferRefOrErr)
1618 return CodeObjectBufferRefOrErr.takeError();
1619
1620 auto TempCodeObjectBuffer =
1621 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1622
1623 // Decompress the buffer if necessary.
1624 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1625 CompressedOffloadBundle::decompress(
1626 *TempCodeObjectBuffer,
1627 BundlerConfig.Verbose ? &llvm::errs() : nullptr);
1628 if (!DecompressedBufferOrErr)
1629 return createStringError(
1630 inconvertibleErrorCode(),
1631 "Failed to decompress code object: " +
1632 llvm::toString(DecompressedBufferOrErr.takeError()));
1633
1634 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1635
1636 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1637 CreateFileHandler(CodeObjectBuffer, BundlerConfig);
1638 if (!FileHandlerOrErr)
1639 return FileHandlerOrErr.takeError();
1640
1641 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1642 assert(FileHandler &&
1643 "FileHandle creation failed for file in the archive!");
1644
1645 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer.getBuffer()))
1646 return ReadErr;
1647
1648 Expected<std::optional<StringRef>> CurBundleIDOrErr =
1649 FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
1650 if (!CurBundleIDOrErr)
1651 return CurBundleIDOrErr.takeError();
1652
1653 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1654 // No device code in this child, skip.
1655 if (!OptionalCurBundleID)
1656 continue;
1657 StringRef CodeObject = *OptionalCurBundleID;
1658
1659 // Process all bundle entries (CodeObjects) found in this child of input
1660 // archive.
1661 while (!CodeObject.empty()) {
1662 SmallVector<StringRef> CompatibleTargets;
1663 if (!checkOffloadBundleID(CodeObject)) {
1664 return createStringError(errc::invalid_argument,
1665 "Invalid bundle id read from code object");
1666 }
1667 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
1668 if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
1669 BundlerConfig)) {
1670 std::string BundleData;
1671 raw_string_ostream DataStream(BundleData);
1672 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1673 return Err;
1674
1675 for (auto &CompatibleTarget : CompatibleTargets) {
1676 SmallString<128> BundledObjectFileName;
1677 BundledObjectFileName.assign(BundledObjectFile);
1678 auto OutputBundleName =
1679 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1680 CodeObject +
1681 getDeviceLibraryFileName(BundledObjectFileName,
1682 CodeObjectInfo.TargetID))
1683 .str();
1684 // Replace ':' in optional target feature list with '_' to ensure
1685 // cross-platform validity.
1686 llvm::replace(OutputBundleName, ':', '_');
1687
1688 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1689 DataStream.str(), OutputBundleName);
1690 ArchiveBuffers.push_back(std::move(MemBuf));
1691 llvm::MemoryBufferRef MemBufRef =
1692 MemoryBufferRef(*(ArchiveBuffers.back()));
1693
1694 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1695 // OutputArchivesMap.
1696 OutputArchivesMap[CompatibleTarget].push_back(
1697 NewArchiveMember(MemBufRef));
1698 }
1699 }
1700
1701 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1702 return Err;
1703
1704 Expected<std::optional<StringRef>> NextTripleOrErr =
1705 FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
1706 if (!NextTripleOrErr)
1707 return NextTripleOrErr.takeError();
1708
1709 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
1710 } // End of processing of all bundle entries of this child of input archive.
1711 } // End of while over children of input archive.
1712
1713 assert(!ArchiveErr && "Error occurred while reading archive!");
1714
1715 /// Write out an archive for each target
1716 for (auto &Target : BundlerConfig.TargetNames) {
1717 StringRef FileName = TargetOutputFileNameMap[Target];
1718 auto CurArchiveMembers = OutputArchivesMap.find(Target);
1719 if (CurArchiveMembers != OutputArchivesMap.end()) {
1720 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1721 SymtabWritingMode::NormalSymtab,
1723 false, nullptr))
1724 return WriteErr;
1725 } else if (!BundlerConfig.AllowMissingBundles) {
1726 std::string ErrMsg =
1727 Twine("no compatible code object found for the target '" + Target +
1728 "' in heterogeneous archive library: " + IFName)
1729 .str();
1730 return createStringError(inconvertibleErrorCode(), ErrMsg);
1731 } else { // Create an empty archive file if no compatible code object is
1732 // found and "allow-missing-bundles" is enabled. It ensures that
1733 // the linker using output of this step doesn't complain about
1734 // the missing input file.
1735 std::vector<llvm::NewArchiveMember> EmptyArchive;
1736 EmptyArchive.clear();
1737 if (Error WriteErr = writeArchive(
1738 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1739 getDefaultArchiveKindForHost(), true, false, nullptr))
1740 return WriteErr;
1741 }
1742 }
1743
1744 return Error::success();
1745}
1746
1747bool clang::checkOffloadBundleID(const llvm::StringRef Str) {
1748 // <kind>-<triple>[-<target id>[:target features]]
1749 // <triple> := <arch>-<vendor>-<os>-<env>
1750 SmallVector<StringRef, 6> Components;
1751 Str.split(Components, '-', /*MaxSplit=*/5);
1752 return Components.size() == 5 || Components.size() == 6;
1753}
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: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:3871
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