brintos

brintos / llvm-project-archived public Read only

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