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