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