xref: /llvm-project/clang/lib/Driver/OffloadBundler.cpp (revision e87b843811e147db8d1edd7fe2dd52bb90be6ebc)
10f3f357eSJacob Lambert //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===//
20f3f357eSJacob Lambert //
30f3f357eSJacob Lambert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40f3f357eSJacob Lambert // See https://llvm.org/LICENSE.txt for license information.
50f3f357eSJacob Lambert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60f3f357eSJacob Lambert //
70f3f357eSJacob Lambert //===----------------------------------------------------------------------===//
80f3f357eSJacob Lambert ///
90f3f357eSJacob Lambert /// \file
100f3f357eSJacob Lambert /// This file implements an offload bundling API that bundles different files
110f3f357eSJacob Lambert /// that relate with the same source code but different targets into a single
120f3f357eSJacob Lambert /// one. Also the implements the opposite functionality, i.e. unbundle files
130f3f357eSJacob Lambert /// previous created by this API.
140f3f357eSJacob Lambert ///
150f3f357eSJacob Lambert //===----------------------------------------------------------------------===//
160f3f357eSJacob Lambert 
175b11caa8Sraghavmedicherla #include "clang/Driver/OffloadBundler.h"
180f3f357eSJacob Lambert #include "clang/Basic/Cuda.h"
19844b84afSYaxun (Sam) Liu #include "clang/Basic/TargetID.h"
200f3f357eSJacob Lambert #include "llvm/ADT/ArrayRef.h"
210f3f357eSJacob Lambert #include "llvm/ADT/SmallString.h"
220f3f357eSJacob Lambert #include "llvm/ADT/SmallVector.h"
237e282343SYaxun (Sam) Liu #include "llvm/ADT/StringExtras.h"
240f3f357eSJacob Lambert #include "llvm/ADT/StringMap.h"
250f3f357eSJacob Lambert #include "llvm/ADT/StringRef.h"
267e282343SYaxun (Sam) Liu #include "llvm/BinaryFormat/Magic.h"
270f3f357eSJacob Lambert #include "llvm/Object/Archive.h"
280f3f357eSJacob Lambert #include "llvm/Object/ArchiveWriter.h"
290f3f357eSJacob Lambert #include "llvm/Object/Binary.h"
300f3f357eSJacob Lambert #include "llvm/Object/ObjectFile.h"
310f3f357eSJacob Lambert #include "llvm/Support/Casting.h"
327e282343SYaxun (Sam) Liu #include "llvm/Support/Compression.h"
330f3f357eSJacob Lambert #include "llvm/Support/Debug.h"
34e68fc86bSBenjamin Kramer #include "llvm/Support/EndianStream.h"
350f3f357eSJacob Lambert #include "llvm/Support/Errc.h"
360f3f357eSJacob Lambert #include "llvm/Support/Error.h"
370f3f357eSJacob Lambert #include "llvm/Support/ErrorOr.h"
380f3f357eSJacob Lambert #include "llvm/Support/FileSystem.h"
397e282343SYaxun (Sam) Liu #include "llvm/Support/MD5.h"
4052c338daSmacurtis-amd #include "llvm/Support/ManagedStatic.h"
410f3f357eSJacob Lambert #include "llvm/Support/MemoryBuffer.h"
420f3f357eSJacob Lambert #include "llvm/Support/Path.h"
430f3f357eSJacob Lambert #include "llvm/Support/Program.h"
440f3f357eSJacob Lambert #include "llvm/Support/Signals.h"
450f3f357eSJacob Lambert #include "llvm/Support/StringSaver.h"
467e282343SYaxun (Sam) Liu #include "llvm/Support/Timer.h"
470f3f357eSJacob Lambert #include "llvm/Support/WithColor.h"
480f3f357eSJacob Lambert #include "llvm/Support/raw_ostream.h"
49d768bf99SArchibald Elliott #include "llvm/TargetParser/Host.h"
5062c7f035SArchibald Elliott #include "llvm/TargetParser/Triple.h"
510f3f357eSJacob Lambert #include <algorithm>
520f3f357eSJacob Lambert #include <cassert>
530f3f357eSJacob Lambert #include <cstddef>
540f3f357eSJacob Lambert #include <cstdint>
550f3f357eSJacob Lambert #include <forward_list>
567e282343SYaxun (Sam) Liu #include <llvm/Support/Process.h>
570f3f357eSJacob Lambert #include <memory>
580f3f357eSJacob Lambert #include <set>
590f3f357eSJacob Lambert #include <string>
600f3f357eSJacob Lambert #include <system_error>
610f3f357eSJacob Lambert #include <utility>
620f3f357eSJacob Lambert 
630f3f357eSJacob Lambert using namespace llvm;
640f3f357eSJacob Lambert using namespace llvm::object;
650f3f357eSJacob Lambert using namespace clang;
660f3f357eSJacob Lambert 
6752c338daSmacurtis-amd namespace {
6852c338daSmacurtis-amd struct CreateClangOffloadBundlerTimerGroup {
6952c338daSmacurtis-amd   static void *call() {
7052c338daSmacurtis-amd     return new TimerGroup("Clang Offload Bundler Timer Group",
717e282343SYaxun (Sam) Liu                           "Timer group for clang offload bundler");
7252c338daSmacurtis-amd   }
7352c338daSmacurtis-amd };
7452c338daSmacurtis-amd } // namespace
7552c338daSmacurtis-amd static llvm::ManagedStatic<llvm::TimerGroup,
7652c338daSmacurtis-amd                            CreateClangOffloadBundlerTimerGroup>
7752c338daSmacurtis-amd     ClangOffloadBundlerTimerGroup;
787e282343SYaxun (Sam) Liu 
790f3f357eSJacob Lambert /// Magic string that marks the existence of offloading data.
800f3f357eSJacob Lambert #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
810f3f357eSJacob Lambert 
820f3f357eSJacob Lambert OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
830f3f357eSJacob Lambert                                      const OffloadBundlerConfig &BC)
840f3f357eSJacob Lambert     : BundlerConfig(BC) {
850f3f357eSJacob Lambert 
860f3f357eSJacob Lambert   // TODO: Add error checking from ClangOffloadBundler.cpp
870f3f357eSJacob Lambert   auto TargetFeatures = Target.split(':');
880f3f357eSJacob Lambert   auto TripleOrGPU = TargetFeatures.first.rsplit('-');
890f3f357eSJacob Lambert 
90ab200864SJakub Chlanda   if (clang::StringToOffloadArch(TripleOrGPU.second) !=
91ab200864SJakub Chlanda       clang::OffloadArch::UNKNOWN) {
920f3f357eSJacob Lambert     auto KindTriple = TripleOrGPU.first.split('-');
930f3f357eSJacob Lambert     this->OffloadKind = KindTriple.first;
94e48ae0dbSJacob Lambert 
95e48ae0dbSJacob Lambert     // Enforce optional env field to standardize bundles
96e48ae0dbSJacob Lambert     llvm::Triple t = llvm::Triple(KindTriple.second);
97e48ae0dbSJacob Lambert     this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(),
98e48ae0dbSJacob Lambert                                 t.getOSName(), t.getEnvironmentName());
99e48ae0dbSJacob Lambert 
100844b84afSYaxun (Sam) Liu     this->TargetID = Target.substr(Target.find(TripleOrGPU.second));
1010f3f357eSJacob Lambert   } else {
1020f3f357eSJacob Lambert     auto KindTriple = TargetFeatures.first.split('-');
1030f3f357eSJacob Lambert     this->OffloadKind = KindTriple.first;
104e48ae0dbSJacob Lambert 
105e48ae0dbSJacob Lambert     // Enforce optional env field to standardize bundles
106e48ae0dbSJacob Lambert     llvm::Triple t = llvm::Triple(KindTriple.second);
107e48ae0dbSJacob Lambert     this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(),
108e48ae0dbSJacob Lambert                                 t.getOSName(), t.getEnvironmentName());
109e48ae0dbSJacob Lambert 
110844b84afSYaxun (Sam) Liu     this->TargetID = "";
1110f3f357eSJacob Lambert   }
1120f3f357eSJacob Lambert }
1130f3f357eSJacob Lambert 
1140f3f357eSJacob Lambert bool OffloadTargetInfo::hasHostKind() const {
1150f3f357eSJacob Lambert   return this->OffloadKind == "host";
1160f3f357eSJacob Lambert }
1170f3f357eSJacob Lambert 
1180f3f357eSJacob Lambert bool OffloadTargetInfo::isOffloadKindValid() const {
1190f3f357eSJacob Lambert   return OffloadKind == "host" || OffloadKind == "openmp" ||
1200f3f357eSJacob Lambert          OffloadKind == "hip" || OffloadKind == "hipv4";
1210f3f357eSJacob Lambert }
1220f3f357eSJacob Lambert 
1230f3f357eSJacob Lambert bool OffloadTargetInfo::isOffloadKindCompatible(
1240f3f357eSJacob Lambert     const StringRef TargetOffloadKind) const {
125ca391753SYaxun (Sam) Liu   if ((OffloadKind == TargetOffloadKind) ||
126ca391753SYaxun (Sam) Liu       (OffloadKind == "hip" && TargetOffloadKind == "hipv4") ||
127ca391753SYaxun (Sam) Liu       (OffloadKind == "hipv4" && TargetOffloadKind == "hip"))
1280f3f357eSJacob Lambert     return true;
129ca391753SYaxun (Sam) Liu 
1300f3f357eSJacob Lambert   if (BundlerConfig.HipOpenmpCompatible) {
131ed1539c6SKazu Hirata     bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") &&
1320f3f357eSJacob Lambert                                    TargetOffloadKind == "openmp";
1330f3f357eSJacob Lambert     bool OpenMPCompatibleWithHIP =
1340f3f357eSJacob Lambert         OffloadKind == "openmp" &&
135ed1539c6SKazu Hirata         TargetOffloadKind.starts_with_insensitive("hip");
1360f3f357eSJacob Lambert     return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
1370f3f357eSJacob Lambert   }
1380f3f357eSJacob Lambert   return false;
1390f3f357eSJacob Lambert }
1400f3f357eSJacob Lambert 
1410f3f357eSJacob Lambert bool OffloadTargetInfo::isTripleValid() const {
1420f3f357eSJacob Lambert   return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
1430f3f357eSJacob Lambert }
1440f3f357eSJacob Lambert 
1450f3f357eSJacob Lambert bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const {
1460f3f357eSJacob Lambert   return OffloadKind == Target.OffloadKind &&
147844b84afSYaxun (Sam) Liu          Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID;
1480f3f357eSJacob Lambert }
1490f3f357eSJacob Lambert 
150844b84afSYaxun (Sam) Liu std::string OffloadTargetInfo::str() const {
151844b84afSYaxun (Sam) Liu   return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str();
1520f3f357eSJacob Lambert }
1530f3f357eSJacob Lambert 
1540f3f357eSJacob Lambert static StringRef getDeviceFileExtension(StringRef Device,
1550f3f357eSJacob Lambert                                         StringRef BundleFileName) {
1560f3f357eSJacob Lambert   if (Device.contains("gfx"))
1570f3f357eSJacob Lambert     return ".bc";
1580f3f357eSJacob Lambert   if (Device.contains("sm_"))
1590f3f357eSJacob Lambert     return ".cubin";
1600f3f357eSJacob Lambert   return sys::path::extension(BundleFileName);
1610f3f357eSJacob Lambert }
1620f3f357eSJacob Lambert 
1630f3f357eSJacob Lambert static std::string getDeviceLibraryFileName(StringRef BundleFileName,
1640f3f357eSJacob Lambert                                             StringRef Device) {
1650f3f357eSJacob Lambert   StringRef LibName = sys::path::stem(BundleFileName);
1660f3f357eSJacob Lambert   StringRef Extension = getDeviceFileExtension(Device, BundleFileName);
1670f3f357eSJacob Lambert 
1680f3f357eSJacob Lambert   std::string Result;
1690f3f357eSJacob Lambert   Result += LibName;
1700f3f357eSJacob Lambert   Result += Extension;
1710f3f357eSJacob Lambert   return Result;
1720f3f357eSJacob Lambert }
1730f3f357eSJacob Lambert 
174b6942a28SBenjamin Kramer namespace {
1750f3f357eSJacob Lambert /// Generic file handler interface.
1760f3f357eSJacob Lambert class FileHandler {
1770f3f357eSJacob Lambert public:
1780f3f357eSJacob Lambert   struct BundleInfo {
1790f3f357eSJacob Lambert     StringRef BundleID;
1800f3f357eSJacob Lambert   };
1810f3f357eSJacob Lambert 
1820f3f357eSJacob Lambert   FileHandler() {}
1830f3f357eSJacob Lambert 
1840f3f357eSJacob Lambert   virtual ~FileHandler() {}
1850f3f357eSJacob Lambert 
1860f3f357eSJacob Lambert   /// Update the file handler with information from the header of the bundled
1870f3f357eSJacob Lambert   /// file.
1880f3f357eSJacob Lambert   virtual Error ReadHeader(MemoryBuffer &Input) = 0;
1890f3f357eSJacob Lambert 
1900f3f357eSJacob Lambert   /// Read the marker of the next bundled to be read in the file. The bundle
19137a3e98cSKazu Hirata   /// name is returned if there is one in the file, or `std::nullopt` if there
19237a3e98cSKazu Hirata   /// are no more bundles to be read.
1932c5d49cfSFangrui Song   virtual Expected<std::optional<StringRef>>
1940f3f357eSJacob Lambert   ReadBundleStart(MemoryBuffer &Input) = 0;
1950f3f357eSJacob Lambert 
1960f3f357eSJacob Lambert   /// Read the marker that closes the current bundle.
1970f3f357eSJacob Lambert   virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
1980f3f357eSJacob Lambert 
1990f3f357eSJacob Lambert   /// Read the current bundle and write the result into the stream \a OS.
2000f3f357eSJacob Lambert   virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
2010f3f357eSJacob Lambert 
2020f3f357eSJacob Lambert   /// Write the header of the bundled file to \a OS based on the information
2030f3f357eSJacob Lambert   /// gathered from \a Inputs.
2047e282343SYaxun (Sam) Liu   virtual Error WriteHeader(raw_ostream &OS,
2050f3f357eSJacob Lambert                             ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
2060f3f357eSJacob Lambert 
2070f3f357eSJacob Lambert   /// Write the marker that initiates a bundle for the triple \a TargetTriple to
2080f3f357eSJacob Lambert   /// \a OS.
2097e282343SYaxun (Sam) Liu   virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0;
2100f3f357eSJacob Lambert 
2110f3f357eSJacob Lambert   /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
2120f3f357eSJacob Lambert   /// OS.
2137e282343SYaxun (Sam) Liu   virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0;
2140f3f357eSJacob Lambert 
2150f3f357eSJacob Lambert   /// Write the bundle from \a Input into \a OS.
2167e282343SYaxun (Sam) Liu   virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
2177e282343SYaxun (Sam) Liu 
2187e282343SYaxun (Sam) Liu   /// Finalize output file.
2197e282343SYaxun (Sam) Liu   virtual Error finalizeOutputFile() { return Error::success(); }
2200f3f357eSJacob Lambert 
2210f3f357eSJacob Lambert   /// List bundle IDs in \a Input.
2220f3f357eSJacob Lambert   virtual Error listBundleIDs(MemoryBuffer &Input) {
2230f3f357eSJacob Lambert     if (Error Err = ReadHeader(Input))
2240f3f357eSJacob Lambert       return Err;
2250f3f357eSJacob Lambert     return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
2260f3f357eSJacob Lambert       llvm::outs() << Info.BundleID << '\n';
2270f3f357eSJacob Lambert       Error Err = listBundleIDsCallback(Input, Info);
2280f3f357eSJacob Lambert       if (Err)
2290f3f357eSJacob Lambert         return Err;
2300f3f357eSJacob Lambert       return Error::success();
2310f3f357eSJacob Lambert     });
2320f3f357eSJacob Lambert   }
2330f3f357eSJacob Lambert 
2343cf19097SJacob Lambert   /// Get bundle IDs in \a Input in \a BundleIds.
2353cf19097SJacob Lambert   virtual Error getBundleIDs(MemoryBuffer &Input,
2363cf19097SJacob Lambert                              std::set<StringRef> &BundleIds) {
2373cf19097SJacob Lambert     if (Error Err = ReadHeader(Input))
2383cf19097SJacob Lambert       return Err;
2393cf19097SJacob Lambert     return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
2403cf19097SJacob Lambert       BundleIds.insert(Info.BundleID);
2413cf19097SJacob Lambert       Error Err = listBundleIDsCallback(Input, Info);
2423cf19097SJacob Lambert       if (Err)
2433cf19097SJacob Lambert         return Err;
2443cf19097SJacob Lambert       return Error::success();
2453cf19097SJacob Lambert     });
2463cf19097SJacob Lambert   }
2473cf19097SJacob Lambert 
2480f3f357eSJacob Lambert   /// For each bundle in \a Input, do \a Func.
2490f3f357eSJacob Lambert   Error forEachBundle(MemoryBuffer &Input,
2500f3f357eSJacob Lambert                       std::function<Error(const BundleInfo &)> Func) {
2510f3f357eSJacob Lambert     while (true) {
2522c5d49cfSFangrui Song       Expected<std::optional<StringRef>> CurTripleOrErr =
2532c5d49cfSFangrui Song           ReadBundleStart(Input);
2540f3f357eSJacob Lambert       if (!CurTripleOrErr)
2550f3f357eSJacob Lambert         return CurTripleOrErr.takeError();
2560f3f357eSJacob Lambert 
2570f3f357eSJacob Lambert       // No more bundles.
2580f3f357eSJacob Lambert       if (!*CurTripleOrErr)
2590f3f357eSJacob Lambert         break;
2600f3f357eSJacob Lambert 
2610f3f357eSJacob Lambert       StringRef CurTriple = **CurTripleOrErr;
2620f3f357eSJacob Lambert       assert(!CurTriple.empty());
2630f3f357eSJacob Lambert 
2640f3f357eSJacob Lambert       BundleInfo Info{CurTriple};
2650f3f357eSJacob Lambert       if (Error Err = Func(Info))
2660f3f357eSJacob Lambert         return Err;
2670f3f357eSJacob Lambert     }
2680f3f357eSJacob Lambert     return Error::success();
2690f3f357eSJacob Lambert   }
2700f3f357eSJacob Lambert 
2710f3f357eSJacob Lambert protected:
2720f3f357eSJacob Lambert   virtual Error listBundleIDsCallback(MemoryBuffer &Input,
2730f3f357eSJacob Lambert                                       const BundleInfo &Info) {
2740f3f357eSJacob Lambert     return Error::success();
2750f3f357eSJacob Lambert   }
2760f3f357eSJacob Lambert };
2770f3f357eSJacob Lambert 
2780f3f357eSJacob Lambert /// Handler for binary files. The bundled file will have the following format
2790f3f357eSJacob Lambert /// (all integers are stored in little-endian format):
2800f3f357eSJacob Lambert ///
2810f3f357eSJacob Lambert /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
2820f3f357eSJacob Lambert ///
2830f3f357eSJacob Lambert /// NumberOfOffloadBundles (8-byte integer)
2840f3f357eSJacob Lambert ///
2850f3f357eSJacob Lambert /// OffsetOfBundle1 (8-byte integer)
2860f3f357eSJacob Lambert /// SizeOfBundle1 (8-byte integer)
2870f3f357eSJacob Lambert /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
2880f3f357eSJacob Lambert /// TripleOfBundle1 (byte length defined before)
2890f3f357eSJacob Lambert ///
2900f3f357eSJacob Lambert /// ...
2910f3f357eSJacob Lambert ///
2920f3f357eSJacob Lambert /// OffsetOfBundleN (8-byte integer)
2930f3f357eSJacob Lambert /// SizeOfBundleN (8-byte integer)
2940f3f357eSJacob Lambert /// NumberOfBytesInTripleOfBundleN (8-byte integer)
2950f3f357eSJacob Lambert /// TripleOfBundleN (byte length defined before)
2960f3f357eSJacob Lambert ///
2970f3f357eSJacob Lambert /// Bundle1
2980f3f357eSJacob Lambert /// ...
2990f3f357eSJacob Lambert /// BundleN
3000f3f357eSJacob Lambert 
3010f3f357eSJacob Lambert /// Read 8-byte integers from a buffer in little-endian format.
3020f3f357eSJacob Lambert static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
303e68fc86bSBenjamin Kramer   return llvm::support::endian::read64le(Buffer.data() + pos);
3040f3f357eSJacob Lambert }
3050f3f357eSJacob Lambert 
3060f3f357eSJacob Lambert /// Write 8-byte integers to a buffer in little-endian format.
3077e282343SYaxun (Sam) Liu static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
308b8885926SKazu Hirata   llvm::support::endian::write(OS, Val, llvm::endianness::little);
3090f3f357eSJacob Lambert }
3100f3f357eSJacob Lambert 
3110f3f357eSJacob Lambert class BinaryFileHandler final : public FileHandler {
3120f3f357eSJacob Lambert   /// Information about the bundles extracted from the header.
3130f3f357eSJacob Lambert   struct BinaryBundleInfo final : public BundleInfo {
3140f3f357eSJacob Lambert     /// Size of the bundle.
3150f3f357eSJacob Lambert     uint64_t Size = 0u;
3160f3f357eSJacob Lambert     /// Offset at which the bundle starts in the bundled file.
3170f3f357eSJacob Lambert     uint64_t Offset = 0u;
3180f3f357eSJacob Lambert 
3190f3f357eSJacob Lambert     BinaryBundleInfo() {}
3200f3f357eSJacob Lambert     BinaryBundleInfo(uint64_t Size, uint64_t Offset)
3210f3f357eSJacob Lambert         : Size(Size), Offset(Offset) {}
3220f3f357eSJacob Lambert   };
3230f3f357eSJacob Lambert 
3240f3f357eSJacob Lambert   /// Map between a triple and the corresponding bundle information.
3250f3f357eSJacob Lambert   StringMap<BinaryBundleInfo> BundlesInfo;
3260f3f357eSJacob Lambert 
3270f3f357eSJacob Lambert   /// Iterator for the bundle information that is being read.
3280f3f357eSJacob Lambert   StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
3290f3f357eSJacob Lambert   StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
3300f3f357eSJacob Lambert 
3310f3f357eSJacob Lambert   /// Current bundle target to be written.
3320f3f357eSJacob Lambert   std::string CurWriteBundleTarget;
3330f3f357eSJacob Lambert 
3340f3f357eSJacob Lambert   /// Configuration options and arrays for this bundler job
3350f3f357eSJacob Lambert   const OffloadBundlerConfig &BundlerConfig;
3360f3f357eSJacob Lambert 
3370f3f357eSJacob Lambert public:
3380f3f357eSJacob Lambert   // TODO: Add error checking from ClangOffloadBundler.cpp
3390f3f357eSJacob Lambert   BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
3400f3f357eSJacob Lambert 
3410f3f357eSJacob Lambert   ~BinaryFileHandler() final {}
3420f3f357eSJacob Lambert 
3430f3f357eSJacob Lambert   Error ReadHeader(MemoryBuffer &Input) final {
3440f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
3450f3f357eSJacob Lambert 
3460f3f357eSJacob Lambert     // Initialize the current bundle with the end of the container.
3470f3f357eSJacob Lambert     CurBundleInfo = BundlesInfo.end();
3480f3f357eSJacob Lambert 
3490f3f357eSJacob Lambert     // Check if buffer is smaller than magic string.
3500f3f357eSJacob Lambert     size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
3510f3f357eSJacob Lambert     if (ReadChars > FC.size())
3520f3f357eSJacob Lambert       return Error::success();
3530f3f357eSJacob Lambert 
3540f3f357eSJacob Lambert     // Check if no magic was found.
3557e282343SYaxun (Sam) Liu     if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
3560f3f357eSJacob Lambert       return Error::success();
3570f3f357eSJacob Lambert 
3580f3f357eSJacob Lambert     // Read number of bundles.
3590f3f357eSJacob Lambert     if (ReadChars + 8 > FC.size())
3600f3f357eSJacob Lambert       return Error::success();
3610f3f357eSJacob Lambert 
3620f3f357eSJacob Lambert     uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
3630f3f357eSJacob Lambert     ReadChars += 8;
3640f3f357eSJacob Lambert 
3650f3f357eSJacob Lambert     // Read bundle offsets, sizes and triples.
3660f3f357eSJacob Lambert     for (uint64_t i = 0; i < NumberOfBundles; ++i) {
3670f3f357eSJacob Lambert 
3680f3f357eSJacob Lambert       // Read offset.
3690f3f357eSJacob Lambert       if (ReadChars + 8 > FC.size())
3700f3f357eSJacob Lambert         return Error::success();
3710f3f357eSJacob Lambert 
3720f3f357eSJacob Lambert       uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
3730f3f357eSJacob Lambert       ReadChars += 8;
3740f3f357eSJacob Lambert 
3750f3f357eSJacob Lambert       // Read size.
3760f3f357eSJacob Lambert       if (ReadChars + 8 > FC.size())
3770f3f357eSJacob Lambert         return Error::success();
3780f3f357eSJacob Lambert 
3790f3f357eSJacob Lambert       uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
3800f3f357eSJacob Lambert       ReadChars += 8;
3810f3f357eSJacob Lambert 
3820f3f357eSJacob Lambert       // Read triple size.
3830f3f357eSJacob Lambert       if (ReadChars + 8 > FC.size())
3840f3f357eSJacob Lambert         return Error::success();
3850f3f357eSJacob Lambert 
3860f3f357eSJacob Lambert       uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
3870f3f357eSJacob Lambert       ReadChars += 8;
3880f3f357eSJacob Lambert 
3890f3f357eSJacob Lambert       // Read triple.
3900f3f357eSJacob Lambert       if (ReadChars + TripleSize > FC.size())
3910f3f357eSJacob Lambert         return Error::success();
3920f3f357eSJacob Lambert 
3930f3f357eSJacob Lambert       StringRef Triple(&FC.data()[ReadChars], TripleSize);
3940f3f357eSJacob Lambert       ReadChars += TripleSize;
3950f3f357eSJacob Lambert 
3960f3f357eSJacob Lambert       // Check if the offset and size make sense.
3970f3f357eSJacob Lambert       if (!Offset || Offset + Size > FC.size())
3980f3f357eSJacob Lambert         return Error::success();
3990f3f357eSJacob Lambert 
4007eaa7b05SKazu Hirata       assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??");
4010f3f357eSJacob Lambert       BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
4020f3f357eSJacob Lambert     }
4030f3f357eSJacob Lambert     // Set the iterator to where we will start to read.
4040f3f357eSJacob Lambert     CurBundleInfo = BundlesInfo.end();
4050f3f357eSJacob Lambert     NextBundleInfo = BundlesInfo.begin();
4060f3f357eSJacob Lambert     return Error::success();
4070f3f357eSJacob Lambert   }
4080f3f357eSJacob Lambert 
4092c5d49cfSFangrui Song   Expected<std::optional<StringRef>>
4102c5d49cfSFangrui Song   ReadBundleStart(MemoryBuffer &Input) final {
4110f3f357eSJacob Lambert     if (NextBundleInfo == BundlesInfo.end())
4120c2f6e36SFangrui Song       return std::nullopt;
4130f3f357eSJacob Lambert     CurBundleInfo = NextBundleInfo++;
4140f3f357eSJacob Lambert     return CurBundleInfo->first();
4150f3f357eSJacob Lambert   }
4160f3f357eSJacob Lambert 
4170f3f357eSJacob Lambert   Error ReadBundleEnd(MemoryBuffer &Input) final {
4180f3f357eSJacob Lambert     assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
4190f3f357eSJacob Lambert     return Error::success();
4200f3f357eSJacob Lambert   }
4210f3f357eSJacob Lambert 
4220f3f357eSJacob Lambert   Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
4230f3f357eSJacob Lambert     assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
4240f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
4250f3f357eSJacob Lambert     OS.write(FC.data() + CurBundleInfo->second.Offset,
4260f3f357eSJacob Lambert              CurBundleInfo->second.Size);
4270f3f357eSJacob Lambert     return Error::success();
4280f3f357eSJacob Lambert   }
4290f3f357eSJacob Lambert 
4307e282343SYaxun (Sam) Liu   Error WriteHeader(raw_ostream &OS,
4310f3f357eSJacob Lambert                     ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
4320f3f357eSJacob Lambert 
4330f3f357eSJacob Lambert     // Compute size of the header.
4340f3f357eSJacob Lambert     uint64_t HeaderSize = 0;
4350f3f357eSJacob Lambert 
4360f3f357eSJacob Lambert     HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
4370f3f357eSJacob Lambert     HeaderSize += 8; // Number of Bundles
4380f3f357eSJacob Lambert 
4390f3f357eSJacob Lambert     for (auto &T : BundlerConfig.TargetNames) {
4400f3f357eSJacob Lambert       HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
4410f3f357eSJacob Lambert       HeaderSize += T.size(); // The triple.
4420f3f357eSJacob Lambert     }
4430f3f357eSJacob Lambert 
4440f3f357eSJacob Lambert     // Write to the buffer the header.
4450f3f357eSJacob Lambert     OS << OFFLOAD_BUNDLER_MAGIC_STR;
4460f3f357eSJacob Lambert 
4470f3f357eSJacob Lambert     Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
4480f3f357eSJacob Lambert 
4490f3f357eSJacob Lambert     unsigned Idx = 0;
4500f3f357eSJacob Lambert     for (auto &T : BundlerConfig.TargetNames) {
4510f3f357eSJacob Lambert       MemoryBuffer &MB = *Inputs[Idx++];
4520f3f357eSJacob Lambert       HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
4530f3f357eSJacob Lambert       // Bundle offset.
4540f3f357eSJacob Lambert       Write8byteIntegerToBuffer(OS, HeaderSize);
4550f3f357eSJacob Lambert       // Size of the bundle (adds to the next bundle's offset)
4560f3f357eSJacob Lambert       Write8byteIntegerToBuffer(OS, MB.getBufferSize());
4570f3f357eSJacob Lambert       BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
4580f3f357eSJacob Lambert       HeaderSize += MB.getBufferSize();
4590f3f357eSJacob Lambert       // Size of the triple
4600f3f357eSJacob Lambert       Write8byteIntegerToBuffer(OS, T.size());
4610f3f357eSJacob Lambert       // Triple
4620f3f357eSJacob Lambert       OS << T;
4630f3f357eSJacob Lambert     }
4640f3f357eSJacob Lambert     return Error::success();
4650f3f357eSJacob Lambert   }
4660f3f357eSJacob Lambert 
4677e282343SYaxun (Sam) Liu   Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
4680f3f357eSJacob Lambert     CurWriteBundleTarget = TargetTriple.str();
4690f3f357eSJacob Lambert     return Error::success();
4700f3f357eSJacob Lambert   }
4710f3f357eSJacob Lambert 
4727e282343SYaxun (Sam) Liu   Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
4730f3f357eSJacob Lambert     return Error::success();
4740f3f357eSJacob Lambert   }
4750f3f357eSJacob Lambert 
4767e282343SYaxun (Sam) Liu   Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
4770f3f357eSJacob Lambert     auto BI = BundlesInfo[CurWriteBundleTarget];
4787e282343SYaxun (Sam) Liu 
4797e282343SYaxun (Sam) Liu     // Pad with 0 to reach specified offset.
4807e282343SYaxun (Sam) Liu     size_t CurrentPos = OS.tell();
4817e282343SYaxun (Sam) Liu     size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0;
4827e282343SYaxun (Sam) Liu     for (size_t I = 0; I < PaddingSize; ++I)
4837e282343SYaxun (Sam) Liu       OS.write('\0');
4847e282343SYaxun (Sam) Liu     assert(OS.tell() == BI.Offset);
4857e282343SYaxun (Sam) Liu 
4860f3f357eSJacob Lambert     OS.write(Input.getBufferStart(), Input.getBufferSize());
4877e282343SYaxun (Sam) Liu 
4880f3f357eSJacob Lambert     return Error::success();
4890f3f357eSJacob Lambert   }
4900f3f357eSJacob Lambert };
4910f3f357eSJacob Lambert 
4920f3f357eSJacob Lambert // This class implements a list of temporary files that are removed upon
4930f3f357eSJacob Lambert // object destruction.
4940f3f357eSJacob Lambert class TempFileHandlerRAII {
4950f3f357eSJacob Lambert public:
4960f3f357eSJacob Lambert   ~TempFileHandlerRAII() {
4970f3f357eSJacob Lambert     for (const auto &File : Files)
4980f3f357eSJacob Lambert       sys::fs::remove(File);
4990f3f357eSJacob Lambert   }
5000f3f357eSJacob Lambert 
5010f3f357eSJacob Lambert   // Creates temporary file with given contents.
5022c5d49cfSFangrui Song   Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) {
5030f3f357eSJacob Lambert     SmallString<128u> File;
5040f3f357eSJacob Lambert     if (std::error_code EC =
5050f3f357eSJacob Lambert             sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
5060f3f357eSJacob Lambert       return createFileError(File, EC);
5070f3f357eSJacob Lambert     Files.push_front(File);
5080f3f357eSJacob Lambert 
5090f3f357eSJacob Lambert     if (Contents) {
5100f3f357eSJacob Lambert       std::error_code EC;
5110f3f357eSJacob Lambert       raw_fd_ostream OS(File, EC);
5120f3f357eSJacob Lambert       if (EC)
5130f3f357eSJacob Lambert         return createFileError(File, EC);
5140f3f357eSJacob Lambert       OS.write(Contents->data(), Contents->size());
5150f3f357eSJacob Lambert     }
5160f3f357eSJacob Lambert     return Files.front().str();
5170f3f357eSJacob Lambert   }
5180f3f357eSJacob Lambert 
5190f3f357eSJacob Lambert private:
5200f3f357eSJacob Lambert   std::forward_list<SmallString<128u>> Files;
5210f3f357eSJacob Lambert };
5220f3f357eSJacob Lambert 
5230f3f357eSJacob Lambert /// Handler for object files. The bundles are organized by sections with a
5240f3f357eSJacob Lambert /// designated name.
5250f3f357eSJacob Lambert ///
5260f3f357eSJacob Lambert /// To unbundle, we just copy the contents of the designated section.
5270f3f357eSJacob Lambert class ObjectFileHandler final : public FileHandler {
5280f3f357eSJacob Lambert 
5290f3f357eSJacob Lambert   /// The object file we are currently dealing with.
5300f3f357eSJacob Lambert   std::unique_ptr<ObjectFile> Obj;
5310f3f357eSJacob Lambert 
5320f3f357eSJacob Lambert   /// Return the input file contents.
5330f3f357eSJacob Lambert   StringRef getInputFileContents() const { return Obj->getData(); }
5340f3f357eSJacob Lambert 
5350f3f357eSJacob Lambert   /// Return bundle name (<kind>-<triple>) if the provided section is an offload
5360f3f357eSJacob Lambert   /// section.
5372c5d49cfSFangrui Song   static Expected<std::optional<StringRef>>
5382c5d49cfSFangrui Song   IsOffloadSection(SectionRef CurSection) {
5390f3f357eSJacob Lambert     Expected<StringRef> NameOrErr = CurSection.getName();
5400f3f357eSJacob Lambert     if (!NameOrErr)
5410f3f357eSJacob Lambert       return NameOrErr.takeError();
5420f3f357eSJacob Lambert 
5430f3f357eSJacob Lambert     // If it does not start with the reserved suffix, just skip this section.
5447e282343SYaxun (Sam) Liu     if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle)
5450c2f6e36SFangrui Song       return std::nullopt;
5460f3f357eSJacob Lambert 
5470f3f357eSJacob Lambert     // Return the triple that is right after the reserved prefix.
5480f3f357eSJacob Lambert     return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
5490f3f357eSJacob Lambert   }
5500f3f357eSJacob Lambert 
5510f3f357eSJacob Lambert   /// Total number of inputs.
5520f3f357eSJacob Lambert   unsigned NumberOfInputs = 0;
5530f3f357eSJacob Lambert 
5540f3f357eSJacob Lambert   /// Total number of processed inputs, i.e, inputs that were already
5550f3f357eSJacob Lambert   /// read from the buffers.
5560f3f357eSJacob Lambert   unsigned NumberOfProcessedInputs = 0;
5570f3f357eSJacob Lambert 
5580f3f357eSJacob Lambert   /// Iterator of the current and next section.
5590f3f357eSJacob Lambert   section_iterator CurrentSection;
5600f3f357eSJacob Lambert   section_iterator NextSection;
5610f3f357eSJacob Lambert 
5620f3f357eSJacob Lambert   /// Configuration options and arrays for this bundler job
5630f3f357eSJacob Lambert   const OffloadBundlerConfig &BundlerConfig;
5640f3f357eSJacob Lambert 
5650f3f357eSJacob Lambert public:
5660f3f357eSJacob Lambert   // TODO: Add error checking from ClangOffloadBundler.cpp
5670f3f357eSJacob Lambert   ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
5680f3f357eSJacob Lambert                     const OffloadBundlerConfig &BC)
5690f3f357eSJacob Lambert       : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()),
5700f3f357eSJacob Lambert         NextSection(Obj->section_begin()), BundlerConfig(BC) {}
5710f3f357eSJacob Lambert 
5720f3f357eSJacob Lambert   ~ObjectFileHandler() final {}
5730f3f357eSJacob Lambert 
5740f3f357eSJacob Lambert   Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
5750f3f357eSJacob Lambert 
5762c5d49cfSFangrui Song   Expected<std::optional<StringRef>>
5772c5d49cfSFangrui Song   ReadBundleStart(MemoryBuffer &Input) final {
5780f3f357eSJacob Lambert     while (NextSection != Obj->section_end()) {
5790f3f357eSJacob Lambert       CurrentSection = NextSection;
5800f3f357eSJacob Lambert       ++NextSection;
5810f3f357eSJacob Lambert 
5820f3f357eSJacob Lambert       // Check if the current section name starts with the reserved prefix. If
5830f3f357eSJacob Lambert       // so, return the triple.
5842c5d49cfSFangrui Song       Expected<std::optional<StringRef>> TripleOrErr =
5850f3f357eSJacob Lambert           IsOffloadSection(*CurrentSection);
5860f3f357eSJacob Lambert       if (!TripleOrErr)
5870f3f357eSJacob Lambert         return TripleOrErr.takeError();
5880f3f357eSJacob Lambert       if (*TripleOrErr)
5890f3f357eSJacob Lambert         return **TripleOrErr;
5900f3f357eSJacob Lambert     }
5910c2f6e36SFangrui Song     return std::nullopt;
5920f3f357eSJacob Lambert   }
5930f3f357eSJacob Lambert 
5940f3f357eSJacob Lambert   Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
5950f3f357eSJacob Lambert 
5960f3f357eSJacob Lambert   Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
5970f3f357eSJacob Lambert     Expected<StringRef> ContentOrErr = CurrentSection->getContents();
5980f3f357eSJacob Lambert     if (!ContentOrErr)
5990f3f357eSJacob Lambert       return ContentOrErr.takeError();
6000f3f357eSJacob Lambert     StringRef Content = *ContentOrErr;
6010f3f357eSJacob Lambert 
6020f3f357eSJacob Lambert     // Copy fat object contents to the output when extracting host bundle.
60333a6ce18SYaxun (Sam) Liu     std::string ModifiedContent;
60433a6ce18SYaxun (Sam) Liu     if (Content.size() == 1u && Content.front() == 0) {
60561b13e0dSYaxun (Sam) Liu       auto HostBundleOrErr = getHostBundle(
60661b13e0dSYaxun (Sam) Liu           StringRef(Input.getBufferStart(), Input.getBufferSize()));
60733a6ce18SYaxun (Sam) Liu       if (!HostBundleOrErr)
60833a6ce18SYaxun (Sam) Liu         return HostBundleOrErr.takeError();
60933a6ce18SYaxun (Sam) Liu 
61033a6ce18SYaxun (Sam) Liu       ModifiedContent = std::move(*HostBundleOrErr);
61133a6ce18SYaxun (Sam) Liu       Content = ModifiedContent;
61233a6ce18SYaxun (Sam) Liu     }
6130f3f357eSJacob Lambert 
6140f3f357eSJacob Lambert     OS.write(Content.data(), Content.size());
6150f3f357eSJacob Lambert     return Error::success();
6160f3f357eSJacob Lambert   }
6170f3f357eSJacob Lambert 
6187e282343SYaxun (Sam) Liu   Error WriteHeader(raw_ostream &OS,
6190f3f357eSJacob Lambert                     ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
6200f3f357eSJacob Lambert     assert(BundlerConfig.HostInputIndex != ~0u &&
6210f3f357eSJacob Lambert            "Host input index not defined.");
6220f3f357eSJacob Lambert 
6230f3f357eSJacob Lambert     // Record number of inputs.
6240f3f357eSJacob Lambert     NumberOfInputs = Inputs.size();
6250f3f357eSJacob Lambert     return Error::success();
6260f3f357eSJacob Lambert   }
6270f3f357eSJacob Lambert 
6287e282343SYaxun (Sam) Liu   Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
6290f3f357eSJacob Lambert     ++NumberOfProcessedInputs;
6300f3f357eSJacob Lambert     return Error::success();
6310f3f357eSJacob Lambert   }
6320f3f357eSJacob Lambert 
6337e282343SYaxun (Sam) Liu   Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
6347e282343SYaxun (Sam) Liu     return Error::success();
6357e282343SYaxun (Sam) Liu   }
6367e282343SYaxun (Sam) Liu 
6377e282343SYaxun (Sam) Liu   Error finalizeOutputFile() final {
6380f3f357eSJacob Lambert     assert(NumberOfProcessedInputs <= NumberOfInputs &&
6390f3f357eSJacob Lambert            "Processing more inputs that actually exist!");
6400f3f357eSJacob Lambert     assert(BundlerConfig.HostInputIndex != ~0u &&
6410f3f357eSJacob Lambert            "Host input index not defined.");
6420f3f357eSJacob Lambert 
6430f3f357eSJacob Lambert     // If this is not the last output, we don't have to do anything.
6440f3f357eSJacob Lambert     if (NumberOfProcessedInputs != NumberOfInputs)
6450f3f357eSJacob Lambert       return Error::success();
6460f3f357eSJacob Lambert 
6470f3f357eSJacob Lambert     // We will use llvm-objcopy to add target objects sections to the output
6480f3f357eSJacob Lambert     // fat object. These sections should have 'exclude' flag set which tells
6490f3f357eSJacob Lambert     // link editor to remove them from linker inputs when linking executable or
6500f3f357eSJacob Lambert     // shared library.
6510f3f357eSJacob Lambert 
6520f3f357eSJacob Lambert     assert(BundlerConfig.ObjcopyPath != "" &&
6530f3f357eSJacob Lambert            "llvm-objcopy path not specified");
6540f3f357eSJacob Lambert 
6550f3f357eSJacob Lambert     // Temporary files that need to be removed.
6560f3f357eSJacob Lambert     TempFileHandlerRAII TempFiles;
6570f3f357eSJacob Lambert 
6580f3f357eSJacob Lambert     // Compose llvm-objcopy command line for add target objects' sections with
6590f3f357eSJacob Lambert     // appropriate flags.
6600f3f357eSJacob Lambert     BumpPtrAllocator Alloc;
6610f3f357eSJacob Lambert     StringSaver SS{Alloc};
6620f3f357eSJacob Lambert     SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
6630f3f357eSJacob Lambert 
6640f3f357eSJacob Lambert     for (unsigned I = 0; I < NumberOfInputs; ++I) {
6650f3f357eSJacob Lambert       StringRef InputFile = BundlerConfig.InputFileNames[I];
6660f3f357eSJacob Lambert       if (I == BundlerConfig.HostInputIndex) {
6670f3f357eSJacob Lambert         // Special handling for the host bundle. We do not need to add a
6680f3f357eSJacob Lambert         // standard bundle for the host object since we are going to use fat
6690f3f357eSJacob Lambert         // object as a host object. Therefore use dummy contents (one zero byte)
6700f3f357eSJacob Lambert         // when creating section for the host bundle.
6710f3f357eSJacob Lambert         Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0));
6720f3f357eSJacob Lambert         if (!TempFileOrErr)
6730f3f357eSJacob Lambert           return TempFileOrErr.takeError();
6740f3f357eSJacob Lambert         InputFile = *TempFileOrErr;
6750f3f357eSJacob Lambert       }
6760f3f357eSJacob Lambert 
6775b11caa8Sraghavmedicherla       ObjcopyArgs.push_back(
6785b11caa8Sraghavmedicherla           SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR +
6795b11caa8Sraghavmedicherla                   BundlerConfig.TargetNames[I] + "=" + InputFile));
6805b11caa8Sraghavmedicherla       ObjcopyArgs.push_back(
6815b11caa8Sraghavmedicherla           SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR +
6825b11caa8Sraghavmedicherla                   BundlerConfig.TargetNames[I] + "=readonly,exclude"));
6830f3f357eSJacob Lambert     }
6840f3f357eSJacob Lambert     ObjcopyArgs.push_back("--");
6850f3f357eSJacob Lambert     ObjcopyArgs.push_back(
6860f3f357eSJacob Lambert         BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
6870f3f357eSJacob Lambert     ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
6880f3f357eSJacob Lambert 
6890f3f357eSJacob Lambert     if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
6900f3f357eSJacob Lambert       return Err;
6910f3f357eSJacob Lambert 
6920f3f357eSJacob Lambert     return Error::success();
6930f3f357eSJacob Lambert   }
6940f3f357eSJacob Lambert 
6957e282343SYaxun (Sam) Liu   Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
6960f3f357eSJacob Lambert     return Error::success();
6970f3f357eSJacob Lambert   }
6980f3f357eSJacob Lambert 
6990f3f357eSJacob Lambert private:
7000f3f357eSJacob Lambert   Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) {
7010f3f357eSJacob Lambert     // If the user asked for the commands to be printed out, we do that
7020f3f357eSJacob Lambert     // instead of executing it.
7030f3f357eSJacob Lambert     if (BundlerConfig.PrintExternalCommands) {
7040f3f357eSJacob Lambert       errs() << "\"" << Objcopy << "\"";
7050f3f357eSJacob Lambert       for (StringRef Arg : drop_begin(Args, 1))
7060f3f357eSJacob Lambert         errs() << " \"" << Arg << "\"";
7070f3f357eSJacob Lambert       errs() << "\n";
7080f3f357eSJacob Lambert     } else {
7090f3f357eSJacob Lambert       if (sys::ExecuteAndWait(Objcopy, Args))
7100f3f357eSJacob Lambert         return createStringError(inconvertibleErrorCode(),
7110f3f357eSJacob Lambert                                  "'llvm-objcopy' tool failed");
7120f3f357eSJacob Lambert     }
7130f3f357eSJacob Lambert     return Error::success();
7140f3f357eSJacob Lambert   }
71533a6ce18SYaxun (Sam) Liu 
71661b13e0dSYaxun (Sam) Liu   Expected<std::string> getHostBundle(StringRef Input) {
71733a6ce18SYaxun (Sam) Liu     TempFileHandlerRAII TempFiles;
71833a6ce18SYaxun (Sam) Liu 
71933a6ce18SYaxun (Sam) Liu     auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
72033a6ce18SYaxun (Sam) Liu     if (!ModifiedObjPathOrErr)
72133a6ce18SYaxun (Sam) Liu       return ModifiedObjPathOrErr.takeError();
72233a6ce18SYaxun (Sam) Liu     StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
72333a6ce18SYaxun (Sam) Liu 
72433a6ce18SYaxun (Sam) Liu     BumpPtrAllocator Alloc;
72533a6ce18SYaxun (Sam) Liu     StringSaver SS{Alloc};
72633a6ce18SYaxun (Sam) Liu     SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"};
72733a6ce18SYaxun (Sam) Liu 
72833a6ce18SYaxun (Sam) Liu     ObjcopyArgs.push_back("--regex");
72933a6ce18SYaxun (Sam) Liu     ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
73033a6ce18SYaxun (Sam) Liu     ObjcopyArgs.push_back("--");
73161b13e0dSYaxun (Sam) Liu 
73261b13e0dSYaxun (Sam) Liu     StringRef ObjcopyInputFileName;
73361b13e0dSYaxun (Sam) Liu     // When unbundling an archive, the content of each object file in the
73461b13e0dSYaxun (Sam) Liu     // archive is passed to this function by parameter Input, which is different
73561b13e0dSYaxun (Sam) Liu     // from the content of the original input archive file, therefore it needs
73661b13e0dSYaxun (Sam) Liu     // to be saved to a temporary file before passed to llvm-objcopy. Otherwise,
73761b13e0dSYaxun (Sam) Liu     // Input is the same as the content of the original input file, therefore
73861b13e0dSYaxun (Sam) Liu     // temporary file is not needed.
73961b13e0dSYaxun (Sam) Liu     if (StringRef(BundlerConfig.FilesType).starts_with("a")) {
74061b13e0dSYaxun (Sam) Liu       auto InputFileOrErr =
74161b13e0dSYaxun (Sam) Liu           TempFiles.Create(ArrayRef<char>(Input.data(), Input.size()));
74261b13e0dSYaxun (Sam) Liu       if (!InputFileOrErr)
74361b13e0dSYaxun (Sam) Liu         return InputFileOrErr.takeError();
74461b13e0dSYaxun (Sam) Liu       ObjcopyInputFileName = *InputFileOrErr;
74561b13e0dSYaxun (Sam) Liu     } else
74661b13e0dSYaxun (Sam) Liu       ObjcopyInputFileName = BundlerConfig.InputFileNames.front();
74761b13e0dSYaxun (Sam) Liu 
74861b13e0dSYaxun (Sam) Liu     ObjcopyArgs.push_back(ObjcopyInputFileName);
74933a6ce18SYaxun (Sam) Liu     ObjcopyArgs.push_back(ModifiedObjPath);
75033a6ce18SYaxun (Sam) Liu 
75133a6ce18SYaxun (Sam) Liu     if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
75233a6ce18SYaxun (Sam) Liu       return std::move(Err);
75333a6ce18SYaxun (Sam) Liu 
75433a6ce18SYaxun (Sam) Liu     auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
75533a6ce18SYaxun (Sam) Liu     if (!BufOrErr)
75633a6ce18SYaxun (Sam) Liu       return createStringError(BufOrErr.getError(),
75733a6ce18SYaxun (Sam) Liu                                "Failed to read back the modified object file");
75833a6ce18SYaxun (Sam) Liu 
75933a6ce18SYaxun (Sam) Liu     return BufOrErr->get()->getBuffer().str();
76033a6ce18SYaxun (Sam) Liu   }
7610f3f357eSJacob Lambert };
7620f3f357eSJacob Lambert 
7630f3f357eSJacob Lambert /// Handler for text files. The bundled file will have the following format.
7640f3f357eSJacob Lambert ///
7650f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
7660f3f357eSJacob Lambert /// Bundle 1
7670f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
7680f3f357eSJacob Lambert /// ...
7690f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
7700f3f357eSJacob Lambert /// Bundle N
7710f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
7720f3f357eSJacob Lambert class TextFileHandler final : public FileHandler {
7730f3f357eSJacob Lambert   /// String that begins a line comment.
7740f3f357eSJacob Lambert   StringRef Comment;
7750f3f357eSJacob Lambert 
7760f3f357eSJacob Lambert   /// String that initiates a bundle.
7770f3f357eSJacob Lambert   std::string BundleStartString;
7780f3f357eSJacob Lambert 
7790f3f357eSJacob Lambert   /// String that closes a bundle.
7800f3f357eSJacob Lambert   std::string BundleEndString;
7810f3f357eSJacob Lambert 
7820f3f357eSJacob Lambert   /// Number of chars read from input.
7830f3f357eSJacob Lambert   size_t ReadChars = 0u;
7840f3f357eSJacob Lambert 
7850f3f357eSJacob Lambert protected:
7860f3f357eSJacob Lambert   Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
7870f3f357eSJacob Lambert 
7882c5d49cfSFangrui Song   Expected<std::optional<StringRef>>
7892c5d49cfSFangrui Song   ReadBundleStart(MemoryBuffer &Input) final {
7900f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
7910f3f357eSJacob Lambert 
7920f3f357eSJacob Lambert     // Find start of the bundle.
7930f3f357eSJacob Lambert     ReadChars = FC.find(BundleStartString, ReadChars);
7940f3f357eSJacob Lambert     if (ReadChars == FC.npos)
7950c2f6e36SFangrui Song       return std::nullopt;
7960f3f357eSJacob Lambert 
7970f3f357eSJacob Lambert     // Get position of the triple.
7980f3f357eSJacob Lambert     size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
7990f3f357eSJacob Lambert 
8000f3f357eSJacob Lambert     // Get position that closes the triple.
8010f3f357eSJacob Lambert     size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
8020f3f357eSJacob Lambert     if (TripleEnd == FC.npos)
8030c2f6e36SFangrui Song       return std::nullopt;
8040f3f357eSJacob Lambert 
8050f3f357eSJacob Lambert     // Next time we read after the new line.
8060f3f357eSJacob Lambert     ++ReadChars;
8070f3f357eSJacob Lambert 
8080f3f357eSJacob Lambert     return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
8090f3f357eSJacob Lambert   }
8100f3f357eSJacob Lambert 
8110f3f357eSJacob Lambert   Error ReadBundleEnd(MemoryBuffer &Input) final {
8120f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
8130f3f357eSJacob Lambert 
8140f3f357eSJacob Lambert     // Read up to the next new line.
8150f3f357eSJacob Lambert     assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
8160f3f357eSJacob Lambert 
8170f3f357eSJacob Lambert     size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
8180f3f357eSJacob Lambert     if (TripleEnd != FC.npos)
8190f3f357eSJacob Lambert       // Next time we read after the new line.
8200f3f357eSJacob Lambert       ++ReadChars;
8210f3f357eSJacob Lambert 
8220f3f357eSJacob Lambert     return Error::success();
8230f3f357eSJacob Lambert   }
8240f3f357eSJacob Lambert 
8250f3f357eSJacob Lambert   Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
8260f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
8270f3f357eSJacob Lambert     size_t BundleStart = ReadChars;
8280f3f357eSJacob Lambert 
8290f3f357eSJacob Lambert     // Find end of the bundle.
8300f3f357eSJacob Lambert     size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
8310f3f357eSJacob Lambert 
8320f3f357eSJacob Lambert     StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
8330f3f357eSJacob Lambert     OS << Bundle;
8340f3f357eSJacob Lambert 
8350f3f357eSJacob Lambert     return Error::success();
8360f3f357eSJacob Lambert   }
8370f3f357eSJacob Lambert 
8387e282343SYaxun (Sam) Liu   Error WriteHeader(raw_ostream &OS,
8390f3f357eSJacob Lambert                     ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
8400f3f357eSJacob Lambert     return Error::success();
8410f3f357eSJacob Lambert   }
8420f3f357eSJacob Lambert 
8437e282343SYaxun (Sam) Liu   Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
8440f3f357eSJacob Lambert     OS << BundleStartString << TargetTriple << "\n";
8450f3f357eSJacob Lambert     return Error::success();
8460f3f357eSJacob Lambert   }
8470f3f357eSJacob Lambert 
8487e282343SYaxun (Sam) Liu   Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
8490f3f357eSJacob Lambert     OS << BundleEndString << TargetTriple << "\n";
8500f3f357eSJacob Lambert     return Error::success();
8510f3f357eSJacob Lambert   }
8520f3f357eSJacob Lambert 
8537e282343SYaxun (Sam) Liu   Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
8540f3f357eSJacob Lambert     OS << Input.getBuffer();
8550f3f357eSJacob Lambert     return Error::success();
8560f3f357eSJacob Lambert   }
8570f3f357eSJacob Lambert 
8580f3f357eSJacob Lambert public:
8590f3f357eSJacob Lambert   TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
8600f3f357eSJacob Lambert     BundleStartString =
8610f3f357eSJacob Lambert         "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
8620f3f357eSJacob Lambert     BundleEndString =
8630f3f357eSJacob Lambert         "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
8640f3f357eSJacob Lambert   }
8650f3f357eSJacob Lambert 
8660f3f357eSJacob Lambert   Error listBundleIDsCallback(MemoryBuffer &Input,
8670f3f357eSJacob Lambert                               const BundleInfo &Info) final {
8680f3f357eSJacob Lambert     // TODO: To list bundle IDs in a bundled text file we need to go through
8690f3f357eSJacob Lambert     // all bundles. The format of bundled text file may need to include a
8700f3f357eSJacob Lambert     // header if the performance of listing bundle IDs of bundled text file is
8710f3f357eSJacob Lambert     // important.
8720f3f357eSJacob Lambert     ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
8730f3f357eSJacob Lambert     if (Error Err = ReadBundleEnd(Input))
8740f3f357eSJacob Lambert       return Err;
8750f3f357eSJacob Lambert     return Error::success();
8760f3f357eSJacob Lambert   }
8770f3f357eSJacob Lambert };
878b6942a28SBenjamin Kramer } // namespace
8790f3f357eSJacob Lambert 
8800f3f357eSJacob Lambert /// Return an appropriate object file handler. We use the specific object
8810f3f357eSJacob Lambert /// handler if we know how to deal with that format, otherwise we use a default
8820f3f357eSJacob Lambert /// binary file handler.
8830f3f357eSJacob Lambert static std::unique_ptr<FileHandler>
8840f3f357eSJacob Lambert CreateObjectFileHandler(MemoryBuffer &FirstInput,
8850f3f357eSJacob Lambert                         const OffloadBundlerConfig &BundlerConfig) {
8860f3f357eSJacob Lambert   // Check if the input file format is one that we know how to deal with.
8870f3f357eSJacob Lambert   Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
8880f3f357eSJacob Lambert 
8890f3f357eSJacob Lambert   // We only support regular object files. If failed to open the input as a
8900f3f357eSJacob Lambert   // known binary or this is not an object file use the default binary handler.
8910f3f357eSJacob Lambert   if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
8920f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
8930f3f357eSJacob Lambert 
8940f3f357eSJacob Lambert   // Otherwise create an object file handler. The handler will be owned by the
8950f3f357eSJacob Lambert   // client of this function.
8960f3f357eSJacob Lambert   return std::make_unique<ObjectFileHandler>(
8970f3f357eSJacob Lambert       std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
8980f3f357eSJacob Lambert       BundlerConfig);
8990f3f357eSJacob Lambert }
9000f3f357eSJacob Lambert 
9010f3f357eSJacob Lambert /// Return an appropriate handler given the input files and options.
9020f3f357eSJacob Lambert static Expected<std::unique_ptr<FileHandler>>
9030f3f357eSJacob Lambert CreateFileHandler(MemoryBuffer &FirstInput,
9040f3f357eSJacob Lambert                   const OffloadBundlerConfig &BundlerConfig) {
9050f3f357eSJacob Lambert   std::string FilesType = BundlerConfig.FilesType;
9060f3f357eSJacob Lambert 
9070f3f357eSJacob Lambert   if (FilesType == "i")
9080f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
9090f3f357eSJacob Lambert   if (FilesType == "ii")
9100f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
9110f3f357eSJacob Lambert   if (FilesType == "cui")
9120f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
913e8f41fdbSYaxun (Sam) Liu   if (FilesType == "hipi")
914e8f41fdbSYaxun (Sam) Liu     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
9150f3f357eSJacob Lambert   // TODO: `.d` should be eventually removed once `-M` and its variants are
9160f3f357eSJacob Lambert   // handled properly in offload compilation.
9170f3f357eSJacob Lambert   if (FilesType == "d")
9180f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"#");
9190f3f357eSJacob Lambert   if (FilesType == "ll")
9200f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/";");
9210f3f357eSJacob Lambert   if (FilesType == "bc")
9220f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
9230f3f357eSJacob Lambert   if (FilesType == "s")
9240f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"#");
9250f3f357eSJacob Lambert   if (FilesType == "o")
9260f3f357eSJacob Lambert     return CreateObjectFileHandler(FirstInput, BundlerConfig);
9270f3f357eSJacob Lambert   if (FilesType == "a")
9280f3f357eSJacob Lambert     return CreateObjectFileHandler(FirstInput, BundlerConfig);
9290f3f357eSJacob Lambert   if (FilesType == "gch")
9300f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
9310f3f357eSJacob Lambert   if (FilesType == "ast")
9320f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
9330f3f357eSJacob Lambert 
9340f3f357eSJacob Lambert   return createStringError(errc::invalid_argument,
9350f3f357eSJacob Lambert                            "'" + FilesType + "': invalid file type specified");
9360f3f357eSJacob Lambert }
9370f3f357eSJacob Lambert 
938*e87b8438SYaxun (Sam) Liu OffloadBundlerConfig::OffloadBundlerConfig()
939*e87b8438SYaxun (Sam) Liu     : CompressedBundleVersion(CompressedOffloadBundle::DefaultVersion) {
940124d0b78SYaxun (Sam) Liu   if (llvm::compression::zstd::isAvailable()) {
941124d0b78SYaxun (Sam) Liu     CompressionFormat = llvm::compression::Format::Zstd;
942124d0b78SYaxun (Sam) Liu     // Compression level 3 is usually sufficient for zstd since long distance
943124d0b78SYaxun (Sam) Liu     // matching is enabled.
944124d0b78SYaxun (Sam) Liu     CompressionLevel = 3;
945124d0b78SYaxun (Sam) Liu   } else if (llvm::compression::zlib::isAvailable()) {
946124d0b78SYaxun (Sam) Liu     CompressionFormat = llvm::compression::Format::Zlib;
947124d0b78SYaxun (Sam) Liu     // Use default level for zlib since higher level does not have significant
948124d0b78SYaxun (Sam) Liu     // improvement.
949124d0b78SYaxun (Sam) Liu     CompressionLevel = llvm::compression::zlib::DefaultCompression;
950124d0b78SYaxun (Sam) Liu   }
9517e282343SYaxun (Sam) Liu   auto IgnoreEnvVarOpt =
9527e282343SYaxun (Sam) Liu       llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
9537e282343SYaxun (Sam) Liu   if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1")
9547e282343SYaxun (Sam) Liu     return;
9557e282343SYaxun (Sam) Liu   auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
9567e282343SYaxun (Sam) Liu   if (VerboseEnvVarOpt.has_value())
9577e282343SYaxun (Sam) Liu     Verbose = VerboseEnvVarOpt.value() == "1";
9587e282343SYaxun (Sam) Liu   auto CompressEnvVarOpt =
9597e282343SYaxun (Sam) Liu       llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
9607e282343SYaxun (Sam) Liu   if (CompressEnvVarOpt.has_value())
9617e282343SYaxun (Sam) Liu     Compress = CompressEnvVarOpt.value() == "1";
962124d0b78SYaxun (Sam) Liu   auto CompressionLevelEnvVarOpt =
963124d0b78SYaxun (Sam) Liu       llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
964124d0b78SYaxun (Sam) Liu   if (CompressionLevelEnvVarOpt.has_value()) {
965124d0b78SYaxun (Sam) Liu     llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
966124d0b78SYaxun (Sam) Liu     int Level;
967124d0b78SYaxun (Sam) Liu     if (!CompressionLevelStr.getAsInteger(10, Level))
968124d0b78SYaxun (Sam) Liu       CompressionLevel = Level;
969124d0b78SYaxun (Sam) Liu     else
970124d0b78SYaxun (Sam) Liu       llvm::errs()
971124d0b78SYaxun (Sam) Liu           << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
972124d0b78SYaxun (Sam) Liu           << CompressionLevelStr.str() << ". Ignoring it.\n";
973124d0b78SYaxun (Sam) Liu   }
974*e87b8438SYaxun (Sam) Liu   auto CompressedBundleFormatVersionOpt =
975*e87b8438SYaxun (Sam) Liu       llvm::sys::Process::GetEnv("COMPRESSED_BUNDLE_FORMAT_VERSION");
976*e87b8438SYaxun (Sam) Liu   if (CompressedBundleFormatVersionOpt.has_value()) {
977*e87b8438SYaxun (Sam) Liu     llvm::StringRef VersionStr = CompressedBundleFormatVersionOpt.value();
978*e87b8438SYaxun (Sam) Liu     uint16_t Version;
979*e87b8438SYaxun (Sam) Liu     if (!VersionStr.getAsInteger(10, Version)) {
980*e87b8438SYaxun (Sam) Liu       if (Version >= 2 && Version <= 3)
981*e87b8438SYaxun (Sam) Liu         CompressedBundleVersion = Version;
982*e87b8438SYaxun (Sam) Liu       else
983*e87b8438SYaxun (Sam) Liu         llvm::errs()
984*e87b8438SYaxun (Sam) Liu             << "Warning: Invalid value for COMPRESSED_BUNDLE_FORMAT_VERSION: "
985*e87b8438SYaxun (Sam) Liu             << VersionStr.str()
986*e87b8438SYaxun (Sam) Liu             << ". Valid values are 2 or 3. Using default version "
987*e87b8438SYaxun (Sam) Liu             << CompressedBundleVersion << ".\n";
988*e87b8438SYaxun (Sam) Liu     } else
989*e87b8438SYaxun (Sam) Liu       llvm::errs()
990*e87b8438SYaxun (Sam) Liu           << "Warning: Invalid value for COMPRESSED_BUNDLE_FORMAT_VERSION: "
991*e87b8438SYaxun (Sam) Liu           << VersionStr.str() << ". Using default version "
992*e87b8438SYaxun (Sam) Liu           << CompressedBundleVersion << ".\n";
993*e87b8438SYaxun (Sam) Liu   }
994124d0b78SYaxun (Sam) Liu }
995124d0b78SYaxun (Sam) Liu 
996124d0b78SYaxun (Sam) Liu // Utility function to format numbers with commas
997124d0b78SYaxun (Sam) Liu static std::string formatWithCommas(unsigned long long Value) {
998124d0b78SYaxun (Sam) Liu   std::string Num = std::to_string(Value);
999124d0b78SYaxun (Sam) Liu   int InsertPosition = Num.length() - 3;
1000124d0b78SYaxun (Sam) Liu   while (InsertPosition > 0) {
1001124d0b78SYaxun (Sam) Liu     Num.insert(InsertPosition, ",");
1002124d0b78SYaxun (Sam) Liu     InsertPosition -= 3;
1003124d0b78SYaxun (Sam) Liu   }
1004124d0b78SYaxun (Sam) Liu   return Num;
10057e282343SYaxun (Sam) Liu }
10067e282343SYaxun (Sam) Liu 
10077e282343SYaxun (Sam) Liu llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
1008124d0b78SYaxun (Sam) Liu CompressedOffloadBundle::compress(llvm::compression::Params P,
1009124d0b78SYaxun (Sam) Liu                                   const llvm::MemoryBuffer &Input,
1010*e87b8438SYaxun (Sam) Liu                                   uint16_t Version, bool Verbose) {
1011124d0b78SYaxun (Sam) Liu   if (!llvm::compression::zstd::isAvailable() &&
1012124d0b78SYaxun (Sam) Liu       !llvm::compression::zlib::isAvailable())
1013124d0b78SYaxun (Sam) Liu     return createStringError(llvm::inconvertibleErrorCode(),
1014124d0b78SYaxun (Sam) Liu                              "Compression not supported");
10157e282343SYaxun (Sam) Liu   llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
101652c338daSmacurtis-amd                         *ClangOffloadBundlerTimerGroup);
10177e282343SYaxun (Sam) Liu   if (Verbose)
10187e282343SYaxun (Sam) Liu     HashTimer.startTimer();
10197e282343SYaxun (Sam) Liu   llvm::MD5 Hash;
10207e282343SYaxun (Sam) Liu   llvm::MD5::MD5Result Result;
10217e282343SYaxun (Sam) Liu   Hash.update(Input.getBuffer());
10227e282343SYaxun (Sam) Liu   Hash.final(Result);
10237e282343SYaxun (Sam) Liu   uint64_t TruncatedHash = Result.low();
10247e282343SYaxun (Sam) Liu   if (Verbose)
10257e282343SYaxun (Sam) Liu     HashTimer.stopTimer();
10267e282343SYaxun (Sam) Liu 
10277e282343SYaxun (Sam) Liu   SmallVector<uint8_t, 0> CompressedBuffer;
10287e282343SYaxun (Sam) Liu   auto BufferUint8 = llvm::ArrayRef<uint8_t>(
10297e282343SYaxun (Sam) Liu       reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
10307e282343SYaxun (Sam) Liu       Input.getBuffer().size());
10317e282343SYaxun (Sam) Liu   llvm::Timer CompressTimer("Compression Timer", "Compression time",
103252c338daSmacurtis-amd                             *ClangOffloadBundlerTimerGroup);
10337e282343SYaxun (Sam) Liu   if (Verbose)
10347e282343SYaxun (Sam) Liu     CompressTimer.startTimer();
1035124d0b78SYaxun (Sam) Liu   llvm::compression::compress(P, BufferUint8, CompressedBuffer);
10367e282343SYaxun (Sam) Liu   if (Verbose)
10377e282343SYaxun (Sam) Liu     CompressTimer.stopTimer();
10387e282343SYaxun (Sam) Liu 
1039124d0b78SYaxun (Sam) Liu   uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
1040*e87b8438SYaxun (Sam) Liu 
1041*e87b8438SYaxun (Sam) Liu   // Store sizes in 64-bit variables first
1042*e87b8438SYaxun (Sam) Liu   uint64_t UncompressedSize64 = Input.getBuffer().size();
1043*e87b8438SYaxun (Sam) Liu   uint64_t TotalFileSize64;
1044*e87b8438SYaxun (Sam) Liu 
1045*e87b8438SYaxun (Sam) Liu   // Calculate total file size based on version
1046*e87b8438SYaxun (Sam) Liu   if (Version == 2) {
1047*e87b8438SYaxun (Sam) Liu     // For V2, ensure the sizes don't exceed 32-bit limit
1048*e87b8438SYaxun (Sam) Liu     if (UncompressedSize64 > std::numeric_limits<uint32_t>::max())
1049*e87b8438SYaxun (Sam) Liu       return createStringError(llvm::inconvertibleErrorCode(),
1050*e87b8438SYaxun (Sam) Liu                                "Uncompressed size exceeds version 2 limit");
1051*e87b8438SYaxun (Sam) Liu     if ((MagicNumber.size() + sizeof(uint32_t) + sizeof(Version) +
1052*e87b8438SYaxun (Sam) Liu          sizeof(CompressionMethod) + sizeof(uint32_t) + sizeof(TruncatedHash) +
1053*e87b8438SYaxun (Sam) Liu          CompressedBuffer.size()) > std::numeric_limits<uint32_t>::max())
1054*e87b8438SYaxun (Sam) Liu       return createStringError(llvm::inconvertibleErrorCode(),
1055*e87b8438SYaxun (Sam) Liu                                "Total file size exceeds version 2 limit");
1056*e87b8438SYaxun (Sam) Liu 
1057*e87b8438SYaxun (Sam) Liu     TotalFileSize64 = MagicNumber.size() + sizeof(uint32_t) + sizeof(Version) +
1058*e87b8438SYaxun (Sam) Liu                       sizeof(CompressionMethod) + sizeof(uint32_t) +
1059*e87b8438SYaxun (Sam) Liu                       sizeof(TruncatedHash) + CompressedBuffer.size();
1060*e87b8438SYaxun (Sam) Liu   } else { // Version 3
1061*e87b8438SYaxun (Sam) Liu     TotalFileSize64 = MagicNumber.size() + sizeof(uint64_t) + sizeof(Version) +
1062*e87b8438SYaxun (Sam) Liu                       sizeof(CompressionMethod) + sizeof(uint64_t) +
1063*e87b8438SYaxun (Sam) Liu                       sizeof(TruncatedHash) + CompressedBuffer.size();
1064*e87b8438SYaxun (Sam) Liu   }
10657e282343SYaxun (Sam) Liu 
10667e282343SYaxun (Sam) Liu   SmallVector<char, 0> FinalBuffer;
10677e282343SYaxun (Sam) Liu   llvm::raw_svector_ostream OS(FinalBuffer);
10687e282343SYaxun (Sam) Liu   OS << MagicNumber;
10697e282343SYaxun (Sam) Liu   OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
10707e282343SYaxun (Sam) Liu   OS.write(reinterpret_cast<const char *>(&CompressionMethod),
10717e282343SYaxun (Sam) Liu            sizeof(CompressionMethod));
1072*e87b8438SYaxun (Sam) Liu 
1073*e87b8438SYaxun (Sam) Liu   // Write size fields according to version
1074*e87b8438SYaxun (Sam) Liu   if (Version == 2) {
1075*e87b8438SYaxun (Sam) Liu     uint32_t TotalFileSize32 = static_cast<uint32_t>(TotalFileSize64);
1076*e87b8438SYaxun (Sam) Liu     uint32_t UncompressedSize32 = static_cast<uint32_t>(UncompressedSize64);
1077*e87b8438SYaxun (Sam) Liu     OS.write(reinterpret_cast<const char *>(&TotalFileSize32),
1078*e87b8438SYaxun (Sam) Liu              sizeof(TotalFileSize32));
1079*e87b8438SYaxun (Sam) Liu     OS.write(reinterpret_cast<const char *>(&UncompressedSize32),
1080*e87b8438SYaxun (Sam) Liu              sizeof(UncompressedSize32));
1081*e87b8438SYaxun (Sam) Liu   } else { // Version 3
1082*e87b8438SYaxun (Sam) Liu     OS.write(reinterpret_cast<const char *>(&TotalFileSize64),
1083*e87b8438SYaxun (Sam) Liu              sizeof(TotalFileSize64));
1084*e87b8438SYaxun (Sam) Liu     OS.write(reinterpret_cast<const char *>(&UncompressedSize64),
1085*e87b8438SYaxun (Sam) Liu              sizeof(UncompressedSize64));
1086*e87b8438SYaxun (Sam) Liu   }
1087*e87b8438SYaxun (Sam) Liu 
10887e282343SYaxun (Sam) Liu   OS.write(reinterpret_cast<const char *>(&TruncatedHash),
10897e282343SYaxun (Sam) Liu            sizeof(TruncatedHash));
10907e282343SYaxun (Sam) Liu   OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
10917e282343SYaxun (Sam) Liu            CompressedBuffer.size());
10927e282343SYaxun (Sam) Liu 
10937e282343SYaxun (Sam) Liu   if (Verbose) {
10947e282343SYaxun (Sam) Liu     auto MethodUsed =
1095124d0b78SYaxun (Sam) Liu         P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
1096124d0b78SYaxun (Sam) Liu     double CompressionRate =
1097*e87b8438SYaxun (Sam) Liu         static_cast<double>(UncompressedSize64) / CompressedBuffer.size();
1098124d0b78SYaxun (Sam) Liu     double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1099124d0b78SYaxun (Sam) Liu     double CompressionSpeedMBs =
1100*e87b8438SYaxun (Sam) Liu         (UncompressedSize64 / (1024.0 * 1024.0)) / CompressionTimeSeconds;
11017e282343SYaxun (Sam) Liu     llvm::errs() << "Compressed bundle format version: " << Version << "\n"
110278dca4afSYaxun (Sam) Liu                  << "Total file size (including headers): "
1103*e87b8438SYaxun (Sam) Liu                  << formatWithCommas(TotalFileSize64) << " bytes\n"
11047e282343SYaxun (Sam) Liu                  << "Compression method used: " << MethodUsed << "\n"
1105124d0b78SYaxun (Sam) Liu                  << "Compression level: " << P.level << "\n"
1106124d0b78SYaxun (Sam) Liu                  << "Binary size before compression: "
1107*e87b8438SYaxun (Sam) Liu                  << formatWithCommas(UncompressedSize64) << " bytes\n"
1108124d0b78SYaxun (Sam) Liu                  << "Binary size after compression: "
1109124d0b78SYaxun (Sam) Liu                  << formatWithCommas(CompressedBuffer.size()) << " bytes\n"
1110124d0b78SYaxun (Sam) Liu                  << "Compression rate: "
1111124d0b78SYaxun (Sam) Liu                  << llvm::format("%.2lf", CompressionRate) << "\n"
1112124d0b78SYaxun (Sam) Liu                  << "Compression ratio: "
1113124d0b78SYaxun (Sam) Liu                  << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1114124d0b78SYaxun (Sam) Liu                  << "Compression speed: "
1115124d0b78SYaxun (Sam) Liu                  << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
11167e282343SYaxun (Sam) Liu                  << "Truncated MD5 hash: "
11177e282343SYaxun (Sam) Liu                  << llvm::format_hex(TruncatedHash, 16) << "\n";
11187e282343SYaxun (Sam) Liu   }
1119*e87b8438SYaxun (Sam) Liu 
11207e282343SYaxun (Sam) Liu   return llvm::MemoryBuffer::getMemBufferCopy(
11217e282343SYaxun (Sam) Liu       llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
11227e282343SYaxun (Sam) Liu }
11237e282343SYaxun (Sam) Liu 
11247e282343SYaxun (Sam) Liu llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
11257e282343SYaxun (Sam) Liu CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
11267e282343SYaxun (Sam) Liu                                     bool Verbose) {
11277e282343SYaxun (Sam) Liu   StringRef Blob = Input.getBuffer();
11287e282343SYaxun (Sam) Liu 
1129*e87b8438SYaxun (Sam) Liu   // Check minimum header size (using V1 as it's the smallest)
113078dca4afSYaxun (Sam) Liu   if (Blob.size() < V1HeaderSize)
11317e282343SYaxun (Sam) Liu     return llvm::MemoryBuffer::getMemBufferCopy(Blob);
113278dca4afSYaxun (Sam) Liu 
11337e282343SYaxun (Sam) Liu   if (llvm::identify_magic(Blob) !=
11347e282343SYaxun (Sam) Liu       llvm::file_magic::offload_bundle_compressed) {
11357e282343SYaxun (Sam) Liu     if (Verbose)
11367e282343SYaxun (Sam) Liu       llvm::errs() << "Uncompressed bundle.\n";
11377e282343SYaxun (Sam) Liu     return llvm::MemoryBuffer::getMemBufferCopy(Blob);
11387e282343SYaxun (Sam) Liu   }
11397e282343SYaxun (Sam) Liu 
114078dca4afSYaxun (Sam) Liu   size_t CurrentOffset = MagicSize;
114178dca4afSYaxun (Sam) Liu 
1142*e87b8438SYaxun (Sam) Liu   // Read version
11437e282343SYaxun (Sam) Liu   uint16_t ThisVersion;
114478dca4afSYaxun (Sam) Liu   memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
114578dca4afSYaxun (Sam) Liu   CurrentOffset += VersionFieldSize;
114678dca4afSYaxun (Sam) Liu 
1147*e87b8438SYaxun (Sam) Liu   // Verify header size based on version
1148*e87b8438SYaxun (Sam) Liu   if (ThisVersion >= 2 && ThisVersion <= 3) {
1149*e87b8438SYaxun (Sam) Liu     size_t RequiredSize = (ThisVersion == 2) ? V2HeaderSize : V3HeaderSize;
1150*e87b8438SYaxun (Sam) Liu     if (Blob.size() < RequiredSize)
1151*e87b8438SYaxun (Sam) Liu       return createStringError(inconvertibleErrorCode(),
1152*e87b8438SYaxun (Sam) Liu                                "Compressed bundle header size too small");
1153*e87b8438SYaxun (Sam) Liu   }
1154*e87b8438SYaxun (Sam) Liu 
1155*e87b8438SYaxun (Sam) Liu   // Read compression method
11567e282343SYaxun (Sam) Liu   uint16_t CompressionMethod;
115778dca4afSYaxun (Sam) Liu   memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
115878dca4afSYaxun (Sam) Liu   CurrentOffset += MethodFieldSize;
115978dca4afSYaxun (Sam) Liu 
1160*e87b8438SYaxun (Sam) Liu   // Read total file size (version 2+)
1161*e87b8438SYaxun (Sam) Liu   uint64_t TotalFileSize = 0;
116278dca4afSYaxun (Sam) Liu   if (ThisVersion >= 2) {
1163*e87b8438SYaxun (Sam) Liu     if (ThisVersion == 2) {
1164*e87b8438SYaxun (Sam) Liu       uint32_t TotalFileSize32;
1165*e87b8438SYaxun (Sam) Liu       memcpy(&TotalFileSize32, Blob.data() + CurrentOffset, sizeof(uint32_t));
1166*e87b8438SYaxun (Sam) Liu       TotalFileSize = TotalFileSize32;
1167*e87b8438SYaxun (Sam) Liu       CurrentOffset += FileSizeFieldSizeV2;
1168*e87b8438SYaxun (Sam) Liu     } else { // Version 3
1169*e87b8438SYaxun (Sam) Liu       memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint64_t));
1170*e87b8438SYaxun (Sam) Liu       CurrentOffset += FileSizeFieldSizeV3;
1171*e87b8438SYaxun (Sam) Liu     }
117278dca4afSYaxun (Sam) Liu   }
117378dca4afSYaxun (Sam) Liu 
1174*e87b8438SYaxun (Sam) Liu   // Read uncompressed size
1175*e87b8438SYaxun (Sam) Liu   uint64_t UncompressedSize = 0;
1176*e87b8438SYaxun (Sam) Liu   if (ThisVersion <= 2) {
1177*e87b8438SYaxun (Sam) Liu     uint32_t UncompressedSize32;
1178*e87b8438SYaxun (Sam) Liu     memcpy(&UncompressedSize32, Blob.data() + CurrentOffset, sizeof(uint32_t));
1179*e87b8438SYaxun (Sam) Liu     UncompressedSize = UncompressedSize32;
1180*e87b8438SYaxun (Sam) Liu     CurrentOffset += UncompressedSizeFieldSizeV2;
1181*e87b8438SYaxun (Sam) Liu   } else { // Version 3
1182*e87b8438SYaxun (Sam) Liu     memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint64_t));
1183*e87b8438SYaxun (Sam) Liu     CurrentOffset += UncompressedSizeFieldSizeV3;
1184*e87b8438SYaxun (Sam) Liu   }
118578dca4afSYaxun (Sam) Liu 
1186*e87b8438SYaxun (Sam) Liu   // Read hash
11877e282343SYaxun (Sam) Liu   uint64_t StoredHash;
118878dca4afSYaxun (Sam) Liu   memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
118978dca4afSYaxun (Sam) Liu   CurrentOffset += HashFieldSize;
11907e282343SYaxun (Sam) Liu 
1191*e87b8438SYaxun (Sam) Liu   // Determine compression format
11927e282343SYaxun (Sam) Liu   llvm::compression::Format CompressionFormat;
11937e282343SYaxun (Sam) Liu   if (CompressionMethod ==
11947e282343SYaxun (Sam) Liu       static_cast<uint16_t>(llvm::compression::Format::Zlib))
11957e282343SYaxun (Sam) Liu     CompressionFormat = llvm::compression::Format::Zlib;
11967e282343SYaxun (Sam) Liu   else if (CompressionMethod ==
11977e282343SYaxun (Sam) Liu            static_cast<uint16_t>(llvm::compression::Format::Zstd))
11987e282343SYaxun (Sam) Liu     CompressionFormat = llvm::compression::Format::Zstd;
11997e282343SYaxun (Sam) Liu   else
12007e282343SYaxun (Sam) Liu     return createStringError(inconvertibleErrorCode(),
12017e282343SYaxun (Sam) Liu                              "Unknown compressing method");
12027e282343SYaxun (Sam) Liu 
12037e282343SYaxun (Sam) Liu   llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
120452c338daSmacurtis-amd                               *ClangOffloadBundlerTimerGroup);
12057e282343SYaxun (Sam) Liu   if (Verbose)
12067e282343SYaxun (Sam) Liu     DecompressTimer.startTimer();
12077e282343SYaxun (Sam) Liu 
12087e282343SYaxun (Sam) Liu   SmallVector<uint8_t, 0> DecompressedData;
120978dca4afSYaxun (Sam) Liu   StringRef CompressedData = Blob.substr(CurrentOffset);
12107e282343SYaxun (Sam) Liu   if (llvm::Error DecompressionError = llvm::compression::decompress(
12117e282343SYaxun (Sam) Liu           CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
12127e282343SYaxun (Sam) Liu           DecompressedData, UncompressedSize))
12137e282343SYaxun (Sam) Liu     return createStringError(inconvertibleErrorCode(),
12147e282343SYaxun (Sam) Liu                              "Could not decompress embedded file contents: " +
12157e282343SYaxun (Sam) Liu                                  llvm::toString(std::move(DecompressionError)));
12167e282343SYaxun (Sam) Liu 
12177e282343SYaxun (Sam) Liu   if (Verbose) {
12187e282343SYaxun (Sam) Liu     DecompressTimer.stopTimer();
12197e282343SYaxun (Sam) Liu 
1220124d0b78SYaxun (Sam) Liu     double DecompressionTimeSeconds =
1221124d0b78SYaxun (Sam) Liu         DecompressTimer.getTotalTime().getWallTime();
1222124d0b78SYaxun (Sam) Liu 
1223124d0b78SYaxun (Sam) Liu     // Recalculate MD5 hash for integrity check
12247e282343SYaxun (Sam) Liu     llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
12257e282343SYaxun (Sam) Liu                                 "Hash recalculation time",
122652c338daSmacurtis-amd                                 *ClangOffloadBundlerTimerGroup);
12277e282343SYaxun (Sam) Liu     HashRecalcTimer.startTimer();
12287e282343SYaxun (Sam) Liu     llvm::MD5 Hash;
12297e282343SYaxun (Sam) Liu     llvm::MD5::MD5Result Result;
12307e282343SYaxun (Sam) Liu     Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData.data(),
12317e282343SYaxun (Sam) Liu                                         DecompressedData.size()));
12327e282343SYaxun (Sam) Liu     Hash.final(Result);
12337e282343SYaxun (Sam) Liu     uint64_t RecalculatedHash = Result.low();
12347e282343SYaxun (Sam) Liu     HashRecalcTimer.stopTimer();
12357e282343SYaxun (Sam) Liu     bool HashMatch = (StoredHash == RecalculatedHash);
12367e282343SYaxun (Sam) Liu 
1237124d0b78SYaxun (Sam) Liu     double CompressionRate =
1238124d0b78SYaxun (Sam) Liu         static_cast<double>(UncompressedSize) / CompressedData.size();
1239124d0b78SYaxun (Sam) Liu     double DecompressionSpeedMBs =
1240124d0b78SYaxun (Sam) Liu         (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1241124d0b78SYaxun (Sam) Liu 
124278dca4afSYaxun (Sam) Liu     llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
124378dca4afSYaxun (Sam) Liu     if (ThisVersion >= 2)
124478dca4afSYaxun (Sam) Liu       llvm::errs() << "Total file size (from header): "
124578dca4afSYaxun (Sam) Liu                    << formatWithCommas(TotalFileSize) << " bytes\n";
124678dca4afSYaxun (Sam) Liu     llvm::errs() << "Decompression method: "
12477e282343SYaxun (Sam) Liu                  << (CompressionFormat == llvm::compression::Format::Zlib
12487e282343SYaxun (Sam) Liu                          ? "zlib"
12497e282343SYaxun (Sam) Liu                          : "zstd")
12507e282343SYaxun (Sam) Liu                  << "\n"
1251124d0b78SYaxun (Sam) Liu                  << "Size before decompression: "
1252124d0b78SYaxun (Sam) Liu                  << formatWithCommas(CompressedData.size()) << " bytes\n"
1253124d0b78SYaxun (Sam) Liu                  << "Size after decompression: "
1254124d0b78SYaxun (Sam) Liu                  << formatWithCommas(UncompressedSize) << " bytes\n"
1255124d0b78SYaxun (Sam) Liu                  << "Compression rate: "
1256124d0b78SYaxun (Sam) Liu                  << llvm::format("%.2lf", CompressionRate) << "\n"
1257124d0b78SYaxun (Sam) Liu                  << "Compression ratio: "
1258124d0b78SYaxun (Sam) Liu                  << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1259124d0b78SYaxun (Sam) Liu                  << "Decompression speed: "
1260124d0b78SYaxun (Sam) Liu                  << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
12617e282343SYaxun (Sam) Liu                  << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
12627e282343SYaxun (Sam) Liu                  << "Recalculated hash: "
12637e282343SYaxun (Sam) Liu                  << llvm::format_hex(RecalculatedHash, 16) << "\n"
12647e282343SYaxun (Sam) Liu                  << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
12657e282343SYaxun (Sam) Liu   }
12667e282343SYaxun (Sam) Liu 
12677e282343SYaxun (Sam) Liu   return llvm::MemoryBuffer::getMemBufferCopy(
12687e282343SYaxun (Sam) Liu       llvm::toStringRef(DecompressedData));
12697e282343SYaxun (Sam) Liu }
12707e282343SYaxun (Sam) Liu 
12710f3f357eSJacob Lambert // List bundle IDs. Return true if an error was found.
12725b11caa8Sraghavmedicherla Error OffloadBundler::ListBundleIDsInFile(
12735b11caa8Sraghavmedicherla     StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
12740f3f357eSJacob Lambert   // Open Input file.
12750f3f357eSJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
127674dcf0b5SAbhina Sree       MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
12770f3f357eSJacob Lambert   if (std::error_code EC = CodeOrErr.getError())
12780f3f357eSJacob Lambert     return createFileError(InputFileName, EC);
12790f3f357eSJacob Lambert 
12807e282343SYaxun (Sam) Liu   // Decompress the input if necessary.
12817e282343SYaxun (Sam) Liu   Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
12827e282343SYaxun (Sam) Liu       CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
12837e282343SYaxun (Sam) Liu   if (!DecompressedBufferOrErr)
12847e282343SYaxun (Sam) Liu     return createStringError(
12857e282343SYaxun (Sam) Liu         inconvertibleErrorCode(),
12867e282343SYaxun (Sam) Liu         "Failed to decompress input: " +
12877e282343SYaxun (Sam) Liu             llvm::toString(DecompressedBufferOrErr.takeError()));
12887e282343SYaxun (Sam) Liu 
12897e282343SYaxun (Sam) Liu   MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
12900f3f357eSJacob Lambert 
12910f3f357eSJacob Lambert   // Select the right files handler.
12920f3f357eSJacob Lambert   Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
12937e282343SYaxun (Sam) Liu       CreateFileHandler(DecompressedInput, BundlerConfig);
12940f3f357eSJacob Lambert   if (!FileHandlerOrErr)
12950f3f357eSJacob Lambert     return FileHandlerOrErr.takeError();
12960f3f357eSJacob Lambert 
12970f3f357eSJacob Lambert   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
12980f3f357eSJacob Lambert   assert(FH);
12997e282343SYaxun (Sam) Liu   return FH->listBundleIDs(DecompressedInput);
13000f3f357eSJacob Lambert }
13010f3f357eSJacob Lambert 
13023cf19097SJacob Lambert /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
13033cf19097SJacob Lambert /// target \p TargetInfo.
13043cf19097SJacob Lambert /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
13053cf19097SJacob Lambert bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo,
13063cf19097SJacob Lambert                             const OffloadTargetInfo &TargetInfo) {
13073cf19097SJacob Lambert 
13083cf19097SJacob Lambert   // Compatible in case of exact match.
13093cf19097SJacob Lambert   if (CodeObjectInfo == TargetInfo) {
13103cf19097SJacob Lambert     DEBUG_WITH_TYPE("CodeObjectCompatibility",
13113cf19097SJacob Lambert                     dbgs() << "Compatible: Exact match: \t[CodeObject: "
13123cf19097SJacob Lambert                            << CodeObjectInfo.str()
13133cf19097SJacob Lambert                            << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
13143cf19097SJacob Lambert     return true;
13153cf19097SJacob Lambert   }
13163cf19097SJacob Lambert 
13173cf19097SJacob Lambert   // Incompatible if Kinds or Triples mismatch.
13183cf19097SJacob Lambert   if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) ||
13193cf19097SJacob Lambert       !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
13203cf19097SJacob Lambert     DEBUG_WITH_TYPE(
13213cf19097SJacob Lambert         "CodeObjectCompatibility",
13223cf19097SJacob Lambert         dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
13233cf19097SJacob Lambert                << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
13243cf19097SJacob Lambert                << "]\n");
13253cf19097SJacob Lambert     return false;
13263cf19097SJacob Lambert   }
13273cf19097SJacob Lambert 
13283cf19097SJacob Lambert   // Incompatible if Processors mismatch.
13293cf19097SJacob Lambert   llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
13303cf19097SJacob Lambert   std::optional<StringRef> CodeObjectProc = clang::parseTargetID(
13313cf19097SJacob Lambert       CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap);
13323cf19097SJacob Lambert   std::optional<StringRef> TargetProc = clang::parseTargetID(
13333cf19097SJacob Lambert       TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap);
13343cf19097SJacob Lambert 
13353cf19097SJacob Lambert   // Both TargetProc and CodeObjectProc can't be empty here.
13363cf19097SJacob Lambert   if (!TargetProc || !CodeObjectProc ||
13373cf19097SJacob Lambert       CodeObjectProc.value() != TargetProc.value()) {
13383cf19097SJacob Lambert     DEBUG_WITH_TYPE("CodeObjectCompatibility",
13393cf19097SJacob Lambert                     dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
13403cf19097SJacob Lambert                            << CodeObjectInfo.str()
13413cf19097SJacob Lambert                            << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
13423cf19097SJacob Lambert     return false;
13433cf19097SJacob Lambert   }
13443cf19097SJacob Lambert 
13453cf19097SJacob Lambert   // Incompatible if CodeObject has more features than Target, irrespective of
13463cf19097SJacob Lambert   // type or sign of features.
13473cf19097SJacob Lambert   if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
13483cf19097SJacob Lambert     DEBUG_WITH_TYPE("CodeObjectCompatibility",
13493cf19097SJacob Lambert                     dbgs() << "Incompatible: CodeObject has more features "
13503cf19097SJacob Lambert                               "than target \t[CodeObject: "
13513cf19097SJacob Lambert                            << CodeObjectInfo.str()
13523cf19097SJacob Lambert                            << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
13533cf19097SJacob Lambert     return false;
13543cf19097SJacob Lambert   }
13553cf19097SJacob Lambert 
13563cf19097SJacob Lambert   // Compatible if each target feature specified by target is compatible with
13573cf19097SJacob Lambert   // target feature of code object. The target feature is compatible if the
13583cf19097SJacob Lambert   // code object does not specify it (meaning Any), or if it specifies it
13593cf19097SJacob Lambert   // with the same value (meaning On or Off).
13603cf19097SJacob Lambert   for (const auto &CodeObjectFeature : CodeObjectFeatureMap) {
13613cf19097SJacob Lambert     auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
13623cf19097SJacob Lambert     if (TargetFeature == TargetFeatureMap.end()) {
13633cf19097SJacob Lambert       DEBUG_WITH_TYPE(
13643cf19097SJacob Lambert           "CodeObjectCompatibility",
13653cf19097SJacob Lambert           dbgs()
13663cf19097SJacob Lambert               << "Incompatible: Value of CodeObject's non-ANY feature is "
13673cf19097SJacob Lambert                  "not matching with Target feature's ANY value \t[CodeObject: "
13683cf19097SJacob Lambert               << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
13693cf19097SJacob Lambert               << "]\n");
13703cf19097SJacob Lambert       return false;
13713cf19097SJacob Lambert     } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
13723cf19097SJacob Lambert       DEBUG_WITH_TYPE(
13733cf19097SJacob Lambert           "CodeObjectCompatibility",
13743cf19097SJacob Lambert           dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
13753cf19097SJacob Lambert                     "not matching with Target feature's non-ANY value "
13763cf19097SJacob Lambert                     "\t[CodeObject: "
13773cf19097SJacob Lambert                  << CodeObjectInfo.str()
13783cf19097SJacob Lambert                  << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
13793cf19097SJacob Lambert       return false;
13803cf19097SJacob Lambert     }
13813cf19097SJacob Lambert   }
13823cf19097SJacob Lambert 
13833cf19097SJacob Lambert   // CodeObject is compatible if all features of Target are:
13843cf19097SJacob Lambert   //   - either, present in the Code Object's features map with the same sign,
13853cf19097SJacob Lambert   //   - or, the feature is missing from CodeObjects's features map i.e. it is
13863cf19097SJacob Lambert   //   set to ANY
13873cf19097SJacob Lambert   DEBUG_WITH_TYPE(
13883cf19097SJacob Lambert       "CodeObjectCompatibility",
13893cf19097SJacob Lambert       dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
13903cf19097SJacob Lambert              << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
13913cf19097SJacob Lambert              << "]\n");
13923cf19097SJacob Lambert   return true;
13933cf19097SJacob Lambert }
13943cf19097SJacob Lambert 
13950f3f357eSJacob Lambert /// Bundle the files. Return true if an error was found.
13960f3f357eSJacob Lambert Error OffloadBundler::BundleFiles() {
13970f3f357eSJacob Lambert   std::error_code EC;
13980f3f357eSJacob Lambert 
13997e282343SYaxun (Sam) Liu   // Create a buffer to hold the content before compressing.
14007e282343SYaxun (Sam) Liu   SmallVector<char, 0> Buffer;
14017e282343SYaxun (Sam) Liu   llvm::raw_svector_ostream BufferStream(Buffer);
14020f3f357eSJacob Lambert 
14030f3f357eSJacob Lambert   // Open input files.
14040f3f357eSJacob Lambert   SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers;
14050f3f357eSJacob Lambert   InputBuffers.reserve(BundlerConfig.InputFileNames.size());
14060f3f357eSJacob Lambert   for (auto &I : BundlerConfig.InputFileNames) {
14070f3f357eSJacob Lambert     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
140874dcf0b5SAbhina Sree         MemoryBuffer::getFileOrSTDIN(I, /*IsText=*/true);
14090f3f357eSJacob Lambert     if (std::error_code EC = CodeOrErr.getError())
14100f3f357eSJacob Lambert       return createFileError(I, EC);
14110f3f357eSJacob Lambert     InputBuffers.emplace_back(std::move(*CodeOrErr));
14120f3f357eSJacob Lambert   }
14130f3f357eSJacob Lambert 
14140f3f357eSJacob Lambert   // Get the file handler. We use the host buffer as reference.
14150f3f357eSJacob Lambert   assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) &&
14160f3f357eSJacob Lambert          "Host input index undefined??");
14175b11caa8Sraghavmedicherla   Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler(
14185b11caa8Sraghavmedicherla       *InputBuffers[BundlerConfig.AllowNoHost ? 0
14190f3f357eSJacob Lambert                                               : BundlerConfig.HostInputIndex],
14200f3f357eSJacob Lambert       BundlerConfig);
14210f3f357eSJacob Lambert   if (!FileHandlerOrErr)
14220f3f357eSJacob Lambert     return FileHandlerOrErr.takeError();
14230f3f357eSJacob Lambert 
14240f3f357eSJacob Lambert   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
14250f3f357eSJacob Lambert   assert(FH);
14260f3f357eSJacob Lambert 
14270f3f357eSJacob Lambert   // Write header.
14287e282343SYaxun (Sam) Liu   if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
14290f3f357eSJacob Lambert     return Err;
14300f3f357eSJacob Lambert 
14310f3f357eSJacob Lambert   // Write all bundles along with the start/end markers. If an error was found
14320f3f357eSJacob Lambert   // writing the end of the bundle component, abort the bundle writing.
14330f3f357eSJacob Lambert   auto Input = InputBuffers.begin();
14340f3f357eSJacob Lambert   for (auto &Triple : BundlerConfig.TargetNames) {
14357e282343SYaxun (Sam) Liu     if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
14360f3f357eSJacob Lambert       return Err;
14377e282343SYaxun (Sam) Liu     if (Error Err = FH->WriteBundle(BufferStream, **Input))
14380f3f357eSJacob Lambert       return Err;
14397e282343SYaxun (Sam) Liu     if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
14400f3f357eSJacob Lambert       return Err;
14410f3f357eSJacob Lambert     ++Input;
14420f3f357eSJacob Lambert   }
14437e282343SYaxun (Sam) Liu 
14447e282343SYaxun (Sam) Liu   raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
14457e282343SYaxun (Sam) Liu                             sys::fs::OF_None);
14467e282343SYaxun (Sam) Liu   if (EC)
14477e282343SYaxun (Sam) Liu     return createFileError(BundlerConfig.OutputFileNames.front(), EC);
14487e282343SYaxun (Sam) Liu 
14497e282343SYaxun (Sam) Liu   SmallVector<char, 0> CompressedBuffer;
14507e282343SYaxun (Sam) Liu   if (BundlerConfig.Compress) {
14517e282343SYaxun (Sam) Liu     std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
14527e282343SYaxun (Sam) Liu         llvm::MemoryBuffer::getMemBufferCopy(
14537e282343SYaxun (Sam) Liu             llvm::StringRef(Buffer.data(), Buffer.size()));
1454124d0b78SYaxun (Sam) Liu     auto CompressionResult = CompressedOffloadBundle::compress(
1455124d0b78SYaxun (Sam) Liu         {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel,
1456124d0b78SYaxun (Sam) Liu          /*zstdEnableLdm=*/true},
1457*e87b8438SYaxun (Sam) Liu         *BufferMemory, BundlerConfig.CompressedBundleVersion,
1458*e87b8438SYaxun (Sam) Liu         BundlerConfig.Verbose);
14597e282343SYaxun (Sam) Liu     if (auto Error = CompressionResult.takeError())
14607e282343SYaxun (Sam) Liu       return Error;
14617e282343SYaxun (Sam) Liu 
14627e282343SYaxun (Sam) Liu     auto CompressedMemBuffer = std::move(CompressionResult.get());
14637e282343SYaxun (Sam) Liu     CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
14647e282343SYaxun (Sam) Liu                             CompressedMemBuffer->getBufferEnd());
14657e282343SYaxun (Sam) Liu   } else
14667e282343SYaxun (Sam) Liu     CompressedBuffer = Buffer;
14677e282343SYaxun (Sam) Liu 
14687e282343SYaxun (Sam) Liu   OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
14697e282343SYaxun (Sam) Liu 
14707e282343SYaxun (Sam) Liu   return FH->finalizeOutputFile();
14710f3f357eSJacob Lambert }
14720f3f357eSJacob Lambert 
14730f3f357eSJacob Lambert // Unbundle the files. Return true if an error was found.
14740f3f357eSJacob Lambert Error OffloadBundler::UnbundleFiles() {
14750f3f357eSJacob Lambert   // Open Input file.
14760f3f357eSJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
147774dcf0b5SAbhina Sree       MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front(),
147874dcf0b5SAbhina Sree                                    /*IsText=*/true);
14790f3f357eSJacob Lambert   if (std::error_code EC = CodeOrErr.getError())
14800f3f357eSJacob Lambert     return createFileError(BundlerConfig.InputFileNames.front(), EC);
14810f3f357eSJacob Lambert 
14827e282343SYaxun (Sam) Liu   // Decompress the input if necessary.
14837e282343SYaxun (Sam) Liu   Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
14847e282343SYaxun (Sam) Liu       CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
14857e282343SYaxun (Sam) Liu   if (!DecompressedBufferOrErr)
14867e282343SYaxun (Sam) Liu     return createStringError(
14877e282343SYaxun (Sam) Liu         inconvertibleErrorCode(),
14887e282343SYaxun (Sam) Liu         "Failed to decompress input: " +
14897e282343SYaxun (Sam) Liu             llvm::toString(DecompressedBufferOrErr.takeError()));
14907e282343SYaxun (Sam) Liu 
14917e282343SYaxun (Sam) Liu   MemoryBuffer &Input = **DecompressedBufferOrErr;
14920f3f357eSJacob Lambert 
14930f3f357eSJacob Lambert   // Select the right files handler.
14940f3f357eSJacob Lambert   Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
14950f3f357eSJacob Lambert       CreateFileHandler(Input, BundlerConfig);
14960f3f357eSJacob Lambert   if (!FileHandlerOrErr)
14970f3f357eSJacob Lambert     return FileHandlerOrErr.takeError();
14980f3f357eSJacob Lambert 
14990f3f357eSJacob Lambert   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
15000f3f357eSJacob Lambert   assert(FH);
15010f3f357eSJacob Lambert 
15020f3f357eSJacob Lambert   // Read the header of the bundled file.
15030f3f357eSJacob Lambert   if (Error Err = FH->ReadHeader(Input))
15040f3f357eSJacob Lambert     return Err;
15050f3f357eSJacob Lambert 
15060f3f357eSJacob Lambert   // Create a work list that consist of the map triple/output file.
15070f3f357eSJacob Lambert   StringMap<StringRef> Worklist;
15080f3f357eSJacob Lambert   auto Output = BundlerConfig.OutputFileNames.begin();
15090f3f357eSJacob Lambert   for (auto &Triple : BundlerConfig.TargetNames) {
15100f3f357eSJacob Lambert     Worklist[Triple] = *Output;
15110f3f357eSJacob Lambert     ++Output;
15120f3f357eSJacob Lambert   }
15130f3f357eSJacob Lambert 
15140f3f357eSJacob Lambert   // Read all the bundles that are in the work list. If we find no bundles we
15150f3f357eSJacob Lambert   // assume the file is meant for the host target.
15160f3f357eSJacob Lambert   bool FoundHostBundle = false;
15170f3f357eSJacob Lambert   while (!Worklist.empty()) {
15182c5d49cfSFangrui Song     Expected<std::optional<StringRef>> CurTripleOrErr =
15192c5d49cfSFangrui Song         FH->ReadBundleStart(Input);
15200f3f357eSJacob Lambert     if (!CurTripleOrErr)
15210f3f357eSJacob Lambert       return CurTripleOrErr.takeError();
15220f3f357eSJacob Lambert 
15230f3f357eSJacob Lambert     // We don't have more bundles.
15240f3f357eSJacob Lambert     if (!*CurTripleOrErr)
15250f3f357eSJacob Lambert       break;
15260f3f357eSJacob Lambert 
15270f3f357eSJacob Lambert     StringRef CurTriple = **CurTripleOrErr;
15280f3f357eSJacob Lambert     assert(!CurTriple.empty());
15290f3f357eSJacob Lambert 
1530844b84afSYaxun (Sam) Liu     auto Output = Worklist.begin();
1531844b84afSYaxun (Sam) Liu     for (auto E = Worklist.end(); Output != E; Output++) {
1532844b84afSYaxun (Sam) Liu       if (isCodeObjectCompatible(
1533844b84afSYaxun (Sam) Liu               OffloadTargetInfo(CurTriple, BundlerConfig),
1534844b84afSYaxun (Sam) Liu               OffloadTargetInfo((*Output).first(), BundlerConfig))) {
1535844b84afSYaxun (Sam) Liu         break;
1536844b84afSYaxun (Sam) Liu       }
1537844b84afSYaxun (Sam) Liu     }
1538844b84afSYaxun (Sam) Liu 
15390f3f357eSJacob Lambert     if (Output == Worklist.end())
15400f3f357eSJacob Lambert       continue;
15410f3f357eSJacob Lambert     // Check if the output file can be opened and copy the bundle to it.
15420f3f357eSJacob Lambert     std::error_code EC;
1543844b84afSYaxun (Sam) Liu     raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
15440f3f357eSJacob Lambert     if (EC)
1545844b84afSYaxun (Sam) Liu       return createFileError((*Output).second, EC);
15460f3f357eSJacob Lambert     if (Error Err = FH->ReadBundle(OutputFile, Input))
15470f3f357eSJacob Lambert       return Err;
15480f3f357eSJacob Lambert     if (Error Err = FH->ReadBundleEnd(Input))
15490f3f357eSJacob Lambert       return Err;
15500f3f357eSJacob Lambert     Worklist.erase(Output);
15510f3f357eSJacob Lambert 
15520f3f357eSJacob Lambert     // Record if we found the host bundle.
15530f3f357eSJacob Lambert     auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
15540f3f357eSJacob Lambert     if (OffloadInfo.hasHostKind())
15550f3f357eSJacob Lambert       FoundHostBundle = true;
15560f3f357eSJacob Lambert   }
15570f3f357eSJacob Lambert 
15580f3f357eSJacob Lambert   if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
15590f3f357eSJacob Lambert     std::string ErrMsg = "Can't find bundles for";
15600f3f357eSJacob Lambert     std::set<StringRef> Sorted;
15610f3f357eSJacob Lambert     for (auto &E : Worklist)
15620f3f357eSJacob Lambert       Sorted.insert(E.first());
15630f3f357eSJacob Lambert     unsigned I = 0;
15640f3f357eSJacob Lambert     unsigned Last = Sorted.size() - 1;
15650f3f357eSJacob Lambert     for (auto &E : Sorted) {
15660f3f357eSJacob Lambert       if (I != 0 && Last > 1)
15670f3f357eSJacob Lambert         ErrMsg += ",";
15680f3f357eSJacob Lambert       ErrMsg += " ";
15690f3f357eSJacob Lambert       if (I == Last && I != 0)
15700f3f357eSJacob Lambert         ErrMsg += "and ";
15710f3f357eSJacob Lambert       ErrMsg += E.str();
15720f3f357eSJacob Lambert       ++I;
15730f3f357eSJacob Lambert     }
15740f3f357eSJacob Lambert     return createStringError(inconvertibleErrorCode(), ErrMsg);
15750f3f357eSJacob Lambert   }
15760f3f357eSJacob Lambert 
15770f3f357eSJacob Lambert   // If no bundles were found, assume the input file is the host bundle and
15780f3f357eSJacob Lambert   // create empty files for the remaining targets.
15790f3f357eSJacob Lambert   if (Worklist.size() == BundlerConfig.TargetNames.size()) {
15800f3f357eSJacob Lambert     for (auto &E : Worklist) {
15810f3f357eSJacob Lambert       std::error_code EC;
15820f3f357eSJacob Lambert       raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
15830f3f357eSJacob Lambert       if (EC)
15840f3f357eSJacob Lambert         return createFileError(E.second, EC);
15850f3f357eSJacob Lambert 
15860f3f357eSJacob Lambert       // If this entry has a host kind, copy the input file to the output file.
15870f3f357eSJacob Lambert       auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
15880f3f357eSJacob Lambert       if (OffloadInfo.hasHostKind())
15890f3f357eSJacob Lambert         OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
15900f3f357eSJacob Lambert     }
15910f3f357eSJacob Lambert     return Error::success();
15920f3f357eSJacob Lambert   }
15930f3f357eSJacob Lambert 
15940f3f357eSJacob Lambert   // If we found elements, we emit an error if none of those were for the host
15950f3f357eSJacob Lambert   // in case host bundle name was provided in command line.
15963006cb2aSYaxun (Sam) Liu   if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
15973006cb2aSYaxun (Sam) Liu         BundlerConfig.AllowMissingBundles))
15980f3f357eSJacob Lambert     return createStringError(inconvertibleErrorCode(),
15990f3f357eSJacob Lambert                              "Can't find bundle for the host target");
16000f3f357eSJacob Lambert 
16010f3f357eSJacob Lambert   // If we still have any elements in the worklist, create empty files for them.
16020f3f357eSJacob Lambert   for (auto &E : Worklist) {
16030f3f357eSJacob Lambert     std::error_code EC;
16040f3f357eSJacob Lambert     raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
16050f3f357eSJacob Lambert     if (EC)
16060f3f357eSJacob Lambert       return createFileError(E.second, EC);
16070f3f357eSJacob Lambert   }
16080f3f357eSJacob Lambert 
16090f3f357eSJacob Lambert   return Error::success();
16100f3f357eSJacob Lambert }
16110f3f357eSJacob Lambert 
16120f3f357eSJacob Lambert static Archive::Kind getDefaultArchiveKindForHost() {
16130f3f357eSJacob Lambert   return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
16140f3f357eSJacob Lambert                                                             : Archive::K_GNU;
16150f3f357eSJacob Lambert }
16160f3f357eSJacob Lambert 
16170f3f357eSJacob Lambert /// @brief Computes a list of targets among all given targets which are
16180f3f357eSJacob Lambert /// compatible with this code object
1619b978fa28SSimon Pilgrim /// @param [in] CodeObjectInfo Code Object
1620b978fa28SSimon Pilgrim /// @param [out] CompatibleTargets List of all compatible targets among all
16210f3f357eSJacob Lambert /// given targets
16220f3f357eSJacob Lambert /// @return false, if no compatible target is found.
16230f3f357eSJacob Lambert static bool
16240f3f357eSJacob Lambert getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo,
16250f3f357eSJacob Lambert                             SmallVectorImpl<StringRef> &CompatibleTargets,
16260f3f357eSJacob Lambert                             const OffloadBundlerConfig &BundlerConfig) {
16270f3f357eSJacob Lambert   if (!CompatibleTargets.empty()) {
16280f3f357eSJacob Lambert     DEBUG_WITH_TYPE("CodeObjectCompatibility",
16290f3f357eSJacob Lambert                     dbgs() << "CompatibleTargets list should be empty\n");
16300f3f357eSJacob Lambert     return false;
16310f3f357eSJacob Lambert   }
16320f3f357eSJacob Lambert   for (auto &Target : BundlerConfig.TargetNames) {
16330f3f357eSJacob Lambert     auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
16340f3f357eSJacob Lambert     if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
16350f3f357eSJacob Lambert       CompatibleTargets.push_back(Target);
16360f3f357eSJacob Lambert   }
16370f3f357eSJacob Lambert   return !CompatibleTargets.empty();
16380f3f357eSJacob Lambert }
16390f3f357eSJacob Lambert 
16403cf19097SJacob Lambert // Check that each code object file in the input archive conforms to following
16413cf19097SJacob Lambert // rule: for a specific processor, a feature either shows up in all target IDs,
16423cf19097SJacob Lambert // or does not show up in any target IDs. Otherwise the target ID combination is
16433cf19097SJacob Lambert // invalid.
16443cf19097SJacob Lambert static Error
16453cf19097SJacob Lambert CheckHeterogeneousArchive(StringRef ArchiveName,
16463cf19097SJacob Lambert                           const OffloadBundlerConfig &BundlerConfig) {
16473cf19097SJacob Lambert   std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
16483cf19097SJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
16493cf19097SJacob Lambert       MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
16503cf19097SJacob Lambert   if (std::error_code EC = BufOrErr.getError())
16513cf19097SJacob Lambert     return createFileError(ArchiveName, EC);
16523cf19097SJacob Lambert 
16533cf19097SJacob Lambert   ArchiveBuffers.push_back(std::move(*BufOrErr));
16543cf19097SJacob Lambert   Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
16553cf19097SJacob Lambert       Archive::create(ArchiveBuffers.back()->getMemBufferRef());
16563cf19097SJacob Lambert   if (!LibOrErr)
16573cf19097SJacob Lambert     return LibOrErr.takeError();
16583cf19097SJacob Lambert 
16593cf19097SJacob Lambert   auto Archive = std::move(*LibOrErr);
16603cf19097SJacob Lambert 
16613cf19097SJacob Lambert   Error ArchiveErr = Error::success();
16623cf19097SJacob Lambert   auto ChildEnd = Archive->child_end();
16633cf19097SJacob Lambert 
16643cf19097SJacob Lambert   /// Iterate over all bundled code object files in the input archive.
16653cf19097SJacob Lambert   for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
16663cf19097SJacob Lambert        ArchiveIter != ChildEnd; ++ArchiveIter) {
16673cf19097SJacob Lambert     if (ArchiveErr)
16683cf19097SJacob Lambert       return ArchiveErr;
16693cf19097SJacob Lambert     auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
16703cf19097SJacob Lambert     if (!ArchiveChildNameOrErr)
16713cf19097SJacob Lambert       return ArchiveChildNameOrErr.takeError();
16723cf19097SJacob Lambert 
16733cf19097SJacob Lambert     auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
16743cf19097SJacob Lambert     if (!CodeObjectBufferRefOrErr)
16753cf19097SJacob Lambert       return CodeObjectBufferRefOrErr.takeError();
16763cf19097SJacob Lambert 
16773cf19097SJacob Lambert     auto CodeObjectBuffer =
16783cf19097SJacob Lambert         MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
16793cf19097SJacob Lambert 
16803cf19097SJacob Lambert     Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
16813cf19097SJacob Lambert         CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
16823cf19097SJacob Lambert     if (!FileHandlerOrErr)
16833cf19097SJacob Lambert       return FileHandlerOrErr.takeError();
16843cf19097SJacob Lambert 
16853cf19097SJacob Lambert     std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
16863cf19097SJacob Lambert     assert(FileHandler);
16873cf19097SJacob Lambert 
16883cf19097SJacob Lambert     std::set<StringRef> BundleIds;
16893cf19097SJacob Lambert     auto CodeObjectFileError =
16903cf19097SJacob Lambert         FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
16913cf19097SJacob Lambert     if (CodeObjectFileError)
16923cf19097SJacob Lambert       return CodeObjectFileError;
16933cf19097SJacob Lambert 
16943cf19097SJacob Lambert     auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds);
16953cf19097SJacob Lambert     if (ConflictingArchs) {
16963cf19097SJacob Lambert       std::string ErrMsg =
16973cf19097SJacob Lambert           Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
16983cf19097SJacob Lambert                 ", " + ConflictingArchs.value().second + "] found in " +
16993cf19097SJacob Lambert                 ArchiveChildNameOrErr.get() + " of " + ArchiveName)
17003cf19097SJacob Lambert               .str();
17013cf19097SJacob Lambert       return createStringError(inconvertibleErrorCode(), ErrMsg);
17023cf19097SJacob Lambert     }
17033cf19097SJacob Lambert   }
17043cf19097SJacob Lambert 
17053cf19097SJacob Lambert   return ArchiveErr;
17063cf19097SJacob Lambert }
17073cf19097SJacob Lambert 
17080f3f357eSJacob Lambert /// UnbundleArchive takes an archive file (".a") as input containing bundled
17090f3f357eSJacob Lambert /// code object files, and a list of offload targets (not host), and extracts
17100f3f357eSJacob Lambert /// the code objects into a new archive file for each offload target. Each
17110f3f357eSJacob Lambert /// resulting archive file contains all code object files corresponding to that
17120f3f357eSJacob Lambert /// particular offload target. The created archive file does not
17130f3f357eSJacob Lambert /// contain an index of the symbols and code object files are named as
17143cf19097SJacob Lambert /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
17150f3f357eSJacob Lambert Error OffloadBundler::UnbundleArchive() {
17160f3f357eSJacob Lambert   std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
17170f3f357eSJacob Lambert 
17180f3f357eSJacob Lambert   /// Map of target names with list of object files that will form the device
17190f3f357eSJacob Lambert   /// specific archive for that target
17200f3f357eSJacob Lambert   StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
17210f3f357eSJacob Lambert 
17220f3f357eSJacob Lambert   // Map of target names and output archive filenames
17230f3f357eSJacob Lambert   StringMap<StringRef> TargetOutputFileNameMap;
17240f3f357eSJacob Lambert 
17250f3f357eSJacob Lambert   auto Output = BundlerConfig.OutputFileNames.begin();
17260f3f357eSJacob Lambert   for (auto &Target : BundlerConfig.TargetNames) {
17270f3f357eSJacob Lambert     TargetOutputFileNameMap[Target] = *Output;
17280f3f357eSJacob Lambert     ++Output;
17290f3f357eSJacob Lambert   }
17300f3f357eSJacob Lambert 
17310f3f357eSJacob Lambert   StringRef IFName = BundlerConfig.InputFileNames.front();
17320f3f357eSJacob Lambert 
17333cf19097SJacob Lambert   if (BundlerConfig.CheckInputArchive) {
17343cf19097SJacob Lambert     // For a specific processor, a feature either shows up in all target IDs, or
17353cf19097SJacob Lambert     // does not show up in any target IDs. Otherwise the target ID combination
17363cf19097SJacob Lambert     // is invalid.
17373cf19097SJacob Lambert     auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig);
17383cf19097SJacob Lambert     if (ArchiveError) {
17393cf19097SJacob Lambert       return ArchiveError;
17403cf19097SJacob Lambert     }
17413cf19097SJacob Lambert   }
17423cf19097SJacob Lambert 
17430f3f357eSJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
17440f3f357eSJacob Lambert       MemoryBuffer::getFileOrSTDIN(IFName, true, false);
17450f3f357eSJacob Lambert   if (std::error_code EC = BufOrErr.getError())
17460f3f357eSJacob Lambert     return createFileError(BundlerConfig.InputFileNames.front(), EC);
17470f3f357eSJacob Lambert 
17480f3f357eSJacob Lambert   ArchiveBuffers.push_back(std::move(*BufOrErr));
17490f3f357eSJacob Lambert   Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
17500f3f357eSJacob Lambert       Archive::create(ArchiveBuffers.back()->getMemBufferRef());
17510f3f357eSJacob Lambert   if (!LibOrErr)
17520f3f357eSJacob Lambert     return LibOrErr.takeError();
17530f3f357eSJacob Lambert 
17540f3f357eSJacob Lambert   auto Archive = std::move(*LibOrErr);
17550f3f357eSJacob Lambert 
17560f3f357eSJacob Lambert   Error ArchiveErr = Error::success();
17570f3f357eSJacob Lambert   auto ChildEnd = Archive->child_end();
17580f3f357eSJacob Lambert 
17590f3f357eSJacob Lambert   /// Iterate over all bundled code object files in the input archive.
17600f3f357eSJacob Lambert   for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
17610f3f357eSJacob Lambert        ArchiveIter != ChildEnd; ++ArchiveIter) {
17620f3f357eSJacob Lambert     if (ArchiveErr)
17630f3f357eSJacob Lambert       return ArchiveErr;
17640f3f357eSJacob Lambert     auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
17650f3f357eSJacob Lambert     if (!ArchiveChildNameOrErr)
17660f3f357eSJacob Lambert       return ArchiveChildNameOrErr.takeError();
17670f3f357eSJacob Lambert 
17680f3f357eSJacob Lambert     StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
17690f3f357eSJacob Lambert 
17700f3f357eSJacob Lambert     auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
17710f3f357eSJacob Lambert     if (!CodeObjectBufferRefOrErr)
17720f3f357eSJacob Lambert       return CodeObjectBufferRefOrErr.takeError();
17730f3f357eSJacob Lambert 
17747e282343SYaxun (Sam) Liu     auto TempCodeObjectBuffer =
17750f3f357eSJacob Lambert         MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
17760f3f357eSJacob Lambert 
17777e282343SYaxun (Sam) Liu     // Decompress the buffer if necessary.
17787e282343SYaxun (Sam) Liu     Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
17797e282343SYaxun (Sam) Liu         CompressedOffloadBundle::decompress(*TempCodeObjectBuffer,
17807e282343SYaxun (Sam) Liu                                             BundlerConfig.Verbose);
17817e282343SYaxun (Sam) Liu     if (!DecompressedBufferOrErr)
17827e282343SYaxun (Sam) Liu       return createStringError(
17837e282343SYaxun (Sam) Liu           inconvertibleErrorCode(),
17847e282343SYaxun (Sam) Liu           "Failed to decompress code object: " +
17857e282343SYaxun (Sam) Liu               llvm::toString(DecompressedBufferOrErr.takeError()));
17867e282343SYaxun (Sam) Liu 
17877e282343SYaxun (Sam) Liu     MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
17887e282343SYaxun (Sam) Liu 
17890f3f357eSJacob Lambert     Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
17907e282343SYaxun (Sam) Liu         CreateFileHandler(CodeObjectBuffer, BundlerConfig);
17910f3f357eSJacob Lambert     if (!FileHandlerOrErr)
17920f3f357eSJacob Lambert       return FileHandlerOrErr.takeError();
17930f3f357eSJacob Lambert 
17940f3f357eSJacob Lambert     std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
17950f3f357eSJacob Lambert     assert(FileHandler &&
17960f3f357eSJacob Lambert            "FileHandle creation failed for file in the archive!");
17970f3f357eSJacob Lambert 
17987e282343SYaxun (Sam) Liu     if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
17990f3f357eSJacob Lambert       return ReadErr;
18000f3f357eSJacob Lambert 
18012c5d49cfSFangrui Song     Expected<std::optional<StringRef>> CurBundleIDOrErr =
18027e282343SYaxun (Sam) Liu         FileHandler->ReadBundleStart(CodeObjectBuffer);
18030f3f357eSJacob Lambert     if (!CurBundleIDOrErr)
18040f3f357eSJacob Lambert       return CurBundleIDOrErr.takeError();
18050f3f357eSJacob Lambert 
18062c5d49cfSFangrui Song     std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
18070f3f357eSJacob Lambert     // No device code in this child, skip.
18087430894aSFangrui Song     if (!OptionalCurBundleID)
18090f3f357eSJacob Lambert       continue;
18100f3f357eSJacob Lambert     StringRef CodeObject = *OptionalCurBundleID;
18110f3f357eSJacob Lambert 
18120f3f357eSJacob Lambert     // Process all bundle entries (CodeObjects) found in this child of input
18130f3f357eSJacob Lambert     // archive.
18140f3f357eSJacob Lambert     while (!CodeObject.empty()) {
18150f3f357eSJacob Lambert       SmallVector<StringRef> CompatibleTargets;
18160f3f357eSJacob Lambert       auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
181761b13e0dSYaxun (Sam) Liu       if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
18180f3f357eSJacob Lambert                                       BundlerConfig)) {
18190f3f357eSJacob Lambert         std::string BundleData;
18200f3f357eSJacob Lambert         raw_string_ostream DataStream(BundleData);
18217e282343SYaxun (Sam) Liu         if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
18220f3f357eSJacob Lambert           return Err;
18230f3f357eSJacob Lambert 
18240f3f357eSJacob Lambert         for (auto &CompatibleTarget : CompatibleTargets) {
18250f3f357eSJacob Lambert           SmallString<128> BundledObjectFileName;
18260f3f357eSJacob Lambert           BundledObjectFileName.assign(BundledObjectFile);
18270f3f357eSJacob Lambert           auto OutputBundleName =
18280f3f357eSJacob Lambert               Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
18290f3f357eSJacob Lambert                     CodeObject +
18300f3f357eSJacob Lambert                     getDeviceLibraryFileName(BundledObjectFileName,
1831844b84afSYaxun (Sam) Liu                                              CodeObjectInfo.TargetID))
18320f3f357eSJacob Lambert                   .str();
18330f3f357eSJacob Lambert           // Replace ':' in optional target feature list with '_' to ensure
18340f3f357eSJacob Lambert           // cross-platform validity.
18350f3f357eSJacob Lambert           std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
18360f3f357eSJacob Lambert                        '_');
18370f3f357eSJacob Lambert 
18380f3f357eSJacob Lambert           std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
18390f3f357eSJacob Lambert               DataStream.str(), OutputBundleName);
18400f3f357eSJacob Lambert           ArchiveBuffers.push_back(std::move(MemBuf));
18410f3f357eSJacob Lambert           llvm::MemoryBufferRef MemBufRef =
18420f3f357eSJacob Lambert               MemoryBufferRef(*(ArchiveBuffers.back()));
18430f3f357eSJacob Lambert 
18440f3f357eSJacob Lambert           // For inserting <CompatibleTarget, list<CodeObject>> entry in
18450f3f357eSJacob Lambert           // OutputArchivesMap.
18460f3f357eSJacob Lambert           OutputArchivesMap[CompatibleTarget].push_back(
18470f3f357eSJacob Lambert               NewArchiveMember(MemBufRef));
18480f3f357eSJacob Lambert         }
18490f3f357eSJacob Lambert       }
18500f3f357eSJacob Lambert 
18517e282343SYaxun (Sam) Liu       if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
18520f3f357eSJacob Lambert         return Err;
18530f3f357eSJacob Lambert 
18542c5d49cfSFangrui Song       Expected<std::optional<StringRef>> NextTripleOrErr =
18557e282343SYaxun (Sam) Liu           FileHandler->ReadBundleStart(CodeObjectBuffer);
18560f3f357eSJacob Lambert       if (!NextTripleOrErr)
18570f3f357eSJacob Lambert         return NextTripleOrErr.takeError();
18580f3f357eSJacob Lambert 
1859a9481170SKazu Hirata       CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
18600f3f357eSJacob Lambert     } // End of processing of all bundle entries of this child of input archive.
18610f3f357eSJacob Lambert   }   // End of while over children of input archive.
18620f3f357eSJacob Lambert 
18630f3f357eSJacob Lambert   assert(!ArchiveErr && "Error occurred while reading archive!");
18640f3f357eSJacob Lambert 
18650f3f357eSJacob Lambert   /// Write out an archive for each target
18660f3f357eSJacob Lambert   for (auto &Target : BundlerConfig.TargetNames) {
18670f3f357eSJacob Lambert     StringRef FileName = TargetOutputFileNameMap[Target];
18680f3f357eSJacob Lambert     StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
18690f3f357eSJacob Lambert         OutputArchivesMap.find(Target);
18700f3f357eSJacob Lambert     if (CurArchiveMembers != OutputArchivesMap.end()) {
18710f3f357eSJacob Lambert       if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1872f740bcb3Szhijian                                         SymtabWritingMode::NormalSymtab,
1873f740bcb3Szhijian                                         getDefaultArchiveKindForHost(), true,
1874f740bcb3Szhijian                                         false, nullptr))
18750f3f357eSJacob Lambert         return WriteErr;
18760f3f357eSJacob Lambert     } else if (!BundlerConfig.AllowMissingBundles) {
18770f3f357eSJacob Lambert       std::string ErrMsg =
18780f3f357eSJacob Lambert           Twine("no compatible code object found for the target '" + Target +
18790f3f357eSJacob Lambert                 "' in heterogeneous archive library: " + IFName)
18800f3f357eSJacob Lambert               .str();
18810f3f357eSJacob Lambert       return createStringError(inconvertibleErrorCode(), ErrMsg);
18820f3f357eSJacob Lambert     } else { // Create an empty archive file if no compatible code object is
18830f3f357eSJacob Lambert              // found and "allow-missing-bundles" is enabled. It ensures that
18840f3f357eSJacob Lambert              // the linker using output of this step doesn't complain about
18850f3f357eSJacob Lambert              // the missing input file.
18860f3f357eSJacob Lambert       std::vector<llvm::NewArchiveMember> EmptyArchive;
18870f3f357eSJacob Lambert       EmptyArchive.clear();
1888f740bcb3Szhijian       if (Error WriteErr = writeArchive(
1889f740bcb3Szhijian               FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1890f740bcb3Szhijian               getDefaultArchiveKindForHost(), true, false, nullptr))
18910f3f357eSJacob Lambert         return WriteErr;
18920f3f357eSJacob Lambert     }
18930f3f357eSJacob Lambert   }
18940f3f357eSJacob Lambert 
18950f3f357eSJacob Lambert   return Error::success();
18960f3f357eSJacob Lambert }
1897