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