[flang] Update CommandTest for AIX (NFC) (#118403)
[llvm-project.git] / clang / lib / Driver / OffloadBundler.cpp
blob87d7303d938c909faafcf7a5e32796a114de7858
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 //===----------------------------------------------------------------------===//
17 #include "clang/Driver/OffloadBundler.h"
18 #include "clang/Basic/Cuda.h"
19 #include "clang/Basic/TargetID.h"
20 #include "llvm/ADT/ArrayRef.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/ADT/StringMap.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/BinaryFormat/Magic.h"
27 #include "llvm/Object/Archive.h"
28 #include "llvm/Object/ArchiveWriter.h"
29 #include "llvm/Object/Binary.h"
30 #include "llvm/Object/ObjectFile.h"
31 #include "llvm/Support/Casting.h"
32 #include "llvm/Support/Compression.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/EndianStream.h"
35 #include "llvm/Support/Errc.h"
36 #include "llvm/Support/Error.h"
37 #include "llvm/Support/ErrorOr.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/MD5.h"
40 #include "llvm/Support/MemoryBuffer.h"
41 #include "llvm/Support/Path.h"
42 #include "llvm/Support/Program.h"
43 #include "llvm/Support/Signals.h"
44 #include "llvm/Support/StringSaver.h"
45 #include "llvm/Support/Timer.h"
46 #include "llvm/Support/WithColor.h"
47 #include "llvm/Support/raw_ostream.h"
48 #include "llvm/TargetParser/Host.h"
49 #include "llvm/TargetParser/Triple.h"
50 #include <algorithm>
51 #include <cassert>
52 #include <cstddef>
53 #include <cstdint>
54 #include <forward_list>
55 #include <llvm/Support/Process.h>
56 #include <memory>
57 #include <set>
58 #include <string>
59 #include <system_error>
60 #include <utility>
62 using namespace llvm;
63 using namespace llvm::object;
64 using namespace clang;
66 static llvm::TimerGroup
67 ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group",
68 "Timer group for clang offload bundler");
70 /// Magic string that marks the existence of offloading data.
71 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
73 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
74 const OffloadBundlerConfig &BC)
75 : BundlerConfig(BC) {
77 // TODO: Add error checking from ClangOffloadBundler.cpp
78 auto TargetFeatures = Target.split(':');
79 auto TripleOrGPU = TargetFeatures.first.rsplit('-');
81 if (clang::StringToOffloadArch(TripleOrGPU.second) !=
82 clang::OffloadArch::UNKNOWN) {
83 auto KindTriple = TripleOrGPU.first.split('-');
84 this->OffloadKind = KindTriple.first;
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());
91 this->TargetID = Target.substr(Target.find(TripleOrGPU.second));
92 } else {
93 auto KindTriple = TargetFeatures.first.split('-');
94 this->OffloadKind = KindTriple.first;
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());
101 this->TargetID = "";
105 bool OffloadTargetInfo::hasHostKind() const {
106 return this->OffloadKind == "host";
109 bool OffloadTargetInfo::isOffloadKindValid() const {
110 return OffloadKind == "host" || OffloadKind == "openmp" ||
111 OffloadKind == "hip" || OffloadKind == "hipv4";
114 bool OffloadTargetInfo::isOffloadKindCompatible(
115 const StringRef TargetOffloadKind) const {
116 if ((OffloadKind == TargetOffloadKind) ||
117 (OffloadKind == "hip" && TargetOffloadKind == "hipv4") ||
118 (OffloadKind == "hipv4" && TargetOffloadKind == "hip"))
119 return true;
121 if (BundlerConfig.HipOpenmpCompatible) {
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;
129 return false;
132 bool OffloadTargetInfo::isTripleValid() const {
133 return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
136 bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const {
137 return OffloadKind == Target.OffloadKind &&
138 Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID;
141 std::string OffloadTargetInfo::str() const {
142 return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str();
145 static 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);
154 static std::string getDeviceLibraryFileName(StringRef BundleFileName,
155 StringRef Device) {
156 StringRef LibName = sys::path::stem(BundleFileName);
157 StringRef Extension = getDeviceFileExtension(Device, BundleFileName);
159 std::string Result;
160 Result += LibName;
161 Result += Extension;
162 return Result;
165 namespace {
166 /// Generic file handler interface.
167 class FileHandler {
168 public:
169 struct BundleInfo {
170 StringRef BundleID;
173 FileHandler() {}
175 virtual ~FileHandler() {}
177 /// Update the file handler with information from the header of the bundled
178 /// file.
179 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
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.
184 virtual Expected<std::optional<StringRef>>
185 ReadBundleStart(MemoryBuffer &Input) = 0;
187 /// Read the marker that closes the current bundle.
188 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
190 /// Read the current bundle and write the result into the stream \a OS.
191 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
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;
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;
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;
206 /// Write the bundle from \a Input into \a OS.
207 virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
209 /// Finalize output file.
210 virtual Error finalizeOutputFile() { return Error::success(); }
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();
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();
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();
248 // No more bundles.
249 if (!*CurTripleOrErr)
250 break;
252 StringRef CurTriple = **CurTripleOrErr;
253 assert(!CurTriple.empty());
255 BundleInfo Info{CurTriple};
256 if (Error Err = Func(Info))
257 return Err;
259 return Error::success();
262 protected:
263 virtual Error listBundleIDsCallback(MemoryBuffer &Input,
264 const BundleInfo &Info) {
265 return Error::success();
269 /// Handler for binary files. The bundled file will have the following format
270 /// (all integers are stored in little-endian format):
272 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
274 /// NumberOfOffloadBundles (8-byte integer)
276 /// OffsetOfBundle1 (8-byte integer)
277 /// SizeOfBundle1 (8-byte integer)
278 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
279 /// TripleOfBundle1 (byte length defined before)
281 /// ...
283 /// OffsetOfBundleN (8-byte integer)
284 /// SizeOfBundleN (8-byte integer)
285 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
286 /// TripleOfBundleN (byte length defined before)
288 /// Bundle1
289 /// ...
290 /// BundleN
292 /// Read 8-byte integers from a buffer in little-endian format.
293 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
294 return llvm::support::endian::read64le(Buffer.data() + pos);
297 /// Write 8-byte integers to a buffer in little-endian format.
298 static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
299 llvm::support::endian::write(OS, Val, llvm::endianness::little);
302 class 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;
310 BinaryBundleInfo() {}
311 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
312 : Size(Size), Offset(Offset) {}
315 /// Map between a triple and the corresponding bundle information.
316 StringMap<BinaryBundleInfo> BundlesInfo;
318 /// Iterator for the bundle information that is being read.
319 StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
320 StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
322 /// Current bundle target to be written.
323 std::string CurWriteBundleTarget;
325 /// Configuration options and arrays for this bundler job
326 const OffloadBundlerConfig &BundlerConfig;
328 public:
329 // TODO: Add error checking from ClangOffloadBundler.cpp
330 BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
332 ~BinaryFileHandler() final {}
334 Error ReadHeader(MemoryBuffer &Input) final {
335 StringRef FC = Input.getBuffer();
337 // Initialize the current bundle with the end of the container.
338 CurBundleInfo = BundlesInfo.end();
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();
345 // Check if no magic was found.
346 if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
347 return Error::success();
349 // Read number of bundles.
350 if (ReadChars + 8 > FC.size())
351 return Error::success();
353 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
354 ReadChars += 8;
356 // Read bundle offsets, sizes and triples.
357 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
359 // Read offset.
360 if (ReadChars + 8 > FC.size())
361 return Error::success();
363 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
364 ReadChars += 8;
366 // Read size.
367 if (ReadChars + 8 > FC.size())
368 return Error::success();
370 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
371 ReadChars += 8;
373 // Read triple size.
374 if (ReadChars + 8 > FC.size())
375 return Error::success();
377 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
378 ReadChars += 8;
380 // Read triple.
381 if (ReadChars + TripleSize > FC.size())
382 return Error::success();
384 StringRef Triple(&FC.data()[ReadChars], TripleSize);
385 ReadChars += TripleSize;
387 // Check if the offset and size make sense.
388 if (!Offset || Offset + Size > FC.size())
389 return Error::success();
391 assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??");
392 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
394 // Set the iterator to where we will start to read.
395 CurBundleInfo = BundlesInfo.end();
396 NextBundleInfo = BundlesInfo.begin();
397 return Error::success();
400 Expected<std::optional<StringRef>>
401 ReadBundleStart(MemoryBuffer &Input) final {
402 if (NextBundleInfo == BundlesInfo.end())
403 return std::nullopt;
404 CurBundleInfo = NextBundleInfo++;
405 return CurBundleInfo->first();
408 Error ReadBundleEnd(MemoryBuffer &Input) final {
409 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
410 return Error::success();
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();
421 Error WriteHeader(raw_ostream &OS,
422 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
424 // Compute size of the header.
425 uint64_t HeaderSize = 0;
427 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
428 HeaderSize += 8; // Number of Bundles
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.
435 // Write to the buffer the header.
436 OS << OFFLOAD_BUNDLER_MAGIC_STR;
438 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
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;
455 return Error::success();
458 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
459 CurWriteBundleTarget = TargetTriple.str();
460 return Error::success();
463 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
464 return Error::success();
467 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
468 auto BI = BundlesInfo[CurWriteBundleTarget];
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);
477 OS.write(Input.getBufferStart(), Input.getBufferSize());
479 return Error::success();
483 // This class implements a list of temporary files that are removed upon
484 // object destruction.
485 class TempFileHandlerRAII {
486 public:
487 ~TempFileHandlerRAII() {
488 for (const auto &File : Files)
489 sys::fs::remove(File);
492 // Creates temporary file with given contents.
493 Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) {
494 SmallString<128u> File;
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);
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());
507 return Files.front().str();
510 private:
511 std::forward_list<SmallString<128u>> Files;
514 /// Handler for object files. The bundles are organized by sections with a
515 /// designated name.
517 /// To unbundle, we just copy the contents of the designated section.
518 class ObjectFileHandler final : public FileHandler {
520 /// The object file we are currently dealing with.
521 std::unique_ptr<ObjectFile> Obj;
523 /// Return the input file contents.
524 StringRef getInputFileContents() const { return Obj->getData(); }
526 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
527 /// section.
528 static Expected<std::optional<StringRef>>
529 IsOffloadSection(SectionRef CurSection) {
530 Expected<StringRef> NameOrErr = CurSection.getName();
531 if (!NameOrErr)
532 return NameOrErr.takeError();
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;
538 // Return the triple that is right after the reserved prefix.
539 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
542 /// Total number of inputs.
543 unsigned NumberOfInputs = 0;
545 /// Total number of processed inputs, i.e, inputs that were already
546 /// read from the buffers.
547 unsigned NumberOfProcessedInputs = 0;
549 /// Iterator of the current and next section.
550 section_iterator CurrentSection;
551 section_iterator NextSection;
553 /// Configuration options and arrays for this bundler job
554 const OffloadBundlerConfig &BundlerConfig;
556 public:
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) {}
563 ~ObjectFileHandler() final {}
565 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
567 Expected<std::optional<StringRef>>
568 ReadBundleStart(MemoryBuffer &Input) final {
569 while (NextSection != Obj->section_end()) {
570 CurrentSection = NextSection;
571 ++NextSection;
573 // Check if the current section name starts with the reserved prefix. If
574 // so, return the triple.
575 Expected<std::optional<StringRef>> TripleOrErr =
576 IsOffloadSection(*CurrentSection);
577 if (!TripleOrErr)
578 return TripleOrErr.takeError();
579 if (*TripleOrErr)
580 return **TripleOrErr;
582 return std::nullopt;
585 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
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;
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();
601 ModifiedContent = std::move(*HostBundleOrErr);
602 Content = ModifiedContent;
605 OS.write(Content.data(), Content.size());
606 return Error::success();
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.");
614 // Record number of inputs.
615 NumberOfInputs = Inputs.size();
616 return Error::success();
619 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
620 ++NumberOfProcessedInputs;
621 return Error::success();
624 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
625 return Error::success();
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.");
634 // If this is not the last output, we don't have to do anything.
635 if (NumberOfProcessedInputs != NumberOfInputs)
636 return Error::success();
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.
643 assert(BundlerConfig.ObjcopyPath != "" &&
644 "llvm-objcopy path not specified");
646 // Temporary files that need to be removed.
647 TempFileHandlerRAII TempFiles;
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"};
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;
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"));
675 ObjcopyArgs.push_back("--");
676 ObjcopyArgs.push_back(
677 BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
678 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
680 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
681 return Err;
683 return Error::success();
686 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
687 return Error::success();
690 private:
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");
704 return Error::success();
707 Expected<std::string> getHostBundle(StringRef Input) {
708 TempFileHandlerRAII TempFiles;
710 auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
711 if (!ModifiedObjPathOrErr)
712 return ModifiedObjPathOrErr.takeError();
713 StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
715 BumpPtrAllocator Alloc;
716 StringSaver SS{Alloc};
717 SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"};
719 ObjcopyArgs.push_back("--regex");
720 ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
721 ObjcopyArgs.push_back("--");
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();
739 ObjcopyArgs.push_back(ObjcopyInputFileName);
740 ObjcopyArgs.push_back(ModifiedObjPath);
742 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
743 return std::move(Err);
745 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
746 if (!BufOrErr)
747 return createStringError(BufOrErr.getError(),
748 "Failed to read back the modified object file");
750 return BufOrErr->get()->getBuffer().str();
754 /// Handler for text files. The bundled file will have the following format.
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"
763 class TextFileHandler final : public FileHandler {
764 /// String that begins a line comment.
765 StringRef Comment;
767 /// String that initiates a bundle.
768 std::string BundleStartString;
770 /// String that closes a bundle.
771 std::string BundleEndString;
773 /// Number of chars read from input.
774 size_t ReadChars = 0u;
776 protected:
777 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
779 Expected<std::optional<StringRef>>
780 ReadBundleStart(MemoryBuffer &Input) final {
781 StringRef FC = Input.getBuffer();
783 // Find start of the bundle.
784 ReadChars = FC.find(BundleStartString, ReadChars);
785 if (ReadChars == FC.npos)
786 return std::nullopt;
788 // Get position of the triple.
789 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
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;
796 // Next time we read after the new line.
797 ++ReadChars;
799 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
802 Error ReadBundleEnd(MemoryBuffer &Input) final {
803 StringRef FC = Input.getBuffer();
805 // Read up to the next new line.
806 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
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;
813 return Error::success();
816 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
817 StringRef FC = Input.getBuffer();
818 size_t BundleStart = ReadChars;
820 // Find end of the bundle.
821 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
823 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
824 OS << Bundle;
826 return Error::success();
829 Error WriteHeader(raw_ostream &OS,
830 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
831 return Error::success();
834 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
835 OS << BundleStartString << TargetTriple << "\n";
836 return Error::success();
839 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
840 OS << BundleEndString << TargetTriple << "\n";
841 return Error::success();
844 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
845 OS << Input.getBuffer();
846 return Error::success();
849 public:
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__ ";
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();
869 } // namespace
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.
874 static std::unique_ptr<FileHandler>
875 CreateObjectFileHandler(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);
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);
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);
892 /// Return an appropriate handler given the input files and options.
893 static Expected<std::unique_ptr<FileHandler>>
894 CreateFileHandler(MemoryBuffer &FirstInput,
895 const OffloadBundlerConfig &BundlerConfig) {
896 std::string FilesType = BundlerConfig.FilesType;
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);
925 return createStringError(errc::invalid_argument,
926 "'" + FilesType + "': invalid file type specified");
929 OffloadBundlerConfig::OffloadBundlerConfig() {
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.
934 CompressionLevel = 3;
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;
941 auto IgnoreEnvVarOpt =
942 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
943 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1")
944 return;
946 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
947 if (VerboseEnvVarOpt.has_value())
948 Verbose = VerboseEnvVarOpt.value() == "1";
950 auto CompressEnvVarOpt =
951 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
952 if (CompressEnvVarOpt.has_value())
953 Compress = CompressEnvVarOpt.value() == "1";
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";
969 // Utility function to format numbers with commas
970 static 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;
977 return Num;
980 llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
981 CompressedOffloadBundle::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");
989 llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
990 ClangOffloadBundlerTimerGroup);
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();
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());
1006 llvm::Timer CompressTimer("Compression Timer", "Compression time",
1007 ClangOffloadBundlerTimerGroup);
1008 if (Verbose)
1009 CompressTimer.startTimer();
1010 llvm::compression::compress(P, BufferUint8, CompressedBuffer);
1011 if (Verbose)
1012 CompressTimer.stopTimer();
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();
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());
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;
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";
1063 return llvm::MemoryBuffer::getMemBufferCopy(
1064 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1067 llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
1068 CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
1069 bool Verbose) {
1071 StringRef Blob = Input.getBuffer();
1073 if (Blob.size() < V1HeaderSize)
1074 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
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);
1083 size_t CurrentOffset = MagicSize;
1085 uint16_t ThisVersion;
1086 memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
1087 CurrentOffset += VersionFieldSize;
1089 uint16_t CompressionMethod;
1090 memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
1091 CurrentOffset += MethodFieldSize;
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;
1102 uint32_t UncompressedSize;
1103 memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
1104 CurrentOffset += UncompressedSizeFieldSize;
1106 uint64_t StoredHash;
1107 memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
1108 CurrentOffset += HashFieldSize;
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");
1121 llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
1122 ClangOffloadBundlerTimerGroup);
1123 if (Verbose)
1124 DecompressTimer.startTimer();
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)));
1135 if (Verbose) {
1136 DecompressTimer.stopTimer();
1138 double DecompressionTimeSeconds =
1139 DecompressTimer.getTotalTime().getWallTime();
1141 // Recalculate MD5 hash for integrity check
1142 llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
1143 "Hash recalculation time",
1144 ClangOffloadBundlerTimerGroup);
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);
1155 double CompressionRate =
1156 static_cast<double>(UncompressedSize) / CompressedData.size();
1157 double DecompressionSpeedMBs =
1158 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
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";
1185 return llvm::MemoryBuffer::getMemBufferCopy(
1186 llvm::toStringRef(DecompressedData));
1189 // List bundle IDs. Return true if an error was found.
1190 Error OffloadBundler::ListBundleIDsInFile(
1191 StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
1192 // Open Input file.
1193 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1194 MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
1195 if (std::error_code EC = CodeOrErr.getError())
1196 return createFileError(InputFileName, EC);
1198 // Decompress the input if necessary.
1199 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1200 CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
1201 if (!DecompressedBufferOrErr)
1202 return createStringError(
1203 inconvertibleErrorCode(),
1204 "Failed to decompress input: " +
1205 llvm::toString(DecompressedBufferOrErr.takeError()));
1207 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1209 // Select the right files handler.
1210 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1211 CreateFileHandler(DecompressedInput, BundlerConfig);
1212 if (!FileHandlerOrErr)
1213 return FileHandlerOrErr.takeError();
1215 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1216 assert(FH);
1217 return FH->listBundleIDs(DecompressedInput);
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
1223 bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo,
1224 const OffloadTargetInfo &TargetInfo) {
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;
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;
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);
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;
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;
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;
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;
1313 /// Bundle the files. Return true if an error was found.
1314 Error OffloadBundler::BundleFiles() {
1315 std::error_code EC;
1317 // Create a buffer to hold the content before compressing.
1318 SmallVector<char, 0> Buffer;
1319 llvm::raw_svector_ostream BufferStream(Buffer);
1321 // Open input files.
1322 SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers;
1323 InputBuffers.reserve(BundlerConfig.InputFileNames.size());
1324 for (auto &I : BundlerConfig.InputFileNames) {
1325 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1326 MemoryBuffer::getFileOrSTDIN(I, /*IsText=*/true);
1327 if (std::error_code EC = CodeOrErr.getError())
1328 return createFileError(I, EC);
1329 InputBuffers.emplace_back(std::move(*CodeOrErr));
1332 // Get the file handler. We use the host buffer as reference.
1333 assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) &&
1334 "Host input index undefined??");
1335 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler(
1336 *InputBuffers[BundlerConfig.AllowNoHost ? 0
1337 : BundlerConfig.HostInputIndex],
1338 BundlerConfig);
1339 if (!FileHandlerOrErr)
1340 return FileHandlerOrErr.takeError();
1342 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1343 assert(FH);
1345 // Write header.
1346 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1347 return Err;
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;
1362 raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
1363 sys::fs::OF_None);
1364 if (EC)
1365 return createFileError(BundlerConfig.OutputFileNames.front(), EC);
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(
1373 {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel,
1374 /*zstdEnableLdm=*/true},
1375 *BufferMemory, BundlerConfig.Verbose);
1376 if (auto Error = CompressionResult.takeError())
1377 return Error;
1379 auto CompressedMemBuffer = std::move(CompressionResult.get());
1380 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1381 CompressedMemBuffer->getBufferEnd());
1382 } else
1383 CompressedBuffer = Buffer;
1385 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1387 return FH->finalizeOutputFile();
1390 // Unbundle the files. Return true if an error was found.
1391 Error OffloadBundler::UnbundleFiles() {
1392 // Open Input file.
1393 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1394 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front(),
1395 /*IsText=*/true);
1396 if (std::error_code EC = CodeOrErr.getError())
1397 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1399 // Decompress the input if necessary.
1400 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1401 CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
1402 if (!DecompressedBufferOrErr)
1403 return createStringError(
1404 inconvertibleErrorCode(),
1405 "Failed to decompress input: " +
1406 llvm::toString(DecompressedBufferOrErr.takeError()));
1408 MemoryBuffer &Input = **DecompressedBufferOrErr;
1410 // Select the right files handler.
1411 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1412 CreateFileHandler(Input, BundlerConfig);
1413 if (!FileHandlerOrErr)
1414 return FileHandlerOrErr.takeError();
1416 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1417 assert(FH);
1419 // Read the header of the bundled file.
1420 if (Error Err = FH->ReadHeader(Input))
1421 return Err;
1423 // Create a work list that consist of the map triple/output file.
1424 StringMap<StringRef> Worklist;
1425 auto Output = BundlerConfig.OutputFileNames.begin();
1426 for (auto &Triple : BundlerConfig.TargetNames) {
1427 Worklist[Triple] = *Output;
1428 ++Output;
1431 // Read all the bundles that are in the work list. If we find no bundles we
1432 // assume the file is meant for the host target.
1433 bool FoundHostBundle = false;
1434 while (!Worklist.empty()) {
1435 Expected<std::optional<StringRef>> CurTripleOrErr =
1436 FH->ReadBundleStart(Input);
1437 if (!CurTripleOrErr)
1438 return CurTripleOrErr.takeError();
1440 // We don't have more bundles.
1441 if (!*CurTripleOrErr)
1442 break;
1444 StringRef CurTriple = **CurTripleOrErr;
1445 assert(!CurTriple.empty());
1447 auto Output = Worklist.begin();
1448 for (auto E = Worklist.end(); Output != E; Output++) {
1449 if (isCodeObjectCompatible(
1450 OffloadTargetInfo(CurTriple, BundlerConfig),
1451 OffloadTargetInfo((*Output).first(), BundlerConfig))) {
1452 break;
1456 if (Output == Worklist.end())
1457 continue;
1458 // Check if the output file can be opened and copy the bundle to it.
1459 std::error_code EC;
1460 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1461 if (EC)
1462 return createFileError((*Output).second, EC);
1463 if (Error Err = FH->ReadBundle(OutputFile, Input))
1464 return Err;
1465 if (Error Err = FH->ReadBundleEnd(Input))
1466 return Err;
1467 Worklist.erase(Output);
1469 // Record if we found the host bundle.
1470 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
1471 if (OffloadInfo.hasHostKind())
1472 FoundHostBundle = true;
1475 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
1476 std::string ErrMsg = "Can't find bundles for";
1477 std::set<StringRef> Sorted;
1478 for (auto &E : Worklist)
1479 Sorted.insert(E.first());
1480 unsigned I = 0;
1481 unsigned Last = Sorted.size() - 1;
1482 for (auto &E : Sorted) {
1483 if (I != 0 && Last > 1)
1484 ErrMsg += ",";
1485 ErrMsg += " ";
1486 if (I == Last && I != 0)
1487 ErrMsg += "and ";
1488 ErrMsg += E.str();
1489 ++I;
1491 return createStringError(inconvertibleErrorCode(), ErrMsg);
1494 // If no bundles were found, assume the input file is the host bundle and
1495 // create empty files for the remaining targets.
1496 if (Worklist.size() == BundlerConfig.TargetNames.size()) {
1497 for (auto &E : Worklist) {
1498 std::error_code EC;
1499 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1500 if (EC)
1501 return createFileError(E.second, EC);
1503 // If this entry has a host kind, copy the input file to the output file.
1504 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
1505 if (OffloadInfo.hasHostKind())
1506 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1508 return Error::success();
1511 // If we found elements, we emit an error if none of those were for the host
1512 // in case host bundle name was provided in command line.
1513 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
1514 BundlerConfig.AllowMissingBundles))
1515 return createStringError(inconvertibleErrorCode(),
1516 "Can't find bundle for the host target");
1518 // If we still have any elements in the worklist, create empty files for them.
1519 for (auto &E : Worklist) {
1520 std::error_code EC;
1521 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1522 if (EC)
1523 return createFileError(E.second, EC);
1526 return Error::success();
1529 static Archive::Kind getDefaultArchiveKindForHost() {
1530 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1531 : Archive::K_GNU;
1534 /// @brief Computes a list of targets among all given targets which are
1535 /// compatible with this code object
1536 /// @param [in] CodeObjectInfo Code Object
1537 /// @param [out] CompatibleTargets List of all compatible targets among all
1538 /// given targets
1539 /// @return false, if no compatible target is found.
1540 static bool
1541 getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo,
1542 SmallVectorImpl<StringRef> &CompatibleTargets,
1543 const OffloadBundlerConfig &BundlerConfig) {
1544 if (!CompatibleTargets.empty()) {
1545 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1546 dbgs() << "CompatibleTargets list should be empty\n");
1547 return false;
1549 for (auto &Target : BundlerConfig.TargetNames) {
1550 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
1551 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
1552 CompatibleTargets.push_back(Target);
1554 return !CompatibleTargets.empty();
1557 // Check that each code object file in the input archive conforms to following
1558 // rule: for a specific processor, a feature either shows up in all target IDs,
1559 // or does not show up in any target IDs. Otherwise the target ID combination is
1560 // invalid.
1561 static Error
1562 CheckHeterogeneousArchive(StringRef ArchiveName,
1563 const OffloadBundlerConfig &BundlerConfig) {
1564 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1565 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1566 MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
1567 if (std::error_code EC = BufOrErr.getError())
1568 return createFileError(ArchiveName, EC);
1570 ArchiveBuffers.push_back(std::move(*BufOrErr));
1571 Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
1572 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1573 if (!LibOrErr)
1574 return LibOrErr.takeError();
1576 auto Archive = std::move(*LibOrErr);
1578 Error ArchiveErr = Error::success();
1579 auto ChildEnd = Archive->child_end();
1581 /// Iterate over all bundled code object files in the input archive.
1582 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1583 ArchiveIter != ChildEnd; ++ArchiveIter) {
1584 if (ArchiveErr)
1585 return ArchiveErr;
1586 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1587 if (!ArchiveChildNameOrErr)
1588 return ArchiveChildNameOrErr.takeError();
1590 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1591 if (!CodeObjectBufferRefOrErr)
1592 return CodeObjectBufferRefOrErr.takeError();
1594 auto CodeObjectBuffer =
1595 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1597 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1598 CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
1599 if (!FileHandlerOrErr)
1600 return FileHandlerOrErr.takeError();
1602 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1603 assert(FileHandler);
1605 std::set<StringRef> BundleIds;
1606 auto CodeObjectFileError =
1607 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1608 if (CodeObjectFileError)
1609 return CodeObjectFileError;
1611 auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds);
1612 if (ConflictingArchs) {
1613 std::string ErrMsg =
1614 Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
1615 ", " + ConflictingArchs.value().second + "] found in " +
1616 ArchiveChildNameOrErr.get() + " of " + ArchiveName)
1617 .str();
1618 return createStringError(inconvertibleErrorCode(), ErrMsg);
1622 return ArchiveErr;
1625 /// UnbundleArchive takes an archive file (".a") as input containing bundled
1626 /// code object files, and a list of offload targets (not host), and extracts
1627 /// the code objects into a new archive file for each offload target. Each
1628 /// resulting archive file contains all code object files corresponding to that
1629 /// particular offload target. The created archive file does not
1630 /// contain an index of the symbols and code object files are named as
1631 /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
1632 Error OffloadBundler::UnbundleArchive() {
1633 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1635 /// Map of target names with list of object files that will form the device
1636 /// specific archive for that target
1637 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1639 // Map of target names and output archive filenames
1640 StringMap<StringRef> TargetOutputFileNameMap;
1642 auto Output = BundlerConfig.OutputFileNames.begin();
1643 for (auto &Target : BundlerConfig.TargetNames) {
1644 TargetOutputFileNameMap[Target] = *Output;
1645 ++Output;
1648 StringRef IFName = BundlerConfig.InputFileNames.front();
1650 if (BundlerConfig.CheckInputArchive) {
1651 // For a specific processor, a feature either shows up in all target IDs, or
1652 // does not show up in any target IDs. Otherwise the target ID combination
1653 // is invalid.
1654 auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig);
1655 if (ArchiveError) {
1656 return ArchiveError;
1660 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1661 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1662 if (std::error_code EC = BufOrErr.getError())
1663 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1665 ArchiveBuffers.push_back(std::move(*BufOrErr));
1666 Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
1667 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1668 if (!LibOrErr)
1669 return LibOrErr.takeError();
1671 auto Archive = std::move(*LibOrErr);
1673 Error ArchiveErr = Error::success();
1674 auto ChildEnd = Archive->child_end();
1676 /// Iterate over all bundled code object files in the input archive.
1677 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1678 ArchiveIter != ChildEnd; ++ArchiveIter) {
1679 if (ArchiveErr)
1680 return ArchiveErr;
1681 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1682 if (!ArchiveChildNameOrErr)
1683 return ArchiveChildNameOrErr.takeError();
1685 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1687 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1688 if (!CodeObjectBufferRefOrErr)
1689 return CodeObjectBufferRefOrErr.takeError();
1691 auto TempCodeObjectBuffer =
1692 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1694 // Decompress the buffer if necessary.
1695 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1696 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer,
1697 BundlerConfig.Verbose);
1698 if (!DecompressedBufferOrErr)
1699 return createStringError(
1700 inconvertibleErrorCode(),
1701 "Failed to decompress code object: " +
1702 llvm::toString(DecompressedBufferOrErr.takeError()));
1704 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1706 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1707 CreateFileHandler(CodeObjectBuffer, BundlerConfig);
1708 if (!FileHandlerOrErr)
1709 return FileHandlerOrErr.takeError();
1711 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1712 assert(FileHandler &&
1713 "FileHandle creation failed for file in the archive!");
1715 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1716 return ReadErr;
1718 Expected<std::optional<StringRef>> CurBundleIDOrErr =
1719 FileHandler->ReadBundleStart(CodeObjectBuffer);
1720 if (!CurBundleIDOrErr)
1721 return CurBundleIDOrErr.takeError();
1723 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1724 // No device code in this child, skip.
1725 if (!OptionalCurBundleID)
1726 continue;
1727 StringRef CodeObject = *OptionalCurBundleID;
1729 // Process all bundle entries (CodeObjects) found in this child of input
1730 // archive.
1731 while (!CodeObject.empty()) {
1732 SmallVector<StringRef> CompatibleTargets;
1733 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
1734 if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
1735 BundlerConfig)) {
1736 std::string BundleData;
1737 raw_string_ostream DataStream(BundleData);
1738 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1739 return Err;
1741 for (auto &CompatibleTarget : CompatibleTargets) {
1742 SmallString<128> BundledObjectFileName;
1743 BundledObjectFileName.assign(BundledObjectFile);
1744 auto OutputBundleName =
1745 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1746 CodeObject +
1747 getDeviceLibraryFileName(BundledObjectFileName,
1748 CodeObjectInfo.TargetID))
1749 .str();
1750 // Replace ':' in optional target feature list with '_' to ensure
1751 // cross-platform validity.
1752 std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
1753 '_');
1755 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1756 DataStream.str(), OutputBundleName);
1757 ArchiveBuffers.push_back(std::move(MemBuf));
1758 llvm::MemoryBufferRef MemBufRef =
1759 MemoryBufferRef(*(ArchiveBuffers.back()));
1761 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1762 // OutputArchivesMap.
1763 OutputArchivesMap[CompatibleTarget].push_back(
1764 NewArchiveMember(MemBufRef));
1768 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1769 return Err;
1771 Expected<std::optional<StringRef>> NextTripleOrErr =
1772 FileHandler->ReadBundleStart(CodeObjectBuffer);
1773 if (!NextTripleOrErr)
1774 return NextTripleOrErr.takeError();
1776 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
1777 } // End of processing of all bundle entries of this child of input archive.
1778 } // End of while over children of input archive.
1780 assert(!ArchiveErr && "Error occurred while reading archive!");
1782 /// Write out an archive for each target
1783 for (auto &Target : BundlerConfig.TargetNames) {
1784 StringRef FileName = TargetOutputFileNameMap[Target];
1785 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1786 OutputArchivesMap.find(Target);
1787 if (CurArchiveMembers != OutputArchivesMap.end()) {
1788 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1789 SymtabWritingMode::NormalSymtab,
1790 getDefaultArchiveKindForHost(), true,
1791 false, nullptr))
1792 return WriteErr;
1793 } else if (!BundlerConfig.AllowMissingBundles) {
1794 std::string ErrMsg =
1795 Twine("no compatible code object found for the target '" + Target +
1796 "' in heterogeneous archive library: " + IFName)
1797 .str();
1798 return createStringError(inconvertibleErrorCode(), ErrMsg);
1799 } else { // Create an empty archive file if no compatible code object is
1800 // found and "allow-missing-bundles" is enabled. It ensures that
1801 // the linker using output of this step doesn't complain about
1802 // the missing input file.
1803 std::vector<llvm::NewArchiveMember> EmptyArchive;
1804 EmptyArchive.clear();
1805 if (Error WriteErr = writeArchive(
1806 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1807 getDefaultArchiveKindForHost(), true, false, nullptr))
1808 return WriteErr;
1812 return Error::success();