clang  19.0.0git
OffloadBundler.cpp
Go to the documentation of this file.
1 //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file implements an offload bundling API that bundles different files
11 /// that relate with the same source code but different targets into a single
12 /// one. Also the implements the opposite functionality, i.e. unbundle files
13 /// previous created by this API.
14 ///
15 //===----------------------------------------------------------------------===//
16 
18 #include "clang/Basic/Cuda.h"
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/Bitcode/BitcodeWriter.h"
28 #include "llvm/IR/Constants.h"
29 #include "llvm/IR/LLVMContext.h"
30 #include "llvm/IRReader/IRReader.h"
31 #include "llvm/BinaryFormat/Magic.h"
32 #include "llvm/Object/Archive.h"
33 #include "llvm/Object/ArchiveWriter.h"
34 #include "llvm/Object/Binary.h"
35 #include "llvm/Object/ObjectFile.h"
36 #include "llvm/Support/Casting.h"
37 #include "llvm/Support/Compression.h"
38 #include "llvm/Support/Debug.h"
39 #include "llvm/Support/EndianStream.h"
40 #include "llvm/Support/Errc.h"
41 #include "llvm/Support/Error.h"
42 #include "llvm/Support/ErrorOr.h"
43 #include "llvm/Support/FileSystem.h"
44 #include "llvm/Support/MD5.h"
45 #include "llvm/Support/MemoryBuffer.h"
46 #include "llvm/Support/Path.h"
47 #include "llvm/Support/Program.h"
48 #include "llvm/Support/Signals.h"
49 #include "llvm/Support/StringSaver.h"
50 #include "llvm/Support/TargetSelect.h"
51 #include "llvm/Support/Timer.h"
52 #include "llvm/Support/WithColor.h"
53 #include "llvm/Support/raw_ostream.h"
54 #include "llvm/Support/SourceMgr.h"
55 #include "llvm/TargetParser/Host.h"
56 #include "llvm/TargetParser/Triple.h"
57 #include <algorithm>
58 #include <cassert>
59 #include <cstddef>
60 #include <cstdint>
61 #include <forward_list>
62 #include <llvm/Support/Process.h>
63 #include <memory>
64 #include <set>
65 #include <string>
66 #include <system_error>
67 #include <unordered_set>
68 #include <utility>
69 
70 using namespace llvm;
71 using namespace llvm::object;
72 using namespace clang;
73 
74 static llvm::TimerGroup
75  ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group",
76  "Timer group for clang offload bundler");
77 
78 /// Magic string that marks the existence of offloading data.
79 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
80 
81 /// Section name which holds target symbol names.
82 #define SYMBOLS_SECTION_NAME ".tgtsym"
83 
84 #define DEBUG_TYPE "clang-offload-bundler"
85 
86 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
87  const OffloadBundlerConfig &BC)
88  : BundlerConfig(BC) {
89 
90  // TODO: Add error checking from ClangOffloadBundler.cpp
91  auto TargetFeatures = Target.split(':');
92  auto TripleOrGPU = TargetFeatures.first.rsplit('-');
93 
94  if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) {
95  auto KindTriple = TripleOrGPU.first.split('-');
96  this->OffloadKind = KindTriple.first;
97  this->Triple = llvm::Triple(KindTriple.second);
98  this->TargetID = Target.substr(Target.find(TripleOrGPU.second));
99  } else {
100  auto KindTriple = TargetFeatures.first.split('-');
101  this->OffloadKind = KindTriple.first;
102  this->Triple = llvm::Triple(KindTriple.second);
103  this->TargetID = "";
104  }
105 }
106 
108  return this->OffloadKind == "host";
109 }
110 
112  return OffloadKind == "host" || OffloadKind == "openmp" ||
113  OffloadKind == "sycl" || OffloadKind == "fpga" ||
114  OffloadKind == "hip" || OffloadKind == "hipv4";
115 }
116 
118  const StringRef TargetOffloadKind) const {
119  if ((OffloadKind == TargetOffloadKind) ||
120  (OffloadKind == "hip" && TargetOffloadKind == "hipv4") ||
121  (OffloadKind == "hipv4" && TargetOffloadKind == "hip"))
122  return true;
123 
125  bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") &&
126  TargetOffloadKind == "openmp";
127  bool OpenMPCompatibleWithHIP =
128  OffloadKind == "openmp" &&
129  TargetOffloadKind.starts_with_insensitive("hip");
130  return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
131  }
132  return false;
133 }
134 
136  return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
137 }
138 
140  return OffloadKind == Target.OffloadKind &&
141  Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID;
142 }
143 
144 std::string OffloadTargetInfo::str() const {
145  return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str();
146 }
147 
148 static Triple getTargetTriple(StringRef Target,
149  const OffloadBundlerConfig &BC) {
150  auto OffloadInfo = OffloadTargetInfo(Target, BC);
151  return Triple(OffloadInfo.getTriple());
152 }
153 
154 static StringRef getDeviceFileExtension(StringRef Device,
155  StringRef BundleFileName) {
156  if (Device.contains("gfx"))
157  return ".bc";
158  if (Device.contains("sm_"))
159  return ".cubin";
160  return sys::path::extension(BundleFileName);
161 }
162 
163 static std::string getDeviceLibraryFileName(StringRef BundleFileName,
164  StringRef Device) {
165  StringRef LibName = sys::path::stem(BundleFileName);
166  StringRef Extension = getDeviceFileExtension(Device, BundleFileName);
167 
168  std::string Result;
169  Result += LibName;
170  Result += Extension;
171  return Result;
172 }
173 
174 namespace {
175 /// Generic file handler interface.
176 class FileHandler {
177 public:
178  struct BundleInfo {
179  StringRef BundleID;
180  };
181 
182  FileHandler() {}
183 
184  virtual ~FileHandler() {}
185 
186  /// Update the file handler with information from the header of the bundled
187  /// file.
188  virtual Error ReadHeader(MemoryBuffer &Input) = 0;
189 
190  /// Read the marker of the next bundled to be read in the file. The bundle
191  /// name is returned if there is one in the file, or `std::nullopt` if there
192  /// are no more bundles to be read.
194  ReadBundleStart(MemoryBuffer &Input) = 0;
195 
196  /// Read the marker that closes the current bundle.
197  virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
198 
199  /// Read the current bundle and write the result into the stream \a OS.
200  virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
201 
202  /// Write the header of the bundled file to \a OS based on the information
203  /// gathered from \a Inputs.
204  virtual Error WriteHeader(raw_ostream &OS,
205  ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
206 
207  /// Write the marker that initiates a bundle for the triple \a TargetTriple to
208  /// \a OS.
209  virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0;
210 
211  /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
212  /// OS.
213  virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0;
214 
215  /// Write the bundle from \a Input into \a OS.
216  virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
217 
218  /// Finalize output file.
219  virtual Error finalizeOutputFile() { return Error::success(); }
220 
221  /// Sets a base name for temporary filename generation.
222  void SetTempFileNameBase(StringRef Base) {
223  TempFileNameBase = std::string(Base);
224  }
225 
226  /// List bundle IDs in \a Input.
227  virtual Error listBundleIDs(MemoryBuffer &Input) {
228  if (Error Err = ReadHeader(Input))
229  return Err;
230  return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
231  llvm::outs() << Info.BundleID << '\n';
232  Error Err = listBundleIDsCallback(Input, Info);
233  if (Err)
234  return Err;
235  return Error::success();
236  });
237  }
238 
239  /// Get bundle IDs in \a Input in \a BundleIds.
240  virtual Error getBundleIDs(MemoryBuffer &Input,
241  std::set<StringRef> &BundleIds) {
242  if (Error Err = ReadHeader(Input))
243  return Err;
244  return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
245  BundleIds.insert(Info.BundleID);
246  Error Err = listBundleIDsCallback(Input, Info);
247  if (Err)
248  return Err;
249  return Error::success();
250  });
251  }
252 
253  /// For each bundle in \a Input, do \a Func.
254  Error forEachBundle(MemoryBuffer &Input,
255  std::function<Error(const BundleInfo &)> Func) {
256  while (true) {
257  Expected<std::optional<StringRef>> CurTripleOrErr =
258  ReadBundleStart(Input);
259  if (!CurTripleOrErr)
260  return CurTripleOrErr.takeError();
261 
262  // No more bundles.
263  if (!*CurTripleOrErr)
264  break;
265 
266  StringRef CurTriple = **CurTripleOrErr;
267  assert(!CurTriple.empty());
268 
269  BundleInfo Info{CurTriple};
270  if (Error Err = Func(Info))
271  return Err;
272  }
273  return Error::success();
274  }
275 
276 protected:
277  /// Serves as a base name for temporary filename generation.
278  std::string TempFileNameBase;
279 
280  virtual Error listBundleIDsCallback(MemoryBuffer &Input,
281  const BundleInfo &Info) {
282  return Error::success();
283  }
284 };
285 
286 /// Handler for binary files. The bundled file will have the following format
287 /// (all integers are stored in little-endian format):
288 ///
289 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
290 ///
291 /// NumberOfOffloadBundles (8-byte integer)
292 ///
293 /// OffsetOfBundle1 (8-byte integer)
294 /// SizeOfBundle1 (8-byte integer)
295 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
296 /// TripleOfBundle1 (byte length defined before)
297 ///
298 /// ...
299 ///
300 /// OffsetOfBundleN (8-byte integer)
301 /// SizeOfBundleN (8-byte integer)
302 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
303 /// TripleOfBundleN (byte length defined before)
304 ///
305 /// Bundle1
306 /// ...
307 /// BundleN
308 
309 /// Read 8-byte integers from a buffer in little-endian format.
310 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
311  return llvm::support::endian::read64le(Buffer.data() + pos);
312 }
313 
314 /// Write 8-byte integers to a buffer in little-endian format.
315 static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
316  llvm::support::endian::write(OS, Val, llvm::endianness::little);
317 }
318 
319 class BinaryFileHandler final : public FileHandler {
320  /// Information about the bundles extracted from the header.
321  struct BinaryBundleInfo final : public BundleInfo {
322  /// Size of the bundle.
323  uint64_t Size = 0u;
324  /// Offset at which the bundle starts in the bundled file.
325  uint64_t Offset = 0u;
326 
327  BinaryBundleInfo() {}
328  BinaryBundleInfo(uint64_t Size, uint64_t Offset)
329  : Size(Size), Offset(Offset) {}
330  };
331 
332  /// Map between a triple and the corresponding bundle information.
333  StringMap<BinaryBundleInfo> BundlesInfo;
334 
335  /// Iterator for the bundle information that is being read.
336  StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
337  StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
338 
339  /// Current bundle target to be written.
340  std::string CurWriteBundleTarget;
341 
342  /// Configuration options and arrays for this bundler job
343  const OffloadBundlerConfig &BundlerConfig;
344 
345 public:
346  // TODO: Add error checking from ClangOffloadBundler.cpp
347  BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
348 
349  ~BinaryFileHandler() final {}
350 
351  Error ReadHeader(MemoryBuffer &Input) final {
352  StringRef FC = Input.getBuffer();
353 
354  // Initialize the current bundle with the end of the container.
355  CurBundleInfo = BundlesInfo.end();
356 
357  // Check if buffer is smaller than magic string.
358  size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
359  if (ReadChars > FC.size())
360  return Error::success();
361 
362  // Check if no magic was found.
363  if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
364  return Error::success();
365 
366  // Read number of bundles.
367  if (ReadChars + 8 > FC.size())
368  return Error::success();
369 
370  uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
371  ReadChars += 8;
372 
373  // Read bundle offsets, sizes and triples.
374  for (uint64_t i = 0; i < NumberOfBundles; ++i) {
375 
376  // Read offset.
377  if (ReadChars + 8 > FC.size())
378  return Error::success();
379 
380  uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
381  ReadChars += 8;
382 
383  // Read size.
384  if (ReadChars + 8 > FC.size())
385  return Error::success();
386 
387  uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
388  ReadChars += 8;
389 
390  // Read triple size.
391  if (ReadChars + 8 > FC.size())
392  return Error::success();
393 
394  uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
395  ReadChars += 8;
396 
397  // Read triple.
398  if (ReadChars + TripleSize > FC.size())
399  return Error::success();
400 
401  StringRef Triple(&FC.data()[ReadChars], TripleSize);
402  ReadChars += TripleSize;
403 
404  // Check if the offset and size make sense.
405  if (!Offset || Offset + Size > FC.size())
406  return Error::success();
407 
408  assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??");
409  BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
410  }
411  // Set the iterator to where we will start to read.
412  CurBundleInfo = BundlesInfo.end();
413  NextBundleInfo = BundlesInfo.begin();
414  return Error::success();
415  }
416 
418  ReadBundleStart(MemoryBuffer &Input) final {
419  if (NextBundleInfo == BundlesInfo.end())
420  return std::nullopt;
421  CurBundleInfo = NextBundleInfo++;
422  return CurBundleInfo->first();
423  }
424 
425  Error ReadBundleEnd(MemoryBuffer &Input) final {
426  assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
427  return Error::success();
428  }
429 
430  Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
431  assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
432  StringRef FC = Input.getBuffer();
433  OS.write(FC.data() + CurBundleInfo->second.Offset,
434  CurBundleInfo->second.Size);
435  return Error::success();
436  }
437 
438  Error WriteHeader(raw_ostream &OS,
439  ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
440 
441  // Compute size of the header.
442  uint64_t HeaderSize = 0;
443 
444  HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
445  HeaderSize += 8; // Number of Bundles
446 
447  for (auto &T : BundlerConfig.TargetNames) {
448  HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
449  HeaderSize += T.size(); // The triple.
450  }
451 
452  // Write to the buffer the header.
454 
455  Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
456 
457  unsigned Idx = 0;
458  for (auto &T : BundlerConfig.TargetNames) {
459  MemoryBuffer &MB = *Inputs[Idx++];
460  HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
461  // Bundle offset.
462  Write8byteIntegerToBuffer(OS, HeaderSize);
463  // Size of the bundle (adds to the next bundle's offset)
464  Write8byteIntegerToBuffer(OS, MB.getBufferSize());
465  BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
466  HeaderSize += MB.getBufferSize();
467  // Size of the triple
468  Write8byteIntegerToBuffer(OS, T.size());
469  // Triple
470  OS << T;
471  }
472  return Error::success();
473  }
474 
475  Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
476  CurWriteBundleTarget = TargetTriple.str();
477  return Error::success();
478  }
479 
480  Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
481  return Error::success();
482  }
483 
484  Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
485  auto BI = BundlesInfo[CurWriteBundleTarget];
486 
487  // Pad with 0 to reach specified offset.
488  size_t CurrentPos = OS.tell();
489  size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0;
490  for (size_t I = 0; I < PaddingSize; ++I)
491  OS.write('\0');
492  assert(OS.tell() == BI.Offset);
493 
494  OS.write(Input.getBufferStart(), Input.getBufferSize());
495 
496  return Error::success();
497  }
498 };
499 
500 // This class implements a list of temporary files that are removed upon
501 // object destruction.
502 class TempFileHandlerRAII {
503 public:
504  ~TempFileHandlerRAII() {
505  for (const auto &File : Files)
506  sys::fs::remove(File);
507  }
508 
509  // Creates temporary file with given contents.
510  Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) {
512  if (std::error_code EC =
513  sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
514  return createFileError(File, EC);
515  Files.push_front(File);
516 
517  if (Contents) {
518  std::error_code EC;
519  raw_fd_ostream OS(File, EC);
520  if (EC)
521  return createFileError(File, EC);
522  OS.write(Contents->data(), Contents->size());
523  }
524  return Files.front().str();
525  }
526 
527 private:
528  std::forward_list<SmallString<128u>> Files;
529 };
530 
531 /// Handler for object files. The bundles are organized by sections with a
532 /// designated name.
533 ///
534 /// To unbundle, we just copy the contents of the designated section.
535 ///
536 /// The bundler produces object file in host target native format (e.g. ELF for
537 /// Linux). The sections it creates are:
538 ///
539 /// <OFFLOAD_BUNDLER_MAGIC_STR><target triple 1>
540 /// |
541 /// | binary data for the <target 1>'s bundle
542 /// |
543 /// ...
544 /// <OFFLOAD_BUNDLER_MAGIC_STR><target triple N>
545 /// |
546 /// | binary data for the <target N>'s bundle
547 /// |
548 /// ...
549 /// <OFFLOAD_BUNDLER_MAGIC_STR><host target>
550 /// | 0 (1 byte long)
551 /// ...
552 ///
553 /// The alignment of all the added sections is set to one to avoid padding
554 /// between concatenated parts.
555 ///
556 class ObjectFileHandler final : public FileHandler {
557 
558  /// The object file we are currently dealing with.
559  std::unique_ptr<ObjectFile> Obj;
560 
561  /// Return the input file contents.
562  StringRef getInputFileContents() const { return Obj->getData(); }
563 
564  /// Return bundle name (<kind>-<triple>) if the provided section is an offload
565  /// section.
567  IsOffloadSection(SectionRef CurSection) {
568  Expected<StringRef> NameOrErr = CurSection.getName();
569  if (!NameOrErr)
570  return NameOrErr.takeError();
571 
572  // If it does not start with the reserved suffix, just skip this section.
573  if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle)
574  return std::nullopt;
575 
576  // Return the triple that is right after the reserved prefix.
577  return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
578  }
579 
580  /// Total number of inputs.
581  unsigned NumberOfInputs = 0;
582 
583  /// Total number of processed inputs, i.e, inputs that were already
584  /// read from the buffers.
585  unsigned NumberOfProcessedInputs = 0;
586 
587  /// Iterator of the current and next section.
588  section_iterator CurrentSection;
589  section_iterator NextSection;
590 
591  /// Configuration options and arrays for this bundler job
592  const OffloadBundlerConfig &BundlerConfig;
593 
594  // Return a buffer with symbol names that are defined in target objects.
595  // Each symbol name is prefixed by a target name <kind>-<triple> to uniquely
596  // identify the target it belongs to, and symbol names are separated from each
597  // other by '\0' characters.
598  Expected<SmallVector<char, 0>> makeTargetSymbolTable() {
599  SmallVector<char, 0> SymbolsBuf;
600  raw_svector_ostream SymbolsOS(SymbolsBuf);
601  LLVMContext Context;
602 
603  for (unsigned I = 0; I < NumberOfInputs; ++I) {
604  if (I == BundlerConfig.HostInputIndex)
605  continue;
606 
607  // Get the list of symbols defined in the target object. Open file and
608  // check if it is a symbolic file.
609  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
610  MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames[I]);
611  if (!BufOrErr)
612  return createFileError(BundlerConfig.InputFileNames[I], BufOrErr.getError());
613 
614  std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
615 
616  // Workaround for the absence of assembly parser for spir target. If this
617  // input is a bitcode for spir target we need to remove module-level
618  // inline asm from it, if there is one, and recreate the buffer with new
619  // contents.
620  // TODO: remove this workaround once spir/spirv target gets asm parser.
621  if (isBitcode((const unsigned char *)Buf->getBufferStart(),
622  (const unsigned char *)Buf->getBufferEnd()))
623  if (getTargetTriple(BundlerConfig.TargetNames[I], BundlerConfig)
624  .isSPIROrSPIRV()) {
625  SMDiagnostic Err;
626  std::unique_ptr<Module> Mod = parseIR(*Buf, Err, Context);
627  if (!Mod)
628  return createStringError(inconvertibleErrorCode(),
629  Err.getMessage());
630 
631  bool UpdateBuf = false;
632  if (!Mod->getModuleInlineAsm().empty()) {
633  Mod->setModuleInlineAsm("");
634  UpdateBuf = true;
635  }
636  for (auto I = Mod->global_begin(), E = Mod->global_end(); I != E;) {
637  GlobalVariable &GV = *I++;
638  // Do not add globals with constant address space to the tgtsym.
639  if (!GV.isDeclaration() && !GV.hasLocalLinkage() &&
640  GV.getAddressSpace() == 2) {
641  GV.replaceAllUsesWith(UndefValue::get(GV.getType()));
642  GV.dropAllReferences();
643  GV.eraseFromParent();
644  UpdateBuf = true;
645  }
646  }
647  if (UpdateBuf) {
648  SmallVector<char, 0> ModuleBuf;
649  raw_svector_ostream ModuleOS(ModuleBuf);
650  WriteBitcodeToFile(*Mod, ModuleOS);
651 
652  Buf = MemoryBuffer::getMemBufferCopy(ModuleOS.str(),
653  Buf->getBufferIdentifier());
654  }
655  }
656 
658  createBinary(Buf->getMemBufferRef(), &Context);
659 
660  // If it is not a symbolic file just ignore it since we cannot do anything
661  // with it.
662  if (!BinOrErr) {
663  if (auto Err = isNotObjectErrorInvalidFileType(BinOrErr.takeError()))
664  return std::move(Err);
665  continue;
666  }
667  auto *SF = dyn_cast<SymbolicFile>(&**BinOrErr);
668  if (!SF)
669  continue;
670 
671  for (BasicSymbolRef Symbol : SF->symbols()) {
672  Expected<uint32_t> FlagsOrErr = Symbol.getFlags();
673  if (!FlagsOrErr)
674  return FlagsOrErr.takeError();
675 
676  // We are interested in externally visible and defined symbols only, so
677  // ignore it if this is not such a symbol.
678  bool Undefined = *FlagsOrErr & SymbolRef::SF_Undefined;
679  bool Global = *FlagsOrErr & SymbolRef::SF_Global;
680  if (Undefined || !Global)
681  continue;
682 
683  // Get symbol name.
684  std::string Name;
685  raw_string_ostream NameOS(Name);
686  if (Error Err = Symbol.printName(NameOS))
687  return std::move(Err);
688 
689  // If we are dealing with a bitcode file do not add special globals
690  // llvm.used and llvm.compiler.used to the list of defined symbols.
691  if (SF->isIR() && (Name == "llvm.used" || Name == "llvm.compiler.used"))
692  continue;
693 
694  // Add symbol name with the target prefix to the buffer.
695  SymbolsOS << BundlerConfig.TargetNames[I] << "." << Name << '\0';
696  }
697  }
698  return SymbolsBuf;
699  }
700 
701 public:
702  // TODO: Add error checking from ClangOffloadBundler.cpp
703  ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
704  const OffloadBundlerConfig &BC)
705  : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()),
706  NextSection(Obj->section_begin()), BundlerConfig(BC) {}
707 
708  ~ObjectFileHandler() final {}
709 
710  Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
711 
713  ReadBundleStart(MemoryBuffer &Input) final {
714  while (NextSection != Obj->section_end()) {
715  CurrentSection = NextSection;
716  ++NextSection;
717 
718  // Check if the current section name starts with the reserved prefix. If
719  // so, return the triple.
720  Expected<std::optional<StringRef>> TripleOrErr =
721  IsOffloadSection(*CurrentSection);
722  if (!TripleOrErr)
723  return TripleOrErr.takeError();
724  if (*TripleOrErr)
725  return **TripleOrErr;
726  }
727  return std::nullopt;
728  }
729 
730  Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
731 
732  Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
733  Expected<StringRef> ContentOrErr = CurrentSection->getContents();
734  if (!ContentOrErr)
735  return ContentOrErr.takeError();
736  StringRef Content = *ContentOrErr;
737 
738  // Copy fat object contents to the output when extracting host bundle.
739  std::string ModifiedContent;
740  if (Content.size() == 1u && Content.front() == 0) {
741  auto HostBundleOrErr = getHostBundle(
742  StringRef(Input.getBufferStart(), Input.getBufferSize()));
743  if (!HostBundleOrErr)
744  return HostBundleOrErr.takeError();
745 
746  ModifiedContent = std::move(*HostBundleOrErr);
747  Content = ModifiedContent;
748  }
749 
750  OS.write(Content.data(), Content.size());
751  return Error::success();
752  }
753 
754  Error WriteHeader(raw_ostream &OS,
755  ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
756  assert(BundlerConfig.HostInputIndex != ~0u &&
757  "Host input index not defined.");
758 
759  // Record number of inputs.
760  NumberOfInputs = Inputs.size();
761  return Error::success();
762  }
763 
764  Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
765  ++NumberOfProcessedInputs;
766  return Error::success();
767  }
768 
769  Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
770  return Error::success();
771  }
772 
773  Error finalizeOutputFile() final {
774  assert(NumberOfProcessedInputs <= NumberOfInputs &&
775  "Processing more inputs that actually exist!");
776  assert(BundlerConfig.HostInputIndex != ~0u &&
777  "Host input index not defined.");
778 
779  // If this is not the last output, we don't have to do anything.
780  if (NumberOfProcessedInputs != NumberOfInputs)
781  return Error::success();
782 
783  // We will use llvm-objcopy to add target objects sections to the output
784  // fat object. These sections should have 'exclude' flag set which tells
785  // link editor to remove them from linker inputs when linking executable or
786  // shared library.
787 
788  assert(BundlerConfig.ObjcopyPath != "" &&
789  "llvm-objcopy path not specified");
790 
791  // Temporary files that need to be removed.
792  TempFileHandlerRAII TempFiles;
793 
794  // Compose llvm-objcopy command line for add target objects' sections with
795  // appropriate flags.
796  BumpPtrAllocator Alloc;
797  StringSaver SS{Alloc};
798  SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
799 
800  for (unsigned I = 0; I < NumberOfInputs; ++I) {
801  StringRef InputFile = BundlerConfig.InputFileNames[I];
802  if (I == BundlerConfig.HostInputIndex) {
803  // Special handling for the host bundle. We do not need to add a
804  // standard bundle for the host object since we are going to use fat
805  // object as a host object. Therefore use dummy contents (one zero byte)
806  // when creating section for the host bundle.
807  Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0));
808  if (!TempFileOrErr)
809  return TempFileOrErr.takeError();
810  InputFile = *TempFileOrErr;
811  }
812 
813  ObjcopyArgs.push_back(
814  SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR +
815  BundlerConfig.TargetNames[I] + "=" + InputFile));
816  ObjcopyArgs.push_back(
817  SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR +
818  BundlerConfig.TargetNames[I] + "=readonly,exclude"));
819  }
820  if (BundlerConfig.AddTargetSymbols) {
821  // Add a section with symbol names that are defined in target objects to
822  // the output fat object.
823  Expected<SmallVector<char, 0>> SymbolsOrErr = makeTargetSymbolTable();
824  if (!SymbolsOrErr)
825  return SymbolsOrErr.takeError();
826 
827  if (!SymbolsOrErr->empty()) {
828  // Add section with symbols names to fat object.
829  Expected<StringRef> SymbolsFileOrErr =
830  TempFiles.Create(ArrayRef<char>(*SymbolsOrErr));
831  if (!SymbolsFileOrErr)
832  return SymbolsFileOrErr.takeError();
833 
834  ObjcopyArgs.push_back(SS.save(Twine("--add-section=") +
835  SYMBOLS_SECTION_NAME + "=" +
836  *SymbolsFileOrErr));
837  }
838  }
839  ObjcopyArgs.push_back("--");
840  ObjcopyArgs.push_back(
841  BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
842  ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
843 
844  if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
845  return Err;
846 
847  return Error::success();
848  }
849 
850  Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
851  return Error::success();
852  }
853 
854 private:
855  Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) {
856  // If the user asked for the commands to be printed out, we do that
857  // instead of executing it.
858  if (BundlerConfig.PrintExternalCommands) {
859  errs() << "\"" << Objcopy << "\"";
860  for (StringRef Arg : drop_begin(Args, 1))
861  errs() << " \"" << Arg << "\"";
862  errs() << "\n";
863  } else {
864  if (sys::ExecuteAndWait(Objcopy, Args))
865  return createStringError(inconvertibleErrorCode(),
866  "'llvm-objcopy' tool failed");
867  }
868  return Error::success();
869  }
870 
871  Expected<std::string> getHostBundle(StringRef Input) {
872  TempFileHandlerRAII TempFiles;
873 
874  auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
875  if (!ModifiedObjPathOrErr)
876  return ModifiedObjPathOrErr.takeError();
877  StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
878 
879  BumpPtrAllocator Alloc;
880  StringSaver SS{Alloc};
881  SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"};
882 
883  ObjcopyArgs.push_back("--regex");
884  ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
885  ObjcopyArgs.push_back("--");
886 
887  StringRef ObjcopyInputFileName;
888  // When unbundling an archive, the content of each object file in the
889  // archive is passed to this function by parameter Input, which is different
890  // from the content of the original input archive file, therefore it needs
891  // to be saved to a temporary file before passed to llvm-objcopy. Otherwise,
892  // Input is the same as the content of the original input file, therefore
893  // temporary file is not needed.
894  if (StringRef(BundlerConfig.FilesType).starts_with("a")) {
895  auto InputFileOrErr =
896  TempFiles.Create(ArrayRef<char>(Input.data(), Input.size()));
897  if (!InputFileOrErr)
898  return InputFileOrErr.takeError();
899  ObjcopyInputFileName = *InputFileOrErr;
900  } else
901  ObjcopyInputFileName = BundlerConfig.InputFileNames.front();
902 
903  ObjcopyArgs.push_back(ObjcopyInputFileName);
904  ObjcopyArgs.push_back(ModifiedObjPath);
905 
906  if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
907  return std::move(Err);
908 
909  auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
910  if (!BufOrErr)
911  return createStringError(BufOrErr.getError(),
912  "Failed to read back the modified object file");
913 
914  return BufOrErr->get()->getBuffer().str();
915  }
916 
917  Expected<std::string> getHostBundle() {
918  TempFileHandlerRAII TempFiles;
919 
920  auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
921  if (!ModifiedObjPathOrErr)
922  return ModifiedObjPathOrErr.takeError();
923  StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
924 
925  BumpPtrAllocator Alloc;
926  StringSaver SS{Alloc};
927  SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"};
928 
929  ObjcopyArgs.push_back("--regex");
930  ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
931  ObjcopyArgs.push_back("--");
932  ObjcopyArgs.push_back(BundlerConfig.InputFileNames.front());
933  ObjcopyArgs.push_back(ModifiedObjPath);
934 
935  if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
936  return std::move(Err);
937 
938  auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
939  if (!BufOrErr)
940  return createStringError(BufOrErr.getError(),
941  "Failed to read back the modified object file");
942 
943  return BufOrErr->get()->getBuffer().str();
944  }
945 };
946 
947 /// Handler for text files. The bundled file will have the following format.
948 ///
949 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
950 /// Bundle 1
951 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
952 /// ...
953 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
954 /// Bundle N
955 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
956 class TextFileHandler final : public FileHandler {
957  /// String that begins a line comment.
958  StringRef Comment;
959 
960  /// String that initiates a bundle.
961  std::string BundleStartString;
962 
963  /// String that closes a bundle.
964  std::string BundleEndString;
965 
966  /// Number of chars read from input.
967  size_t ReadChars = 0u;
968 
969 protected:
970  Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
971 
973  ReadBundleStart(MemoryBuffer &Input) final {
974  StringRef FC = Input.getBuffer();
975 
976  // Find start of the bundle.
977  ReadChars = FC.find(BundleStartString, ReadChars);
978  if (ReadChars == FC.npos)
979  return std::nullopt;
980 
981  // Get position of the triple.
982  size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
983 
984  // Get position that closes the triple.
985  size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
986  if (TripleEnd == FC.npos)
987  return std::nullopt;
988 
989  // Next time we read after the new line.
990  ++ReadChars;
991 
992  return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
993  }
994 
995  Error ReadBundleEnd(MemoryBuffer &Input) final {
996  StringRef FC = Input.getBuffer();
997 
998  // Read up to the next new line.
999  assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
1000 
1001  size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
1002  if (TripleEnd != FC.npos)
1003  // Next time we read after the new line.
1004  ++ReadChars;
1005 
1006  return Error::success();
1007  }
1008 
1009  Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
1010  StringRef FC = Input.getBuffer();
1011  size_t BundleStart = ReadChars;
1012 
1013  // Find end of the bundle.
1014  size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
1015 
1016  StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
1017  OS << Bundle;
1018 
1019  return Error::success();
1020  }
1021 
1022  Error WriteHeader(raw_ostream &OS,
1023  ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
1024  return Error::success();
1025  }
1026 
1027  Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
1028  OS << BundleStartString << TargetTriple << "\n";
1029  return Error::success();
1030  }
1031 
1032  Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
1033  OS << BundleEndString << TargetTriple << "\n";
1034  return Error::success();
1035  }
1036 
1037  Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
1038  OS << Input.getBuffer();
1039  return Error::success();
1040  }
1041 
1042 public:
1043  TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
1044  BundleStartString =
1045  "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
1046  BundleEndString =
1047  "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
1048  }
1049 
1050  Error listBundleIDsCallback(MemoryBuffer &Input,
1051  const BundleInfo &Info) final {
1052  // TODO: To list bundle IDs in a bundled text file we need to go through
1053  // all bundles. The format of bundled text file may need to include a
1054  // header if the performance of listing bundle IDs of bundled text file is
1055  // important.
1056  ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
1057  if (Error Err = ReadBundleEnd(Input))
1058  return Err;
1059  return Error::success();
1060  }
1061 };
1062 } // namespace
1063 
1064 /// Archive file handler. Only unbundling is supported so far.
1065 class ArchiveFileHandler final : public FileHandler {
1066  /// Archive we are dealing with.
1067  std::unique_ptr<Archive> Ar;
1068 
1069  /// Configuration options and arrays for this bundler job
1070  const OffloadBundlerConfig &BundlerConfig;
1071 
1072  /// Union of bundle names from all object. The value is a count of how many
1073  /// times we've seen the bundle in the archive object(s).
1074  StringMap<unsigned> Bundles;
1075 
1076  /// Iterators over the bundle names.
1077  StringMap<unsigned>::iterator CurrBundle = Bundles.end();
1078  StringMap<unsigned>::iterator NextBundle = Bundles.end();
1079 
1080  /// Output mode for the archive unbundler.
1081  enum class OutputType {
1082  Unknown,
1083  FileList, // Output is a list file with extracted object file names
1084  Object, // Output is a single object file
1085  Archive // Output is an archive with extracted objects
1086  };
1087  const OutputType Mode =
1088  StringSwitch<OutputType>(BundlerConfig.FilesType)
1089  .Cases("aoo", "aocx", "aocr", OutputType::FileList)
1090  .Case("ao", OutputType::Object)
1091  .Case("a", OutputType::Archive)
1092  .Default(OutputType::Unknown);
1093 
1094  // Set contains indexes of Children that should be skipped during
1095  // unbundling.
1096  std::unordered_set<size_t> ExcludedChildIndexes;
1097 
1098 public:
1099  ArchiveFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
1100  ~ArchiveFileHandler() = default;
1101 
1102  Error ReadHeader(MemoryBuffer &Input) override {
1103  assert(Mode != OutputType::Unknown && "unknown output mode");
1104 
1105  // Create archive instance for the given input.
1106  auto ArOrErr = Archive::create(Input);
1107  if (!ArOrErr)
1108  return ArOrErr.takeError();
1109  Ar = std::move(*ArOrErr);
1110 
1111  // Read all children.
1112  ssize_t ChildIndex = -1;
1113  Error Err = Error::success();
1114  for (auto &C : Ar->children(Err)) {
1115  ++ChildIndex;
1116  auto BinOrErr = C.getAsBinary();
1117 
1118  std::unique_ptr<FileHandler> FH{nullptr};
1119  std::unique_ptr<MemoryBuffer> Buf{nullptr};
1120 
1121  if (!BinOrErr) {
1122  if (auto Err = isNotObjectErrorInvalidFileType(BinOrErr.takeError()))
1123  return Err;
1124 
1125  // Handle bundled BC Files
1126  FH = std::make_unique<BinaryFileHandler>(BundlerConfig);
1127  auto MR = C.getMemoryBufferRef();
1128  assert(MR);
1129  Buf = MemoryBuffer::getMemBuffer(*MR, false);
1130  } else {
1131  auto &Bin = BinOrErr.get();
1132  if (!Bin->isObject())
1133  continue;
1134 
1135  auto CheckOrErr = CheckIfObjectFileContainsExcludedTargets(C);
1136  if (!CheckOrErr)
1137  return CheckOrErr.takeError();
1138 
1139  if (*CheckOrErr) {
1140  LLVM_DEBUG(outs()
1141  << "Add child to ban list. Index: " << ChildIndex << "\n");
1142  ExcludedChildIndexes.emplace(ChildIndex);
1143  }
1144 
1145  auto Obj = std::unique_ptr<ObjectFile>(cast<ObjectFile>(Bin.release()));
1146  Buf = MemoryBuffer::getMemBuffer(Obj->getMemoryBufferRef(), false);
1147 
1148  FH = std::make_unique<ObjectFileHandler>(std::move(Obj), BundlerConfig);
1149  }
1150 
1151  // Collect the list of bundles from the object or bundled BC file.
1152  if (Error Err = FH->ReadHeader(*Buf))
1153  return Err;
1154  Expected<std::optional<StringRef>> NameOrErr = FH->ReadBundleStart(*Buf);
1155  if (!NameOrErr)
1156  return NameOrErr.takeError();
1157  while (*NameOrErr) {
1158  ++Bundles[**NameOrErr];
1159  NameOrErr = FH->ReadBundleStart(*Buf);
1160  if (!NameOrErr)
1161  return NameOrErr.takeError();
1162  }
1163  }
1164  if (Err)
1165  return Err;
1166 
1167  CurrBundle = Bundles.end();
1168  NextBundle = Bundles.begin();
1169  return Error::success();
1170  }
1171 
1173  ReadBundleStart(MemoryBuffer &Input) override {
1174  if (NextBundle == Bundles.end())
1175  return std::nullopt;
1176  CurrBundle = NextBundle++;
1177  return CurrBundle->first();
1178  }
1179 
1180  Error ReadBundleEnd(MemoryBuffer &Input) override { return Error::success(); }
1181 
1182  Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) override {
1183  assert(CurrBundle->second && "attempt to extract nonexistent bundle");
1184 
1185  // In single-file mode we do not expect to see bundle more than once.
1186  if (Mode == OutputType::Object && CurrBundle->second > 1)
1187  return createStringError(
1188  errc::invalid_argument,
1189  "'ao' file type is requested, but the archive contains multiple "
1190  "device objects; use 'aoo' instead");
1191 
1192  // For 'host' archive bundle just copy input data to the output stream.
1193  if (Mode == OutputType::Archive &&
1194  OffloadTargetInfo(CurrBundle->first(), BundlerConfig).hasHostKind()) {
1195  OS << Input.getBuffer();
1196  return Error::success();
1197  }
1198 
1199  // Extracted objects data for archive mode.
1201 
1202  // Read all children.
1203  Error Err = Error::success();
1204  ssize_t ChildIndex = -1;
1205  for (auto &C : Ar->children(Err)) {
1206  ++ChildIndex;
1207  if (ExcludedChildIndexes.count(ChildIndex)) {
1208  LLVM_DEBUG(outs() << "Skip Child. Index: " << ChildIndex << "\n");
1209  continue;
1210  }
1211 
1212  std::unique_ptr<FileHandler> FH{nullptr};
1213  std::unique_ptr<MemoryBuffer> Buf{nullptr};
1214  StringRef Ext("o");
1215  if (BundlerConfig.FilesType == "aocr" ||
1216  BundlerConfig.FilesType == "aocx")
1217  Ext = BundlerConfig.FilesType;
1218 
1219  auto BinOrErr = C.getAsBinary();
1220  if (!BinOrErr) {
1221  // Not a recognized binary file. Specifically not an object file
1222  if (auto Err = isNotObjectErrorInvalidFileType(BinOrErr.takeError()))
1223  return Err;
1224 
1225  if (BundlerConfig.FilesType == "aoo") {
1226  // Handle bundled BC Files
1227  Ext = "bc";
1228  FH = std::make_unique<BinaryFileHandler>(BundlerConfig);
1229  auto MR = C.getMemoryBufferRef();
1230  assert(MR);
1231  Buf = MemoryBuffer::getMemBuffer(*MR, false);
1232  } else
1233  continue;
1234  } else {
1235  auto &Bin = BinOrErr.get();
1236  if (!Bin->isObject())
1237  continue;
1238  auto Obj = std::unique_ptr<ObjectFile>(cast<ObjectFile>(Bin.release()));
1239  Buf = MemoryBuffer::getMemBuffer(Obj->getMemoryBufferRef(), false);
1240  FH = std::make_unique<ObjectFileHandler>(std::move(Obj), BundlerConfig);
1241  }
1242 
1243  if (Error Err = FH->ReadHeader(*Buf))
1244  return Err;
1245  Expected<std::optional<StringRef>> NameOrErr = FH->ReadBundleStart(*Buf);
1246  if (!NameOrErr)
1247  return NameOrErr.takeError();
1248  while (*NameOrErr) {
1249  auto TT = **NameOrErr;
1250  if (TT == CurrBundle->first()) {
1251  // This is the bundle we are looking for.
1252  if (Mode == OutputType::FileList) {
1253  // Create temporary file where the device part will be extracted to.
1254  SmallString<128u> ChildFileName;
1255 
1256  auto EC = sys::fs::createTemporaryFile(TempFileNameBase, Ext,
1257  ChildFileName);
1258  if (EC)
1259  return createFileError(ChildFileName, EC);
1260 
1261  raw_fd_ostream ChildOS(ChildFileName, EC);
1262  if (EC)
1263  return createFileError(ChildFileName, EC);
1264 
1265  if (Error Err = FH->ReadBundle(ChildOS, *Buf))
1266  return Err;
1267 
1268  if (ChildOS.has_error())
1269  return createFileError(ChildFileName, ChildOS.error());
1270 
1271  // Add temporary file name with the device part to the output file
1272  // list.
1273  OS << ChildFileName << "\n";
1274  } else if (Mode == OutputType::Object) {
1275  // Extract the bundle to the output file in single file mode.
1276  if (Error Err = FH->ReadBundle(OS, *Buf))
1277  return Err;
1278  } else if (Mode == OutputType::Archive) {
1279  auto ChildNameOrErr = C.getName();
1280  if (!ChildNameOrErr)
1281  return ChildNameOrErr.takeError();
1282 
1283  // Extract the bundle to a buffer.
1285  raw_svector_ostream ChildOS{Data};
1286  if (Error Err = FH->ReadBundle(ChildOS, *Buf))
1287  return Err;
1288 
1289  // Add new archive member.
1290  NewArchiveMember &Member = ArMembers.emplace_back();
1291  std::string Name = (TT + "." + *ChildNameOrErr).str();
1292  Member.Buf = MemoryBuffer::getMemBufferCopy(ChildOS.str(), Name);
1293  Member.MemberName = Member.Buf->getBufferIdentifier();
1294  }
1295  if (Error Err = FH->ReadBundleEnd(*Buf))
1296  return Err;
1297  }
1298  NameOrErr = FH->ReadBundleStart(*Buf);
1299  if (!NameOrErr)
1300  return NameOrErr.takeError();
1301  }
1302  }
1303  if (Err)
1304  return Err;
1305 
1306  if (Mode == OutputType::Archive) {
1307  // Determine archive kind for the offload target.
1308  auto ArKind =
1309  getTargetTriple(CurrBundle->first(), BundlerConfig).isOSDarwin()
1310  ? Archive::K_DARWIN
1311  : Archive::K_GNU;
1312 
1313  // And write archive to the output.
1314  Expected<std::unique_ptr<MemoryBuffer>> NewAr = writeArchiveToBuffer(
1315  ArMembers, SymtabWritingMode::NormalSymtab, ArKind,
1316  /*Deterministic=*/true, /*Thin=*/false);
1317  if (!NewAr)
1318  return NewAr.takeError();
1319  OS << NewAr.get()->getBuffer();
1320  }
1321  return Error::success();
1322  }
1323 
1324  Error WriteHeader(raw_ostream &OS,
1325  ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) override {
1326  llvm_unreachable("unsupported for the ArchiveFileHandler");
1327  }
1328 
1329  Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) override {
1330  llvm_unreachable("unsupported for the ArchiveFileHandler");
1331  }
1332 
1333  Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) override {
1334  llvm_unreachable("unsupported for the ArchiveFileHandler");
1335  }
1336 
1337  Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) override {
1338  llvm_unreachable("unsupported for the ArchiveFileHandler");
1339  }
1340 
1341 private:
1342  // NOTE: mostly a copy-paste of ReadHeader method.
1344  ReadTargetsFromChild(const Archive::Child &C) {
1345  Expected<std::unique_ptr<Binary>> BinOrErr = C.getAsBinary();
1346  if (!BinOrErr)
1347  return BinOrErr.takeError();
1348 
1349  std::unique_ptr<Binary> &Bin = BinOrErr.get();
1350  auto Obj = std::unique_ptr<ObjectFile>(cast<ObjectFile>(Bin.release()));
1351  std::unique_ptr<MemoryBuffer> Buf =
1352  MemoryBuffer::getMemBuffer(Obj->getMemoryBufferRef(), false);
1353  ObjectFileHandler OFH(std::move(Obj), BundlerConfig);
1354  if (Error Err = OFH.ReadHeader(*Buf))
1355  return {std::move(Err)};
1356  Expected<std::optional<StringRef>> NameOrErr = OFH.ReadBundleStart(*Buf);
1357  if (!NameOrErr)
1358  return NameOrErr.takeError();
1359 
1360  std::vector<std::string> Targets;
1361  while (*NameOrErr) {
1362  if (*NameOrErr)
1363  Targets.emplace_back((**NameOrErr).str());
1364  NameOrErr = OFH.ReadBundleStart(*Buf);
1365  if (!NameOrErr)
1366  return NameOrErr.takeError();
1367  }
1368 
1369  return Targets;
1370  }
1371 
1372  // Function reads targets from Child and checks whether one of Targets
1373  // is in Excluded list.
1375  CheckIfObjectFileContainsExcludedTargets(const Archive::Child &C) {
1376  if (BundlerConfig.ExcludedTargetNames.empty())
1377  return false;
1378 
1379  auto TargetNamesOrErr = ReadTargetsFromChild(C);
1380  if (!TargetNamesOrErr)
1381  return TargetNamesOrErr.takeError();
1382 
1383  auto TargetNames = TargetNamesOrErr.get();
1384  const auto &ExcludedTargets = BundlerConfig.ExcludedTargetNames;
1385  return std::any_of(TargetNames.begin(), TargetNames.end(),
1386  [&ExcludedTargets](const std::string &Target) {
1387  auto It = std::find(ExcludedTargets.begin(),
1388  ExcludedTargets.end(), Target);
1389  return It != ExcludedTargets.end();
1390  });
1391  }
1392 };
1393 
1394 /// Return an appropriate object file handler. We use the specific object
1395 /// handler if we know how to deal with that format, otherwise we use a default
1396 /// binary file handler.
1397 static std::unique_ptr<FileHandler>
1398 CreateObjectFileHandler(MemoryBuffer &FirstInput,
1399  const OffloadBundlerConfig &BundlerConfig) {
1400  // Check if the input file format is one that we know how to deal with.
1401  Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
1402 
1403  // We only support regular object files. If failed to open the input as a
1404  // known binary or this is not an object file use the default binary handler.
1405  if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
1406  return std::make_unique<BinaryFileHandler>(BundlerConfig);
1407 
1408  // Otherwise create an object file handler. The handler will be owned by the
1409  // client of this function.
1410  return std::make_unique<ObjectFileHandler>(
1411  std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
1412  BundlerConfig);
1413 }
1414 
1415 static bool FilesTypeIsArchiveToList(const std::string& FilesType) {
1416  return FilesType == "ao" || FilesType == "aoo" || FilesType == "aocr" ||
1417  FilesType == "aocx";
1418 }
1419 
1420 static bool FilesTypeIsArchive(const std::string& FilesType) {
1421  return FilesType == "a" || FilesTypeIsArchiveToList(FilesType);
1422 }
1423 
1424 /// Return an appropriate handler given the input files and options.
1426 CreateFileHandler(MemoryBuffer &FirstInput,
1427  const OffloadBundlerConfig &BundlerConfig) {
1428  std::string FilesType = BundlerConfig.FilesType;
1429 
1430  if (FilesType == "i")
1431  return std::make_unique<TextFileHandler>(/*Comment=*/"//");
1432  if (FilesType == "ii")
1433  return std::make_unique<TextFileHandler>(/*Comment=*/"//");
1434  if (FilesType == "cui")
1435  return std::make_unique<TextFileHandler>(/*Comment=*/"//");
1436  if (FilesType == "hipi")
1437  return std::make_unique<TextFileHandler>(/*Comment=*/"//");
1438  // TODO: `.d` should be eventually removed once `-M` and its variants are
1439  // handled properly in offload compilation.
1440  if (FilesType == "d")
1441  return std::make_unique<TextFileHandler>(/*Comment=*/"#");
1442  if (FilesType == "ll")
1443  return std::make_unique<TextFileHandler>(/*Comment=*/";");
1444  if (FilesType == "bc")
1445  return std::make_unique<BinaryFileHandler>(BundlerConfig);
1446  if (FilesType == "s")
1447  return std::make_unique<TextFileHandler>(/*Comment=*/"#");
1448  if (FilesType == "o")
1449  return CreateObjectFileHandler(FirstInput, BundlerConfig);
1450  if (FilesType == "a")
1451  return CreateObjectFileHandler(FirstInput, BundlerConfig);
1452  if (FilesType == "gch")
1453  return std::make_unique<BinaryFileHandler>(BundlerConfig);
1454  if (FilesType == "ast")
1455  return std::make_unique<BinaryFileHandler>(BundlerConfig);
1456  if (FilesTypeIsArchive(FilesType))
1457  return std::make_unique<ArchiveFileHandler>(BundlerConfig);
1458 
1459  return createStringError(errc::invalid_argument,
1460  "'" + FilesType + "': invalid file type specified");
1461 }
1462 
1464  if (llvm::compression::zstd::isAvailable()) {
1465  CompressionFormat = llvm::compression::Format::Zstd;
1466  // Compression level 3 is usually sufficient for zstd since long distance
1467  // matching is enabled.
1468  CompressionLevel = 3;
1469  } else if (llvm::compression::zlib::isAvailable()) {
1470  CompressionFormat = llvm::compression::Format::Zlib;
1471  // Use default level for zlib since higher level does not have significant
1472  // improvement.
1473  CompressionLevel = llvm::compression::zlib::DefaultCompression;
1474  }
1475  auto IgnoreEnvVarOpt =
1476  llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
1477  if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1")
1478  return;
1479 
1480  auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
1481  if (VerboseEnvVarOpt.has_value())
1482  Verbose = VerboseEnvVarOpt.value() == "1";
1483 
1484  auto CompressEnvVarOpt =
1485  llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
1486  if (CompressEnvVarOpt.has_value())
1487  Compress = CompressEnvVarOpt.value() == "1";
1488 
1489  auto CompressionLevelEnvVarOpt =
1490  llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
1491  if (CompressionLevelEnvVarOpt.has_value()) {
1492  llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
1493  int Level;
1494  if (!CompressionLevelStr.getAsInteger(10, Level))
1496  else
1497  llvm::errs()
1498  << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
1499  << CompressionLevelStr.str() << ". Ignoring it.\n";
1500  }
1501 }
1502 
1503 // Utility function to format numbers with commas
1504 static std::string formatWithCommas(unsigned long long Value) {
1505  std::string Num = std::to_string(Value);
1506  int InsertPosition = Num.length() - 3;
1507  while (InsertPosition > 0) {
1508  Num.insert(InsertPosition, ",");
1509  InsertPosition -= 3;
1510  }
1511  return Num;
1512 }
1513 
1515 CompressedOffloadBundle::compress(llvm::compression::Params P,
1516  const llvm::MemoryBuffer &Input,
1517  bool Verbose) {
1518  if (!llvm::compression::zstd::isAvailable() &&
1519  !llvm::compression::zlib::isAvailable())
1520  return createStringError(llvm::inconvertibleErrorCode(),
1521  "Compression not supported");
1522 
1523  llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
1525  if (Verbose)
1526  HashTimer.startTimer();
1527  llvm::MD5 Hash;
1528  llvm::MD5::MD5Result Result;
1529  Hash.update(Input.getBuffer());
1530  Hash.final(Result);
1531  uint64_t TruncatedHash = Result.low();
1532  if (Verbose)
1533  HashTimer.stopTimer();
1534 
1535  SmallVector<uint8_t, 0> CompressedBuffer;
1536  auto BufferUint8 = llvm::ArrayRef<uint8_t>(
1537  reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
1538  Input.getBuffer().size());
1539 
1540  llvm::Timer CompressTimer("Compression Timer", "Compression time",
1542  if (Verbose)
1543  CompressTimer.startTimer();
1544  llvm::compression::compress(P, BufferUint8, CompressedBuffer);
1545  if (Verbose)
1546  CompressTimer.stopTimer();
1547 
1548  uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
1549  uint32_t UncompressedSize = Input.getBuffer().size();
1550  uint32_t TotalFileSize = MagicNumber.size() + sizeof(TotalFileSize) +
1551  sizeof(Version) + sizeof(CompressionMethod) +
1552  sizeof(UncompressedSize) + sizeof(TruncatedHash) +
1553  CompressedBuffer.size();
1554 
1555  SmallVector<char, 0> FinalBuffer;
1556  llvm::raw_svector_ostream OS(FinalBuffer);
1557  OS << MagicNumber;
1558  OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
1559  OS.write(reinterpret_cast<const char *>(&CompressionMethod),
1560  sizeof(CompressionMethod));
1561  OS.write(reinterpret_cast<const char *>(&TotalFileSize),
1562  sizeof(TotalFileSize));
1563  OS.write(reinterpret_cast<const char *>(&UncompressedSize),
1564  sizeof(UncompressedSize));
1565  OS.write(reinterpret_cast<const char *>(&TruncatedHash),
1566  sizeof(TruncatedHash));
1567  OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
1568  CompressedBuffer.size());
1569 
1570  if (Verbose) {
1571  auto MethodUsed =
1572  P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
1573  double CompressionRate =
1574  static_cast<double>(UncompressedSize) / CompressedBuffer.size();
1575  double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1576  double CompressionSpeedMBs =
1577  (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
1578 
1579  llvm::errs() << "Compressed bundle format version: " << Version << "\n"
1580  << "Total file size (including headers): "
1581  << formatWithCommas(TotalFileSize) << " bytes\n"
1582  << "Compression method used: " << MethodUsed << "\n"
1583  << "Compression level: " << P.level << "\n"
1584  << "Binary size before compression: "
1585  << formatWithCommas(UncompressedSize) << " bytes\n"
1586  << "Binary size after compression: "
1587  << formatWithCommas(CompressedBuffer.size()) << " bytes\n"
1588  << "Compression rate: "
1589  << llvm::format("%.2lf", CompressionRate) << "\n"
1590  << "Compression ratio: "
1591  << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1592  << "Compression speed: "
1593  << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
1594  << "Truncated MD5 hash: "
1595  << llvm::format_hex(TruncatedHash, 16) << "\n";
1596  }
1597  return llvm::MemoryBuffer::getMemBufferCopy(
1598  llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1599 }
1600 
1602 CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
1603  bool Verbose) {
1604 
1605  StringRef Blob = Input.getBuffer();
1606 
1607  if (Blob.size() < V1HeaderSize)
1608  return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1609 
1610  if (llvm::identify_magic(Blob) !=
1611  llvm::file_magic::offload_bundle_compressed) {
1612  if (Verbose)
1613  llvm::errs() << "Uncompressed bundle.\n";
1614  return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1615  }
1616 
1617  size_t CurrentOffset = MagicSize;
1618 
1619  uint16_t ThisVersion;
1620  memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
1621  CurrentOffset += VersionFieldSize;
1622 
1623  uint16_t CompressionMethod;
1624  memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
1625  CurrentOffset += MethodFieldSize;
1626 
1627  uint32_t TotalFileSize;
1628  if (ThisVersion >= 2) {
1629  if (Blob.size() < V2HeaderSize)
1630  return createStringError(inconvertibleErrorCode(),
1631  "Compressed bundle header size too small");
1632  memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
1633  CurrentOffset += FileSizeFieldSize;
1634  }
1635 
1636  uint32_t UncompressedSize;
1637  memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
1638  CurrentOffset += UncompressedSizeFieldSize;
1639 
1640  uint64_t StoredHash;
1641  memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
1642  CurrentOffset += HashFieldSize;
1643 
1644  llvm::compression::Format CompressionFormat;
1645  if (CompressionMethod ==
1646  static_cast<uint16_t>(llvm::compression::Format::Zlib))
1647  CompressionFormat = llvm::compression::Format::Zlib;
1648  else if (CompressionMethod ==
1649  static_cast<uint16_t>(llvm::compression::Format::Zstd))
1650  CompressionFormat = llvm::compression::Format::Zstd;
1651  else
1652  return createStringError(inconvertibleErrorCode(),
1653  "Unknown compressing method");
1654 
1655  llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
1657  if (Verbose)
1658  DecompressTimer.startTimer();
1659 
1660  SmallVector<uint8_t, 0> DecompressedData;
1661  StringRef CompressedData = Blob.substr(CurrentOffset);
1662  if (llvm::Error DecompressionError = llvm::compression::decompress(
1663  CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
1664  DecompressedData, UncompressedSize))
1665  return createStringError(inconvertibleErrorCode(),
1666  "Could not decompress embedded file contents: " +
1667  llvm::toString(std::move(DecompressionError)));
1668 
1669  if (Verbose) {
1670  DecompressTimer.stopTimer();
1671 
1672  double DecompressionTimeSeconds =
1673  DecompressTimer.getTotalTime().getWallTime();
1674 
1675  // Recalculate MD5 hash for integrity check
1676  llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
1677  "Hash recalculation time",
1679  HashRecalcTimer.startTimer();
1680  llvm::MD5 Hash;
1681  llvm::MD5::MD5Result Result;
1682  Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData.data(),
1683  DecompressedData.size()));
1684  Hash.final(Result);
1685  uint64_t RecalculatedHash = Result.low();
1686  HashRecalcTimer.stopTimer();
1687  bool HashMatch = (StoredHash == RecalculatedHash);
1688 
1689  double CompressionRate =
1690  static_cast<double>(UncompressedSize) / CompressedData.size();
1691  double DecompressionSpeedMBs =
1692  (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1693 
1694  llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
1695  if (ThisVersion >= 2)
1696  llvm::errs() << "Total file size (from header): "
1697  << formatWithCommas(TotalFileSize) << " bytes\n";
1698  llvm::errs() << "Decompression method: "
1699  << (CompressionFormat == llvm::compression::Format::Zlib
1700  ? "zlib"
1701  : "zstd")
1702  << "\n"
1703  << "Size before decompression: "
1704  << formatWithCommas(CompressedData.size()) << " bytes\n"
1705  << "Size after decompression: "
1706  << formatWithCommas(UncompressedSize) << " bytes\n"
1707  << "Compression rate: "
1708  << llvm::format("%.2lf", CompressionRate) << "\n"
1709  << "Compression ratio: "
1710  << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1711  << "Decompression speed: "
1712  << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
1713  << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
1714  << "Recalculated hash: "
1715  << llvm::format_hex(RecalculatedHash, 16) << "\n"
1716  << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
1717  }
1718 
1719  return llvm::MemoryBuffer::getMemBufferCopy(
1720  llvm::toStringRef(DecompressedData));
1721 }
1722 
1723 // List bundle IDs. Return true if an error was found.
1725  StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
1726  // Open Input file.
1727  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1728  MemoryBuffer::getFileOrSTDIN(InputFileName);
1729  if (std::error_code EC = CodeOrErr.getError())
1730  return createFileError(InputFileName, EC);
1731 
1732  // Decompress the input if necessary.
1733  Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1735  if (!DecompressedBufferOrErr)
1736  return createStringError(
1737  inconvertibleErrorCode(),
1738  "Failed to decompress input: " +
1739  llvm::toString(DecompressedBufferOrErr.takeError()));
1740 
1741  MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1742 
1743  // Select the right files handler.
1744  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1745  CreateFileHandler(DecompressedInput, BundlerConfig);
1746  if (!FileHandlerOrErr)
1747  return FileHandlerOrErr.takeError();
1748 
1749  std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1750  assert(FH);
1751  return FH->listBundleIDs(DecompressedInput);
1752 }
1753 
1754 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
1755 /// target \p TargetInfo.
1756 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
1757 bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo,
1758  const OffloadTargetInfo &TargetInfo) {
1759 
1760  // Compatible in case of exact match.
1761  if (CodeObjectInfo == TargetInfo) {
1762  DEBUG_WITH_TYPE("CodeObjectCompatibility",
1763  dbgs() << "Compatible: Exact match: \t[CodeObject: "
1764  << CodeObjectInfo.str()
1765  << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1766  return true;
1767  }
1768 
1769  // Incompatible if Kinds or Triples mismatch.
1770  if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) ||
1771  !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
1772  DEBUG_WITH_TYPE(
1773  "CodeObjectCompatibility",
1774  dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
1775  << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1776  << "]\n");
1777  return false;
1778  }
1779 
1780  // Incompatible if Processors mismatch.
1781  llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
1782  std::optional<StringRef> CodeObjectProc = clang::parseTargetID(
1783  CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap);
1784  std::optional<StringRef> TargetProc = clang::parseTargetID(
1785  TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap);
1786 
1787  // Both TargetProc and CodeObjectProc can't be empty here.
1788  if (!TargetProc || !CodeObjectProc ||
1789  CodeObjectProc.value() != TargetProc.value()) {
1790  DEBUG_WITH_TYPE("CodeObjectCompatibility",
1791  dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
1792  << CodeObjectInfo.str()
1793  << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1794  return false;
1795  }
1796 
1797  // Incompatible if CodeObject has more features than Target, irrespective of
1798  // type or sign of features.
1799  if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1800  DEBUG_WITH_TYPE("CodeObjectCompatibility",
1801  dbgs() << "Incompatible: CodeObject has more features "
1802  "than target \t[CodeObject: "
1803  << CodeObjectInfo.str()
1804  << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1805  return false;
1806  }
1807 
1808  // Compatible if each target feature specified by target is compatible with
1809  // target feature of code object. The target feature is compatible if the
1810  // code object does not specify it (meaning Any), or if it specifies it
1811  // with the same value (meaning On or Off).
1812  for (const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1813  auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1814  if (TargetFeature == TargetFeatureMap.end()) {
1815  DEBUG_WITH_TYPE(
1816  "CodeObjectCompatibility",
1817  dbgs()
1818  << "Incompatible: Value of CodeObject's non-ANY feature is "
1819  "not matching with Target feature's ANY value \t[CodeObject: "
1820  << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1821  << "]\n");
1822  return false;
1823  } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1824  DEBUG_WITH_TYPE(
1825  "CodeObjectCompatibility",
1826  dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
1827  "not matching with Target feature's non-ANY value "
1828  "\t[CodeObject: "
1829  << CodeObjectInfo.str()
1830  << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1831  return false;
1832  }
1833  }
1834 
1835  // CodeObject is compatible if all features of Target are:
1836  // - either, present in the Code Object's features map with the same sign,
1837  // - or, the feature is missing from CodeObjects's features map i.e. it is
1838  // set to ANY
1839  DEBUG_WITH_TYPE(
1840  "CodeObjectCompatibility",
1841  dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
1842  << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1843  << "]\n");
1844  return true;
1845 }
1846 
1847 /// Bundle the files. Return true if an error was found.
1848 
1850  std::error_code EC;
1851 
1853  return createStringError(errc::invalid_argument,
1854  "bundling is not supported for archives");
1855 
1856  // Create a buffer to hold the content before compressing.
1857  SmallVector<char, 0> Buffer;
1858  llvm::raw_svector_ostream BufferStream(Buffer);
1859 
1860  // Open input files.
1862  InputBuffers.reserve(BundlerConfig.InputFileNames.size());
1863  for (auto &I : BundlerConfig.InputFileNames) {
1864  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1865  MemoryBuffer::getFileOrSTDIN(I);
1866  if (std::error_code EC = CodeOrErr.getError())
1867  return createFileError(I, EC);
1868  InputBuffers.emplace_back(std::move(*CodeOrErr));
1869  }
1870 
1871  // Get the file handler. We use the host buffer as reference.
1873  "Host input index undefined??");
1875  *InputBuffers[BundlerConfig.AllowNoHost ? 0
1877  BundlerConfig);
1878  if (!FileHandlerOrErr)
1879  return FileHandlerOrErr.takeError();
1880 
1881  std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1882  assert(FH);
1883 
1884  // Write header.
1885  if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1886  return Err;
1887 
1888  // Write all bundles along with the start/end markers. If an error was found
1889  // writing the end of the bundle component, abort the bundle writing.
1890  auto Input = InputBuffers.begin();
1891  for (auto &Triple : BundlerConfig.TargetNames) {
1892  if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1893  return Err;
1894  if (Error Err = FH->WriteBundle(BufferStream, **Input))
1895  return Err;
1896  if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1897  return Err;
1898  ++Input;
1899  }
1900 
1901  raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
1902  sys::fs::OF_None);
1903  if (EC)
1904  return createFileError(BundlerConfig.OutputFileNames.front(), EC);
1905 
1906  SmallVector<char, 0> CompressedBuffer;
1907  if (BundlerConfig.Compress) {
1908  std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1909  llvm::MemoryBuffer::getMemBufferCopy(
1910  llvm::StringRef(Buffer.data(), Buffer.size()));
1911  auto CompressionResult = CompressedOffloadBundle::compress(
1913  /*zstdEnableLdm=*/true},
1914  *BufferMemory, BundlerConfig.Verbose);
1915  if (auto Error = CompressionResult.takeError())
1916  return Error;
1917 
1918  auto CompressedMemBuffer = std::move(CompressionResult.get());
1919  CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1920  CompressedMemBuffer->getBufferEnd());
1921  } else
1922  CompressedBuffer = Buffer;
1923 
1924  OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1925 
1926  return FH->finalizeOutputFile();
1927 }
1928 
1929 // Unbundle the files. Return true if an error was found.
1931  // Open Input file.
1932  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1933  MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front());
1934  if (std::error_code EC = CodeOrErr.getError())
1935  return createFileError(BundlerConfig.InputFileNames.front(), EC);
1936 
1937  // Decompress the input if necessary.
1938  Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
1940  if (!DecompressedBufferOrErr)
1941  return createStringError(
1942  inconvertibleErrorCode(),
1943  "Failed to decompress input: " +
1944  llvm::toString(DecompressedBufferOrErr.takeError()));
1945 
1946  MemoryBuffer &Input = **DecompressedBufferOrErr;
1947 
1948  // Select the right files handler.
1949  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1951  if (!FileHandlerOrErr)
1952  return FileHandlerOrErr.takeError();
1953 
1954  std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1955  assert(FH);
1956 
1957  // Seed temporary filename generation with the stem of the input file.
1958  FH->SetTempFileNameBase(llvm::sys::path::stem(BundlerConfig.InputFileNames.front()));
1959 
1960  // Read the header of the bundled file.
1961  if (Error Err = FH->ReadHeader(Input))
1962  return Err;
1963 
1964  // Create a work list that consist of the map triple/output file.
1965  StringMap<StringRef> Worklist;
1966  auto Output = BundlerConfig.OutputFileNames.begin();
1967  for (auto &Triple : BundlerConfig.TargetNames) {
1968  Worklist[Triple] = *Output;
1969  ++Output;
1970  }
1971 
1972  // Read all the bundles that are in the work list. If we find no bundles we
1973  // assume the file is meant for the host target.
1974  bool FoundHostBundle = false;
1975  while (!Worklist.empty()) {
1976  Expected<std::optional<StringRef>> CurTripleOrErr =
1977  FH->ReadBundleStart(Input);
1978  if (!CurTripleOrErr)
1979  return CurTripleOrErr.takeError();
1980 
1981  // We don't have more bundles.
1982  if (!*CurTripleOrErr)
1983  break;
1984 
1985  StringRef CurTriple = **CurTripleOrErr;
1986  assert(!CurTriple.empty());
1987 
1988  auto Output = Worklist.begin();
1989  for (auto E = Worklist.end(); Output != E; Output++) {
1991  OffloadTargetInfo(CurTriple, BundlerConfig),
1992  OffloadTargetInfo((*Output).first(), BundlerConfig))) {
1993  break;
1994  }
1995  }
1996 
1997  if (Output == Worklist.end())
1998  continue;
1999  // Check if the output file can be opened and copy the bundle to it.
2000  std::error_code EC;
2001  raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
2002  if (EC)
2003  return createFileError((*Output).second, EC);
2004  if (Error Err = FH->ReadBundle(OutputFile, Input))
2005  return Err;
2006  if (Error Err = FH->ReadBundleEnd(Input))
2007  return Err;
2008  Worklist.erase(Output);
2009 
2010  // Record if we found the host bundle.
2011  auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
2012  if (OffloadInfo.hasHostKind())
2013  FoundHostBundle = true;
2014  }
2015 
2016  if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
2017  std::string ErrMsg = "Can't find bundles for";
2018  std::set<StringRef> Sorted;
2019  for (auto &E : Worklist)
2020  Sorted.insert(E.first());
2021  unsigned I = 0;
2022  unsigned Last = Sorted.size() - 1;
2023  for (auto &E : Sorted) {
2024  if (I != 0 && Last > 1)
2025  ErrMsg += ",";
2026  ErrMsg += " ";
2027  if (I == Last && I != 0)
2028  ErrMsg += "and ";
2029  ErrMsg += E.str();
2030  ++I;
2031  }
2032  return createStringError(inconvertibleErrorCode(), ErrMsg);
2033  }
2034 
2035  // If no bundles were found, assume the input file is the host bundle and
2036  // create empty files for the remaining targets.
2037  if (Worklist.size() == BundlerConfig.TargetNames.size()) {
2038  for (auto &E : Worklist) {
2039  std::error_code EC;
2040  raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
2041  if (EC)
2042  return createFileError(E.second, EC);
2043 
2044  // If this entry has a host kind, copy the input file to the output file
2045  // except for the archive unbundling where output is a list file.
2046  auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
2047  if (OffloadInfo.hasHostKind() &&
2049  OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
2050  }
2051  return Error::success();
2052  }
2053 
2054  // If we found elements, we emit an error if none of those were for the host
2055  // in case host bundle name was provided in command line.
2056  if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
2058  return createStringError(inconvertibleErrorCode(),
2059  "Can't find bundle for the host target");
2060 
2061  // If we still have any elements in the worklist, create empty files for them.
2062  for (auto &E : Worklist) {
2063  std::error_code EC;
2064  raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
2065  if (EC)
2066  return createFileError(E.second, EC);
2067  }
2068 
2069  return Error::success();
2070 }
2071 
2072 // Unbundle the files. Return true if an error was found.
2075  // Open Input file.
2076  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
2077  MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front());
2078  if (std::error_code EC = CodeOrErr.getError())
2079  return createFileError(BundlerConfig.InputFileNames.front(), EC);
2080  MemoryBuffer &Input = *CodeOrErr.get();
2081 
2082  // Select the right files handler.
2083  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
2084  CreateFileHandler(Input, BundlerConfig);
2085  if (!FileHandlerOrErr)
2086  return FileHandlerOrErr.takeError();
2087 
2088  std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
2089 
2090  // Quit if we don't have a handler.
2091  if (!FH)
2092  return true;
2093 
2094  // Seed temporary filename generation with the stem of the input file.
2095  FH->SetTempFileNameBase(llvm::sys::path::stem(BundlerConfig.InputFileNames.front()));
2096 
2097  // Read the header of the bundled file.
2098  if (Error Err = FH->ReadHeader(Input))
2099  return std::move(Err);
2100 
2101  StringRef triple = BundlerConfig.TargetNames.front();
2102 
2103  // Read all the bundles that are in the work list. If we find no bundles we
2104  // assume the file is meant for the host target.
2105  bool found = false;
2106  while (!found) {
2107  Expected<std::optional<StringRef>> CurTripleOrErr =
2108  FH->ReadBundleStart(Input);
2109  if (!CurTripleOrErr)
2110  return CurTripleOrErr.takeError();
2111 
2112  // We don't have more bundles.
2113  if (!*CurTripleOrErr)
2114  break;
2115 
2116  if (*CurTripleOrErr == triple) {
2117  found = true;
2118  break;
2119  }
2120  }
2121  return found;
2122 }
2123 
2125  return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
2126  : Archive::K_GNU;
2127 }
2128 
2129 /// @brief Computes a list of targets among all given targets which are
2130 /// compatible with this code object
2131 /// @param [in] CodeObjectInfo Code Object
2132 /// @param [out] CompatibleTargets List of all compatible targets among all
2133 /// given targets
2134 /// @return false, if no compatible target is found.
2135 static bool
2137  SmallVectorImpl<StringRef> &CompatibleTargets,
2138  const OffloadBundlerConfig &BundlerConfig) {
2139  if (!CompatibleTargets.empty()) {
2140  DEBUG_WITH_TYPE("CodeObjectCompatibility",
2141  dbgs() << "CompatibleTargets list should be empty\n");
2142  return false;
2143  }
2144  for (auto &Target : BundlerConfig.TargetNames) {
2145  auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
2146  if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
2147  CompatibleTargets.push_back(Target);
2148  }
2149  return !CompatibleTargets.empty();
2150 }
2151 
2152 // Check that each code object file in the input archive conforms to following
2153 // rule: for a specific processor, a feature either shows up in all target IDs,
2154 // or does not show up in any target IDs. Otherwise the target ID combination is
2155 // invalid.
2156 static Error
2157 CheckHeterogeneousArchive(StringRef ArchiveName,
2158  const OffloadBundlerConfig &BundlerConfig) {
2159  std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
2160  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
2161  MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
2162  if (std::error_code EC = BufOrErr.getError())
2163  return createFileError(ArchiveName, EC);
2164 
2165  ArchiveBuffers.push_back(std::move(*BufOrErr));
2167  Archive::create(ArchiveBuffers.back()->getMemBufferRef());
2168  if (!LibOrErr)
2169  return LibOrErr.takeError();
2170 
2171  auto Archive = std::move(*LibOrErr);
2172 
2173  Error ArchiveErr = Error::success();
2174  auto ChildEnd = Archive->child_end();
2175 
2176  /// Iterate over all bundled code object files in the input archive.
2177  for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
2178  ArchiveIter != ChildEnd; ++ArchiveIter) {
2179  if (ArchiveErr)
2180  return ArchiveErr;
2181  auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
2182  if (!ArchiveChildNameOrErr)
2183  return ArchiveChildNameOrErr.takeError();
2184 
2185  auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
2186  if (!CodeObjectBufferRefOrErr)
2187  return CodeObjectBufferRefOrErr.takeError();
2188 
2189  auto CodeObjectBuffer =
2190  MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
2191 
2192  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
2193  CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
2194  if (!FileHandlerOrErr)
2195  return FileHandlerOrErr.takeError();
2196 
2197  std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
2198  assert(FileHandler);
2199 
2200  std::set<StringRef> BundleIds;
2201  auto CodeObjectFileError =
2202  FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
2203  if (CodeObjectFileError)
2204  return CodeObjectFileError;
2205 
2206  auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds);
2207  if (ConflictingArchs) {
2208  std::string ErrMsg =
2209  Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
2210  ", " + ConflictingArchs.value().second + "] found in " +
2211  ArchiveChildNameOrErr.get() + " of " + ArchiveName)
2212  .str();
2213  return createStringError(inconvertibleErrorCode(), ErrMsg);
2214  }
2215  }
2216 
2217  return ArchiveErr;
2218 }
2219 
2220 /// UnbundleArchive takes an archive file (".a") as input containing bundled
2221 /// code object files, and a list of offload targets (not host), and extracts
2222 /// the code objects into a new archive file for each offload target. Each
2223 /// resulting archive file contains all code object files corresponding to that
2224 /// particular offload target. The created archive file does not
2225 /// contain an index of the symbols and code object files are named as
2226 /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
2228  std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
2229 
2230  /// Map of target names with list of object files that will form the device
2231  /// specific archive for that target
2232  StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
2233 
2234  // Map of target names and output archive filenames
2235  StringMap<StringRef> TargetOutputFileNameMap;
2236 
2237  auto Output = BundlerConfig.OutputFileNames.begin();
2238  for (auto &Target : BundlerConfig.TargetNames) {
2239  TargetOutputFileNameMap[Target] = *Output;
2240  ++Output;
2241  }
2242 
2243  StringRef IFName = BundlerConfig.InputFileNames.front();
2244 
2246  // For a specific processor, a feature either shows up in all target IDs, or
2247  // does not show up in any target IDs. Otherwise the target ID combination
2248  // is invalid.
2249  auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig);
2250  if (ArchiveError) {
2251  return ArchiveError;
2252  }
2253  }
2254 
2255  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
2256  MemoryBuffer::getFileOrSTDIN(IFName, true, false);
2257  if (std::error_code EC = BufOrErr.getError())
2258  return createFileError(BundlerConfig.InputFileNames.front(), EC);
2259 
2260  ArchiveBuffers.push_back(std::move(*BufOrErr));
2262  Archive::create(ArchiveBuffers.back()->getMemBufferRef());
2263  if (!LibOrErr)
2264  return LibOrErr.takeError();
2265 
2266  auto Archive = std::move(*LibOrErr);
2267 
2268  Error ArchiveErr = Error::success();
2269  auto ChildEnd = Archive->child_end();
2270 
2271  /// Iterate over all bundled code object files in the input archive.
2272  for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
2273  ArchiveIter != ChildEnd; ++ArchiveIter) {
2274  if (ArchiveErr)
2275  return ArchiveErr;
2276  auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
2277  if (!ArchiveChildNameOrErr)
2278  return ArchiveChildNameOrErr.takeError();
2279 
2280  StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
2281 
2282  auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
2283  if (!CodeObjectBufferRefOrErr)
2284  return CodeObjectBufferRefOrErr.takeError();
2285 
2286  auto TempCodeObjectBuffer =
2287  MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
2288 
2289  // Decompress the buffer if necessary.
2290  Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
2291  CompressedOffloadBundle::decompress(*TempCodeObjectBuffer,
2293  if (!DecompressedBufferOrErr)
2294  return createStringError(
2295  inconvertibleErrorCode(),
2296  "Failed to decompress code object: " +
2297  llvm::toString(DecompressedBufferOrErr.takeError()));
2298 
2299  MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
2300 
2301  Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
2302  CreateFileHandler(CodeObjectBuffer, BundlerConfig);
2303  if (!FileHandlerOrErr)
2304  return FileHandlerOrErr.takeError();
2305 
2306  std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
2307  assert(FileHandler &&
2308  "FileHandle creation failed for file in the archive!");
2309 
2310  if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
2311  return ReadErr;
2312 
2313  Expected<std::optional<StringRef>> CurBundleIDOrErr =
2314  FileHandler->ReadBundleStart(CodeObjectBuffer);
2315  if (!CurBundleIDOrErr)
2316  return CurBundleIDOrErr.takeError();
2317 
2318  std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
2319  // No device code in this child, skip.
2320  if (!OptionalCurBundleID)
2321  continue;
2322  StringRef CodeObject = *OptionalCurBundleID;
2323 
2324  // Process all bundle entries (CodeObjects) found in this child of input
2325  // archive.
2326  while (!CodeObject.empty()) {
2327  SmallVector<StringRef> CompatibleTargets;
2328  auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
2329  if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
2330  BundlerConfig)) {
2331  std::string BundleData;
2332  raw_string_ostream DataStream(BundleData);
2333  if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
2334  return Err;
2335 
2336  for (auto &CompatibleTarget : CompatibleTargets) {
2337  SmallString<128> BundledObjectFileName;
2338  BundledObjectFileName.assign(BundledObjectFile);
2339  auto OutputBundleName =
2340  Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
2341  CodeObject +
2342  getDeviceLibraryFileName(BundledObjectFileName,
2343  CodeObjectInfo.TargetID))
2344  .str();
2345  // Replace ':' in optional target feature list with '_' to ensure
2346  // cross-platform validity.
2347  std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
2348  '_');
2349 
2350  std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
2351  DataStream.str(), OutputBundleName);
2352  ArchiveBuffers.push_back(std::move(MemBuf));
2353  llvm::MemoryBufferRef MemBufRef =
2354  MemoryBufferRef(*(ArchiveBuffers.back()));
2355 
2356  // For inserting <CompatibleTarget, list<CodeObject>> entry in
2357  // OutputArchivesMap.
2358  if (!OutputArchivesMap.contains(CompatibleTarget)) {
2359 
2360  std::vector<NewArchiveMember> ArchiveMembers;
2361  ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
2362  OutputArchivesMap.insert_or_assign(CompatibleTarget,
2363  std::move(ArchiveMembers));
2364  } else {
2365  OutputArchivesMap[CompatibleTarget].push_back(
2366  NewArchiveMember(MemBufRef));
2367  }
2368  }
2369  }
2370 
2371  if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
2372  return Err;
2373 
2374  Expected<std::optional<StringRef>> NextTripleOrErr =
2375  FileHandler->ReadBundleStart(CodeObjectBuffer);
2376  if (!NextTripleOrErr)
2377  return NextTripleOrErr.takeError();
2378 
2379  CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
2380  } // End of processing of all bundle entries of this child of input archive.
2381  } // End of while over children of input archive.
2382 
2383  assert(!ArchiveErr && "Error occurred while reading archive!");
2384 
2385  /// Write out an archive for each target
2386  for (auto &Target : BundlerConfig.TargetNames) {
2387  StringRef FileName = TargetOutputFileNameMap[Target];
2388  StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
2389  OutputArchivesMap.find(Target);
2390  if (CurArchiveMembers != OutputArchivesMap.end()) {
2391  if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
2392  SymtabWritingMode::NormalSymtab,
2394  false, nullptr))
2395  return WriteErr;
2396  } else if (!BundlerConfig.AllowMissingBundles) {
2397  std::string ErrMsg =
2398  Twine("no compatible code object found for the target '" + Target +
2399  "' in heterogeneous archive library: " + IFName)
2400  .str();
2401  return createStringError(inconvertibleErrorCode(), ErrMsg);
2402  } else { // Create an empty archive file if no compatible code object is
2403  // found and "allow-missing-bundles" is enabled. It ensures that
2404  // the linker using output of this step doesn't complain about
2405  // the missing input file.
2406  std::vector<llvm::NewArchiveMember> EmptyArchive;
2407  EmptyArchive.clear();
2408  if (Error WriteErr = writeArchive(
2409  FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
2410  getDefaultArchiveKindForHost(), true, false, nullptr))
2411  return WriteErr;
2412  }
2413  }
2414 
2415  return Error::success();
2416 }
StringRef P
unsigned Offset
Definition: Format.cpp:2978
llvm::MachO::Target Target
Definition: MachO.h:50
static std::string getDeviceLibraryFileName(StringRef BundleFileName, StringRef Device)
static std::unique_ptr< FileHandler > CreateObjectFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate object file handler.
#define SYMBOLS_SECTION_NAME
Section name which holds target symbol names.
static StringRef getDeviceFileExtension(StringRef Device, StringRef BundleFileName)
static bool FilesTypeIsArchive(const std::string &FilesType)
#define OFFLOAD_BUNDLER_MAGIC_STR
Magic string that marks the existence of offloading data.
bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, const OffloadTargetInfo &TargetInfo)
Checks if a code object CodeObjectInfo is compatible with a given target TargetInfo.
static Triple getTargetTriple(StringRef Target, const OffloadBundlerConfig &BC)
static llvm::TimerGroup ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group", "Timer group for clang offload bundler")
static Error CheckHeterogeneousArchive(StringRef ArchiveName, const OffloadBundlerConfig &BundlerConfig)
static bool FilesTypeIsArchiveToList(const std::string &FilesType)
static Archive::Kind getDefaultArchiveKindForHost()
static std::string formatWithCommas(unsigned long long Value)
static Expected< std::unique_ptr< FileHandler > > CreateFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate handler given the input files and options.
static bool getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, SmallVectorImpl< StringRef > &CompatibleTargets, const OffloadBundlerConfig &BundlerConfig)
Computes a list of targets among all given targets which are compatible with this code object.
This file defines an offload bundling API that bundles different files that relate with the same sour...
const char * Data
Defines version macros and version-related utility functions for Clang.
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
Archive file handler. Only unbundling is supported so far.
Error ReadHeader(MemoryBuffer &Input) override
Expected< std::optional< StringRef > > ReadBundleStart(MemoryBuffer &Input) override
Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) override
Error WriteHeader(raw_ostream &OS, ArrayRef< std::unique_ptr< MemoryBuffer >> Inputs) override
Error ReadBundleEnd(MemoryBuffer &Input) override
~ArchiveFileHandler()=default
Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) override
Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) override
Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) override
ArchiveFileHandler(const OffloadBundlerConfig &BC)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > compress(llvm::compression::Params P, const llvm::MemoryBuffer &Input, bool Verbose=false)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > decompress(const llvm::MemoryBuffer &Input, bool Verbose=false)
llvm::compression::Format CompressionFormat
std::vector< std::string > OutputFileNames
std::vector< std::string > TargetNames
std::vector< std::string > ExcludedTargetNames
std::vector< std::string > InputFileNames
llvm::Error BundleFiles()
Bundle the files. Return true if an error was found.
llvm::Error UnbundleArchive()
UnbundleArchive takes an archive file (".a") as input containing bundled code object files,...
static llvm::Error ListBundleIDsInFile(llvm::StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig)
const OffloadBundlerConfig & BundlerConfig
Exposes information about the current target.
Definition: TargetInfo.h:218
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
std::string toString(const til::SExpr *E)
ASTEdit remove(RangeSelector S)
Removes the source selected by S.
The JSON file list parser is used to communicate input to InstallAPI.
@ Create
'create' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
std::optional< llvm::StringRef > parseTargetID(const llvm::Triple &T, llvm::StringRef OffloadArch, llvm::StringMap< bool > *FeatureMap)
Parse a target ID to get processor and feature map.
Definition: TargetID.cpp:105
std::optional< std::pair< llvm::StringRef, llvm::StringRef > > getConflictTargetIDCombination(const std::set< llvm::StringRef > &TargetIDs)
Get the conflicted pair of target IDs for a compilation or a bundled code object, assuming TargetIDs ...
Definition: TargetID.cpp:145
CudaArch StringToCudaArch(llvm::StringRef S)
Definition: Cuda.cpp:169
llvm::Expected< bool > CheckBundledSection(const OffloadBundlerConfig &)
const FunctionProtoType * T
unsigned long uint64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
Definition: Format.h:5433
Obtain the offload kind, real machine triple, and an optional TargetID out of the target information ...
bool operator==(const OffloadTargetInfo &Target) const
bool isOffloadKindCompatible(const llvm::StringRef TargetOffloadKind) const
llvm::StringRef OffloadKind
std::string str() const
llvm::StringRef TargetID
const OffloadBundlerConfig & BundlerConfig