clang 19.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 "clang/Basic/Version.h"
21#include "llvm/ADT/ArrayRef.h"
22#include "llvm/ADT/SmallString.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/StringExtras.h"
25#include "llvm/ADT/StringMap.h"
26#include "llvm/ADT/StringRef.h"
27#include "llvm/BinaryFormat/Magic.h"
28#include "llvm/Object/Archive.h"
29#include "llvm/Object/ArchiveWriter.h"
30#include "llvm/Object/Binary.h"
31#include "llvm/Object/ObjectFile.h"
32#include "llvm/Support/Casting.h"
33#include "llvm/Support/Compression.h"
34#include "llvm/Support/Debug.h"
35#include "llvm/Support/EndianStream.h"
36#include "llvm/Support/Errc.h"
37#include "llvm/Support/Error.h"
38#include "llvm/Support/ErrorOr.h"
39#include "llvm/Support/FileSystem.h"
40#include "llvm/Support/MD5.h"
41#include "llvm/Support/MemoryBuffer.h"
42#include "llvm/Support/Path.h"
43#include "llvm/Support/Program.h"
44#include "llvm/Support/Signals.h"
45#include "llvm/Support/StringSaver.h"
46#include "llvm/Support/Timer.h"
47#include "llvm/Support/WithColor.h"
48#include "llvm/Support/raw_ostream.h"
49#include "llvm/TargetParser/Host.h"
50#include "llvm/TargetParser/Triple.h"
51#include <algorithm>
52#include <cassert>
53#include <cstddef>
54#include <cstdint>
55#include <forward_list>
56#include <llvm/Support/Process.h>
57#include <memory>
58#include <set>
59#include <string>
60#include <system_error>
61#include <utility>
62
63using namespace llvm;
64using namespace llvm::object;
65using namespace clang;
66
67static llvm::TimerGroup
68 ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group",
69 "Timer group for clang offload bundler");
70
71/// Magic string that marks the existence of offloading data.
72#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
73
75 const OffloadBundlerConfig &BC)
76 : BundlerConfig(BC) {
77
78 // TODO: Add error checking from ClangOffloadBundler.cpp
79 auto TargetFeatures = Target.split(':');
80 auto TripleOrGPU = TargetFeatures.first.rsplit('-');
81
82 if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) {
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);
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);
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 if (std::error_code EC = CodeOrErr.getError())
1396 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1397
1398 // Decompress the input if necessary.
1399 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1401 if (!DecompressedBufferOrErr)
1402 return createStringError(
1403 inconvertibleErrorCode(),
1404 "Failed to decompress input: " +
1405 llvm::toString(DecompressedBufferOrErr.takeError()));
1406
1407 MemoryBuffer &Input = **DecompressedBufferOrErr;
1408
1409 // Select the right files handler.
1410 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1412 if (!FileHandlerOrErr)
1413 return FileHandlerOrErr.takeError();
1414
1415 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1416 assert(FH);
1417
1418 // Read the header of the bundled file.
1419 if (Error Err = FH->ReadHeader(Input))
1420 return Err;
1421
1422 // Create a work list that consist of the map triple/output file.
1423 StringMap<StringRef> Worklist;
1424 auto Output = BundlerConfig.OutputFileNames.begin();
1425 for (auto &Triple : BundlerConfig.TargetNames) {
1426 Worklist[Triple] = *Output;
1427 ++Output;
1428 }
1429
1430 // Read all the bundles that are in the work list. If we find no bundles we
1431 // assume the file is meant for the host target.
1432 bool FoundHostBundle = false;
1433 while (!Worklist.empty()) {
1434 Expected<std::optional<StringRef>> CurTripleOrErr =
1435 FH->ReadBundleStart(Input);
1436 if (!CurTripleOrErr)
1437 return CurTripleOrErr.takeError();
1438
1439 // We don't have more bundles.
1440 if (!*CurTripleOrErr)
1441 break;
1442
1443 StringRef CurTriple = **CurTripleOrErr;
1444 assert(!CurTriple.empty());
1445
1446 auto Output = Worklist.begin();
1447 for (auto E = Worklist.end(); Output != E; Output++) {
1449 OffloadTargetInfo(CurTriple, BundlerConfig),
1450 OffloadTargetInfo((*Output).first(), BundlerConfig))) {
1451 break;
1452 }
1453 }
1454
1455 if (Output == Worklist.end())
1456 continue;
1457 // Check if the output file can be opened and copy the bundle to it.
1458 std::error_code EC;
1459 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1460 if (EC)
1461 return createFileError((*Output).second, EC);
1462 if (Error Err = FH->ReadBundle(OutputFile, Input))
1463 return Err;
1464 if (Error Err = FH->ReadBundleEnd(Input))
1465 return Err;
1466 Worklist.erase(Output);
1467
1468 // Record if we found the host bundle.
1469 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
1470 if (OffloadInfo.hasHostKind())
1471 FoundHostBundle = true;
1472 }
1473
1474 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
1475 std::string ErrMsg = "Can't find bundles for";
1476 std::set<StringRef> Sorted;
1477 for (auto &E : Worklist)
1478 Sorted.insert(E.first());
1479 unsigned I = 0;
1480 unsigned Last = Sorted.size() - 1;
1481 for (auto &E : Sorted) {
1482 if (I != 0 && Last > 1)
1483 ErrMsg += ",";
1484 ErrMsg += " ";
1485 if (I == Last && I != 0)
1486 ErrMsg += "and ";
1487 ErrMsg += E.str();
1488 ++I;
1489 }
1490 return createStringError(inconvertibleErrorCode(), ErrMsg);
1491 }
1492
1493 // If no bundles were found, assume the input file is the host bundle and
1494 // create empty files for the remaining targets.
1495 if (Worklist.size() == BundlerConfig.TargetNames.size()) {
1496 for (auto &E : Worklist) {
1497 std::error_code EC;
1498 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1499 if (EC)
1500 return createFileError(E.second, EC);
1501
1502 // If this entry has a host kind, copy the input file to the output file.
1503 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
1504 if (OffloadInfo.hasHostKind())
1505 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1506 }
1507 return Error::success();
1508 }
1509
1510 // If we found elements, we emit an error if none of those were for the host
1511 // in case host bundle name was provided in command line.
1512 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
1514 return createStringError(inconvertibleErrorCode(),
1515 "Can't find bundle for the host target");
1516
1517 // If we still have any elements in the worklist, create empty files for them.
1518 for (auto &E : Worklist) {
1519 std::error_code EC;
1520 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1521 if (EC)
1522 return createFileError(E.second, EC);
1523 }
1524
1525 return Error::success();
1526}
1527
1528static Archive::Kind getDefaultArchiveKindForHost() {
1529 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1530 : Archive::K_GNU;
1531}
1532
1533/// @brief Computes a list of targets among all given targets which are
1534/// compatible with this code object
1535/// @param [in] CodeObjectInfo Code Object
1536/// @param [out] CompatibleTargets List of all compatible targets among all
1537/// given targets
1538/// @return false, if no compatible target is found.
1539static bool
1541 SmallVectorImpl<StringRef> &CompatibleTargets,
1542 const OffloadBundlerConfig &BundlerConfig) {
1543 if (!CompatibleTargets.empty()) {
1544 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1545 dbgs() << "CompatibleTargets list should be empty\n");
1546 return false;
1547 }
1548 for (auto &Target : BundlerConfig.TargetNames) {
1549 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
1550 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
1551 CompatibleTargets.push_back(Target);
1552 }
1553 return !CompatibleTargets.empty();
1554}
1555
1556// Check that each code object file in the input archive conforms to following
1557// rule: for a specific processor, a feature either shows up in all target IDs,
1558// or does not show up in any target IDs. Otherwise the target ID combination is
1559// invalid.
1560static Error
1561CheckHeterogeneousArchive(StringRef ArchiveName,
1562 const OffloadBundlerConfig &BundlerConfig) {
1563 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1564 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1565 MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
1566 if (std::error_code EC = BufOrErr.getError())
1567 return createFileError(ArchiveName, EC);
1568
1569 ArchiveBuffers.push_back(std::move(*BufOrErr));
1571 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1572 if (!LibOrErr)
1573 return LibOrErr.takeError();
1574
1575 auto Archive = std::move(*LibOrErr);
1576
1577 Error ArchiveErr = Error::success();
1578 auto ChildEnd = Archive->child_end();
1579
1580 /// Iterate over all bundled code object files in the input archive.
1581 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1582 ArchiveIter != ChildEnd; ++ArchiveIter) {
1583 if (ArchiveErr)
1584 return ArchiveErr;
1585 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1586 if (!ArchiveChildNameOrErr)
1587 return ArchiveChildNameOrErr.takeError();
1588
1589 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1590 if (!CodeObjectBufferRefOrErr)
1591 return CodeObjectBufferRefOrErr.takeError();
1592
1593 auto CodeObjectBuffer =
1594 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1595
1596 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1597 CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
1598 if (!FileHandlerOrErr)
1599 return FileHandlerOrErr.takeError();
1600
1601 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1602 assert(FileHandler);
1603
1604 std::set<StringRef> BundleIds;
1605 auto CodeObjectFileError =
1606 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1607 if (CodeObjectFileError)
1608 return CodeObjectFileError;
1609
1610 auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds);
1611 if (ConflictingArchs) {
1612 std::string ErrMsg =
1613 Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
1614 ", " + ConflictingArchs.value().second + "] found in " +
1615 ArchiveChildNameOrErr.get() + " of " + ArchiveName)
1616 .str();
1617 return createStringError(inconvertibleErrorCode(), ErrMsg);
1618 }
1619 }
1620
1621 return ArchiveErr;
1622}
1623
1624/// UnbundleArchive takes an archive file (".a") as input containing bundled
1625/// code object files, and a list of offload targets (not host), and extracts
1626/// the code objects into a new archive file for each offload target. Each
1627/// resulting archive file contains all code object files corresponding to that
1628/// particular offload target. The created archive file does not
1629/// contain an index of the symbols and code object files are named as
1630/// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
1632 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1633
1634 /// Map of target names with list of object files that will form the device
1635 /// specific archive for that target
1636 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1637
1638 // Map of target names and output archive filenames
1639 StringMap<StringRef> TargetOutputFileNameMap;
1640
1641 auto Output = BundlerConfig.OutputFileNames.begin();
1642 for (auto &Target : BundlerConfig.TargetNames) {
1643 TargetOutputFileNameMap[Target] = *Output;
1644 ++Output;
1645 }
1646
1647 StringRef IFName = BundlerConfig.InputFileNames.front();
1648
1650 // For a specific processor, a feature either shows up in all target IDs, or
1651 // does not show up in any target IDs. Otherwise the target ID combination
1652 // is invalid.
1653 auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig);
1654 if (ArchiveError) {
1655 return ArchiveError;
1656 }
1657 }
1658
1659 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1660 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1661 if (std::error_code EC = BufOrErr.getError())
1662 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1663
1664 ArchiveBuffers.push_back(std::move(*BufOrErr));
1666 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1667 if (!LibOrErr)
1668 return LibOrErr.takeError();
1669
1670 auto Archive = std::move(*LibOrErr);
1671
1672 Error ArchiveErr = Error::success();
1673 auto ChildEnd = Archive->child_end();
1674
1675 /// Iterate over all bundled code object files in the input archive.
1676 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1677 ArchiveIter != ChildEnd; ++ArchiveIter) {
1678 if (ArchiveErr)
1679 return ArchiveErr;
1680 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1681 if (!ArchiveChildNameOrErr)
1682 return ArchiveChildNameOrErr.takeError();
1683
1684 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1685
1686 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1687 if (!CodeObjectBufferRefOrErr)
1688 return CodeObjectBufferRefOrErr.takeError();
1689
1690 auto TempCodeObjectBuffer =
1691 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1692
1693 // Decompress the buffer if necessary.
1694 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1695 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer,
1697 if (!DecompressedBufferOrErr)
1698 return createStringError(
1699 inconvertibleErrorCode(),
1700 "Failed to decompress code object: " +
1701 llvm::toString(DecompressedBufferOrErr.takeError()));
1702
1703 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1704
1705 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1706 CreateFileHandler(CodeObjectBuffer, BundlerConfig);
1707 if (!FileHandlerOrErr)
1708 return FileHandlerOrErr.takeError();
1709
1710 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1711 assert(FileHandler &&
1712 "FileHandle creation failed for file in the archive!");
1713
1714 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1715 return ReadErr;
1716
1717 Expected<std::optional<StringRef>> CurBundleIDOrErr =
1718 FileHandler->ReadBundleStart(CodeObjectBuffer);
1719 if (!CurBundleIDOrErr)
1720 return CurBundleIDOrErr.takeError();
1721
1722 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1723 // No device code in this child, skip.
1724 if (!OptionalCurBundleID)
1725 continue;
1726 StringRef CodeObject = *OptionalCurBundleID;
1727
1728 // Process all bundle entries (CodeObjects) found in this child of input
1729 // archive.
1730 while (!CodeObject.empty()) {
1731 SmallVector<StringRef> CompatibleTargets;
1732 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
1733 if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
1734 BundlerConfig)) {
1735 std::string BundleData;
1736 raw_string_ostream DataStream(BundleData);
1737 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1738 return Err;
1739
1740 for (auto &CompatibleTarget : CompatibleTargets) {
1741 SmallString<128> BundledObjectFileName;
1742 BundledObjectFileName.assign(BundledObjectFile);
1743 auto OutputBundleName =
1744 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1745 CodeObject +
1746 getDeviceLibraryFileName(BundledObjectFileName,
1747 CodeObjectInfo.TargetID))
1748 .str();
1749 // Replace ':' in optional target feature list with '_' to ensure
1750 // cross-platform validity.
1751 std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
1752 '_');
1753
1754 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1755 DataStream.str(), OutputBundleName);
1756 ArchiveBuffers.push_back(std::move(MemBuf));
1757 llvm::MemoryBufferRef MemBufRef =
1758 MemoryBufferRef(*(ArchiveBuffers.back()));
1759
1760 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1761 // OutputArchivesMap.
1762 if (!OutputArchivesMap.contains(CompatibleTarget)) {
1763
1764 std::vector<NewArchiveMember> ArchiveMembers;
1765 ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
1766 OutputArchivesMap.insert_or_assign(CompatibleTarget,
1767 std::move(ArchiveMembers));
1768 } else {
1769 OutputArchivesMap[CompatibleTarget].push_back(
1770 NewArchiveMember(MemBufRef));
1771 }
1772 }
1773 }
1774
1775 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1776 return Err;
1777
1778 Expected<std::optional<StringRef>> NextTripleOrErr =
1779 FileHandler->ReadBundleStart(CodeObjectBuffer);
1780 if (!NextTripleOrErr)
1781 return NextTripleOrErr.takeError();
1782
1783 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
1784 } // End of processing of all bundle entries of this child of input archive.
1785 } // End of while over children of input archive.
1786
1787 assert(!ArchiveErr && "Error occurred while reading archive!");
1788
1789 /// Write out an archive for each target
1790 for (auto &Target : BundlerConfig.TargetNames) {
1791 StringRef FileName = TargetOutputFileNameMap[Target];
1792 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1793 OutputArchivesMap.find(Target);
1794 if (CurArchiveMembers != OutputArchivesMap.end()) {
1795 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1796 SymtabWritingMode::NormalSymtab,
1798 false, nullptr))
1799 return WriteErr;
1800 } else if (!BundlerConfig.AllowMissingBundles) {
1801 std::string ErrMsg =
1802 Twine("no compatible code object found for the target '" + Target +
1803 "' in heterogeneous archive library: " + IFName)
1804 .str();
1805 return createStringError(inconvertibleErrorCode(), ErrMsg);
1806 } else { // Create an empty archive file if no compatible code object is
1807 // found and "allow-missing-bundles" is enabled. It ensures that
1808 // the linker using output of this step doesn't complain about
1809 // the missing input file.
1810 std::vector<llvm::NewArchiveMember> EmptyArchive;
1811 EmptyArchive.clear();
1812 if (Error WriteErr = writeArchive(
1813 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1814 getDefaultArchiveKindForHost(), true, false, nullptr))
1815 return WriteErr;
1816 }
1817 }
1818
1819 return Error::success();
1820}
StringRef P
llvm::MachO::Target Target
Definition: MachO.h:50
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...
Defines version macros and version-related utility functions for Clang.
__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:218
The JSON file list parser is used to communicate input to InstallAPI.
@ Create
'create' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
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:105
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:145
CudaArch StringToCudaArch(llvm::StringRef S)
Definition: Cuda.cpp:169
@ Result
The result type of a method or function.
const FunctionProtoType * T
unsigned long uint64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
Definition: Format.h:5428
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