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