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