xref: /llvm-project/clang/lib/Driver/OffloadBundler.cpp (revision e8f41fdb5c6fd7903f65216d70c64eb52c898b51)
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 "clang/Basic/Version.h"
210f3f357eSJacob Lambert #include "llvm/ADT/ArrayRef.h"
220f3f357eSJacob Lambert #include "llvm/ADT/SmallString.h"
230f3f357eSJacob Lambert #include "llvm/ADT/SmallVector.h"
240f3f357eSJacob Lambert #include "llvm/ADT/StringMap.h"
250f3f357eSJacob Lambert #include "llvm/ADT/StringRef.h"
260f3f357eSJacob Lambert #include "llvm/ADT/Triple.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"
320f3f357eSJacob Lambert #include "llvm/Support/Debug.h"
33e68fc86bSBenjamin Kramer #include "llvm/Support/EndianStream.h"
340f3f357eSJacob Lambert #include "llvm/Support/Errc.h"
350f3f357eSJacob Lambert #include "llvm/Support/Error.h"
360f3f357eSJacob Lambert #include "llvm/Support/ErrorOr.h"
370f3f357eSJacob Lambert #include "llvm/Support/FileSystem.h"
380f3f357eSJacob Lambert #include "llvm/Support/Host.h"
390f3f357eSJacob Lambert #include "llvm/Support/MemoryBuffer.h"
400f3f357eSJacob Lambert #include "llvm/Support/Path.h"
410f3f357eSJacob Lambert #include "llvm/Support/Program.h"
420f3f357eSJacob Lambert #include "llvm/Support/Signals.h"
430f3f357eSJacob Lambert #include "llvm/Support/StringSaver.h"
440f3f357eSJacob Lambert #include "llvm/Support/WithColor.h"
450f3f357eSJacob Lambert #include "llvm/Support/raw_ostream.h"
460f3f357eSJacob Lambert #include <algorithm>
470f3f357eSJacob Lambert #include <cassert>
480f3f357eSJacob Lambert #include <cstddef>
490f3f357eSJacob Lambert #include <cstdint>
500f3f357eSJacob Lambert #include <forward_list>
510f3f357eSJacob Lambert #include <memory>
520f3f357eSJacob Lambert #include <set>
530f3f357eSJacob Lambert #include <string>
540f3f357eSJacob Lambert #include <system_error>
550f3f357eSJacob Lambert #include <utility>
560f3f357eSJacob Lambert 
570f3f357eSJacob Lambert using namespace llvm;
580f3f357eSJacob Lambert using namespace llvm::object;
590f3f357eSJacob Lambert using namespace clang;
600f3f357eSJacob Lambert 
610f3f357eSJacob Lambert /// Magic string that marks the existence of offloading data.
620f3f357eSJacob Lambert #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
630f3f357eSJacob Lambert 
640f3f357eSJacob Lambert OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
650f3f357eSJacob Lambert                                      const OffloadBundlerConfig &BC)
660f3f357eSJacob Lambert     : BundlerConfig(BC) {
670f3f357eSJacob Lambert 
680f3f357eSJacob Lambert   // TODO: Add error checking from ClangOffloadBundler.cpp
690f3f357eSJacob Lambert   auto TargetFeatures = Target.split(':');
700f3f357eSJacob Lambert   auto TripleOrGPU = TargetFeatures.first.rsplit('-');
710f3f357eSJacob Lambert 
725b11caa8Sraghavmedicherla   if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) {
730f3f357eSJacob Lambert     auto KindTriple = TripleOrGPU.first.split('-');
740f3f357eSJacob Lambert     this->OffloadKind = KindTriple.first;
750f3f357eSJacob Lambert     this->Triple = llvm::Triple(KindTriple.second);
76844b84afSYaxun (Sam) Liu     this->TargetID = Target.substr(Target.find(TripleOrGPU.second));
770f3f357eSJacob Lambert   } else {
780f3f357eSJacob Lambert     auto KindTriple = TargetFeatures.first.split('-');
790f3f357eSJacob Lambert     this->OffloadKind = KindTriple.first;
800f3f357eSJacob Lambert     this->Triple = llvm::Triple(KindTriple.second);
81844b84afSYaxun (Sam) Liu     this->TargetID = "";
820f3f357eSJacob Lambert   }
830f3f357eSJacob Lambert }
840f3f357eSJacob Lambert 
850f3f357eSJacob Lambert bool OffloadTargetInfo::hasHostKind() const {
860f3f357eSJacob Lambert   return this->OffloadKind == "host";
870f3f357eSJacob Lambert }
880f3f357eSJacob Lambert 
890f3f357eSJacob Lambert bool OffloadTargetInfo::isOffloadKindValid() const {
900f3f357eSJacob Lambert   return OffloadKind == "host" || OffloadKind == "openmp" ||
910f3f357eSJacob Lambert          OffloadKind == "hip" || OffloadKind == "hipv4";
920f3f357eSJacob Lambert }
930f3f357eSJacob Lambert 
940f3f357eSJacob Lambert bool OffloadTargetInfo::isOffloadKindCompatible(
950f3f357eSJacob Lambert     const StringRef TargetOffloadKind) const {
960f3f357eSJacob Lambert   if (OffloadKind == TargetOffloadKind)
970f3f357eSJacob Lambert     return true;
980f3f357eSJacob Lambert   if (BundlerConfig.HipOpenmpCompatible) {
995b11caa8Sraghavmedicherla     bool HIPCompatibleWithOpenMP = OffloadKind.startswith_insensitive("hip") &&
1000f3f357eSJacob Lambert                                    TargetOffloadKind == "openmp";
1010f3f357eSJacob Lambert     bool OpenMPCompatibleWithHIP =
1020f3f357eSJacob Lambert         OffloadKind == "openmp" &&
1030f3f357eSJacob Lambert         TargetOffloadKind.startswith_insensitive("hip");
1040f3f357eSJacob Lambert     return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
1050f3f357eSJacob Lambert   }
1060f3f357eSJacob Lambert   return false;
1070f3f357eSJacob Lambert }
1080f3f357eSJacob Lambert 
1090f3f357eSJacob Lambert bool OffloadTargetInfo::isTripleValid() const {
1100f3f357eSJacob Lambert   return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
1110f3f357eSJacob Lambert }
1120f3f357eSJacob Lambert 
1130f3f357eSJacob Lambert bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const {
1140f3f357eSJacob Lambert   return OffloadKind == Target.OffloadKind &&
115844b84afSYaxun (Sam) Liu          Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID;
1160f3f357eSJacob Lambert }
1170f3f357eSJacob Lambert 
118844b84afSYaxun (Sam) Liu std::string OffloadTargetInfo::str() const {
119844b84afSYaxun (Sam) Liu   return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str();
1200f3f357eSJacob Lambert }
1210f3f357eSJacob Lambert 
1220f3f357eSJacob Lambert static StringRef getDeviceFileExtension(StringRef Device,
1230f3f357eSJacob Lambert                                         StringRef BundleFileName) {
1240f3f357eSJacob Lambert   if (Device.contains("gfx"))
1250f3f357eSJacob Lambert     return ".bc";
1260f3f357eSJacob Lambert   if (Device.contains("sm_"))
1270f3f357eSJacob Lambert     return ".cubin";
1280f3f357eSJacob Lambert   return sys::path::extension(BundleFileName);
1290f3f357eSJacob Lambert }
1300f3f357eSJacob Lambert 
1310f3f357eSJacob Lambert static std::string getDeviceLibraryFileName(StringRef BundleFileName,
1320f3f357eSJacob Lambert                                             StringRef Device) {
1330f3f357eSJacob Lambert   StringRef LibName = sys::path::stem(BundleFileName);
1340f3f357eSJacob Lambert   StringRef Extension = getDeviceFileExtension(Device, BundleFileName);
1350f3f357eSJacob Lambert 
1360f3f357eSJacob Lambert   std::string Result;
1370f3f357eSJacob Lambert   Result += LibName;
1380f3f357eSJacob Lambert   Result += Extension;
1390f3f357eSJacob Lambert   return Result;
1400f3f357eSJacob Lambert }
1410f3f357eSJacob Lambert 
142844b84afSYaxun (Sam) Liu /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
143844b84afSYaxun (Sam) Liu /// target \p TargetInfo.
144844b84afSYaxun (Sam) Liu /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
145844b84afSYaxun (Sam) Liu bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo,
146844b84afSYaxun (Sam) Liu                             const OffloadTargetInfo &TargetInfo) {
147844b84afSYaxun (Sam) Liu 
148844b84afSYaxun (Sam) Liu   // Compatible in case of exact match.
149844b84afSYaxun (Sam) Liu   if (CodeObjectInfo == TargetInfo) {
150844b84afSYaxun (Sam) Liu     DEBUG_WITH_TYPE("CodeObjectCompatibility",
151844b84afSYaxun (Sam) Liu                     dbgs() << "Compatible: Exact match: \t[CodeObject: "
152844b84afSYaxun (Sam) Liu                            << CodeObjectInfo.str()
153844b84afSYaxun (Sam) Liu                            << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
154844b84afSYaxun (Sam) Liu     return true;
155844b84afSYaxun (Sam) Liu   }
156844b84afSYaxun (Sam) Liu 
157844b84afSYaxun (Sam) Liu   // Incompatible if Kinds or Triples mismatch.
158844b84afSYaxun (Sam) Liu   if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) ||
159844b84afSYaxun (Sam) Liu       !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
160844b84afSYaxun (Sam) Liu     DEBUG_WITH_TYPE(
161844b84afSYaxun (Sam) Liu         "CodeObjectCompatibility",
162844b84afSYaxun (Sam) Liu         dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
163844b84afSYaxun (Sam) Liu                << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
164844b84afSYaxun (Sam) Liu                << "]\n");
165844b84afSYaxun (Sam) Liu     return false;
166844b84afSYaxun (Sam) Liu   }
167844b84afSYaxun (Sam) Liu 
168844b84afSYaxun (Sam) Liu   // Incompatible if target IDs are incompatible.
169844b84afSYaxun (Sam) Liu   if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID,
170844b84afSYaxun (Sam) Liu                                    TargetInfo.TargetID)) {
171844b84afSYaxun (Sam) Liu     DEBUG_WITH_TYPE(
172844b84afSYaxun (Sam) Liu         "CodeObjectCompatibility",
173844b84afSYaxun (Sam) Liu         dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: "
174844b84afSYaxun (Sam) Liu                << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
175844b84afSYaxun (Sam) Liu                << "]\n");
176844b84afSYaxun (Sam) Liu     return false;
177844b84afSYaxun (Sam) Liu   }
178844b84afSYaxun (Sam) Liu 
179844b84afSYaxun (Sam) Liu   DEBUG_WITH_TYPE(
180844b84afSYaxun (Sam) Liu       "CodeObjectCompatibility",
181844b84afSYaxun (Sam) Liu       dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: "
182844b84afSYaxun (Sam) Liu              << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
183844b84afSYaxun (Sam) Liu              << "]\n");
184844b84afSYaxun (Sam) Liu   return true;
185844b84afSYaxun (Sam) Liu }
186844b84afSYaxun (Sam) Liu 
187b6942a28SBenjamin Kramer namespace {
1880f3f357eSJacob Lambert /// Generic file handler interface.
1890f3f357eSJacob Lambert class FileHandler {
1900f3f357eSJacob Lambert public:
1910f3f357eSJacob Lambert   struct BundleInfo {
1920f3f357eSJacob Lambert     StringRef BundleID;
1930f3f357eSJacob Lambert   };
1940f3f357eSJacob Lambert 
1950f3f357eSJacob Lambert   FileHandler() {}
1960f3f357eSJacob Lambert 
1970f3f357eSJacob Lambert   virtual ~FileHandler() {}
1980f3f357eSJacob Lambert 
1990f3f357eSJacob Lambert   /// Update the file handler with information from the header of the bundled
2000f3f357eSJacob Lambert   /// file.
2010f3f357eSJacob Lambert   virtual Error ReadHeader(MemoryBuffer &Input) = 0;
2020f3f357eSJacob Lambert 
2030f3f357eSJacob Lambert   /// Read the marker of the next bundled to be read in the file. The bundle
20437a3e98cSKazu Hirata   /// name is returned if there is one in the file, or `std::nullopt` if there
20537a3e98cSKazu Hirata   /// are no more bundles to be read.
2062c5d49cfSFangrui Song   virtual Expected<std::optional<StringRef>>
2070f3f357eSJacob Lambert   ReadBundleStart(MemoryBuffer &Input) = 0;
2080f3f357eSJacob Lambert 
2090f3f357eSJacob Lambert   /// Read the marker that closes the current bundle.
2100f3f357eSJacob Lambert   virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
2110f3f357eSJacob Lambert 
2120f3f357eSJacob Lambert   /// Read the current bundle and write the result into the stream \a OS.
2130f3f357eSJacob Lambert   virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
2140f3f357eSJacob Lambert 
2150f3f357eSJacob Lambert   /// Write the header of the bundled file to \a OS based on the information
2160f3f357eSJacob Lambert   /// gathered from \a Inputs.
2170f3f357eSJacob Lambert   virtual Error WriteHeader(raw_fd_ostream &OS,
2180f3f357eSJacob Lambert                             ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
2190f3f357eSJacob Lambert 
2200f3f357eSJacob Lambert   /// Write the marker that initiates a bundle for the triple \a TargetTriple to
2210f3f357eSJacob Lambert   /// \a OS.
2220f3f357eSJacob Lambert   virtual Error WriteBundleStart(raw_fd_ostream &OS,
2230f3f357eSJacob Lambert                                  StringRef TargetTriple) = 0;
2240f3f357eSJacob Lambert 
2250f3f357eSJacob Lambert   /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
2260f3f357eSJacob Lambert   /// OS.
2270f3f357eSJacob Lambert   virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0;
2280f3f357eSJacob Lambert 
2290f3f357eSJacob Lambert   /// Write the bundle from \a Input into \a OS.
2300f3f357eSJacob Lambert   virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
2310f3f357eSJacob Lambert 
2320f3f357eSJacob Lambert   /// List bundle IDs in \a Input.
2330f3f357eSJacob Lambert   virtual Error listBundleIDs(MemoryBuffer &Input) {
2340f3f357eSJacob Lambert     if (Error Err = ReadHeader(Input))
2350f3f357eSJacob Lambert       return Err;
2360f3f357eSJacob Lambert     return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
2370f3f357eSJacob Lambert       llvm::outs() << Info.BundleID << '\n';
2380f3f357eSJacob Lambert       Error Err = listBundleIDsCallback(Input, Info);
2390f3f357eSJacob Lambert       if (Err)
2400f3f357eSJacob Lambert         return Err;
2410f3f357eSJacob Lambert       return Error::success();
2420f3f357eSJacob Lambert     });
2430f3f357eSJacob Lambert   }
2440f3f357eSJacob Lambert 
2450f3f357eSJacob Lambert   /// For each bundle in \a Input, do \a Func.
2460f3f357eSJacob Lambert   Error forEachBundle(MemoryBuffer &Input,
2470f3f357eSJacob Lambert                       std::function<Error(const BundleInfo &)> Func) {
2480f3f357eSJacob Lambert     while (true) {
2492c5d49cfSFangrui Song       Expected<std::optional<StringRef>> CurTripleOrErr =
2502c5d49cfSFangrui Song           ReadBundleStart(Input);
2510f3f357eSJacob Lambert       if (!CurTripleOrErr)
2520f3f357eSJacob Lambert         return CurTripleOrErr.takeError();
2530f3f357eSJacob Lambert 
2540f3f357eSJacob Lambert       // No more bundles.
2550f3f357eSJacob Lambert       if (!*CurTripleOrErr)
2560f3f357eSJacob Lambert         break;
2570f3f357eSJacob Lambert 
2580f3f357eSJacob Lambert       StringRef CurTriple = **CurTripleOrErr;
2590f3f357eSJacob Lambert       assert(!CurTriple.empty());
2600f3f357eSJacob Lambert 
2610f3f357eSJacob Lambert       BundleInfo Info{CurTriple};
2620f3f357eSJacob Lambert       if (Error Err = Func(Info))
2630f3f357eSJacob Lambert         return Err;
2640f3f357eSJacob Lambert     }
2650f3f357eSJacob Lambert     return Error::success();
2660f3f357eSJacob Lambert   }
2670f3f357eSJacob Lambert 
2680f3f357eSJacob Lambert protected:
2690f3f357eSJacob Lambert   virtual Error listBundleIDsCallback(MemoryBuffer &Input,
2700f3f357eSJacob Lambert                                       const BundleInfo &Info) {
2710f3f357eSJacob Lambert     return Error::success();
2720f3f357eSJacob Lambert   }
2730f3f357eSJacob Lambert };
2740f3f357eSJacob Lambert 
2750f3f357eSJacob Lambert /// Handler for binary files. The bundled file will have the following format
2760f3f357eSJacob Lambert /// (all integers are stored in little-endian format):
2770f3f357eSJacob Lambert ///
2780f3f357eSJacob Lambert /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
2790f3f357eSJacob Lambert ///
2800f3f357eSJacob Lambert /// NumberOfOffloadBundles (8-byte integer)
2810f3f357eSJacob Lambert ///
2820f3f357eSJacob Lambert /// OffsetOfBundle1 (8-byte integer)
2830f3f357eSJacob Lambert /// SizeOfBundle1 (8-byte integer)
2840f3f357eSJacob Lambert /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
2850f3f357eSJacob Lambert /// TripleOfBundle1 (byte length defined before)
2860f3f357eSJacob Lambert ///
2870f3f357eSJacob Lambert /// ...
2880f3f357eSJacob Lambert ///
2890f3f357eSJacob Lambert /// OffsetOfBundleN (8-byte integer)
2900f3f357eSJacob Lambert /// SizeOfBundleN (8-byte integer)
2910f3f357eSJacob Lambert /// NumberOfBytesInTripleOfBundleN (8-byte integer)
2920f3f357eSJacob Lambert /// TripleOfBundleN (byte length defined before)
2930f3f357eSJacob Lambert ///
2940f3f357eSJacob Lambert /// Bundle1
2950f3f357eSJacob Lambert /// ...
2960f3f357eSJacob Lambert /// BundleN
2970f3f357eSJacob Lambert 
2980f3f357eSJacob Lambert /// Read 8-byte integers from a buffer in little-endian format.
2990f3f357eSJacob Lambert static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
300e68fc86bSBenjamin Kramer   return llvm::support::endian::read64le(Buffer.data() + pos);
3010f3f357eSJacob Lambert }
3020f3f357eSJacob Lambert 
3030f3f357eSJacob Lambert /// Write 8-byte integers to a buffer in little-endian format.
3040f3f357eSJacob Lambert static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) {
305e68fc86bSBenjamin Kramer   llvm::support::endian::write(OS, Val, llvm::support::little);
3060f3f357eSJacob Lambert }
3070f3f357eSJacob Lambert 
3080f3f357eSJacob Lambert class BinaryFileHandler final : public FileHandler {
3090f3f357eSJacob Lambert   /// Information about the bundles extracted from the header.
3100f3f357eSJacob Lambert   struct BinaryBundleInfo final : public BundleInfo {
3110f3f357eSJacob Lambert     /// Size of the bundle.
3120f3f357eSJacob Lambert     uint64_t Size = 0u;
3130f3f357eSJacob Lambert     /// Offset at which the bundle starts in the bundled file.
3140f3f357eSJacob Lambert     uint64_t Offset = 0u;
3150f3f357eSJacob Lambert 
3160f3f357eSJacob Lambert     BinaryBundleInfo() {}
3170f3f357eSJacob Lambert     BinaryBundleInfo(uint64_t Size, uint64_t Offset)
3180f3f357eSJacob Lambert         : Size(Size), Offset(Offset) {}
3190f3f357eSJacob Lambert   };
3200f3f357eSJacob Lambert 
3210f3f357eSJacob Lambert   /// Map between a triple and the corresponding bundle information.
3220f3f357eSJacob Lambert   StringMap<BinaryBundleInfo> BundlesInfo;
3230f3f357eSJacob Lambert 
3240f3f357eSJacob Lambert   /// Iterator for the bundle information that is being read.
3250f3f357eSJacob Lambert   StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
3260f3f357eSJacob Lambert   StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
3270f3f357eSJacob Lambert 
3280f3f357eSJacob Lambert   /// Current bundle target to be written.
3290f3f357eSJacob Lambert   std::string CurWriteBundleTarget;
3300f3f357eSJacob Lambert 
3310f3f357eSJacob Lambert   /// Configuration options and arrays for this bundler job
3320f3f357eSJacob Lambert   const OffloadBundlerConfig &BundlerConfig;
3330f3f357eSJacob Lambert 
3340f3f357eSJacob Lambert public:
3350f3f357eSJacob Lambert   // TODO: Add error checking from ClangOffloadBundler.cpp
3360f3f357eSJacob Lambert   BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
3370f3f357eSJacob Lambert 
3380f3f357eSJacob Lambert   ~BinaryFileHandler() final {}
3390f3f357eSJacob Lambert 
3400f3f357eSJacob Lambert   Error ReadHeader(MemoryBuffer &Input) final {
3410f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
3420f3f357eSJacob Lambert 
3430f3f357eSJacob Lambert     // Initialize the current bundle with the end of the container.
3440f3f357eSJacob Lambert     CurBundleInfo = BundlesInfo.end();
3450f3f357eSJacob Lambert 
3460f3f357eSJacob Lambert     // Check if buffer is smaller than magic string.
3470f3f357eSJacob Lambert     size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
3480f3f357eSJacob Lambert     if (ReadChars > FC.size())
3490f3f357eSJacob Lambert       return Error::success();
3500f3f357eSJacob Lambert 
3510f3f357eSJacob Lambert     // Check if no magic was found.
3520f3f357eSJacob Lambert     StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
3530f3f357eSJacob Lambert     if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR))
3540f3f357eSJacob Lambert       return Error::success();
3550f3f357eSJacob Lambert 
3560f3f357eSJacob Lambert     // Read number of bundles.
3570f3f357eSJacob Lambert     if (ReadChars + 8 > FC.size())
3580f3f357eSJacob Lambert       return Error::success();
3590f3f357eSJacob Lambert 
3600f3f357eSJacob Lambert     uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
3610f3f357eSJacob Lambert     ReadChars += 8;
3620f3f357eSJacob Lambert 
3630f3f357eSJacob Lambert     // Read bundle offsets, sizes and triples.
3640f3f357eSJacob Lambert     for (uint64_t i = 0; i < NumberOfBundles; ++i) {
3650f3f357eSJacob Lambert 
3660f3f357eSJacob Lambert       // Read offset.
3670f3f357eSJacob Lambert       if (ReadChars + 8 > FC.size())
3680f3f357eSJacob Lambert         return Error::success();
3690f3f357eSJacob Lambert 
3700f3f357eSJacob Lambert       uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
3710f3f357eSJacob Lambert       ReadChars += 8;
3720f3f357eSJacob Lambert 
3730f3f357eSJacob Lambert       // Read size.
3740f3f357eSJacob Lambert       if (ReadChars + 8 > FC.size())
3750f3f357eSJacob Lambert         return Error::success();
3760f3f357eSJacob Lambert 
3770f3f357eSJacob Lambert       uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
3780f3f357eSJacob Lambert       ReadChars += 8;
3790f3f357eSJacob Lambert 
3800f3f357eSJacob Lambert       // Read triple size.
3810f3f357eSJacob Lambert       if (ReadChars + 8 > FC.size())
3820f3f357eSJacob Lambert         return Error::success();
3830f3f357eSJacob Lambert 
3840f3f357eSJacob Lambert       uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
3850f3f357eSJacob Lambert       ReadChars += 8;
3860f3f357eSJacob Lambert 
3870f3f357eSJacob Lambert       // Read triple.
3880f3f357eSJacob Lambert       if (ReadChars + TripleSize > FC.size())
3890f3f357eSJacob Lambert         return Error::success();
3900f3f357eSJacob Lambert 
3910f3f357eSJacob Lambert       StringRef Triple(&FC.data()[ReadChars], TripleSize);
3920f3f357eSJacob Lambert       ReadChars += TripleSize;
3930f3f357eSJacob Lambert 
3940f3f357eSJacob Lambert       // Check if the offset and size make sense.
3950f3f357eSJacob Lambert       if (!Offset || Offset + Size > FC.size())
3960f3f357eSJacob Lambert         return Error::success();
3970f3f357eSJacob Lambert 
3980f3f357eSJacob Lambert       assert(BundlesInfo.find(Triple) == BundlesInfo.end() &&
3990f3f357eSJacob Lambert              "Triple is duplicated??");
4000f3f357eSJacob Lambert       BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
4010f3f357eSJacob Lambert     }
4020f3f357eSJacob Lambert     // Set the iterator to where we will start to read.
4030f3f357eSJacob Lambert     CurBundleInfo = BundlesInfo.end();
4040f3f357eSJacob Lambert     NextBundleInfo = BundlesInfo.begin();
4050f3f357eSJacob Lambert     return Error::success();
4060f3f357eSJacob Lambert   }
4070f3f357eSJacob Lambert 
4082c5d49cfSFangrui Song   Expected<std::optional<StringRef>>
4092c5d49cfSFangrui Song   ReadBundleStart(MemoryBuffer &Input) final {
4100f3f357eSJacob Lambert     if (NextBundleInfo == BundlesInfo.end())
4110c2f6e36SFangrui Song       return std::nullopt;
4120f3f357eSJacob Lambert     CurBundleInfo = NextBundleInfo++;
4130f3f357eSJacob Lambert     return CurBundleInfo->first();
4140f3f357eSJacob Lambert   }
4150f3f357eSJacob Lambert 
4160f3f357eSJacob Lambert   Error ReadBundleEnd(MemoryBuffer &Input) final {
4170f3f357eSJacob Lambert     assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
4180f3f357eSJacob Lambert     return Error::success();
4190f3f357eSJacob Lambert   }
4200f3f357eSJacob Lambert 
4210f3f357eSJacob Lambert   Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
4220f3f357eSJacob Lambert     assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
4230f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
4240f3f357eSJacob Lambert     OS.write(FC.data() + CurBundleInfo->second.Offset,
4250f3f357eSJacob Lambert              CurBundleInfo->second.Size);
4260f3f357eSJacob Lambert     return Error::success();
4270f3f357eSJacob Lambert   }
4280f3f357eSJacob Lambert 
4290f3f357eSJacob Lambert   Error WriteHeader(raw_fd_ostream &OS,
4300f3f357eSJacob Lambert                     ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
4310f3f357eSJacob Lambert 
4320f3f357eSJacob Lambert     // Compute size of the header.
4330f3f357eSJacob Lambert     uint64_t HeaderSize = 0;
4340f3f357eSJacob Lambert 
4350f3f357eSJacob Lambert     HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
4360f3f357eSJacob Lambert     HeaderSize += 8; // Number of Bundles
4370f3f357eSJacob Lambert 
4380f3f357eSJacob Lambert     for (auto &T : BundlerConfig.TargetNames) {
4390f3f357eSJacob Lambert       HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
4400f3f357eSJacob Lambert       HeaderSize += T.size(); // The triple.
4410f3f357eSJacob Lambert     }
4420f3f357eSJacob Lambert 
4430f3f357eSJacob Lambert     // Write to the buffer the header.
4440f3f357eSJacob Lambert     OS << OFFLOAD_BUNDLER_MAGIC_STR;
4450f3f357eSJacob Lambert 
4460f3f357eSJacob Lambert     Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
4470f3f357eSJacob Lambert 
4480f3f357eSJacob Lambert     unsigned Idx = 0;
4490f3f357eSJacob Lambert     for (auto &T : BundlerConfig.TargetNames) {
4500f3f357eSJacob Lambert       MemoryBuffer &MB = *Inputs[Idx++];
4510f3f357eSJacob Lambert       HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
4520f3f357eSJacob Lambert       // Bundle offset.
4530f3f357eSJacob Lambert       Write8byteIntegerToBuffer(OS, HeaderSize);
4540f3f357eSJacob Lambert       // Size of the bundle (adds to the next bundle's offset)
4550f3f357eSJacob Lambert       Write8byteIntegerToBuffer(OS, MB.getBufferSize());
4560f3f357eSJacob Lambert       BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
4570f3f357eSJacob Lambert       HeaderSize += MB.getBufferSize();
4580f3f357eSJacob Lambert       // Size of the triple
4590f3f357eSJacob Lambert       Write8byteIntegerToBuffer(OS, T.size());
4600f3f357eSJacob Lambert       // Triple
4610f3f357eSJacob Lambert       OS << T;
4620f3f357eSJacob Lambert     }
4630f3f357eSJacob Lambert     return Error::success();
4640f3f357eSJacob Lambert   }
4650f3f357eSJacob Lambert 
4660f3f357eSJacob Lambert   Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
4670f3f357eSJacob Lambert     CurWriteBundleTarget = TargetTriple.str();
4680f3f357eSJacob Lambert     return Error::success();
4690f3f357eSJacob Lambert   }
4700f3f357eSJacob Lambert 
4710f3f357eSJacob Lambert   Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
4720f3f357eSJacob Lambert     return Error::success();
4730f3f357eSJacob Lambert   }
4740f3f357eSJacob Lambert 
4750f3f357eSJacob Lambert   Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
4760f3f357eSJacob Lambert     auto BI = BundlesInfo[CurWriteBundleTarget];
4770f3f357eSJacob Lambert     OS.seek(BI.Offset);
4780f3f357eSJacob Lambert     OS.write(Input.getBufferStart(), Input.getBufferSize());
4790f3f357eSJacob Lambert     return Error::success();
4800f3f357eSJacob Lambert   }
4810f3f357eSJacob Lambert };
4820f3f357eSJacob Lambert 
4830f3f357eSJacob Lambert // This class implements a list of temporary files that are removed upon
4840f3f357eSJacob Lambert // object destruction.
4850f3f357eSJacob Lambert class TempFileHandlerRAII {
4860f3f357eSJacob Lambert public:
4870f3f357eSJacob Lambert   ~TempFileHandlerRAII() {
4880f3f357eSJacob Lambert     for (const auto &File : Files)
4890f3f357eSJacob Lambert       sys::fs::remove(File);
4900f3f357eSJacob Lambert   }
4910f3f357eSJacob Lambert 
4920f3f357eSJacob Lambert   // Creates temporary file with given contents.
4932c5d49cfSFangrui Song   Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) {
4940f3f357eSJacob Lambert     SmallString<128u> File;
4950f3f357eSJacob Lambert     if (std::error_code EC =
4960f3f357eSJacob Lambert             sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
4970f3f357eSJacob Lambert       return createFileError(File, EC);
4980f3f357eSJacob Lambert     Files.push_front(File);
4990f3f357eSJacob Lambert 
5000f3f357eSJacob Lambert     if (Contents) {
5010f3f357eSJacob Lambert       std::error_code EC;
5020f3f357eSJacob Lambert       raw_fd_ostream OS(File, EC);
5030f3f357eSJacob Lambert       if (EC)
5040f3f357eSJacob Lambert         return createFileError(File, EC);
5050f3f357eSJacob Lambert       OS.write(Contents->data(), Contents->size());
5060f3f357eSJacob Lambert     }
5070f3f357eSJacob Lambert     return Files.front().str();
5080f3f357eSJacob Lambert   }
5090f3f357eSJacob Lambert 
5100f3f357eSJacob Lambert private:
5110f3f357eSJacob Lambert   std::forward_list<SmallString<128u>> Files;
5120f3f357eSJacob Lambert };
5130f3f357eSJacob Lambert 
5140f3f357eSJacob Lambert /// Handler for object files. The bundles are organized by sections with a
5150f3f357eSJacob Lambert /// designated name.
5160f3f357eSJacob Lambert ///
5170f3f357eSJacob Lambert /// To unbundle, we just copy the contents of the designated section.
5180f3f357eSJacob Lambert class ObjectFileHandler final : public FileHandler {
5190f3f357eSJacob Lambert 
5200f3f357eSJacob Lambert   /// The object file we are currently dealing with.
5210f3f357eSJacob Lambert   std::unique_ptr<ObjectFile> Obj;
5220f3f357eSJacob Lambert 
5230f3f357eSJacob Lambert   /// Return the input file contents.
5240f3f357eSJacob Lambert   StringRef getInputFileContents() const { return Obj->getData(); }
5250f3f357eSJacob Lambert 
5260f3f357eSJacob Lambert   /// Return bundle name (<kind>-<triple>) if the provided section is an offload
5270f3f357eSJacob Lambert   /// section.
5282c5d49cfSFangrui Song   static Expected<std::optional<StringRef>>
5292c5d49cfSFangrui Song   IsOffloadSection(SectionRef CurSection) {
5300f3f357eSJacob Lambert     Expected<StringRef> NameOrErr = CurSection.getName();
5310f3f357eSJacob Lambert     if (!NameOrErr)
5320f3f357eSJacob Lambert       return NameOrErr.takeError();
5330f3f357eSJacob Lambert 
5340f3f357eSJacob Lambert     // If it does not start with the reserved suffix, just skip this section.
5350f3f357eSJacob Lambert     if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR))
5360c2f6e36SFangrui Song       return std::nullopt;
5370f3f357eSJacob Lambert 
5380f3f357eSJacob Lambert     // Return the triple that is right after the reserved prefix.
5390f3f357eSJacob Lambert     return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
5400f3f357eSJacob Lambert   }
5410f3f357eSJacob Lambert 
5420f3f357eSJacob Lambert   /// Total number of inputs.
5430f3f357eSJacob Lambert   unsigned NumberOfInputs = 0;
5440f3f357eSJacob Lambert 
5450f3f357eSJacob Lambert   /// Total number of processed inputs, i.e, inputs that were already
5460f3f357eSJacob Lambert   /// read from the buffers.
5470f3f357eSJacob Lambert   unsigned NumberOfProcessedInputs = 0;
5480f3f357eSJacob Lambert 
5490f3f357eSJacob Lambert   /// Iterator of the current and next section.
5500f3f357eSJacob Lambert   section_iterator CurrentSection;
5510f3f357eSJacob Lambert   section_iterator NextSection;
5520f3f357eSJacob Lambert 
5530f3f357eSJacob Lambert   /// Configuration options and arrays for this bundler job
5540f3f357eSJacob Lambert   const OffloadBundlerConfig &BundlerConfig;
5550f3f357eSJacob Lambert 
5560f3f357eSJacob Lambert public:
5570f3f357eSJacob Lambert   // TODO: Add error checking from ClangOffloadBundler.cpp
5580f3f357eSJacob Lambert   ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
5590f3f357eSJacob Lambert                     const OffloadBundlerConfig &BC)
5600f3f357eSJacob Lambert       : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()),
5610f3f357eSJacob Lambert         NextSection(Obj->section_begin()), BundlerConfig(BC) {}
5620f3f357eSJacob Lambert 
5630f3f357eSJacob Lambert   ~ObjectFileHandler() final {}
5640f3f357eSJacob Lambert 
5650f3f357eSJacob Lambert   Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
5660f3f357eSJacob Lambert 
5672c5d49cfSFangrui Song   Expected<std::optional<StringRef>>
5682c5d49cfSFangrui Song   ReadBundleStart(MemoryBuffer &Input) final {
5690f3f357eSJacob Lambert     while (NextSection != Obj->section_end()) {
5700f3f357eSJacob Lambert       CurrentSection = NextSection;
5710f3f357eSJacob Lambert       ++NextSection;
5720f3f357eSJacob Lambert 
5730f3f357eSJacob Lambert       // Check if the current section name starts with the reserved prefix. If
5740f3f357eSJacob Lambert       // so, return the triple.
5752c5d49cfSFangrui Song       Expected<std::optional<StringRef>> TripleOrErr =
5760f3f357eSJacob Lambert           IsOffloadSection(*CurrentSection);
5770f3f357eSJacob Lambert       if (!TripleOrErr)
5780f3f357eSJacob Lambert         return TripleOrErr.takeError();
5790f3f357eSJacob Lambert       if (*TripleOrErr)
5800f3f357eSJacob Lambert         return **TripleOrErr;
5810f3f357eSJacob Lambert     }
5820c2f6e36SFangrui Song     return std::nullopt;
5830f3f357eSJacob Lambert   }
5840f3f357eSJacob Lambert 
5850f3f357eSJacob Lambert   Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
5860f3f357eSJacob Lambert 
5870f3f357eSJacob Lambert   Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
5880f3f357eSJacob Lambert     Expected<StringRef> ContentOrErr = CurrentSection->getContents();
5890f3f357eSJacob Lambert     if (!ContentOrErr)
5900f3f357eSJacob Lambert       return ContentOrErr.takeError();
5910f3f357eSJacob Lambert     StringRef Content = *ContentOrErr;
5920f3f357eSJacob Lambert 
5930f3f357eSJacob Lambert     // Copy fat object contents to the output when extracting host bundle.
5940f3f357eSJacob Lambert     if (Content.size() == 1u && Content.front() == 0)
5950f3f357eSJacob Lambert       Content = StringRef(Input.getBufferStart(), Input.getBufferSize());
5960f3f357eSJacob Lambert 
5970f3f357eSJacob Lambert     OS.write(Content.data(), Content.size());
5980f3f357eSJacob Lambert     return Error::success();
5990f3f357eSJacob Lambert   }
6000f3f357eSJacob Lambert 
6010f3f357eSJacob Lambert   Error WriteHeader(raw_fd_ostream &OS,
6020f3f357eSJacob Lambert                     ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
6030f3f357eSJacob Lambert     assert(BundlerConfig.HostInputIndex != ~0u &&
6040f3f357eSJacob Lambert            "Host input index not defined.");
6050f3f357eSJacob Lambert 
6060f3f357eSJacob Lambert     // Record number of inputs.
6070f3f357eSJacob Lambert     NumberOfInputs = Inputs.size();
6080f3f357eSJacob Lambert     return Error::success();
6090f3f357eSJacob Lambert   }
6100f3f357eSJacob Lambert 
6110f3f357eSJacob Lambert   Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
6120f3f357eSJacob Lambert     ++NumberOfProcessedInputs;
6130f3f357eSJacob Lambert     return Error::success();
6140f3f357eSJacob Lambert   }
6150f3f357eSJacob Lambert 
6160f3f357eSJacob Lambert   Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
6170f3f357eSJacob Lambert     assert(NumberOfProcessedInputs <= NumberOfInputs &&
6180f3f357eSJacob Lambert            "Processing more inputs that actually exist!");
6190f3f357eSJacob Lambert     assert(BundlerConfig.HostInputIndex != ~0u &&
6200f3f357eSJacob Lambert            "Host input index not defined.");
6210f3f357eSJacob Lambert 
6220f3f357eSJacob Lambert     // If this is not the last output, we don't have to do anything.
6230f3f357eSJacob Lambert     if (NumberOfProcessedInputs != NumberOfInputs)
6240f3f357eSJacob Lambert       return Error::success();
6250f3f357eSJacob Lambert 
6260f3f357eSJacob Lambert     // We will use llvm-objcopy to add target objects sections to the output
6270f3f357eSJacob Lambert     // fat object. These sections should have 'exclude' flag set which tells
6280f3f357eSJacob Lambert     // link editor to remove them from linker inputs when linking executable or
6290f3f357eSJacob Lambert     // shared library.
6300f3f357eSJacob Lambert 
6310f3f357eSJacob Lambert     assert(BundlerConfig.ObjcopyPath != "" &&
6320f3f357eSJacob Lambert            "llvm-objcopy path not specified");
6330f3f357eSJacob Lambert 
6340f3f357eSJacob Lambert     // We write to the output file directly. So, we close it and use the name
6350f3f357eSJacob Lambert     // to pass down to llvm-objcopy.
6360f3f357eSJacob Lambert     OS.close();
6370f3f357eSJacob Lambert 
6380f3f357eSJacob Lambert     // Temporary files that need to be removed.
6390f3f357eSJacob Lambert     TempFileHandlerRAII TempFiles;
6400f3f357eSJacob Lambert 
6410f3f357eSJacob Lambert     // Compose llvm-objcopy command line for add target objects' sections with
6420f3f357eSJacob Lambert     // appropriate flags.
6430f3f357eSJacob Lambert     BumpPtrAllocator Alloc;
6440f3f357eSJacob Lambert     StringSaver SS{Alloc};
6450f3f357eSJacob Lambert     SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
6460f3f357eSJacob Lambert 
6470f3f357eSJacob Lambert     for (unsigned I = 0; I < NumberOfInputs; ++I) {
6480f3f357eSJacob Lambert       StringRef InputFile = BundlerConfig.InputFileNames[I];
6490f3f357eSJacob Lambert       if (I == BundlerConfig.HostInputIndex) {
6500f3f357eSJacob Lambert         // Special handling for the host bundle. We do not need to add a
6510f3f357eSJacob Lambert         // standard bundle for the host object since we are going to use fat
6520f3f357eSJacob Lambert         // object as a host object. Therefore use dummy contents (one zero byte)
6530f3f357eSJacob Lambert         // when creating section for the host bundle.
6540f3f357eSJacob Lambert         Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0));
6550f3f357eSJacob Lambert         if (!TempFileOrErr)
6560f3f357eSJacob Lambert           return TempFileOrErr.takeError();
6570f3f357eSJacob Lambert         InputFile = *TempFileOrErr;
6580f3f357eSJacob Lambert       }
6590f3f357eSJacob Lambert 
6605b11caa8Sraghavmedicherla       ObjcopyArgs.push_back(
6615b11caa8Sraghavmedicherla           SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR +
6625b11caa8Sraghavmedicherla                   BundlerConfig.TargetNames[I] + "=" + InputFile));
6635b11caa8Sraghavmedicherla       ObjcopyArgs.push_back(
6645b11caa8Sraghavmedicherla           SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR +
6655b11caa8Sraghavmedicherla                   BundlerConfig.TargetNames[I] + "=readonly,exclude"));
6660f3f357eSJacob Lambert     }
6670f3f357eSJacob Lambert     ObjcopyArgs.push_back("--");
6680f3f357eSJacob Lambert     ObjcopyArgs.push_back(
6690f3f357eSJacob Lambert         BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
6700f3f357eSJacob Lambert     ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
6710f3f357eSJacob Lambert 
6720f3f357eSJacob Lambert     if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
6730f3f357eSJacob Lambert       return Err;
6740f3f357eSJacob Lambert 
6750f3f357eSJacob Lambert     return Error::success();
6760f3f357eSJacob Lambert   }
6770f3f357eSJacob Lambert 
6780f3f357eSJacob Lambert   Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
6790f3f357eSJacob Lambert     return Error::success();
6800f3f357eSJacob Lambert   }
6810f3f357eSJacob Lambert 
6820f3f357eSJacob Lambert private:
6830f3f357eSJacob Lambert   Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) {
6840f3f357eSJacob Lambert     // If the user asked for the commands to be printed out, we do that
6850f3f357eSJacob Lambert     // instead of executing it.
6860f3f357eSJacob Lambert     if (BundlerConfig.PrintExternalCommands) {
6870f3f357eSJacob Lambert       errs() << "\"" << Objcopy << "\"";
6880f3f357eSJacob Lambert       for (StringRef Arg : drop_begin(Args, 1))
6890f3f357eSJacob Lambert         errs() << " \"" << Arg << "\"";
6900f3f357eSJacob Lambert       errs() << "\n";
6910f3f357eSJacob Lambert     } else {
6920f3f357eSJacob Lambert       if (sys::ExecuteAndWait(Objcopy, Args))
6930f3f357eSJacob Lambert         return createStringError(inconvertibleErrorCode(),
6940f3f357eSJacob Lambert                                  "'llvm-objcopy' tool failed");
6950f3f357eSJacob Lambert     }
6960f3f357eSJacob Lambert     return Error::success();
6970f3f357eSJacob Lambert   }
6980f3f357eSJacob Lambert };
6990f3f357eSJacob Lambert 
7000f3f357eSJacob Lambert /// Handler for text files. The bundled file will have the following format.
7010f3f357eSJacob Lambert ///
7020f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
7030f3f357eSJacob Lambert /// Bundle 1
7040f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
7050f3f357eSJacob Lambert /// ...
7060f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
7070f3f357eSJacob Lambert /// Bundle N
7080f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
7090f3f357eSJacob Lambert class TextFileHandler final : public FileHandler {
7100f3f357eSJacob Lambert   /// String that begins a line comment.
7110f3f357eSJacob Lambert   StringRef Comment;
7120f3f357eSJacob Lambert 
7130f3f357eSJacob Lambert   /// String that initiates a bundle.
7140f3f357eSJacob Lambert   std::string BundleStartString;
7150f3f357eSJacob Lambert 
7160f3f357eSJacob Lambert   /// String that closes a bundle.
7170f3f357eSJacob Lambert   std::string BundleEndString;
7180f3f357eSJacob Lambert 
7190f3f357eSJacob Lambert   /// Number of chars read from input.
7200f3f357eSJacob Lambert   size_t ReadChars = 0u;
7210f3f357eSJacob Lambert 
7220f3f357eSJacob Lambert protected:
7230f3f357eSJacob Lambert   Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
7240f3f357eSJacob Lambert 
7252c5d49cfSFangrui Song   Expected<std::optional<StringRef>>
7262c5d49cfSFangrui Song   ReadBundleStart(MemoryBuffer &Input) final {
7270f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
7280f3f357eSJacob Lambert 
7290f3f357eSJacob Lambert     // Find start of the bundle.
7300f3f357eSJacob Lambert     ReadChars = FC.find(BundleStartString, ReadChars);
7310f3f357eSJacob Lambert     if (ReadChars == FC.npos)
7320c2f6e36SFangrui Song       return std::nullopt;
7330f3f357eSJacob Lambert 
7340f3f357eSJacob Lambert     // Get position of the triple.
7350f3f357eSJacob Lambert     size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
7360f3f357eSJacob Lambert 
7370f3f357eSJacob Lambert     // Get position that closes the triple.
7380f3f357eSJacob Lambert     size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
7390f3f357eSJacob Lambert     if (TripleEnd == FC.npos)
7400c2f6e36SFangrui Song       return std::nullopt;
7410f3f357eSJacob Lambert 
7420f3f357eSJacob Lambert     // Next time we read after the new line.
7430f3f357eSJacob Lambert     ++ReadChars;
7440f3f357eSJacob Lambert 
7450f3f357eSJacob Lambert     return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
7460f3f357eSJacob Lambert   }
7470f3f357eSJacob Lambert 
7480f3f357eSJacob Lambert   Error ReadBundleEnd(MemoryBuffer &Input) final {
7490f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
7500f3f357eSJacob Lambert 
7510f3f357eSJacob Lambert     // Read up to the next new line.
7520f3f357eSJacob Lambert     assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
7530f3f357eSJacob Lambert 
7540f3f357eSJacob Lambert     size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
7550f3f357eSJacob Lambert     if (TripleEnd != FC.npos)
7560f3f357eSJacob Lambert       // Next time we read after the new line.
7570f3f357eSJacob Lambert       ++ReadChars;
7580f3f357eSJacob Lambert 
7590f3f357eSJacob Lambert     return Error::success();
7600f3f357eSJacob Lambert   }
7610f3f357eSJacob Lambert 
7620f3f357eSJacob Lambert   Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
7630f3f357eSJacob Lambert     StringRef FC = Input.getBuffer();
7640f3f357eSJacob Lambert     size_t BundleStart = ReadChars;
7650f3f357eSJacob Lambert 
7660f3f357eSJacob Lambert     // Find end of the bundle.
7670f3f357eSJacob Lambert     size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
7680f3f357eSJacob Lambert 
7690f3f357eSJacob Lambert     StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
7700f3f357eSJacob Lambert     OS << Bundle;
7710f3f357eSJacob Lambert 
7720f3f357eSJacob Lambert     return Error::success();
7730f3f357eSJacob Lambert   }
7740f3f357eSJacob Lambert 
7750f3f357eSJacob Lambert   Error WriteHeader(raw_fd_ostream &OS,
7760f3f357eSJacob Lambert                     ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
7770f3f357eSJacob Lambert     return Error::success();
7780f3f357eSJacob Lambert   }
7790f3f357eSJacob Lambert 
7800f3f357eSJacob Lambert   Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
7810f3f357eSJacob Lambert     OS << BundleStartString << TargetTriple << "\n";
7820f3f357eSJacob Lambert     return Error::success();
7830f3f357eSJacob Lambert   }
7840f3f357eSJacob Lambert 
7850f3f357eSJacob Lambert   Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
7860f3f357eSJacob Lambert     OS << BundleEndString << TargetTriple << "\n";
7870f3f357eSJacob Lambert     return Error::success();
7880f3f357eSJacob Lambert   }
7890f3f357eSJacob Lambert 
7900f3f357eSJacob Lambert   Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
7910f3f357eSJacob Lambert     OS << Input.getBuffer();
7920f3f357eSJacob Lambert     return Error::success();
7930f3f357eSJacob Lambert   }
7940f3f357eSJacob Lambert 
7950f3f357eSJacob Lambert public:
7960f3f357eSJacob Lambert   TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
7970f3f357eSJacob Lambert     BundleStartString =
7980f3f357eSJacob Lambert         "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
7990f3f357eSJacob Lambert     BundleEndString =
8000f3f357eSJacob Lambert         "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
8010f3f357eSJacob Lambert   }
8020f3f357eSJacob Lambert 
8030f3f357eSJacob Lambert   Error listBundleIDsCallback(MemoryBuffer &Input,
8040f3f357eSJacob Lambert                               const BundleInfo &Info) final {
8050f3f357eSJacob Lambert     // TODO: To list bundle IDs in a bundled text file we need to go through
8060f3f357eSJacob Lambert     // all bundles. The format of bundled text file may need to include a
8070f3f357eSJacob Lambert     // header if the performance of listing bundle IDs of bundled text file is
8080f3f357eSJacob Lambert     // important.
8090f3f357eSJacob Lambert     ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
8100f3f357eSJacob Lambert     if (Error Err = ReadBundleEnd(Input))
8110f3f357eSJacob Lambert       return Err;
8120f3f357eSJacob Lambert     return Error::success();
8130f3f357eSJacob Lambert   }
8140f3f357eSJacob Lambert };
815b6942a28SBenjamin Kramer } // namespace
8160f3f357eSJacob Lambert 
8170f3f357eSJacob Lambert /// Return an appropriate object file handler. We use the specific object
8180f3f357eSJacob Lambert /// handler if we know how to deal with that format, otherwise we use a default
8190f3f357eSJacob Lambert /// binary file handler.
8200f3f357eSJacob Lambert static std::unique_ptr<FileHandler>
8210f3f357eSJacob Lambert CreateObjectFileHandler(MemoryBuffer &FirstInput,
8220f3f357eSJacob Lambert                         const OffloadBundlerConfig &BundlerConfig) {
8230f3f357eSJacob Lambert   // Check if the input file format is one that we know how to deal with.
8240f3f357eSJacob Lambert   Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
8250f3f357eSJacob Lambert 
8260f3f357eSJacob Lambert   // We only support regular object files. If failed to open the input as a
8270f3f357eSJacob Lambert   // known binary or this is not an object file use the default binary handler.
8280f3f357eSJacob Lambert   if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
8290f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
8300f3f357eSJacob Lambert 
8310f3f357eSJacob Lambert   // Otherwise create an object file handler. The handler will be owned by the
8320f3f357eSJacob Lambert   // client of this function.
8330f3f357eSJacob Lambert   return std::make_unique<ObjectFileHandler>(
8340f3f357eSJacob Lambert       std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
8350f3f357eSJacob Lambert       BundlerConfig);
8360f3f357eSJacob Lambert }
8370f3f357eSJacob Lambert 
8380f3f357eSJacob Lambert /// Return an appropriate handler given the input files and options.
8390f3f357eSJacob Lambert static Expected<std::unique_ptr<FileHandler>>
8400f3f357eSJacob Lambert CreateFileHandler(MemoryBuffer &FirstInput,
8410f3f357eSJacob Lambert                   const OffloadBundlerConfig &BundlerConfig) {
8420f3f357eSJacob Lambert   std::string FilesType = BundlerConfig.FilesType;
8430f3f357eSJacob Lambert 
8440f3f357eSJacob Lambert   if (FilesType == "i")
8450f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
8460f3f357eSJacob Lambert   if (FilesType == "ii")
8470f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
8480f3f357eSJacob Lambert   if (FilesType == "cui")
8490f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
850*e8f41fdbSYaxun (Sam) Liu   if (FilesType == "hipi")
851*e8f41fdbSYaxun (Sam) Liu     return std::make_unique<TextFileHandler>(/*Comment=*/"//");
8520f3f357eSJacob Lambert   // TODO: `.d` should be eventually removed once `-M` and its variants are
8530f3f357eSJacob Lambert   // handled properly in offload compilation.
8540f3f357eSJacob Lambert   if (FilesType == "d")
8550f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"#");
8560f3f357eSJacob Lambert   if (FilesType == "ll")
8570f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/";");
8580f3f357eSJacob Lambert   if (FilesType == "bc")
8590f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
8600f3f357eSJacob Lambert   if (FilesType == "s")
8610f3f357eSJacob Lambert     return std::make_unique<TextFileHandler>(/*Comment=*/"#");
8620f3f357eSJacob Lambert   if (FilesType == "o")
8630f3f357eSJacob Lambert     return CreateObjectFileHandler(FirstInput, BundlerConfig);
8640f3f357eSJacob Lambert   if (FilesType == "a")
8650f3f357eSJacob Lambert     return CreateObjectFileHandler(FirstInput, BundlerConfig);
8660f3f357eSJacob Lambert   if (FilesType == "gch")
8670f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
8680f3f357eSJacob Lambert   if (FilesType == "ast")
8690f3f357eSJacob Lambert     return std::make_unique<BinaryFileHandler>(BundlerConfig);
8700f3f357eSJacob Lambert 
8710f3f357eSJacob Lambert   return createStringError(errc::invalid_argument,
8720f3f357eSJacob Lambert                            "'" + FilesType + "': invalid file type specified");
8730f3f357eSJacob Lambert }
8740f3f357eSJacob Lambert 
8750f3f357eSJacob Lambert // List bundle IDs. Return true if an error was found.
8765b11caa8Sraghavmedicherla Error OffloadBundler::ListBundleIDsInFile(
8775b11caa8Sraghavmedicherla     StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
8780f3f357eSJacob Lambert   // Open Input file.
8790f3f357eSJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
8800f3f357eSJacob Lambert       MemoryBuffer::getFileOrSTDIN(InputFileName);
8810f3f357eSJacob Lambert   if (std::error_code EC = CodeOrErr.getError())
8820f3f357eSJacob Lambert     return createFileError(InputFileName, EC);
8830f3f357eSJacob Lambert 
8840f3f357eSJacob Lambert   MemoryBuffer &Input = **CodeOrErr;
8850f3f357eSJacob Lambert 
8860f3f357eSJacob Lambert   // Select the right files handler.
8870f3f357eSJacob Lambert   Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
8880f3f357eSJacob Lambert       CreateFileHandler(Input, BundlerConfig);
8890f3f357eSJacob Lambert   if (!FileHandlerOrErr)
8900f3f357eSJacob Lambert     return FileHandlerOrErr.takeError();
8910f3f357eSJacob Lambert 
8920f3f357eSJacob Lambert   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
8930f3f357eSJacob Lambert   assert(FH);
8940f3f357eSJacob Lambert   return FH->listBundleIDs(Input);
8950f3f357eSJacob Lambert }
8960f3f357eSJacob Lambert 
8970f3f357eSJacob Lambert /// Bundle the files. Return true if an error was found.
8980f3f357eSJacob Lambert Error OffloadBundler::BundleFiles() {
8990f3f357eSJacob Lambert   std::error_code EC;
9000f3f357eSJacob Lambert 
9010f3f357eSJacob Lambert   // Create output file.
9025b11caa8Sraghavmedicherla   raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
9035b11caa8Sraghavmedicherla                             sys::fs::OF_None);
9040f3f357eSJacob Lambert   if (EC)
9050f3f357eSJacob Lambert     return createFileError(BundlerConfig.OutputFileNames.front(), EC);
9060f3f357eSJacob Lambert 
9070f3f357eSJacob Lambert   // Open input files.
9080f3f357eSJacob Lambert   SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers;
9090f3f357eSJacob Lambert   InputBuffers.reserve(BundlerConfig.InputFileNames.size());
9100f3f357eSJacob Lambert   for (auto &I : BundlerConfig.InputFileNames) {
9110f3f357eSJacob Lambert     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
9120f3f357eSJacob Lambert         MemoryBuffer::getFileOrSTDIN(I);
9130f3f357eSJacob Lambert     if (std::error_code EC = CodeOrErr.getError())
9140f3f357eSJacob Lambert       return createFileError(I, EC);
9150f3f357eSJacob Lambert     InputBuffers.emplace_back(std::move(*CodeOrErr));
9160f3f357eSJacob Lambert   }
9170f3f357eSJacob Lambert 
9180f3f357eSJacob Lambert   // Get the file handler. We use the host buffer as reference.
9190f3f357eSJacob Lambert   assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) &&
9200f3f357eSJacob Lambert          "Host input index undefined??");
9215b11caa8Sraghavmedicherla   Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler(
9225b11caa8Sraghavmedicherla       *InputBuffers[BundlerConfig.AllowNoHost ? 0
9230f3f357eSJacob Lambert                                               : BundlerConfig.HostInputIndex],
9240f3f357eSJacob Lambert       BundlerConfig);
9250f3f357eSJacob Lambert   if (!FileHandlerOrErr)
9260f3f357eSJacob Lambert     return FileHandlerOrErr.takeError();
9270f3f357eSJacob Lambert 
9280f3f357eSJacob Lambert   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
9290f3f357eSJacob Lambert   assert(FH);
9300f3f357eSJacob Lambert 
9310f3f357eSJacob Lambert   // Write header.
9320f3f357eSJacob Lambert   if (Error Err = FH->WriteHeader(OutputFile, InputBuffers))
9330f3f357eSJacob Lambert     return Err;
9340f3f357eSJacob Lambert 
9350f3f357eSJacob Lambert   // Write all bundles along with the start/end markers. If an error was found
9360f3f357eSJacob Lambert   // writing the end of the bundle component, abort the bundle writing.
9370f3f357eSJacob Lambert   auto Input = InputBuffers.begin();
9380f3f357eSJacob Lambert   for (auto &Triple : BundlerConfig.TargetNames) {
9390f3f357eSJacob Lambert     if (Error Err = FH->WriteBundleStart(OutputFile, Triple))
9400f3f357eSJacob Lambert       return Err;
9410f3f357eSJacob Lambert     if (Error Err = FH->WriteBundle(OutputFile, **Input))
9420f3f357eSJacob Lambert       return Err;
9430f3f357eSJacob Lambert     if (Error Err = FH->WriteBundleEnd(OutputFile, Triple))
9440f3f357eSJacob Lambert       return Err;
9450f3f357eSJacob Lambert     ++Input;
9460f3f357eSJacob Lambert   }
9470f3f357eSJacob Lambert   return Error::success();
9480f3f357eSJacob Lambert }
9490f3f357eSJacob Lambert 
9500f3f357eSJacob Lambert // Unbundle the files. Return true if an error was found.
9510f3f357eSJacob Lambert Error OffloadBundler::UnbundleFiles() {
9520f3f357eSJacob Lambert   // Open Input file.
9530f3f357eSJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
9540f3f357eSJacob Lambert       MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front());
9550f3f357eSJacob Lambert   if (std::error_code EC = CodeOrErr.getError())
9560f3f357eSJacob Lambert     return createFileError(BundlerConfig.InputFileNames.front(), EC);
9570f3f357eSJacob Lambert 
9580f3f357eSJacob Lambert   MemoryBuffer &Input = **CodeOrErr;
9590f3f357eSJacob Lambert 
9600f3f357eSJacob Lambert   // Select the right files handler.
9610f3f357eSJacob Lambert   Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
9620f3f357eSJacob Lambert       CreateFileHandler(Input, BundlerConfig);
9630f3f357eSJacob Lambert   if (!FileHandlerOrErr)
9640f3f357eSJacob Lambert     return FileHandlerOrErr.takeError();
9650f3f357eSJacob Lambert 
9660f3f357eSJacob Lambert   std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
9670f3f357eSJacob Lambert   assert(FH);
9680f3f357eSJacob Lambert 
9690f3f357eSJacob Lambert   // Read the header of the bundled file.
9700f3f357eSJacob Lambert   if (Error Err = FH->ReadHeader(Input))
9710f3f357eSJacob Lambert     return Err;
9720f3f357eSJacob Lambert 
9730f3f357eSJacob Lambert   // Create a work list that consist of the map triple/output file.
9740f3f357eSJacob Lambert   StringMap<StringRef> Worklist;
9750f3f357eSJacob Lambert   auto Output = BundlerConfig.OutputFileNames.begin();
9760f3f357eSJacob Lambert   for (auto &Triple : BundlerConfig.TargetNames) {
9770f3f357eSJacob Lambert     Worklist[Triple] = *Output;
9780f3f357eSJacob Lambert     ++Output;
9790f3f357eSJacob Lambert   }
9800f3f357eSJacob Lambert 
9810f3f357eSJacob Lambert   // Read all the bundles that are in the work list. If we find no bundles we
9820f3f357eSJacob Lambert   // assume the file is meant for the host target.
9830f3f357eSJacob Lambert   bool FoundHostBundle = false;
9840f3f357eSJacob Lambert   while (!Worklist.empty()) {
9852c5d49cfSFangrui Song     Expected<std::optional<StringRef>> CurTripleOrErr =
9862c5d49cfSFangrui Song         FH->ReadBundleStart(Input);
9870f3f357eSJacob Lambert     if (!CurTripleOrErr)
9880f3f357eSJacob Lambert       return CurTripleOrErr.takeError();
9890f3f357eSJacob Lambert 
9900f3f357eSJacob Lambert     // We don't have more bundles.
9910f3f357eSJacob Lambert     if (!*CurTripleOrErr)
9920f3f357eSJacob Lambert       break;
9930f3f357eSJacob Lambert 
9940f3f357eSJacob Lambert     StringRef CurTriple = **CurTripleOrErr;
9950f3f357eSJacob Lambert     assert(!CurTriple.empty());
9960f3f357eSJacob Lambert 
997844b84afSYaxun (Sam) Liu     auto Output = Worklist.begin();
998844b84afSYaxun (Sam) Liu     for (auto E = Worklist.end(); Output != E; Output++) {
999844b84afSYaxun (Sam) Liu       if (isCodeObjectCompatible(
1000844b84afSYaxun (Sam) Liu               OffloadTargetInfo(CurTriple, BundlerConfig),
1001844b84afSYaxun (Sam) Liu               OffloadTargetInfo((*Output).first(), BundlerConfig))) {
1002844b84afSYaxun (Sam) Liu         break;
1003844b84afSYaxun (Sam) Liu       }
1004844b84afSYaxun (Sam) Liu     }
1005844b84afSYaxun (Sam) Liu 
10060f3f357eSJacob Lambert     if (Output == Worklist.end())
10070f3f357eSJacob Lambert       continue;
10080f3f357eSJacob Lambert     // Check if the output file can be opened and copy the bundle to it.
10090f3f357eSJacob Lambert     std::error_code EC;
1010844b84afSYaxun (Sam) Liu     raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
10110f3f357eSJacob Lambert     if (EC)
1012844b84afSYaxun (Sam) Liu       return createFileError((*Output).second, EC);
10130f3f357eSJacob Lambert     if (Error Err = FH->ReadBundle(OutputFile, Input))
10140f3f357eSJacob Lambert       return Err;
10150f3f357eSJacob Lambert     if (Error Err = FH->ReadBundleEnd(Input))
10160f3f357eSJacob Lambert       return Err;
10170f3f357eSJacob Lambert     Worklist.erase(Output);
10180f3f357eSJacob Lambert 
10190f3f357eSJacob Lambert     // Record if we found the host bundle.
10200f3f357eSJacob Lambert     auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
10210f3f357eSJacob Lambert     if (OffloadInfo.hasHostKind())
10220f3f357eSJacob Lambert       FoundHostBundle = true;
10230f3f357eSJacob Lambert   }
10240f3f357eSJacob Lambert 
10250f3f357eSJacob Lambert   if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
10260f3f357eSJacob Lambert     std::string ErrMsg = "Can't find bundles for";
10270f3f357eSJacob Lambert     std::set<StringRef> Sorted;
10280f3f357eSJacob Lambert     for (auto &E : Worklist)
10290f3f357eSJacob Lambert       Sorted.insert(E.first());
10300f3f357eSJacob Lambert     unsigned I = 0;
10310f3f357eSJacob Lambert     unsigned Last = Sorted.size() - 1;
10320f3f357eSJacob Lambert     for (auto &E : Sorted) {
10330f3f357eSJacob Lambert       if (I != 0 && Last > 1)
10340f3f357eSJacob Lambert         ErrMsg += ",";
10350f3f357eSJacob Lambert       ErrMsg += " ";
10360f3f357eSJacob Lambert       if (I == Last && I != 0)
10370f3f357eSJacob Lambert         ErrMsg += "and ";
10380f3f357eSJacob Lambert       ErrMsg += E.str();
10390f3f357eSJacob Lambert       ++I;
10400f3f357eSJacob Lambert     }
10410f3f357eSJacob Lambert     return createStringError(inconvertibleErrorCode(), ErrMsg);
10420f3f357eSJacob Lambert   }
10430f3f357eSJacob Lambert 
10440f3f357eSJacob Lambert   // If no bundles were found, assume the input file is the host bundle and
10450f3f357eSJacob Lambert   // create empty files for the remaining targets.
10460f3f357eSJacob Lambert   if (Worklist.size() == BundlerConfig.TargetNames.size()) {
10470f3f357eSJacob Lambert     for (auto &E : Worklist) {
10480f3f357eSJacob Lambert       std::error_code EC;
10490f3f357eSJacob Lambert       raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
10500f3f357eSJacob Lambert       if (EC)
10510f3f357eSJacob Lambert         return createFileError(E.second, EC);
10520f3f357eSJacob Lambert 
10530f3f357eSJacob Lambert       // If this entry has a host kind, copy the input file to the output file.
10540f3f357eSJacob Lambert       auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
10550f3f357eSJacob Lambert       if (OffloadInfo.hasHostKind())
10560f3f357eSJacob Lambert         OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
10570f3f357eSJacob Lambert     }
10580f3f357eSJacob Lambert     return Error::success();
10590f3f357eSJacob Lambert   }
10600f3f357eSJacob Lambert 
10610f3f357eSJacob Lambert   // If we found elements, we emit an error if none of those were for the host
10620f3f357eSJacob Lambert   // in case host bundle name was provided in command line.
10630f3f357eSJacob Lambert   if (!FoundHostBundle && BundlerConfig.HostInputIndex != ~0u)
10640f3f357eSJacob Lambert     return createStringError(inconvertibleErrorCode(),
10650f3f357eSJacob Lambert                              "Can't find bundle for the host target");
10660f3f357eSJacob Lambert 
10670f3f357eSJacob Lambert   // If we still have any elements in the worklist, create empty files for them.
10680f3f357eSJacob Lambert   for (auto &E : Worklist) {
10690f3f357eSJacob Lambert     std::error_code EC;
10700f3f357eSJacob Lambert     raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
10710f3f357eSJacob Lambert     if (EC)
10720f3f357eSJacob Lambert       return createFileError(E.second, EC);
10730f3f357eSJacob Lambert   }
10740f3f357eSJacob Lambert 
10750f3f357eSJacob Lambert   return Error::success();
10760f3f357eSJacob Lambert }
10770f3f357eSJacob Lambert 
10780f3f357eSJacob Lambert static Archive::Kind getDefaultArchiveKindForHost() {
10790f3f357eSJacob Lambert   return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
10800f3f357eSJacob Lambert                                                             : Archive::K_GNU;
10810f3f357eSJacob Lambert }
10820f3f357eSJacob Lambert 
10830f3f357eSJacob Lambert /// @brief Computes a list of targets among all given targets which are
10840f3f357eSJacob Lambert /// compatible with this code object
1085b978fa28SSimon Pilgrim /// @param [in] CodeObjectInfo Code Object
1086b978fa28SSimon Pilgrim /// @param [out] CompatibleTargets List of all compatible targets among all
10870f3f357eSJacob Lambert /// given targets
10880f3f357eSJacob Lambert /// @return false, if no compatible target is found.
10890f3f357eSJacob Lambert static bool
10900f3f357eSJacob Lambert getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo,
10910f3f357eSJacob Lambert                             SmallVectorImpl<StringRef> &CompatibleTargets,
10920f3f357eSJacob Lambert                             const OffloadBundlerConfig &BundlerConfig) {
10930f3f357eSJacob Lambert   if (!CompatibleTargets.empty()) {
10940f3f357eSJacob Lambert     DEBUG_WITH_TYPE("CodeObjectCompatibility",
10950f3f357eSJacob Lambert                     dbgs() << "CompatibleTargets list should be empty\n");
10960f3f357eSJacob Lambert     return false;
10970f3f357eSJacob Lambert   }
10980f3f357eSJacob Lambert   for (auto &Target : BundlerConfig.TargetNames) {
10990f3f357eSJacob Lambert     auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
11000f3f357eSJacob Lambert     if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
11010f3f357eSJacob Lambert       CompatibleTargets.push_back(Target);
11020f3f357eSJacob Lambert   }
11030f3f357eSJacob Lambert   return !CompatibleTargets.empty();
11040f3f357eSJacob Lambert }
11050f3f357eSJacob Lambert 
11060f3f357eSJacob Lambert /// UnbundleArchive takes an archive file (".a") as input containing bundled
11070f3f357eSJacob Lambert /// code object files, and a list of offload targets (not host), and extracts
11080f3f357eSJacob Lambert /// the code objects into a new archive file for each offload target. Each
11090f3f357eSJacob Lambert /// resulting archive file contains all code object files corresponding to that
11100f3f357eSJacob Lambert /// particular offload target. The created archive file does not
11110f3f357eSJacob Lambert /// contain an index of the symbols and code object files are named as
11120f3f357eSJacob Lambert /// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'.
11130f3f357eSJacob Lambert Error OffloadBundler::UnbundleArchive() {
11140f3f357eSJacob Lambert   std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
11150f3f357eSJacob Lambert 
11160f3f357eSJacob Lambert   /// Map of target names with list of object files that will form the device
11170f3f357eSJacob Lambert   /// specific archive for that target
11180f3f357eSJacob Lambert   StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
11190f3f357eSJacob Lambert 
11200f3f357eSJacob Lambert   // Map of target names and output archive filenames
11210f3f357eSJacob Lambert   StringMap<StringRef> TargetOutputFileNameMap;
11220f3f357eSJacob Lambert 
11230f3f357eSJacob Lambert   auto Output = BundlerConfig.OutputFileNames.begin();
11240f3f357eSJacob Lambert   for (auto &Target : BundlerConfig.TargetNames) {
11250f3f357eSJacob Lambert     TargetOutputFileNameMap[Target] = *Output;
11260f3f357eSJacob Lambert     ++Output;
11270f3f357eSJacob Lambert   }
11280f3f357eSJacob Lambert 
11290f3f357eSJacob Lambert   StringRef IFName = BundlerConfig.InputFileNames.front();
11300f3f357eSJacob Lambert 
11310f3f357eSJacob Lambert   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
11320f3f357eSJacob Lambert       MemoryBuffer::getFileOrSTDIN(IFName, true, false);
11330f3f357eSJacob Lambert   if (std::error_code EC = BufOrErr.getError())
11340f3f357eSJacob Lambert     return createFileError(BundlerConfig.InputFileNames.front(), EC);
11350f3f357eSJacob Lambert 
11360f3f357eSJacob Lambert   ArchiveBuffers.push_back(std::move(*BufOrErr));
11370f3f357eSJacob Lambert   Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
11380f3f357eSJacob Lambert       Archive::create(ArchiveBuffers.back()->getMemBufferRef());
11390f3f357eSJacob Lambert   if (!LibOrErr)
11400f3f357eSJacob Lambert     return LibOrErr.takeError();
11410f3f357eSJacob Lambert 
11420f3f357eSJacob Lambert   auto Archive = std::move(*LibOrErr);
11430f3f357eSJacob Lambert 
11440f3f357eSJacob Lambert   Error ArchiveErr = Error::success();
11450f3f357eSJacob Lambert   auto ChildEnd = Archive->child_end();
11460f3f357eSJacob Lambert 
11470f3f357eSJacob Lambert   /// Iterate over all bundled code object files in the input archive.
11480f3f357eSJacob Lambert   for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
11490f3f357eSJacob Lambert        ArchiveIter != ChildEnd; ++ArchiveIter) {
11500f3f357eSJacob Lambert     if (ArchiveErr)
11510f3f357eSJacob Lambert       return ArchiveErr;
11520f3f357eSJacob Lambert     auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
11530f3f357eSJacob Lambert     if (!ArchiveChildNameOrErr)
11540f3f357eSJacob Lambert       return ArchiveChildNameOrErr.takeError();
11550f3f357eSJacob Lambert 
11560f3f357eSJacob Lambert     StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
11570f3f357eSJacob Lambert 
11580f3f357eSJacob Lambert     auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
11590f3f357eSJacob Lambert     if (!CodeObjectBufferRefOrErr)
11600f3f357eSJacob Lambert       return CodeObjectBufferRefOrErr.takeError();
11610f3f357eSJacob Lambert 
11620f3f357eSJacob Lambert     auto CodeObjectBuffer =
11630f3f357eSJacob Lambert         MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
11640f3f357eSJacob Lambert 
11650f3f357eSJacob Lambert     Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
11660f3f357eSJacob Lambert         CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
11670f3f357eSJacob Lambert     if (!FileHandlerOrErr)
11680f3f357eSJacob Lambert       return FileHandlerOrErr.takeError();
11690f3f357eSJacob Lambert 
11700f3f357eSJacob Lambert     std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
11710f3f357eSJacob Lambert     assert(FileHandler &&
11720f3f357eSJacob Lambert            "FileHandle creation failed for file in the archive!");
11730f3f357eSJacob Lambert 
1174d22f050eSGregory Alfonso     if (Error ReadErr = FileHandler->ReadHeader(*CodeObjectBuffer))
11750f3f357eSJacob Lambert       return ReadErr;
11760f3f357eSJacob Lambert 
11772c5d49cfSFangrui Song     Expected<std::optional<StringRef>> CurBundleIDOrErr =
11780f3f357eSJacob Lambert         FileHandler->ReadBundleStart(*CodeObjectBuffer);
11790f3f357eSJacob Lambert     if (!CurBundleIDOrErr)
11800f3f357eSJacob Lambert       return CurBundleIDOrErr.takeError();
11810f3f357eSJacob Lambert 
11822c5d49cfSFangrui Song     std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
11830f3f357eSJacob Lambert     // No device code in this child, skip.
11847430894aSFangrui Song     if (!OptionalCurBundleID)
11850f3f357eSJacob Lambert       continue;
11860f3f357eSJacob Lambert     StringRef CodeObject = *OptionalCurBundleID;
11870f3f357eSJacob Lambert 
11880f3f357eSJacob Lambert     // Process all bundle entries (CodeObjects) found in this child of input
11890f3f357eSJacob Lambert     // archive.
11900f3f357eSJacob Lambert     while (!CodeObject.empty()) {
11910f3f357eSJacob Lambert       SmallVector<StringRef> CompatibleTargets;
11920f3f357eSJacob Lambert       auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
11930f3f357eSJacob Lambert       if (CodeObjectInfo.hasHostKind()) {
11940f3f357eSJacob Lambert         // Do nothing, we don't extract host code yet.
11955b11caa8Sraghavmedicherla       } else if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
11960f3f357eSJacob Lambert                                              BundlerConfig)) {
11970f3f357eSJacob Lambert         std::string BundleData;
11980f3f357eSJacob Lambert         raw_string_ostream DataStream(BundleData);
1199d22f050eSGregory Alfonso         if (Error Err = FileHandler->ReadBundle(DataStream, *CodeObjectBuffer))
12000f3f357eSJacob Lambert           return Err;
12010f3f357eSJacob Lambert 
12020f3f357eSJacob Lambert         for (auto &CompatibleTarget : CompatibleTargets) {
12030f3f357eSJacob Lambert           SmallString<128> BundledObjectFileName;
12040f3f357eSJacob Lambert           BundledObjectFileName.assign(BundledObjectFile);
12050f3f357eSJacob Lambert           auto OutputBundleName =
12060f3f357eSJacob Lambert               Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
12070f3f357eSJacob Lambert                     CodeObject +
12080f3f357eSJacob Lambert                     getDeviceLibraryFileName(BundledObjectFileName,
1209844b84afSYaxun (Sam) Liu                                              CodeObjectInfo.TargetID))
12100f3f357eSJacob Lambert                   .str();
12110f3f357eSJacob Lambert           // Replace ':' in optional target feature list with '_' to ensure
12120f3f357eSJacob Lambert           // cross-platform validity.
12130f3f357eSJacob Lambert           std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
12140f3f357eSJacob Lambert                        '_');
12150f3f357eSJacob Lambert 
12160f3f357eSJacob Lambert           std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
12170f3f357eSJacob Lambert               DataStream.str(), OutputBundleName);
12180f3f357eSJacob Lambert           ArchiveBuffers.push_back(std::move(MemBuf));
12190f3f357eSJacob Lambert           llvm::MemoryBufferRef MemBufRef =
12200f3f357eSJacob Lambert               MemoryBufferRef(*(ArchiveBuffers.back()));
12210f3f357eSJacob Lambert 
12220f3f357eSJacob Lambert           // For inserting <CompatibleTarget, list<CodeObject>> entry in
12230f3f357eSJacob Lambert           // OutputArchivesMap.
12240f3f357eSJacob Lambert           if (OutputArchivesMap.find(CompatibleTarget) ==
12250f3f357eSJacob Lambert               OutputArchivesMap.end()) {
12260f3f357eSJacob Lambert 
12270f3f357eSJacob Lambert             std::vector<NewArchiveMember> ArchiveMembers;
12280f3f357eSJacob Lambert             ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
12290f3f357eSJacob Lambert             OutputArchivesMap.insert_or_assign(CompatibleTarget,
12300f3f357eSJacob Lambert                                                std::move(ArchiveMembers));
12310f3f357eSJacob Lambert           } else {
12320f3f357eSJacob Lambert             OutputArchivesMap[CompatibleTarget].push_back(
12330f3f357eSJacob Lambert                 NewArchiveMember(MemBufRef));
12340f3f357eSJacob Lambert           }
12350f3f357eSJacob Lambert         }
12360f3f357eSJacob Lambert       }
12370f3f357eSJacob Lambert 
1238d22f050eSGregory Alfonso       if (Error Err = FileHandler->ReadBundleEnd(*CodeObjectBuffer))
12390f3f357eSJacob Lambert         return Err;
12400f3f357eSJacob Lambert 
12412c5d49cfSFangrui Song       Expected<std::optional<StringRef>> NextTripleOrErr =
12420f3f357eSJacob Lambert           FileHandler->ReadBundleStart(*CodeObjectBuffer);
12430f3f357eSJacob Lambert       if (!NextTripleOrErr)
12440f3f357eSJacob Lambert         return NextTripleOrErr.takeError();
12450f3f357eSJacob Lambert 
1246a9481170SKazu Hirata       CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
12470f3f357eSJacob Lambert     } // End of processing of all bundle entries of this child of input archive.
12480f3f357eSJacob Lambert   }   // End of while over children of input archive.
12490f3f357eSJacob Lambert 
12500f3f357eSJacob Lambert   assert(!ArchiveErr && "Error occurred while reading archive!");
12510f3f357eSJacob Lambert 
12520f3f357eSJacob Lambert   /// Write out an archive for each target
12530f3f357eSJacob Lambert   for (auto &Target : BundlerConfig.TargetNames) {
12540f3f357eSJacob Lambert     StringRef FileName = TargetOutputFileNameMap[Target];
12550f3f357eSJacob Lambert     StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
12560f3f357eSJacob Lambert         OutputArchivesMap.find(Target);
12570f3f357eSJacob Lambert     if (CurArchiveMembers != OutputArchivesMap.end()) {
12580f3f357eSJacob Lambert       if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
12590f3f357eSJacob Lambert                                         true, getDefaultArchiveKindForHost(),
12600f3f357eSJacob Lambert                                         true, false, nullptr))
12610f3f357eSJacob Lambert         return WriteErr;
12620f3f357eSJacob Lambert     } else if (!BundlerConfig.AllowMissingBundles) {
12630f3f357eSJacob Lambert       std::string ErrMsg =
12640f3f357eSJacob Lambert           Twine("no compatible code object found for the target '" + Target +
12650f3f357eSJacob Lambert                 "' in heterogeneous archive library: " + IFName)
12660f3f357eSJacob Lambert               .str();
12670f3f357eSJacob Lambert       return createStringError(inconvertibleErrorCode(), ErrMsg);
12680f3f357eSJacob Lambert     } else { // Create an empty archive file if no compatible code object is
12690f3f357eSJacob Lambert              // found and "allow-missing-bundles" is enabled. It ensures that
12700f3f357eSJacob Lambert              // the linker using output of this step doesn't complain about
12710f3f357eSJacob Lambert              // the missing input file.
12720f3f357eSJacob Lambert       std::vector<llvm::NewArchiveMember> EmptyArchive;
12730f3f357eSJacob Lambert       EmptyArchive.clear();
12740f3f357eSJacob Lambert       if (Error WriteErr = writeArchive(FileName, EmptyArchive, true,
12750f3f357eSJacob Lambert                                         getDefaultArchiveKindForHost(), true,
12760f3f357eSJacob Lambert                                         false, nullptr))
12770f3f357eSJacob Lambert         return WriteErr;
12780f3f357eSJacob Lambert     }
12790f3f357eSJacob Lambert   }
12800f3f357eSJacob Lambert 
12810f3f357eSJacob Lambert   return Error::success();
12820f3f357eSJacob Lambert }
1283