1bdd1243dSDimitry Andric //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric /// 9bdd1243dSDimitry Andric /// \file 10bdd1243dSDimitry Andric /// This file implements an offload bundling API that bundles different files 11bdd1243dSDimitry Andric /// that relate with the same source code but different targets into a single 12bdd1243dSDimitry Andric /// one. Also the implements the opposite functionality, i.e. unbundle files 13bdd1243dSDimitry Andric /// previous created by this API. 14bdd1243dSDimitry Andric /// 15bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 16bdd1243dSDimitry Andric 17bdd1243dSDimitry Andric #include "clang/Driver/OffloadBundler.h" 18bdd1243dSDimitry Andric #include "clang/Basic/Cuda.h" 19bdd1243dSDimitry Andric #include "clang/Basic/TargetID.h" 20bdd1243dSDimitry Andric #include "clang/Basic/Version.h" 21bdd1243dSDimitry Andric #include "llvm/ADT/ArrayRef.h" 22bdd1243dSDimitry Andric #include "llvm/ADT/SmallString.h" 23bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h" 245f757f3fSDimitry Andric #include "llvm/ADT/StringExtras.h" 25bdd1243dSDimitry Andric #include "llvm/ADT/StringMap.h" 26bdd1243dSDimitry Andric #include "llvm/ADT/StringRef.h" 275f757f3fSDimitry Andric #include "llvm/BinaryFormat/Magic.h" 28bdd1243dSDimitry Andric #include "llvm/Object/Archive.h" 29bdd1243dSDimitry Andric #include "llvm/Object/ArchiveWriter.h" 30bdd1243dSDimitry Andric #include "llvm/Object/Binary.h" 31bdd1243dSDimitry Andric #include "llvm/Object/ObjectFile.h" 32bdd1243dSDimitry Andric #include "llvm/Support/Casting.h" 335f757f3fSDimitry Andric #include "llvm/Support/Compression.h" 34bdd1243dSDimitry Andric #include "llvm/Support/Debug.h" 35bdd1243dSDimitry Andric #include "llvm/Support/EndianStream.h" 36bdd1243dSDimitry Andric #include "llvm/Support/Errc.h" 37bdd1243dSDimitry Andric #include "llvm/Support/Error.h" 38bdd1243dSDimitry Andric #include "llvm/Support/ErrorOr.h" 39bdd1243dSDimitry Andric #include "llvm/Support/FileSystem.h" 405f757f3fSDimitry Andric #include "llvm/Support/MD5.h" 41bdd1243dSDimitry Andric #include "llvm/Support/MemoryBuffer.h" 42bdd1243dSDimitry Andric #include "llvm/Support/Path.h" 43bdd1243dSDimitry Andric #include "llvm/Support/Program.h" 44bdd1243dSDimitry Andric #include "llvm/Support/Signals.h" 45bdd1243dSDimitry Andric #include "llvm/Support/StringSaver.h" 465f757f3fSDimitry Andric #include "llvm/Support/Timer.h" 47bdd1243dSDimitry Andric #include "llvm/Support/WithColor.h" 48bdd1243dSDimitry Andric #include "llvm/Support/raw_ostream.h" 4906c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h" 5006c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h" 51bdd1243dSDimitry Andric #include <algorithm> 52bdd1243dSDimitry Andric #include <cassert> 53bdd1243dSDimitry Andric #include <cstddef> 54bdd1243dSDimitry Andric #include <cstdint> 55bdd1243dSDimitry Andric #include <forward_list> 565f757f3fSDimitry Andric #include <llvm/Support/Process.h> 57bdd1243dSDimitry Andric #include <memory> 58bdd1243dSDimitry Andric #include <set> 59bdd1243dSDimitry Andric #include <string> 60bdd1243dSDimitry Andric #include <system_error> 61bdd1243dSDimitry Andric #include <utility> 62bdd1243dSDimitry Andric 63bdd1243dSDimitry Andric using namespace llvm; 64bdd1243dSDimitry Andric using namespace llvm::object; 65bdd1243dSDimitry Andric using namespace clang; 66bdd1243dSDimitry Andric 675f757f3fSDimitry Andric static llvm::TimerGroup 685f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group", 695f757f3fSDimitry Andric "Timer group for clang offload bundler"); 705f757f3fSDimitry Andric 71bdd1243dSDimitry Andric /// Magic string that marks the existence of offloading data. 72bdd1243dSDimitry Andric #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" 73bdd1243dSDimitry Andric 74bdd1243dSDimitry Andric OffloadTargetInfo::OffloadTargetInfo(const StringRef Target, 75bdd1243dSDimitry Andric const OffloadBundlerConfig &BC) 76bdd1243dSDimitry Andric : BundlerConfig(BC) { 77bdd1243dSDimitry Andric 78bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 79bdd1243dSDimitry Andric auto TargetFeatures = Target.split(':'); 80bdd1243dSDimitry Andric auto TripleOrGPU = TargetFeatures.first.rsplit('-'); 81bdd1243dSDimitry Andric 82*0fca6ea1SDimitry Andric if (clang::StringToOffloadArch(TripleOrGPU.second) != 83*0fca6ea1SDimitry Andric clang::OffloadArch::UNKNOWN) { 84bdd1243dSDimitry Andric auto KindTriple = TripleOrGPU.first.split('-'); 85bdd1243dSDimitry Andric this->OffloadKind = KindTriple.first; 8606c3fb27SDimitry Andric 8706c3fb27SDimitry Andric // Enforce optional env field to standardize bundles 8806c3fb27SDimitry Andric llvm::Triple t = llvm::Triple(KindTriple.second); 8906c3fb27SDimitry Andric this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(), 9006c3fb27SDimitry Andric t.getOSName(), t.getEnvironmentName()); 9106c3fb27SDimitry Andric 92bdd1243dSDimitry Andric this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); 93bdd1243dSDimitry Andric } else { 94bdd1243dSDimitry Andric auto KindTriple = TargetFeatures.first.split('-'); 95bdd1243dSDimitry Andric this->OffloadKind = KindTriple.first; 9606c3fb27SDimitry Andric 9706c3fb27SDimitry Andric // Enforce optional env field to standardize bundles 9806c3fb27SDimitry Andric llvm::Triple t = llvm::Triple(KindTriple.second); 9906c3fb27SDimitry Andric this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(), 10006c3fb27SDimitry Andric t.getOSName(), t.getEnvironmentName()); 10106c3fb27SDimitry Andric 102bdd1243dSDimitry Andric this->TargetID = ""; 103bdd1243dSDimitry Andric } 104bdd1243dSDimitry Andric } 105bdd1243dSDimitry Andric 106bdd1243dSDimitry Andric bool OffloadTargetInfo::hasHostKind() const { 107bdd1243dSDimitry Andric return this->OffloadKind == "host"; 108bdd1243dSDimitry Andric } 109bdd1243dSDimitry Andric 110bdd1243dSDimitry Andric bool OffloadTargetInfo::isOffloadKindValid() const { 111bdd1243dSDimitry Andric return OffloadKind == "host" || OffloadKind == "openmp" || 112bdd1243dSDimitry Andric OffloadKind == "hip" || OffloadKind == "hipv4"; 113bdd1243dSDimitry Andric } 114bdd1243dSDimitry Andric 115bdd1243dSDimitry Andric bool OffloadTargetInfo::isOffloadKindCompatible( 116bdd1243dSDimitry Andric const StringRef TargetOffloadKind) const { 117*0fca6ea1SDimitry Andric if ((OffloadKind == TargetOffloadKind) || 118*0fca6ea1SDimitry Andric (OffloadKind == "hip" && TargetOffloadKind == "hipv4") || 119*0fca6ea1SDimitry Andric (OffloadKind == "hipv4" && TargetOffloadKind == "hip")) 120bdd1243dSDimitry Andric return true; 121*0fca6ea1SDimitry Andric 122bdd1243dSDimitry Andric if (BundlerConfig.HipOpenmpCompatible) { 12306c3fb27SDimitry Andric bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") && 124bdd1243dSDimitry Andric TargetOffloadKind == "openmp"; 125bdd1243dSDimitry Andric bool OpenMPCompatibleWithHIP = 126bdd1243dSDimitry Andric OffloadKind == "openmp" && 12706c3fb27SDimitry Andric TargetOffloadKind.starts_with_insensitive("hip"); 128bdd1243dSDimitry Andric return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP; 129bdd1243dSDimitry Andric } 130bdd1243dSDimitry Andric return false; 131bdd1243dSDimitry Andric } 132bdd1243dSDimitry Andric 133bdd1243dSDimitry Andric bool OffloadTargetInfo::isTripleValid() const { 134bdd1243dSDimitry Andric return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; 135bdd1243dSDimitry Andric } 136bdd1243dSDimitry Andric 137bdd1243dSDimitry Andric bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { 138bdd1243dSDimitry Andric return OffloadKind == Target.OffloadKind && 139bdd1243dSDimitry Andric Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; 140bdd1243dSDimitry Andric } 141bdd1243dSDimitry Andric 142bdd1243dSDimitry Andric std::string OffloadTargetInfo::str() const { 143bdd1243dSDimitry Andric return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); 144bdd1243dSDimitry Andric } 145bdd1243dSDimitry Andric 146bdd1243dSDimitry Andric static StringRef getDeviceFileExtension(StringRef Device, 147bdd1243dSDimitry Andric StringRef BundleFileName) { 148bdd1243dSDimitry Andric if (Device.contains("gfx")) 149bdd1243dSDimitry Andric return ".bc"; 150bdd1243dSDimitry Andric if (Device.contains("sm_")) 151bdd1243dSDimitry Andric return ".cubin"; 152bdd1243dSDimitry Andric return sys::path::extension(BundleFileName); 153bdd1243dSDimitry Andric } 154bdd1243dSDimitry Andric 155bdd1243dSDimitry Andric static std::string getDeviceLibraryFileName(StringRef BundleFileName, 156bdd1243dSDimitry Andric StringRef Device) { 157bdd1243dSDimitry Andric StringRef LibName = sys::path::stem(BundleFileName); 158bdd1243dSDimitry Andric StringRef Extension = getDeviceFileExtension(Device, BundleFileName); 159bdd1243dSDimitry Andric 160bdd1243dSDimitry Andric std::string Result; 161bdd1243dSDimitry Andric Result += LibName; 162bdd1243dSDimitry Andric Result += Extension; 163bdd1243dSDimitry Andric return Result; 164bdd1243dSDimitry Andric } 165bdd1243dSDimitry Andric 166bdd1243dSDimitry Andric namespace { 167bdd1243dSDimitry Andric /// Generic file handler interface. 168bdd1243dSDimitry Andric class FileHandler { 169bdd1243dSDimitry Andric public: 170bdd1243dSDimitry Andric struct BundleInfo { 171bdd1243dSDimitry Andric StringRef BundleID; 172bdd1243dSDimitry Andric }; 173bdd1243dSDimitry Andric 174bdd1243dSDimitry Andric FileHandler() {} 175bdd1243dSDimitry Andric 176bdd1243dSDimitry Andric virtual ~FileHandler() {} 177bdd1243dSDimitry Andric 178bdd1243dSDimitry Andric /// Update the file handler with information from the header of the bundled 179bdd1243dSDimitry Andric /// file. 180bdd1243dSDimitry Andric virtual Error ReadHeader(MemoryBuffer &Input) = 0; 181bdd1243dSDimitry Andric 182bdd1243dSDimitry Andric /// Read the marker of the next bundled to be read in the file. The bundle 183bdd1243dSDimitry Andric /// name is returned if there is one in the file, or `std::nullopt` if there 184bdd1243dSDimitry Andric /// are no more bundles to be read. 185bdd1243dSDimitry Andric virtual Expected<std::optional<StringRef>> 186bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) = 0; 187bdd1243dSDimitry Andric 188bdd1243dSDimitry Andric /// Read the marker that closes the current bundle. 189bdd1243dSDimitry Andric virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; 190bdd1243dSDimitry Andric 191bdd1243dSDimitry Andric /// Read the current bundle and write the result into the stream \a OS. 192bdd1243dSDimitry Andric virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 193bdd1243dSDimitry Andric 194bdd1243dSDimitry Andric /// Write the header of the bundled file to \a OS based on the information 195bdd1243dSDimitry Andric /// gathered from \a Inputs. 1965f757f3fSDimitry Andric virtual Error WriteHeader(raw_ostream &OS, 197bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; 198bdd1243dSDimitry Andric 199bdd1243dSDimitry Andric /// Write the marker that initiates a bundle for the triple \a TargetTriple to 200bdd1243dSDimitry Andric /// \a OS. 2015f757f3fSDimitry Andric virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0; 202bdd1243dSDimitry Andric 203bdd1243dSDimitry Andric /// Write the marker that closes a bundle for the triple \a TargetTriple to \a 204bdd1243dSDimitry Andric /// OS. 2055f757f3fSDimitry Andric virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0; 206bdd1243dSDimitry Andric 207bdd1243dSDimitry Andric /// Write the bundle from \a Input into \a OS. 2085f757f3fSDimitry Andric virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 2095f757f3fSDimitry Andric 2105f757f3fSDimitry Andric /// Finalize output file. 2115f757f3fSDimitry Andric virtual Error finalizeOutputFile() { return Error::success(); } 212bdd1243dSDimitry Andric 213bdd1243dSDimitry Andric /// List bundle IDs in \a Input. 214bdd1243dSDimitry Andric virtual Error listBundleIDs(MemoryBuffer &Input) { 215bdd1243dSDimitry Andric if (Error Err = ReadHeader(Input)) 216bdd1243dSDimitry Andric return Err; 217bdd1243dSDimitry Andric return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 218bdd1243dSDimitry Andric llvm::outs() << Info.BundleID << '\n'; 219bdd1243dSDimitry Andric Error Err = listBundleIDsCallback(Input, Info); 220bdd1243dSDimitry Andric if (Err) 221bdd1243dSDimitry Andric return Err; 222bdd1243dSDimitry Andric return Error::success(); 223bdd1243dSDimitry Andric }); 224bdd1243dSDimitry Andric } 225bdd1243dSDimitry Andric 2265f757f3fSDimitry Andric /// Get bundle IDs in \a Input in \a BundleIds. 2275f757f3fSDimitry Andric virtual Error getBundleIDs(MemoryBuffer &Input, 2285f757f3fSDimitry Andric std::set<StringRef> &BundleIds) { 2295f757f3fSDimitry Andric if (Error Err = ReadHeader(Input)) 2305f757f3fSDimitry Andric return Err; 2315f757f3fSDimitry Andric return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 2325f757f3fSDimitry Andric BundleIds.insert(Info.BundleID); 2335f757f3fSDimitry Andric Error Err = listBundleIDsCallback(Input, Info); 2345f757f3fSDimitry Andric if (Err) 2355f757f3fSDimitry Andric return Err; 2365f757f3fSDimitry Andric return Error::success(); 2375f757f3fSDimitry Andric }); 2385f757f3fSDimitry Andric } 2395f757f3fSDimitry Andric 240bdd1243dSDimitry Andric /// For each bundle in \a Input, do \a Func. 241bdd1243dSDimitry Andric Error forEachBundle(MemoryBuffer &Input, 242bdd1243dSDimitry Andric std::function<Error(const BundleInfo &)> Func) { 243bdd1243dSDimitry Andric while (true) { 244bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurTripleOrErr = 245bdd1243dSDimitry Andric ReadBundleStart(Input); 246bdd1243dSDimitry Andric if (!CurTripleOrErr) 247bdd1243dSDimitry Andric return CurTripleOrErr.takeError(); 248bdd1243dSDimitry Andric 249bdd1243dSDimitry Andric // No more bundles. 250bdd1243dSDimitry Andric if (!*CurTripleOrErr) 251bdd1243dSDimitry Andric break; 252bdd1243dSDimitry Andric 253bdd1243dSDimitry Andric StringRef CurTriple = **CurTripleOrErr; 254bdd1243dSDimitry Andric assert(!CurTriple.empty()); 255bdd1243dSDimitry Andric 256bdd1243dSDimitry Andric BundleInfo Info{CurTriple}; 257bdd1243dSDimitry Andric if (Error Err = Func(Info)) 258bdd1243dSDimitry Andric return Err; 259bdd1243dSDimitry Andric } 260bdd1243dSDimitry Andric return Error::success(); 261bdd1243dSDimitry Andric } 262bdd1243dSDimitry Andric 263bdd1243dSDimitry Andric protected: 264bdd1243dSDimitry Andric virtual Error listBundleIDsCallback(MemoryBuffer &Input, 265bdd1243dSDimitry Andric const BundleInfo &Info) { 266bdd1243dSDimitry Andric return Error::success(); 267bdd1243dSDimitry Andric } 268bdd1243dSDimitry Andric }; 269bdd1243dSDimitry Andric 270bdd1243dSDimitry Andric /// Handler for binary files. The bundled file will have the following format 271bdd1243dSDimitry Andric /// (all integers are stored in little-endian format): 272bdd1243dSDimitry Andric /// 273bdd1243dSDimitry Andric /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) 274bdd1243dSDimitry Andric /// 275bdd1243dSDimitry Andric /// NumberOfOffloadBundles (8-byte integer) 276bdd1243dSDimitry Andric /// 277bdd1243dSDimitry Andric /// OffsetOfBundle1 (8-byte integer) 278bdd1243dSDimitry Andric /// SizeOfBundle1 (8-byte integer) 279bdd1243dSDimitry Andric /// NumberOfBytesInTripleOfBundle1 (8-byte integer) 280bdd1243dSDimitry Andric /// TripleOfBundle1 (byte length defined before) 281bdd1243dSDimitry Andric /// 282bdd1243dSDimitry Andric /// ... 283bdd1243dSDimitry Andric /// 284bdd1243dSDimitry Andric /// OffsetOfBundleN (8-byte integer) 285bdd1243dSDimitry Andric /// SizeOfBundleN (8-byte integer) 286bdd1243dSDimitry Andric /// NumberOfBytesInTripleOfBundleN (8-byte integer) 287bdd1243dSDimitry Andric /// TripleOfBundleN (byte length defined before) 288bdd1243dSDimitry Andric /// 289bdd1243dSDimitry Andric /// Bundle1 290bdd1243dSDimitry Andric /// ... 291bdd1243dSDimitry Andric /// BundleN 292bdd1243dSDimitry Andric 293bdd1243dSDimitry Andric /// Read 8-byte integers from a buffer in little-endian format. 294bdd1243dSDimitry Andric static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { 295bdd1243dSDimitry Andric return llvm::support::endian::read64le(Buffer.data() + pos); 296bdd1243dSDimitry Andric } 297bdd1243dSDimitry Andric 298bdd1243dSDimitry Andric /// Write 8-byte integers to a buffer in little-endian format. 2995f757f3fSDimitry Andric static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) { 3005f757f3fSDimitry Andric llvm::support::endian::write(OS, Val, llvm::endianness::little); 301bdd1243dSDimitry Andric } 302bdd1243dSDimitry Andric 303bdd1243dSDimitry Andric class BinaryFileHandler final : public FileHandler { 304bdd1243dSDimitry Andric /// Information about the bundles extracted from the header. 305bdd1243dSDimitry Andric struct BinaryBundleInfo final : public BundleInfo { 306bdd1243dSDimitry Andric /// Size of the bundle. 307bdd1243dSDimitry Andric uint64_t Size = 0u; 308bdd1243dSDimitry Andric /// Offset at which the bundle starts in the bundled file. 309bdd1243dSDimitry Andric uint64_t Offset = 0u; 310bdd1243dSDimitry Andric 311bdd1243dSDimitry Andric BinaryBundleInfo() {} 312bdd1243dSDimitry Andric BinaryBundleInfo(uint64_t Size, uint64_t Offset) 313bdd1243dSDimitry Andric : Size(Size), Offset(Offset) {} 314bdd1243dSDimitry Andric }; 315bdd1243dSDimitry Andric 316bdd1243dSDimitry Andric /// Map between a triple and the corresponding bundle information. 317bdd1243dSDimitry Andric StringMap<BinaryBundleInfo> BundlesInfo; 318bdd1243dSDimitry Andric 319bdd1243dSDimitry Andric /// Iterator for the bundle information that is being read. 320bdd1243dSDimitry Andric StringMap<BinaryBundleInfo>::iterator CurBundleInfo; 321bdd1243dSDimitry Andric StringMap<BinaryBundleInfo>::iterator NextBundleInfo; 322bdd1243dSDimitry Andric 323bdd1243dSDimitry Andric /// Current bundle target to be written. 324bdd1243dSDimitry Andric std::string CurWriteBundleTarget; 325bdd1243dSDimitry Andric 326bdd1243dSDimitry Andric /// Configuration options and arrays for this bundler job 327bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig; 328bdd1243dSDimitry Andric 329bdd1243dSDimitry Andric public: 330bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 331bdd1243dSDimitry Andric BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {} 332bdd1243dSDimitry Andric 333bdd1243dSDimitry Andric ~BinaryFileHandler() final {} 334bdd1243dSDimitry Andric 335bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { 336bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 337bdd1243dSDimitry Andric 338bdd1243dSDimitry Andric // Initialize the current bundle with the end of the container. 339bdd1243dSDimitry Andric CurBundleInfo = BundlesInfo.end(); 340bdd1243dSDimitry Andric 341bdd1243dSDimitry Andric // Check if buffer is smaller than magic string. 342bdd1243dSDimitry Andric size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 343bdd1243dSDimitry Andric if (ReadChars > FC.size()) 344bdd1243dSDimitry Andric return Error::success(); 345bdd1243dSDimitry Andric 346bdd1243dSDimitry Andric // Check if no magic was found. 3475f757f3fSDimitry Andric if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle) 348bdd1243dSDimitry Andric return Error::success(); 349bdd1243dSDimitry Andric 350bdd1243dSDimitry Andric // Read number of bundles. 351bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 352bdd1243dSDimitry Andric return Error::success(); 353bdd1243dSDimitry Andric 354bdd1243dSDimitry Andric uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); 355bdd1243dSDimitry Andric ReadChars += 8; 356bdd1243dSDimitry Andric 357bdd1243dSDimitry Andric // Read bundle offsets, sizes and triples. 358bdd1243dSDimitry Andric for (uint64_t i = 0; i < NumberOfBundles; ++i) { 359bdd1243dSDimitry Andric 360bdd1243dSDimitry Andric // Read offset. 361bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 362bdd1243dSDimitry Andric return Error::success(); 363bdd1243dSDimitry Andric 364bdd1243dSDimitry Andric uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); 365bdd1243dSDimitry Andric ReadChars += 8; 366bdd1243dSDimitry Andric 367bdd1243dSDimitry Andric // Read size. 368bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 369bdd1243dSDimitry Andric return Error::success(); 370bdd1243dSDimitry Andric 371bdd1243dSDimitry Andric uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); 372bdd1243dSDimitry Andric ReadChars += 8; 373bdd1243dSDimitry Andric 374bdd1243dSDimitry Andric // Read triple size. 375bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 376bdd1243dSDimitry Andric return Error::success(); 377bdd1243dSDimitry Andric 378bdd1243dSDimitry Andric uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); 379bdd1243dSDimitry Andric ReadChars += 8; 380bdd1243dSDimitry Andric 381bdd1243dSDimitry Andric // Read triple. 382bdd1243dSDimitry Andric if (ReadChars + TripleSize > FC.size()) 383bdd1243dSDimitry Andric return Error::success(); 384bdd1243dSDimitry Andric 385bdd1243dSDimitry Andric StringRef Triple(&FC.data()[ReadChars], TripleSize); 386bdd1243dSDimitry Andric ReadChars += TripleSize; 387bdd1243dSDimitry Andric 388bdd1243dSDimitry Andric // Check if the offset and size make sense. 389bdd1243dSDimitry Andric if (!Offset || Offset + Size > FC.size()) 390bdd1243dSDimitry Andric return Error::success(); 391bdd1243dSDimitry Andric 39206c3fb27SDimitry Andric assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??"); 393bdd1243dSDimitry Andric BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); 394bdd1243dSDimitry Andric } 395bdd1243dSDimitry Andric // Set the iterator to where we will start to read. 396bdd1243dSDimitry Andric CurBundleInfo = BundlesInfo.end(); 397bdd1243dSDimitry Andric NextBundleInfo = BundlesInfo.begin(); 398bdd1243dSDimitry Andric return Error::success(); 399bdd1243dSDimitry Andric } 400bdd1243dSDimitry Andric 401bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 402bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 403bdd1243dSDimitry Andric if (NextBundleInfo == BundlesInfo.end()) 404bdd1243dSDimitry Andric return std::nullopt; 405bdd1243dSDimitry Andric CurBundleInfo = NextBundleInfo++; 406bdd1243dSDimitry Andric return CurBundleInfo->first(); 407bdd1243dSDimitry Andric } 408bdd1243dSDimitry Andric 409bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { 410bdd1243dSDimitry Andric assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 411bdd1243dSDimitry Andric return Error::success(); 412bdd1243dSDimitry Andric } 413bdd1243dSDimitry Andric 414bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 415bdd1243dSDimitry Andric assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 416bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 417bdd1243dSDimitry Andric OS.write(FC.data() + CurBundleInfo->second.Offset, 418bdd1243dSDimitry Andric CurBundleInfo->second.Size); 419bdd1243dSDimitry Andric return Error::success(); 420bdd1243dSDimitry Andric } 421bdd1243dSDimitry Andric 4225f757f3fSDimitry Andric Error WriteHeader(raw_ostream &OS, 423bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 424bdd1243dSDimitry Andric 425bdd1243dSDimitry Andric // Compute size of the header. 426bdd1243dSDimitry Andric uint64_t HeaderSize = 0; 427bdd1243dSDimitry Andric 428bdd1243dSDimitry Andric HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 429bdd1243dSDimitry Andric HeaderSize += 8; // Number of Bundles 430bdd1243dSDimitry Andric 431bdd1243dSDimitry Andric for (auto &T : BundlerConfig.TargetNames) { 432bdd1243dSDimitry Andric HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. 433bdd1243dSDimitry Andric HeaderSize += T.size(); // The triple. 434bdd1243dSDimitry Andric } 435bdd1243dSDimitry Andric 436bdd1243dSDimitry Andric // Write to the buffer the header. 437bdd1243dSDimitry Andric OS << OFFLOAD_BUNDLER_MAGIC_STR; 438bdd1243dSDimitry Andric 439bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size()); 440bdd1243dSDimitry Andric 441bdd1243dSDimitry Andric unsigned Idx = 0; 442bdd1243dSDimitry Andric for (auto &T : BundlerConfig.TargetNames) { 443bdd1243dSDimitry Andric MemoryBuffer &MB = *Inputs[Idx++]; 444bdd1243dSDimitry Andric HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment); 445bdd1243dSDimitry Andric // Bundle offset. 446bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, HeaderSize); 447bdd1243dSDimitry Andric // Size of the bundle (adds to the next bundle's offset) 448bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, MB.getBufferSize()); 449bdd1243dSDimitry Andric BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); 450bdd1243dSDimitry Andric HeaderSize += MB.getBufferSize(); 451bdd1243dSDimitry Andric // Size of the triple 452bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, T.size()); 453bdd1243dSDimitry Andric // Triple 454bdd1243dSDimitry Andric OS << T; 455bdd1243dSDimitry Andric } 456bdd1243dSDimitry Andric return Error::success(); 457bdd1243dSDimitry Andric } 458bdd1243dSDimitry Andric 4595f757f3fSDimitry Andric Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 460bdd1243dSDimitry Andric CurWriteBundleTarget = TargetTriple.str(); 461bdd1243dSDimitry Andric return Error::success(); 462bdd1243dSDimitry Andric } 463bdd1243dSDimitry Andric 4645f757f3fSDimitry Andric Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 465bdd1243dSDimitry Andric return Error::success(); 466bdd1243dSDimitry Andric } 467bdd1243dSDimitry Andric 4685f757f3fSDimitry Andric Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 469bdd1243dSDimitry Andric auto BI = BundlesInfo[CurWriteBundleTarget]; 4705f757f3fSDimitry Andric 4715f757f3fSDimitry Andric // Pad with 0 to reach specified offset. 4725f757f3fSDimitry Andric size_t CurrentPos = OS.tell(); 4735f757f3fSDimitry Andric size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0; 4745f757f3fSDimitry Andric for (size_t I = 0; I < PaddingSize; ++I) 4755f757f3fSDimitry Andric OS.write('\0'); 4765f757f3fSDimitry Andric assert(OS.tell() == BI.Offset); 4775f757f3fSDimitry Andric 478bdd1243dSDimitry Andric OS.write(Input.getBufferStart(), Input.getBufferSize()); 4795f757f3fSDimitry Andric 480bdd1243dSDimitry Andric return Error::success(); 481bdd1243dSDimitry Andric } 482bdd1243dSDimitry Andric }; 483bdd1243dSDimitry Andric 484bdd1243dSDimitry Andric // This class implements a list of temporary files that are removed upon 485bdd1243dSDimitry Andric // object destruction. 486bdd1243dSDimitry Andric class TempFileHandlerRAII { 487bdd1243dSDimitry Andric public: 488bdd1243dSDimitry Andric ~TempFileHandlerRAII() { 489bdd1243dSDimitry Andric for (const auto &File : Files) 490bdd1243dSDimitry Andric sys::fs::remove(File); 491bdd1243dSDimitry Andric } 492bdd1243dSDimitry Andric 493bdd1243dSDimitry Andric // Creates temporary file with given contents. 494bdd1243dSDimitry Andric Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) { 495bdd1243dSDimitry Andric SmallString<128u> File; 496bdd1243dSDimitry Andric if (std::error_code EC = 497bdd1243dSDimitry Andric sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) 498bdd1243dSDimitry Andric return createFileError(File, EC); 499bdd1243dSDimitry Andric Files.push_front(File); 500bdd1243dSDimitry Andric 501bdd1243dSDimitry Andric if (Contents) { 502bdd1243dSDimitry Andric std::error_code EC; 503bdd1243dSDimitry Andric raw_fd_ostream OS(File, EC); 504bdd1243dSDimitry Andric if (EC) 505bdd1243dSDimitry Andric return createFileError(File, EC); 506bdd1243dSDimitry Andric OS.write(Contents->data(), Contents->size()); 507bdd1243dSDimitry Andric } 508bdd1243dSDimitry Andric return Files.front().str(); 509bdd1243dSDimitry Andric } 510bdd1243dSDimitry Andric 511bdd1243dSDimitry Andric private: 512bdd1243dSDimitry Andric std::forward_list<SmallString<128u>> Files; 513bdd1243dSDimitry Andric }; 514bdd1243dSDimitry Andric 515bdd1243dSDimitry Andric /// Handler for object files. The bundles are organized by sections with a 516bdd1243dSDimitry Andric /// designated name. 517bdd1243dSDimitry Andric /// 518bdd1243dSDimitry Andric /// To unbundle, we just copy the contents of the designated section. 519bdd1243dSDimitry Andric class ObjectFileHandler final : public FileHandler { 520bdd1243dSDimitry Andric 521bdd1243dSDimitry Andric /// The object file we are currently dealing with. 522bdd1243dSDimitry Andric std::unique_ptr<ObjectFile> Obj; 523bdd1243dSDimitry Andric 524bdd1243dSDimitry Andric /// Return the input file contents. 525bdd1243dSDimitry Andric StringRef getInputFileContents() const { return Obj->getData(); } 526bdd1243dSDimitry Andric 527bdd1243dSDimitry Andric /// Return bundle name (<kind>-<triple>) if the provided section is an offload 528bdd1243dSDimitry Andric /// section. 529bdd1243dSDimitry Andric static Expected<std::optional<StringRef>> 530bdd1243dSDimitry Andric IsOffloadSection(SectionRef CurSection) { 531bdd1243dSDimitry Andric Expected<StringRef> NameOrErr = CurSection.getName(); 532bdd1243dSDimitry Andric if (!NameOrErr) 533bdd1243dSDimitry Andric return NameOrErr.takeError(); 534bdd1243dSDimitry Andric 535bdd1243dSDimitry Andric // If it does not start with the reserved suffix, just skip this section. 5365f757f3fSDimitry Andric if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle) 537bdd1243dSDimitry Andric return std::nullopt; 538bdd1243dSDimitry Andric 539bdd1243dSDimitry Andric // Return the triple that is right after the reserved prefix. 540bdd1243dSDimitry Andric return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 541bdd1243dSDimitry Andric } 542bdd1243dSDimitry Andric 543bdd1243dSDimitry Andric /// Total number of inputs. 544bdd1243dSDimitry Andric unsigned NumberOfInputs = 0; 545bdd1243dSDimitry Andric 546bdd1243dSDimitry Andric /// Total number of processed inputs, i.e, inputs that were already 547bdd1243dSDimitry Andric /// read from the buffers. 548bdd1243dSDimitry Andric unsigned NumberOfProcessedInputs = 0; 549bdd1243dSDimitry Andric 550bdd1243dSDimitry Andric /// Iterator of the current and next section. 551bdd1243dSDimitry Andric section_iterator CurrentSection; 552bdd1243dSDimitry Andric section_iterator NextSection; 553bdd1243dSDimitry Andric 554bdd1243dSDimitry Andric /// Configuration options and arrays for this bundler job 555bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig; 556bdd1243dSDimitry Andric 557bdd1243dSDimitry Andric public: 558bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 559bdd1243dSDimitry Andric ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn, 560bdd1243dSDimitry Andric const OffloadBundlerConfig &BC) 561bdd1243dSDimitry Andric : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()), 562bdd1243dSDimitry Andric NextSection(Obj->section_begin()), BundlerConfig(BC) {} 563bdd1243dSDimitry Andric 564bdd1243dSDimitry Andric ~ObjectFileHandler() final {} 565bdd1243dSDimitry Andric 566bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 567bdd1243dSDimitry Andric 568bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 569bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 570bdd1243dSDimitry Andric while (NextSection != Obj->section_end()) { 571bdd1243dSDimitry Andric CurrentSection = NextSection; 572bdd1243dSDimitry Andric ++NextSection; 573bdd1243dSDimitry Andric 574bdd1243dSDimitry Andric // Check if the current section name starts with the reserved prefix. If 575bdd1243dSDimitry Andric // so, return the triple. 576bdd1243dSDimitry Andric Expected<std::optional<StringRef>> TripleOrErr = 577bdd1243dSDimitry Andric IsOffloadSection(*CurrentSection); 578bdd1243dSDimitry Andric if (!TripleOrErr) 579bdd1243dSDimitry Andric return TripleOrErr.takeError(); 580bdd1243dSDimitry Andric if (*TripleOrErr) 581bdd1243dSDimitry Andric return **TripleOrErr; 582bdd1243dSDimitry Andric } 583bdd1243dSDimitry Andric return std::nullopt; 584bdd1243dSDimitry Andric } 585bdd1243dSDimitry Andric 586bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 587bdd1243dSDimitry Andric 588bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 589bdd1243dSDimitry Andric Expected<StringRef> ContentOrErr = CurrentSection->getContents(); 590bdd1243dSDimitry Andric if (!ContentOrErr) 591bdd1243dSDimitry Andric return ContentOrErr.takeError(); 592bdd1243dSDimitry Andric StringRef Content = *ContentOrErr; 593bdd1243dSDimitry Andric 594bdd1243dSDimitry Andric // Copy fat object contents to the output when extracting host bundle. 595*0fca6ea1SDimitry Andric std::string ModifiedContent; 596*0fca6ea1SDimitry Andric if (Content.size() == 1u && Content.front() == 0) { 597*0fca6ea1SDimitry Andric auto HostBundleOrErr = getHostBundle( 598*0fca6ea1SDimitry Andric StringRef(Input.getBufferStart(), Input.getBufferSize())); 599*0fca6ea1SDimitry Andric if (!HostBundleOrErr) 600*0fca6ea1SDimitry Andric return HostBundleOrErr.takeError(); 601*0fca6ea1SDimitry Andric 602*0fca6ea1SDimitry Andric ModifiedContent = std::move(*HostBundleOrErr); 603*0fca6ea1SDimitry Andric Content = ModifiedContent; 604*0fca6ea1SDimitry Andric } 605bdd1243dSDimitry Andric 606bdd1243dSDimitry Andric OS.write(Content.data(), Content.size()); 607bdd1243dSDimitry Andric return Error::success(); 608bdd1243dSDimitry Andric } 609bdd1243dSDimitry Andric 6105f757f3fSDimitry Andric Error WriteHeader(raw_ostream &OS, 611bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 612bdd1243dSDimitry Andric assert(BundlerConfig.HostInputIndex != ~0u && 613bdd1243dSDimitry Andric "Host input index not defined."); 614bdd1243dSDimitry Andric 615bdd1243dSDimitry Andric // Record number of inputs. 616bdd1243dSDimitry Andric NumberOfInputs = Inputs.size(); 617bdd1243dSDimitry Andric return Error::success(); 618bdd1243dSDimitry Andric } 619bdd1243dSDimitry Andric 6205f757f3fSDimitry Andric Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 621bdd1243dSDimitry Andric ++NumberOfProcessedInputs; 622bdd1243dSDimitry Andric return Error::success(); 623bdd1243dSDimitry Andric } 624bdd1243dSDimitry Andric 6255f757f3fSDimitry Andric Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 6265f757f3fSDimitry Andric return Error::success(); 6275f757f3fSDimitry Andric } 6285f757f3fSDimitry Andric 6295f757f3fSDimitry Andric Error finalizeOutputFile() final { 630bdd1243dSDimitry Andric assert(NumberOfProcessedInputs <= NumberOfInputs && 631bdd1243dSDimitry Andric "Processing more inputs that actually exist!"); 632bdd1243dSDimitry Andric assert(BundlerConfig.HostInputIndex != ~0u && 633bdd1243dSDimitry Andric "Host input index not defined."); 634bdd1243dSDimitry Andric 635bdd1243dSDimitry Andric // If this is not the last output, we don't have to do anything. 636bdd1243dSDimitry Andric if (NumberOfProcessedInputs != NumberOfInputs) 637bdd1243dSDimitry Andric return Error::success(); 638bdd1243dSDimitry Andric 639bdd1243dSDimitry Andric // We will use llvm-objcopy to add target objects sections to the output 640bdd1243dSDimitry Andric // fat object. These sections should have 'exclude' flag set which tells 641bdd1243dSDimitry Andric // link editor to remove them from linker inputs when linking executable or 642bdd1243dSDimitry Andric // shared library. 643bdd1243dSDimitry Andric 644bdd1243dSDimitry Andric assert(BundlerConfig.ObjcopyPath != "" && 645bdd1243dSDimitry Andric "llvm-objcopy path not specified"); 646bdd1243dSDimitry Andric 647bdd1243dSDimitry Andric // Temporary files that need to be removed. 648bdd1243dSDimitry Andric TempFileHandlerRAII TempFiles; 649bdd1243dSDimitry Andric 650bdd1243dSDimitry Andric // Compose llvm-objcopy command line for add target objects' sections with 651bdd1243dSDimitry Andric // appropriate flags. 652bdd1243dSDimitry Andric BumpPtrAllocator Alloc; 653bdd1243dSDimitry Andric StringSaver SS{Alloc}; 654bdd1243dSDimitry Andric SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; 655bdd1243dSDimitry Andric 656bdd1243dSDimitry Andric for (unsigned I = 0; I < NumberOfInputs; ++I) { 657bdd1243dSDimitry Andric StringRef InputFile = BundlerConfig.InputFileNames[I]; 658bdd1243dSDimitry Andric if (I == BundlerConfig.HostInputIndex) { 659bdd1243dSDimitry Andric // Special handling for the host bundle. We do not need to add a 660bdd1243dSDimitry Andric // standard bundle for the host object since we are going to use fat 661bdd1243dSDimitry Andric // object as a host object. Therefore use dummy contents (one zero byte) 662bdd1243dSDimitry Andric // when creating section for the host bundle. 663bdd1243dSDimitry Andric Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); 664bdd1243dSDimitry Andric if (!TempFileOrErr) 665bdd1243dSDimitry Andric return TempFileOrErr.takeError(); 666bdd1243dSDimitry Andric InputFile = *TempFileOrErr; 667bdd1243dSDimitry Andric } 668bdd1243dSDimitry Andric 669bdd1243dSDimitry Andric ObjcopyArgs.push_back( 670bdd1243dSDimitry Andric SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR + 671bdd1243dSDimitry Andric BundlerConfig.TargetNames[I] + "=" + InputFile)); 672bdd1243dSDimitry Andric ObjcopyArgs.push_back( 673bdd1243dSDimitry Andric SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR + 674bdd1243dSDimitry Andric BundlerConfig.TargetNames[I] + "=readonly,exclude")); 675bdd1243dSDimitry Andric } 676bdd1243dSDimitry Andric ObjcopyArgs.push_back("--"); 677bdd1243dSDimitry Andric ObjcopyArgs.push_back( 678bdd1243dSDimitry Andric BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]); 679bdd1243dSDimitry Andric ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front()); 680bdd1243dSDimitry Andric 681bdd1243dSDimitry Andric if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 682bdd1243dSDimitry Andric return Err; 683bdd1243dSDimitry Andric 684bdd1243dSDimitry Andric return Error::success(); 685bdd1243dSDimitry Andric } 686bdd1243dSDimitry Andric 6875f757f3fSDimitry Andric Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 688bdd1243dSDimitry Andric return Error::success(); 689bdd1243dSDimitry Andric } 690bdd1243dSDimitry Andric 691bdd1243dSDimitry Andric private: 692bdd1243dSDimitry Andric Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { 693bdd1243dSDimitry Andric // If the user asked for the commands to be printed out, we do that 694bdd1243dSDimitry Andric // instead of executing it. 695bdd1243dSDimitry Andric if (BundlerConfig.PrintExternalCommands) { 696bdd1243dSDimitry Andric errs() << "\"" << Objcopy << "\""; 697bdd1243dSDimitry Andric for (StringRef Arg : drop_begin(Args, 1)) 698bdd1243dSDimitry Andric errs() << " \"" << Arg << "\""; 699bdd1243dSDimitry Andric errs() << "\n"; 700bdd1243dSDimitry Andric } else { 701bdd1243dSDimitry Andric if (sys::ExecuteAndWait(Objcopy, Args)) 702bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), 703bdd1243dSDimitry Andric "'llvm-objcopy' tool failed"); 704bdd1243dSDimitry Andric } 705bdd1243dSDimitry Andric return Error::success(); 706bdd1243dSDimitry Andric } 707*0fca6ea1SDimitry Andric 708*0fca6ea1SDimitry Andric Expected<std::string> getHostBundle(StringRef Input) { 709*0fca6ea1SDimitry Andric TempFileHandlerRAII TempFiles; 710*0fca6ea1SDimitry Andric 711*0fca6ea1SDimitry Andric auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt); 712*0fca6ea1SDimitry Andric if (!ModifiedObjPathOrErr) 713*0fca6ea1SDimitry Andric return ModifiedObjPathOrErr.takeError(); 714*0fca6ea1SDimitry Andric StringRef ModifiedObjPath = *ModifiedObjPathOrErr; 715*0fca6ea1SDimitry Andric 716*0fca6ea1SDimitry Andric BumpPtrAllocator Alloc; 717*0fca6ea1SDimitry Andric StringSaver SS{Alloc}; 718*0fca6ea1SDimitry Andric SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"}; 719*0fca6ea1SDimitry Andric 720*0fca6ea1SDimitry Andric ObjcopyArgs.push_back("--regex"); 721*0fca6ea1SDimitry Andric ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*"); 722*0fca6ea1SDimitry Andric ObjcopyArgs.push_back("--"); 723*0fca6ea1SDimitry Andric 724*0fca6ea1SDimitry Andric StringRef ObjcopyInputFileName; 725*0fca6ea1SDimitry Andric // When unbundling an archive, the content of each object file in the 726*0fca6ea1SDimitry Andric // archive is passed to this function by parameter Input, which is different 727*0fca6ea1SDimitry Andric // from the content of the original input archive file, therefore it needs 728*0fca6ea1SDimitry Andric // to be saved to a temporary file before passed to llvm-objcopy. Otherwise, 729*0fca6ea1SDimitry Andric // Input is the same as the content of the original input file, therefore 730*0fca6ea1SDimitry Andric // temporary file is not needed. 731*0fca6ea1SDimitry Andric if (StringRef(BundlerConfig.FilesType).starts_with("a")) { 732*0fca6ea1SDimitry Andric auto InputFileOrErr = 733*0fca6ea1SDimitry Andric TempFiles.Create(ArrayRef<char>(Input.data(), Input.size())); 734*0fca6ea1SDimitry Andric if (!InputFileOrErr) 735*0fca6ea1SDimitry Andric return InputFileOrErr.takeError(); 736*0fca6ea1SDimitry Andric ObjcopyInputFileName = *InputFileOrErr; 737*0fca6ea1SDimitry Andric } else 738*0fca6ea1SDimitry Andric ObjcopyInputFileName = BundlerConfig.InputFileNames.front(); 739*0fca6ea1SDimitry Andric 740*0fca6ea1SDimitry Andric ObjcopyArgs.push_back(ObjcopyInputFileName); 741*0fca6ea1SDimitry Andric ObjcopyArgs.push_back(ModifiedObjPath); 742*0fca6ea1SDimitry Andric 743*0fca6ea1SDimitry Andric if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 744*0fca6ea1SDimitry Andric return std::move(Err); 745*0fca6ea1SDimitry Andric 746*0fca6ea1SDimitry Andric auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath); 747*0fca6ea1SDimitry Andric if (!BufOrErr) 748*0fca6ea1SDimitry Andric return createStringError(BufOrErr.getError(), 749*0fca6ea1SDimitry Andric "Failed to read back the modified object file"); 750*0fca6ea1SDimitry Andric 751*0fca6ea1SDimitry Andric return BufOrErr->get()->getBuffer().str(); 752*0fca6ea1SDimitry Andric } 753bdd1243dSDimitry Andric }; 754bdd1243dSDimitry Andric 755bdd1243dSDimitry Andric /// Handler for text files. The bundled file will have the following format. 756bdd1243dSDimitry Andric /// 757bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 758bdd1243dSDimitry Andric /// Bundle 1 759bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 760bdd1243dSDimitry Andric /// ... 761bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 762bdd1243dSDimitry Andric /// Bundle N 763bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 764bdd1243dSDimitry Andric class TextFileHandler final : public FileHandler { 765bdd1243dSDimitry Andric /// String that begins a line comment. 766bdd1243dSDimitry Andric StringRef Comment; 767bdd1243dSDimitry Andric 768bdd1243dSDimitry Andric /// String that initiates a bundle. 769bdd1243dSDimitry Andric std::string BundleStartString; 770bdd1243dSDimitry Andric 771bdd1243dSDimitry Andric /// String that closes a bundle. 772bdd1243dSDimitry Andric std::string BundleEndString; 773bdd1243dSDimitry Andric 774bdd1243dSDimitry Andric /// Number of chars read from input. 775bdd1243dSDimitry Andric size_t ReadChars = 0u; 776bdd1243dSDimitry Andric 777bdd1243dSDimitry Andric protected: 778bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 779bdd1243dSDimitry Andric 780bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 781bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 782bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 783bdd1243dSDimitry Andric 784bdd1243dSDimitry Andric // Find start of the bundle. 785bdd1243dSDimitry Andric ReadChars = FC.find(BundleStartString, ReadChars); 786bdd1243dSDimitry Andric if (ReadChars == FC.npos) 787bdd1243dSDimitry Andric return std::nullopt; 788bdd1243dSDimitry Andric 789bdd1243dSDimitry Andric // Get position of the triple. 790bdd1243dSDimitry Andric size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); 791bdd1243dSDimitry Andric 792bdd1243dSDimitry Andric // Get position that closes the triple. 793bdd1243dSDimitry Andric size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); 794bdd1243dSDimitry Andric if (TripleEnd == FC.npos) 795bdd1243dSDimitry Andric return std::nullopt; 796bdd1243dSDimitry Andric 797bdd1243dSDimitry Andric // Next time we read after the new line. 798bdd1243dSDimitry Andric ++ReadChars; 799bdd1243dSDimitry Andric 800bdd1243dSDimitry Andric return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); 801bdd1243dSDimitry Andric } 802bdd1243dSDimitry Andric 803bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { 804bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 805bdd1243dSDimitry Andric 806bdd1243dSDimitry Andric // Read up to the next new line. 807bdd1243dSDimitry Andric assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); 808bdd1243dSDimitry Andric 809bdd1243dSDimitry Andric size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); 810bdd1243dSDimitry Andric if (TripleEnd != FC.npos) 811bdd1243dSDimitry Andric // Next time we read after the new line. 812bdd1243dSDimitry Andric ++ReadChars; 813bdd1243dSDimitry Andric 814bdd1243dSDimitry Andric return Error::success(); 815bdd1243dSDimitry Andric } 816bdd1243dSDimitry Andric 817bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 818bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 819bdd1243dSDimitry Andric size_t BundleStart = ReadChars; 820bdd1243dSDimitry Andric 821bdd1243dSDimitry Andric // Find end of the bundle. 822bdd1243dSDimitry Andric size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); 823bdd1243dSDimitry Andric 824bdd1243dSDimitry Andric StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); 825bdd1243dSDimitry Andric OS << Bundle; 826bdd1243dSDimitry Andric 827bdd1243dSDimitry Andric return Error::success(); 828bdd1243dSDimitry Andric } 829bdd1243dSDimitry Andric 8305f757f3fSDimitry Andric Error WriteHeader(raw_ostream &OS, 831bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 832bdd1243dSDimitry Andric return Error::success(); 833bdd1243dSDimitry Andric } 834bdd1243dSDimitry Andric 8355f757f3fSDimitry Andric Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 836bdd1243dSDimitry Andric OS << BundleStartString << TargetTriple << "\n"; 837bdd1243dSDimitry Andric return Error::success(); 838bdd1243dSDimitry Andric } 839bdd1243dSDimitry Andric 8405f757f3fSDimitry Andric Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 841bdd1243dSDimitry Andric OS << BundleEndString << TargetTriple << "\n"; 842bdd1243dSDimitry Andric return Error::success(); 843bdd1243dSDimitry Andric } 844bdd1243dSDimitry Andric 8455f757f3fSDimitry Andric Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 846bdd1243dSDimitry Andric OS << Input.getBuffer(); 847bdd1243dSDimitry Andric return Error::success(); 848bdd1243dSDimitry Andric } 849bdd1243dSDimitry Andric 850bdd1243dSDimitry Andric public: 851bdd1243dSDimitry Andric TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) { 852bdd1243dSDimitry Andric BundleStartString = 853bdd1243dSDimitry Andric "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; 854bdd1243dSDimitry Andric BundleEndString = 855bdd1243dSDimitry Andric "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; 856bdd1243dSDimitry Andric } 857bdd1243dSDimitry Andric 858bdd1243dSDimitry Andric Error listBundleIDsCallback(MemoryBuffer &Input, 859bdd1243dSDimitry Andric const BundleInfo &Info) final { 860bdd1243dSDimitry Andric // TODO: To list bundle IDs in a bundled text file we need to go through 861bdd1243dSDimitry Andric // all bundles. The format of bundled text file may need to include a 862bdd1243dSDimitry Andric // header if the performance of listing bundle IDs of bundled text file is 863bdd1243dSDimitry Andric // important. 864bdd1243dSDimitry Andric ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); 865bdd1243dSDimitry Andric if (Error Err = ReadBundleEnd(Input)) 866bdd1243dSDimitry Andric return Err; 867bdd1243dSDimitry Andric return Error::success(); 868bdd1243dSDimitry Andric } 869bdd1243dSDimitry Andric }; 870bdd1243dSDimitry Andric } // namespace 871bdd1243dSDimitry Andric 872bdd1243dSDimitry Andric /// Return an appropriate object file handler. We use the specific object 873bdd1243dSDimitry Andric /// handler if we know how to deal with that format, otherwise we use a default 874bdd1243dSDimitry Andric /// binary file handler. 875bdd1243dSDimitry Andric static std::unique_ptr<FileHandler> 876bdd1243dSDimitry Andric CreateObjectFileHandler(MemoryBuffer &FirstInput, 877bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 878bdd1243dSDimitry Andric // Check if the input file format is one that we know how to deal with. 879bdd1243dSDimitry Andric Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); 880bdd1243dSDimitry Andric 881bdd1243dSDimitry Andric // We only support regular object files. If failed to open the input as a 882bdd1243dSDimitry Andric // known binary or this is not an object file use the default binary handler. 883bdd1243dSDimitry Andric if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) 884bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 885bdd1243dSDimitry Andric 886bdd1243dSDimitry Andric // Otherwise create an object file handler. The handler will be owned by the 887bdd1243dSDimitry Andric // client of this function. 888bdd1243dSDimitry Andric return std::make_unique<ObjectFileHandler>( 889bdd1243dSDimitry Andric std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())), 890bdd1243dSDimitry Andric BundlerConfig); 891bdd1243dSDimitry Andric } 892bdd1243dSDimitry Andric 893bdd1243dSDimitry Andric /// Return an appropriate handler given the input files and options. 894bdd1243dSDimitry Andric static Expected<std::unique_ptr<FileHandler>> 895bdd1243dSDimitry Andric CreateFileHandler(MemoryBuffer &FirstInput, 896bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 897bdd1243dSDimitry Andric std::string FilesType = BundlerConfig.FilesType; 898bdd1243dSDimitry Andric 899bdd1243dSDimitry Andric if (FilesType == "i") 900bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 901bdd1243dSDimitry Andric if (FilesType == "ii") 902bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 903bdd1243dSDimitry Andric if (FilesType == "cui") 904bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 905bdd1243dSDimitry Andric if (FilesType == "hipi") 906bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 907bdd1243dSDimitry Andric // TODO: `.d` should be eventually removed once `-M` and its variants are 908bdd1243dSDimitry Andric // handled properly in offload compilation. 909bdd1243dSDimitry Andric if (FilesType == "d") 910bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 911bdd1243dSDimitry Andric if (FilesType == "ll") 912bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/";"); 913bdd1243dSDimitry Andric if (FilesType == "bc") 914bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 915bdd1243dSDimitry Andric if (FilesType == "s") 916bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 917bdd1243dSDimitry Andric if (FilesType == "o") 918bdd1243dSDimitry Andric return CreateObjectFileHandler(FirstInput, BundlerConfig); 919bdd1243dSDimitry Andric if (FilesType == "a") 920bdd1243dSDimitry Andric return CreateObjectFileHandler(FirstInput, BundlerConfig); 921bdd1243dSDimitry Andric if (FilesType == "gch") 922bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 923bdd1243dSDimitry Andric if (FilesType == "ast") 924bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 925bdd1243dSDimitry Andric 926bdd1243dSDimitry Andric return createStringError(errc::invalid_argument, 927bdd1243dSDimitry Andric "'" + FilesType + "': invalid file type specified"); 928bdd1243dSDimitry Andric } 929bdd1243dSDimitry Andric 9305f757f3fSDimitry Andric OffloadBundlerConfig::OffloadBundlerConfig() { 931*0fca6ea1SDimitry Andric if (llvm::compression::zstd::isAvailable()) { 932*0fca6ea1SDimitry Andric CompressionFormat = llvm::compression::Format::Zstd; 933*0fca6ea1SDimitry Andric // Compression level 3 is usually sufficient for zstd since long distance 934*0fca6ea1SDimitry Andric // matching is enabled. 935*0fca6ea1SDimitry Andric CompressionLevel = 3; 936*0fca6ea1SDimitry Andric } else if (llvm::compression::zlib::isAvailable()) { 937*0fca6ea1SDimitry Andric CompressionFormat = llvm::compression::Format::Zlib; 938*0fca6ea1SDimitry Andric // Use default level for zlib since higher level does not have significant 939*0fca6ea1SDimitry Andric // improvement. 940*0fca6ea1SDimitry Andric CompressionLevel = llvm::compression::zlib::DefaultCompression; 941*0fca6ea1SDimitry Andric } 9425f757f3fSDimitry Andric auto IgnoreEnvVarOpt = 9435f757f3fSDimitry Andric llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR"); 9445f757f3fSDimitry Andric if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1") 9455f757f3fSDimitry Andric return; 9465f757f3fSDimitry Andric 9475f757f3fSDimitry Andric auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE"); 9485f757f3fSDimitry Andric if (VerboseEnvVarOpt.has_value()) 9495f757f3fSDimitry Andric Verbose = VerboseEnvVarOpt.value() == "1"; 9505f757f3fSDimitry Andric 9515f757f3fSDimitry Andric auto CompressEnvVarOpt = 9525f757f3fSDimitry Andric llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS"); 9535f757f3fSDimitry Andric if (CompressEnvVarOpt.has_value()) 9545f757f3fSDimitry Andric Compress = CompressEnvVarOpt.value() == "1"; 955*0fca6ea1SDimitry Andric 956*0fca6ea1SDimitry Andric auto CompressionLevelEnvVarOpt = 957*0fca6ea1SDimitry Andric llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL"); 958*0fca6ea1SDimitry Andric if (CompressionLevelEnvVarOpt.has_value()) { 959*0fca6ea1SDimitry Andric llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value(); 960*0fca6ea1SDimitry Andric int Level; 961*0fca6ea1SDimitry Andric if (!CompressionLevelStr.getAsInteger(10, Level)) 962*0fca6ea1SDimitry Andric CompressionLevel = Level; 963*0fca6ea1SDimitry Andric else 964*0fca6ea1SDimitry Andric llvm::errs() 965*0fca6ea1SDimitry Andric << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: " 966*0fca6ea1SDimitry Andric << CompressionLevelStr.str() << ". Ignoring it.\n"; 967*0fca6ea1SDimitry Andric } 968*0fca6ea1SDimitry Andric } 969*0fca6ea1SDimitry Andric 970*0fca6ea1SDimitry Andric // Utility function to format numbers with commas 971*0fca6ea1SDimitry Andric static std::string formatWithCommas(unsigned long long Value) { 972*0fca6ea1SDimitry Andric std::string Num = std::to_string(Value); 973*0fca6ea1SDimitry Andric int InsertPosition = Num.length() - 3; 974*0fca6ea1SDimitry Andric while (InsertPosition > 0) { 975*0fca6ea1SDimitry Andric Num.insert(InsertPosition, ","); 976*0fca6ea1SDimitry Andric InsertPosition -= 3; 977*0fca6ea1SDimitry Andric } 978*0fca6ea1SDimitry Andric return Num; 9795f757f3fSDimitry Andric } 9805f757f3fSDimitry Andric 9815f757f3fSDimitry Andric llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> 982*0fca6ea1SDimitry Andric CompressedOffloadBundle::compress(llvm::compression::Params P, 983*0fca6ea1SDimitry Andric const llvm::MemoryBuffer &Input, 9845f757f3fSDimitry Andric bool Verbose) { 985*0fca6ea1SDimitry Andric if (!llvm::compression::zstd::isAvailable() && 986*0fca6ea1SDimitry Andric !llvm::compression::zlib::isAvailable()) 987*0fca6ea1SDimitry Andric return createStringError(llvm::inconvertibleErrorCode(), 988*0fca6ea1SDimitry Andric "Compression not supported"); 989*0fca6ea1SDimitry Andric 9905f757f3fSDimitry Andric llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time", 9915f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 9925f757f3fSDimitry Andric if (Verbose) 9935f757f3fSDimitry Andric HashTimer.startTimer(); 9945f757f3fSDimitry Andric llvm::MD5 Hash; 9955f757f3fSDimitry Andric llvm::MD5::MD5Result Result; 9965f757f3fSDimitry Andric Hash.update(Input.getBuffer()); 9975f757f3fSDimitry Andric Hash.final(Result); 9985f757f3fSDimitry Andric uint64_t TruncatedHash = Result.low(); 9995f757f3fSDimitry Andric if (Verbose) 10005f757f3fSDimitry Andric HashTimer.stopTimer(); 10015f757f3fSDimitry Andric 10025f757f3fSDimitry Andric SmallVector<uint8_t, 0> CompressedBuffer; 10035f757f3fSDimitry Andric auto BufferUint8 = llvm::ArrayRef<uint8_t>( 10045f757f3fSDimitry Andric reinterpret_cast<const uint8_t *>(Input.getBuffer().data()), 10055f757f3fSDimitry Andric Input.getBuffer().size()); 10065f757f3fSDimitry Andric 10075f757f3fSDimitry Andric llvm::Timer CompressTimer("Compression Timer", "Compression time", 10085f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 10095f757f3fSDimitry Andric if (Verbose) 10105f757f3fSDimitry Andric CompressTimer.startTimer(); 1011*0fca6ea1SDimitry Andric llvm::compression::compress(P, BufferUint8, CompressedBuffer); 10125f757f3fSDimitry Andric if (Verbose) 10135f757f3fSDimitry Andric CompressTimer.stopTimer(); 10145f757f3fSDimitry Andric 1015*0fca6ea1SDimitry Andric uint16_t CompressionMethod = static_cast<uint16_t>(P.format); 10165f757f3fSDimitry Andric uint32_t UncompressedSize = Input.getBuffer().size(); 1017*0fca6ea1SDimitry Andric uint32_t TotalFileSize = MagicNumber.size() + sizeof(TotalFileSize) + 1018*0fca6ea1SDimitry Andric sizeof(Version) + sizeof(CompressionMethod) + 1019*0fca6ea1SDimitry Andric sizeof(UncompressedSize) + sizeof(TruncatedHash) + 1020*0fca6ea1SDimitry Andric CompressedBuffer.size(); 10215f757f3fSDimitry Andric 10225f757f3fSDimitry Andric SmallVector<char, 0> FinalBuffer; 10235f757f3fSDimitry Andric llvm::raw_svector_ostream OS(FinalBuffer); 10245f757f3fSDimitry Andric OS << MagicNumber; 10255f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version)); 10265f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&CompressionMethod), 10275f757f3fSDimitry Andric sizeof(CompressionMethod)); 1028*0fca6ea1SDimitry Andric OS.write(reinterpret_cast<const char *>(&TotalFileSize), 1029*0fca6ea1SDimitry Andric sizeof(TotalFileSize)); 10305f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&UncompressedSize), 10315f757f3fSDimitry Andric sizeof(UncompressedSize)); 10325f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&TruncatedHash), 10335f757f3fSDimitry Andric sizeof(TruncatedHash)); 10345f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()), 10355f757f3fSDimitry Andric CompressedBuffer.size()); 10365f757f3fSDimitry Andric 10375f757f3fSDimitry Andric if (Verbose) { 10385f757f3fSDimitry Andric auto MethodUsed = 1039*0fca6ea1SDimitry Andric P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib"; 1040*0fca6ea1SDimitry Andric double CompressionRate = 1041*0fca6ea1SDimitry Andric static_cast<double>(UncompressedSize) / CompressedBuffer.size(); 1042*0fca6ea1SDimitry Andric double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime(); 1043*0fca6ea1SDimitry Andric double CompressionSpeedMBs = 1044*0fca6ea1SDimitry Andric (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds; 1045*0fca6ea1SDimitry Andric 10465f757f3fSDimitry Andric llvm::errs() << "Compressed bundle format version: " << Version << "\n" 1047*0fca6ea1SDimitry Andric << "Total file size (including headers): " 1048*0fca6ea1SDimitry Andric << formatWithCommas(TotalFileSize) << " bytes\n" 10495f757f3fSDimitry Andric << "Compression method used: " << MethodUsed << "\n" 1050*0fca6ea1SDimitry Andric << "Compression level: " << P.level << "\n" 1051*0fca6ea1SDimitry Andric << "Binary size before compression: " 1052*0fca6ea1SDimitry Andric << formatWithCommas(UncompressedSize) << " bytes\n" 1053*0fca6ea1SDimitry Andric << "Binary size after compression: " 1054*0fca6ea1SDimitry Andric << formatWithCommas(CompressedBuffer.size()) << " bytes\n" 1055*0fca6ea1SDimitry Andric << "Compression rate: " 1056*0fca6ea1SDimitry Andric << llvm::format("%.2lf", CompressionRate) << "\n" 1057*0fca6ea1SDimitry Andric << "Compression ratio: " 1058*0fca6ea1SDimitry Andric << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n" 1059*0fca6ea1SDimitry Andric << "Compression speed: " 1060*0fca6ea1SDimitry Andric << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n" 10615f757f3fSDimitry Andric << "Truncated MD5 hash: " 10625f757f3fSDimitry Andric << llvm::format_hex(TruncatedHash, 16) << "\n"; 10635f757f3fSDimitry Andric } 10645f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy( 10655f757f3fSDimitry Andric llvm::StringRef(FinalBuffer.data(), FinalBuffer.size())); 10665f757f3fSDimitry Andric } 10675f757f3fSDimitry Andric 10685f757f3fSDimitry Andric llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> 10695f757f3fSDimitry Andric CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input, 10705f757f3fSDimitry Andric bool Verbose) { 10715f757f3fSDimitry Andric 10725f757f3fSDimitry Andric StringRef Blob = Input.getBuffer(); 10735f757f3fSDimitry Andric 1074*0fca6ea1SDimitry Andric if (Blob.size() < V1HeaderSize) 10755f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Blob); 1076*0fca6ea1SDimitry Andric 10775f757f3fSDimitry Andric if (llvm::identify_magic(Blob) != 10785f757f3fSDimitry Andric llvm::file_magic::offload_bundle_compressed) { 10795f757f3fSDimitry Andric if (Verbose) 10805f757f3fSDimitry Andric llvm::errs() << "Uncompressed bundle.\n"; 10815f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Blob); 10825f757f3fSDimitry Andric } 10835f757f3fSDimitry Andric 1084*0fca6ea1SDimitry Andric size_t CurrentOffset = MagicSize; 1085*0fca6ea1SDimitry Andric 10865f757f3fSDimitry Andric uint16_t ThisVersion; 1087*0fca6ea1SDimitry Andric memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t)); 1088*0fca6ea1SDimitry Andric CurrentOffset += VersionFieldSize; 1089*0fca6ea1SDimitry Andric 10905f757f3fSDimitry Andric uint16_t CompressionMethod; 1091*0fca6ea1SDimitry Andric memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t)); 1092*0fca6ea1SDimitry Andric CurrentOffset += MethodFieldSize; 1093*0fca6ea1SDimitry Andric 1094*0fca6ea1SDimitry Andric uint32_t TotalFileSize; 1095*0fca6ea1SDimitry Andric if (ThisVersion >= 2) { 1096*0fca6ea1SDimitry Andric if (Blob.size() < V2HeaderSize) 1097*0fca6ea1SDimitry Andric return createStringError(inconvertibleErrorCode(), 1098*0fca6ea1SDimitry Andric "Compressed bundle header size too small"); 1099*0fca6ea1SDimitry Andric memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint32_t)); 1100*0fca6ea1SDimitry Andric CurrentOffset += FileSizeFieldSize; 1101*0fca6ea1SDimitry Andric } 1102*0fca6ea1SDimitry Andric 11035f757f3fSDimitry Andric uint32_t UncompressedSize; 1104*0fca6ea1SDimitry Andric memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t)); 1105*0fca6ea1SDimitry Andric CurrentOffset += UncompressedSizeFieldSize; 1106*0fca6ea1SDimitry Andric 11075f757f3fSDimitry Andric uint64_t StoredHash; 1108*0fca6ea1SDimitry Andric memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t)); 1109*0fca6ea1SDimitry Andric CurrentOffset += HashFieldSize; 11105f757f3fSDimitry Andric 11115f757f3fSDimitry Andric llvm::compression::Format CompressionFormat; 11125f757f3fSDimitry Andric if (CompressionMethod == 11135f757f3fSDimitry Andric static_cast<uint16_t>(llvm::compression::Format::Zlib)) 11145f757f3fSDimitry Andric CompressionFormat = llvm::compression::Format::Zlib; 11155f757f3fSDimitry Andric else if (CompressionMethod == 11165f757f3fSDimitry Andric static_cast<uint16_t>(llvm::compression::Format::Zstd)) 11175f757f3fSDimitry Andric CompressionFormat = llvm::compression::Format::Zstd; 11185f757f3fSDimitry Andric else 11195f757f3fSDimitry Andric return createStringError(inconvertibleErrorCode(), 11205f757f3fSDimitry Andric "Unknown compressing method"); 11215f757f3fSDimitry Andric 11225f757f3fSDimitry Andric llvm::Timer DecompressTimer("Decompression Timer", "Decompression time", 11235f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 11245f757f3fSDimitry Andric if (Verbose) 11255f757f3fSDimitry Andric DecompressTimer.startTimer(); 11265f757f3fSDimitry Andric 11275f757f3fSDimitry Andric SmallVector<uint8_t, 0> DecompressedData; 1128*0fca6ea1SDimitry Andric StringRef CompressedData = Blob.substr(CurrentOffset); 11295f757f3fSDimitry Andric if (llvm::Error DecompressionError = llvm::compression::decompress( 11305f757f3fSDimitry Andric CompressionFormat, llvm::arrayRefFromStringRef(CompressedData), 11315f757f3fSDimitry Andric DecompressedData, UncompressedSize)) 11325f757f3fSDimitry Andric return createStringError(inconvertibleErrorCode(), 11335f757f3fSDimitry Andric "Could not decompress embedded file contents: " + 11345f757f3fSDimitry Andric llvm::toString(std::move(DecompressionError))); 11355f757f3fSDimitry Andric 11365f757f3fSDimitry Andric if (Verbose) { 11375f757f3fSDimitry Andric DecompressTimer.stopTimer(); 11385f757f3fSDimitry Andric 1139*0fca6ea1SDimitry Andric double DecompressionTimeSeconds = 1140*0fca6ea1SDimitry Andric DecompressTimer.getTotalTime().getWallTime(); 1141*0fca6ea1SDimitry Andric 1142*0fca6ea1SDimitry Andric // Recalculate MD5 hash for integrity check 11435f757f3fSDimitry Andric llvm::Timer HashRecalcTimer("Hash Recalculation Timer", 11445f757f3fSDimitry Andric "Hash recalculation time", 11455f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 11465f757f3fSDimitry Andric HashRecalcTimer.startTimer(); 11475f757f3fSDimitry Andric llvm::MD5 Hash; 11485f757f3fSDimitry Andric llvm::MD5::MD5Result Result; 11495f757f3fSDimitry Andric Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData.data(), 11505f757f3fSDimitry Andric DecompressedData.size())); 11515f757f3fSDimitry Andric Hash.final(Result); 11525f757f3fSDimitry Andric uint64_t RecalculatedHash = Result.low(); 11535f757f3fSDimitry Andric HashRecalcTimer.stopTimer(); 11545f757f3fSDimitry Andric bool HashMatch = (StoredHash == RecalculatedHash); 11555f757f3fSDimitry Andric 1156*0fca6ea1SDimitry Andric double CompressionRate = 1157*0fca6ea1SDimitry Andric static_cast<double>(UncompressedSize) / CompressedData.size(); 1158*0fca6ea1SDimitry Andric double DecompressionSpeedMBs = 1159*0fca6ea1SDimitry Andric (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds; 1160*0fca6ea1SDimitry Andric 1161*0fca6ea1SDimitry Andric llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n"; 1162*0fca6ea1SDimitry Andric if (ThisVersion >= 2) 1163*0fca6ea1SDimitry Andric llvm::errs() << "Total file size (from header): " 1164*0fca6ea1SDimitry Andric << formatWithCommas(TotalFileSize) << " bytes\n"; 1165*0fca6ea1SDimitry Andric llvm::errs() << "Decompression method: " 11665f757f3fSDimitry Andric << (CompressionFormat == llvm::compression::Format::Zlib 11675f757f3fSDimitry Andric ? "zlib" 11685f757f3fSDimitry Andric : "zstd") 11695f757f3fSDimitry Andric << "\n" 1170*0fca6ea1SDimitry Andric << "Size before decompression: " 1171*0fca6ea1SDimitry Andric << formatWithCommas(CompressedData.size()) << " bytes\n" 1172*0fca6ea1SDimitry Andric << "Size after decompression: " 1173*0fca6ea1SDimitry Andric << formatWithCommas(UncompressedSize) << " bytes\n" 1174*0fca6ea1SDimitry Andric << "Compression rate: " 1175*0fca6ea1SDimitry Andric << llvm::format("%.2lf", CompressionRate) << "\n" 1176*0fca6ea1SDimitry Andric << "Compression ratio: " 1177*0fca6ea1SDimitry Andric << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n" 1178*0fca6ea1SDimitry Andric << "Decompression speed: " 1179*0fca6ea1SDimitry Andric << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n" 11805f757f3fSDimitry Andric << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n" 11815f757f3fSDimitry Andric << "Recalculated hash: " 11825f757f3fSDimitry Andric << llvm::format_hex(RecalculatedHash, 16) << "\n" 11835f757f3fSDimitry Andric << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n"; 11845f757f3fSDimitry Andric } 11855f757f3fSDimitry Andric 11865f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy( 11875f757f3fSDimitry Andric llvm::toStringRef(DecompressedData)); 11885f757f3fSDimitry Andric } 11895f757f3fSDimitry Andric 1190bdd1243dSDimitry Andric // List bundle IDs. Return true if an error was found. 1191bdd1243dSDimitry Andric Error OffloadBundler::ListBundleIDsInFile( 1192bdd1243dSDimitry Andric StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) { 1193bdd1243dSDimitry Andric // Open Input file. 1194bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1195bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(InputFileName); 1196bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 1197bdd1243dSDimitry Andric return createFileError(InputFileName, EC); 1198bdd1243dSDimitry Andric 11995f757f3fSDimitry Andric // Decompress the input if necessary. 12005f757f3fSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 12015f757f3fSDimitry Andric CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose); 12025f757f3fSDimitry Andric if (!DecompressedBufferOrErr) 12035f757f3fSDimitry Andric return createStringError( 12045f757f3fSDimitry Andric inconvertibleErrorCode(), 12055f757f3fSDimitry Andric "Failed to decompress input: " + 12065f757f3fSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError())); 12075f757f3fSDimitry Andric 12085f757f3fSDimitry Andric MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr; 1209bdd1243dSDimitry Andric 1210bdd1243dSDimitry Andric // Select the right files handler. 1211bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 12125f757f3fSDimitry Andric CreateFileHandler(DecompressedInput, BundlerConfig); 1213bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1214bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1215bdd1243dSDimitry Andric 1216bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1217bdd1243dSDimitry Andric assert(FH); 12185f757f3fSDimitry Andric return FH->listBundleIDs(DecompressedInput); 12195f757f3fSDimitry Andric } 12205f757f3fSDimitry Andric 12215f757f3fSDimitry Andric /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given 12225f757f3fSDimitry Andric /// target \p TargetInfo. 12235f757f3fSDimitry Andric /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id 12245f757f3fSDimitry Andric bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, 12255f757f3fSDimitry Andric const OffloadTargetInfo &TargetInfo) { 12265f757f3fSDimitry Andric 12275f757f3fSDimitry Andric // Compatible in case of exact match. 12285f757f3fSDimitry Andric if (CodeObjectInfo == TargetInfo) { 12295f757f3fSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 12305f757f3fSDimitry Andric dbgs() << "Compatible: Exact match: \t[CodeObject: " 12315f757f3fSDimitry Andric << CodeObjectInfo.str() 12325f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 12335f757f3fSDimitry Andric return true; 12345f757f3fSDimitry Andric } 12355f757f3fSDimitry Andric 12365f757f3fSDimitry Andric // Incompatible if Kinds or Triples mismatch. 12375f757f3fSDimitry Andric if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || 12385f757f3fSDimitry Andric !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { 12395f757f3fSDimitry Andric DEBUG_WITH_TYPE( 12405f757f3fSDimitry Andric "CodeObjectCompatibility", 12415f757f3fSDimitry Andric dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " 12425f757f3fSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 12435f757f3fSDimitry Andric << "]\n"); 12445f757f3fSDimitry Andric return false; 12455f757f3fSDimitry Andric } 12465f757f3fSDimitry Andric 12475f757f3fSDimitry Andric // Incompatible if Processors mismatch. 12485f757f3fSDimitry Andric llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap; 12495f757f3fSDimitry Andric std::optional<StringRef> CodeObjectProc = clang::parseTargetID( 12505f757f3fSDimitry Andric CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap); 12515f757f3fSDimitry Andric std::optional<StringRef> TargetProc = clang::parseTargetID( 12525f757f3fSDimitry Andric TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap); 12535f757f3fSDimitry Andric 12545f757f3fSDimitry Andric // Both TargetProc and CodeObjectProc can't be empty here. 12555f757f3fSDimitry Andric if (!TargetProc || !CodeObjectProc || 12565f757f3fSDimitry Andric CodeObjectProc.value() != TargetProc.value()) { 12575f757f3fSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 12585f757f3fSDimitry Andric dbgs() << "Incompatible: Processor mismatch \t[CodeObject: " 12595f757f3fSDimitry Andric << CodeObjectInfo.str() 12605f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 12615f757f3fSDimitry Andric return false; 12625f757f3fSDimitry Andric } 12635f757f3fSDimitry Andric 12645f757f3fSDimitry Andric // Incompatible if CodeObject has more features than Target, irrespective of 12655f757f3fSDimitry Andric // type or sign of features. 12665f757f3fSDimitry Andric if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) { 12675f757f3fSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 12685f757f3fSDimitry Andric dbgs() << "Incompatible: CodeObject has more features " 12695f757f3fSDimitry Andric "than target \t[CodeObject: " 12705f757f3fSDimitry Andric << CodeObjectInfo.str() 12715f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 12725f757f3fSDimitry Andric return false; 12735f757f3fSDimitry Andric } 12745f757f3fSDimitry Andric 12755f757f3fSDimitry Andric // Compatible if each target feature specified by target is compatible with 12765f757f3fSDimitry Andric // target feature of code object. The target feature is compatible if the 12775f757f3fSDimitry Andric // code object does not specify it (meaning Any), or if it specifies it 12785f757f3fSDimitry Andric // with the same value (meaning On or Off). 12795f757f3fSDimitry Andric for (const auto &CodeObjectFeature : CodeObjectFeatureMap) { 12805f757f3fSDimitry Andric auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey()); 12815f757f3fSDimitry Andric if (TargetFeature == TargetFeatureMap.end()) { 12825f757f3fSDimitry Andric DEBUG_WITH_TYPE( 12835f757f3fSDimitry Andric "CodeObjectCompatibility", 12845f757f3fSDimitry Andric dbgs() 12855f757f3fSDimitry Andric << "Incompatible: Value of CodeObject's non-ANY feature is " 12865f757f3fSDimitry Andric "not matching with Target feature's ANY value \t[CodeObject: " 12875f757f3fSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 12885f757f3fSDimitry Andric << "]\n"); 12895f757f3fSDimitry Andric return false; 12905f757f3fSDimitry Andric } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) { 12915f757f3fSDimitry Andric DEBUG_WITH_TYPE( 12925f757f3fSDimitry Andric "CodeObjectCompatibility", 12935f757f3fSDimitry Andric dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is " 12945f757f3fSDimitry Andric "not matching with Target feature's non-ANY value " 12955f757f3fSDimitry Andric "\t[CodeObject: " 12965f757f3fSDimitry Andric << CodeObjectInfo.str() 12975f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 12985f757f3fSDimitry Andric return false; 12995f757f3fSDimitry Andric } 13005f757f3fSDimitry Andric } 13015f757f3fSDimitry Andric 13025f757f3fSDimitry Andric // CodeObject is compatible if all features of Target are: 13035f757f3fSDimitry Andric // - either, present in the Code Object's features map with the same sign, 13045f757f3fSDimitry Andric // - or, the feature is missing from CodeObjects's features map i.e. it is 13055f757f3fSDimitry Andric // set to ANY 13065f757f3fSDimitry Andric DEBUG_WITH_TYPE( 13075f757f3fSDimitry Andric "CodeObjectCompatibility", 13085f757f3fSDimitry Andric dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: " 13095f757f3fSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 13105f757f3fSDimitry Andric << "]\n"); 13115f757f3fSDimitry Andric return true; 1312bdd1243dSDimitry Andric } 1313bdd1243dSDimitry Andric 1314bdd1243dSDimitry Andric /// Bundle the files. Return true if an error was found. 1315bdd1243dSDimitry Andric Error OffloadBundler::BundleFiles() { 1316bdd1243dSDimitry Andric std::error_code EC; 1317bdd1243dSDimitry Andric 13185f757f3fSDimitry Andric // Create a buffer to hold the content before compressing. 13195f757f3fSDimitry Andric SmallVector<char, 0> Buffer; 13205f757f3fSDimitry Andric llvm::raw_svector_ostream BufferStream(Buffer); 1321bdd1243dSDimitry Andric 1322bdd1243dSDimitry Andric // Open input files. 1323bdd1243dSDimitry Andric SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; 1324bdd1243dSDimitry Andric InputBuffers.reserve(BundlerConfig.InputFileNames.size()); 1325bdd1243dSDimitry Andric for (auto &I : BundlerConfig.InputFileNames) { 1326bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1327bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(I); 1328bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 1329bdd1243dSDimitry Andric return createFileError(I, EC); 1330bdd1243dSDimitry Andric InputBuffers.emplace_back(std::move(*CodeOrErr)); 1331bdd1243dSDimitry Andric } 1332bdd1243dSDimitry Andric 1333bdd1243dSDimitry Andric // Get the file handler. We use the host buffer as reference. 1334bdd1243dSDimitry Andric assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) && 1335bdd1243dSDimitry Andric "Host input index undefined??"); 1336bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler( 1337bdd1243dSDimitry Andric *InputBuffers[BundlerConfig.AllowNoHost ? 0 1338bdd1243dSDimitry Andric : BundlerConfig.HostInputIndex], 1339bdd1243dSDimitry Andric BundlerConfig); 1340bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1341bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1342bdd1243dSDimitry Andric 1343bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1344bdd1243dSDimitry Andric assert(FH); 1345bdd1243dSDimitry Andric 1346bdd1243dSDimitry Andric // Write header. 13475f757f3fSDimitry Andric if (Error Err = FH->WriteHeader(BufferStream, InputBuffers)) 1348bdd1243dSDimitry Andric return Err; 1349bdd1243dSDimitry Andric 1350bdd1243dSDimitry Andric // Write all bundles along with the start/end markers. If an error was found 1351bdd1243dSDimitry Andric // writing the end of the bundle component, abort the bundle writing. 1352bdd1243dSDimitry Andric auto Input = InputBuffers.begin(); 1353bdd1243dSDimitry Andric for (auto &Triple : BundlerConfig.TargetNames) { 13545f757f3fSDimitry Andric if (Error Err = FH->WriteBundleStart(BufferStream, Triple)) 1355bdd1243dSDimitry Andric return Err; 13565f757f3fSDimitry Andric if (Error Err = FH->WriteBundle(BufferStream, **Input)) 1357bdd1243dSDimitry Andric return Err; 13585f757f3fSDimitry Andric if (Error Err = FH->WriteBundleEnd(BufferStream, Triple)) 1359bdd1243dSDimitry Andric return Err; 1360bdd1243dSDimitry Andric ++Input; 1361bdd1243dSDimitry Andric } 13625f757f3fSDimitry Andric 13635f757f3fSDimitry Andric raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC, 13645f757f3fSDimitry Andric sys::fs::OF_None); 13655f757f3fSDimitry Andric if (EC) 13665f757f3fSDimitry Andric return createFileError(BundlerConfig.OutputFileNames.front(), EC); 13675f757f3fSDimitry Andric 13685f757f3fSDimitry Andric SmallVector<char, 0> CompressedBuffer; 13695f757f3fSDimitry Andric if (BundlerConfig.Compress) { 13705f757f3fSDimitry Andric std::unique_ptr<llvm::MemoryBuffer> BufferMemory = 13715f757f3fSDimitry Andric llvm::MemoryBuffer::getMemBufferCopy( 13725f757f3fSDimitry Andric llvm::StringRef(Buffer.data(), Buffer.size())); 1373*0fca6ea1SDimitry Andric auto CompressionResult = CompressedOffloadBundle::compress( 1374*0fca6ea1SDimitry Andric {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel, 1375*0fca6ea1SDimitry Andric /*zstdEnableLdm=*/true}, 1376*0fca6ea1SDimitry Andric *BufferMemory, BundlerConfig.Verbose); 13775f757f3fSDimitry Andric if (auto Error = CompressionResult.takeError()) 13785f757f3fSDimitry Andric return Error; 13795f757f3fSDimitry Andric 13805f757f3fSDimitry Andric auto CompressedMemBuffer = std::move(CompressionResult.get()); 13815f757f3fSDimitry Andric CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(), 13825f757f3fSDimitry Andric CompressedMemBuffer->getBufferEnd()); 13835f757f3fSDimitry Andric } else 13845f757f3fSDimitry Andric CompressedBuffer = Buffer; 13855f757f3fSDimitry Andric 13865f757f3fSDimitry Andric OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size()); 13875f757f3fSDimitry Andric 13885f757f3fSDimitry Andric return FH->finalizeOutputFile(); 1389bdd1243dSDimitry Andric } 1390bdd1243dSDimitry Andric 1391bdd1243dSDimitry Andric // Unbundle the files. Return true if an error was found. 1392bdd1243dSDimitry Andric Error OffloadBundler::UnbundleFiles() { 1393bdd1243dSDimitry Andric // Open Input file. 1394bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1395bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front()); 1396bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 1397bdd1243dSDimitry Andric return createFileError(BundlerConfig.InputFileNames.front(), EC); 1398bdd1243dSDimitry Andric 13995f757f3fSDimitry Andric // Decompress the input if necessary. 14005f757f3fSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 14015f757f3fSDimitry Andric CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose); 14025f757f3fSDimitry Andric if (!DecompressedBufferOrErr) 14035f757f3fSDimitry Andric return createStringError( 14045f757f3fSDimitry Andric inconvertibleErrorCode(), 14055f757f3fSDimitry Andric "Failed to decompress input: " + 14065f757f3fSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError())); 14075f757f3fSDimitry Andric 14085f757f3fSDimitry Andric MemoryBuffer &Input = **DecompressedBufferOrErr; 1409bdd1243dSDimitry Andric 1410bdd1243dSDimitry Andric // Select the right files handler. 1411bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1412bdd1243dSDimitry Andric CreateFileHandler(Input, BundlerConfig); 1413bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1414bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1415bdd1243dSDimitry Andric 1416bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1417bdd1243dSDimitry Andric assert(FH); 1418bdd1243dSDimitry Andric 1419bdd1243dSDimitry Andric // Read the header of the bundled file. 1420bdd1243dSDimitry Andric if (Error Err = FH->ReadHeader(Input)) 1421bdd1243dSDimitry Andric return Err; 1422bdd1243dSDimitry Andric 1423bdd1243dSDimitry Andric // Create a work list that consist of the map triple/output file. 1424bdd1243dSDimitry Andric StringMap<StringRef> Worklist; 1425bdd1243dSDimitry Andric auto Output = BundlerConfig.OutputFileNames.begin(); 1426bdd1243dSDimitry Andric for (auto &Triple : BundlerConfig.TargetNames) { 1427bdd1243dSDimitry Andric Worklist[Triple] = *Output; 1428bdd1243dSDimitry Andric ++Output; 1429bdd1243dSDimitry Andric } 1430bdd1243dSDimitry Andric 1431bdd1243dSDimitry Andric // Read all the bundles that are in the work list. If we find no bundles we 1432bdd1243dSDimitry Andric // assume the file is meant for the host target. 1433bdd1243dSDimitry Andric bool FoundHostBundle = false; 1434bdd1243dSDimitry Andric while (!Worklist.empty()) { 1435bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurTripleOrErr = 1436bdd1243dSDimitry Andric FH->ReadBundleStart(Input); 1437bdd1243dSDimitry Andric if (!CurTripleOrErr) 1438bdd1243dSDimitry Andric return CurTripleOrErr.takeError(); 1439bdd1243dSDimitry Andric 1440bdd1243dSDimitry Andric // We don't have more bundles. 1441bdd1243dSDimitry Andric if (!*CurTripleOrErr) 1442bdd1243dSDimitry Andric break; 1443bdd1243dSDimitry Andric 1444bdd1243dSDimitry Andric StringRef CurTriple = **CurTripleOrErr; 1445bdd1243dSDimitry Andric assert(!CurTriple.empty()); 1446bdd1243dSDimitry Andric 1447bdd1243dSDimitry Andric auto Output = Worklist.begin(); 1448bdd1243dSDimitry Andric for (auto E = Worklist.end(); Output != E; Output++) { 1449bdd1243dSDimitry Andric if (isCodeObjectCompatible( 1450bdd1243dSDimitry Andric OffloadTargetInfo(CurTriple, BundlerConfig), 1451bdd1243dSDimitry Andric OffloadTargetInfo((*Output).first(), BundlerConfig))) { 1452bdd1243dSDimitry Andric break; 1453bdd1243dSDimitry Andric } 1454bdd1243dSDimitry Andric } 1455bdd1243dSDimitry Andric 1456bdd1243dSDimitry Andric if (Output == Worklist.end()) 1457bdd1243dSDimitry Andric continue; 1458bdd1243dSDimitry Andric // Check if the output file can be opened and copy the bundle to it. 1459bdd1243dSDimitry Andric std::error_code EC; 1460bdd1243dSDimitry Andric raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); 1461bdd1243dSDimitry Andric if (EC) 1462bdd1243dSDimitry Andric return createFileError((*Output).second, EC); 1463bdd1243dSDimitry Andric if (Error Err = FH->ReadBundle(OutputFile, Input)) 1464bdd1243dSDimitry Andric return Err; 1465bdd1243dSDimitry Andric if (Error Err = FH->ReadBundleEnd(Input)) 1466bdd1243dSDimitry Andric return Err; 1467bdd1243dSDimitry Andric Worklist.erase(Output); 1468bdd1243dSDimitry Andric 1469bdd1243dSDimitry Andric // Record if we found the host bundle. 1470bdd1243dSDimitry Andric auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig); 1471bdd1243dSDimitry Andric if (OffloadInfo.hasHostKind()) 1472bdd1243dSDimitry Andric FoundHostBundle = true; 1473bdd1243dSDimitry Andric } 1474bdd1243dSDimitry Andric 1475bdd1243dSDimitry Andric if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) { 1476bdd1243dSDimitry Andric std::string ErrMsg = "Can't find bundles for"; 1477bdd1243dSDimitry Andric std::set<StringRef> Sorted; 1478bdd1243dSDimitry Andric for (auto &E : Worklist) 1479bdd1243dSDimitry Andric Sorted.insert(E.first()); 1480bdd1243dSDimitry Andric unsigned I = 0; 1481bdd1243dSDimitry Andric unsigned Last = Sorted.size() - 1; 1482bdd1243dSDimitry Andric for (auto &E : Sorted) { 1483bdd1243dSDimitry Andric if (I != 0 && Last > 1) 1484bdd1243dSDimitry Andric ErrMsg += ","; 1485bdd1243dSDimitry Andric ErrMsg += " "; 1486bdd1243dSDimitry Andric if (I == Last && I != 0) 1487bdd1243dSDimitry Andric ErrMsg += "and "; 1488bdd1243dSDimitry Andric ErrMsg += E.str(); 1489bdd1243dSDimitry Andric ++I; 1490bdd1243dSDimitry Andric } 1491bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1492bdd1243dSDimitry Andric } 1493bdd1243dSDimitry Andric 1494bdd1243dSDimitry Andric // If no bundles were found, assume the input file is the host bundle and 1495bdd1243dSDimitry Andric // create empty files for the remaining targets. 1496bdd1243dSDimitry Andric if (Worklist.size() == BundlerConfig.TargetNames.size()) { 1497bdd1243dSDimitry Andric for (auto &E : Worklist) { 1498bdd1243dSDimitry Andric std::error_code EC; 1499bdd1243dSDimitry Andric raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1500bdd1243dSDimitry Andric if (EC) 1501bdd1243dSDimitry Andric return createFileError(E.second, EC); 1502bdd1243dSDimitry Andric 1503bdd1243dSDimitry Andric // If this entry has a host kind, copy the input file to the output file. 1504bdd1243dSDimitry Andric auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig); 1505bdd1243dSDimitry Andric if (OffloadInfo.hasHostKind()) 1506bdd1243dSDimitry Andric OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); 1507bdd1243dSDimitry Andric } 1508bdd1243dSDimitry Andric return Error::success(); 1509bdd1243dSDimitry Andric } 1510bdd1243dSDimitry Andric 1511bdd1243dSDimitry Andric // If we found elements, we emit an error if none of those were for the host 1512bdd1243dSDimitry Andric // in case host bundle name was provided in command line. 1513bdd1243dSDimitry Andric if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u || 1514bdd1243dSDimitry Andric BundlerConfig.AllowMissingBundles)) 1515bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), 1516bdd1243dSDimitry Andric "Can't find bundle for the host target"); 1517bdd1243dSDimitry Andric 1518bdd1243dSDimitry Andric // If we still have any elements in the worklist, create empty files for them. 1519bdd1243dSDimitry Andric for (auto &E : Worklist) { 1520bdd1243dSDimitry Andric std::error_code EC; 1521bdd1243dSDimitry Andric raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1522bdd1243dSDimitry Andric if (EC) 1523bdd1243dSDimitry Andric return createFileError(E.second, EC); 1524bdd1243dSDimitry Andric } 1525bdd1243dSDimitry Andric 1526bdd1243dSDimitry Andric return Error::success(); 1527bdd1243dSDimitry Andric } 1528bdd1243dSDimitry Andric 1529bdd1243dSDimitry Andric static Archive::Kind getDefaultArchiveKindForHost() { 1530bdd1243dSDimitry Andric return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN 1531bdd1243dSDimitry Andric : Archive::K_GNU; 1532bdd1243dSDimitry Andric } 1533bdd1243dSDimitry Andric 1534bdd1243dSDimitry Andric /// @brief Computes a list of targets among all given targets which are 1535bdd1243dSDimitry Andric /// compatible with this code object 1536bdd1243dSDimitry Andric /// @param [in] CodeObjectInfo Code Object 1537bdd1243dSDimitry Andric /// @param [out] CompatibleTargets List of all compatible targets among all 1538bdd1243dSDimitry Andric /// given targets 1539bdd1243dSDimitry Andric /// @return false, if no compatible target is found. 1540bdd1243dSDimitry Andric static bool 1541bdd1243dSDimitry Andric getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, 1542bdd1243dSDimitry Andric SmallVectorImpl<StringRef> &CompatibleTargets, 1543bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 1544bdd1243dSDimitry Andric if (!CompatibleTargets.empty()) { 1545bdd1243dSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 1546bdd1243dSDimitry Andric dbgs() << "CompatibleTargets list should be empty\n"); 1547bdd1243dSDimitry Andric return false; 1548bdd1243dSDimitry Andric } 1549bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1550bdd1243dSDimitry Andric auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig); 1551bdd1243dSDimitry Andric if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) 1552bdd1243dSDimitry Andric CompatibleTargets.push_back(Target); 1553bdd1243dSDimitry Andric } 1554bdd1243dSDimitry Andric return !CompatibleTargets.empty(); 1555bdd1243dSDimitry Andric } 1556bdd1243dSDimitry Andric 15575f757f3fSDimitry Andric // Check that each code object file in the input archive conforms to following 15585f757f3fSDimitry Andric // rule: for a specific processor, a feature either shows up in all target IDs, 15595f757f3fSDimitry Andric // or does not show up in any target IDs. Otherwise the target ID combination is 15605f757f3fSDimitry Andric // invalid. 15615f757f3fSDimitry Andric static Error 15625f757f3fSDimitry Andric CheckHeterogeneousArchive(StringRef ArchiveName, 15635f757f3fSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 15645f757f3fSDimitry Andric std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 15655f757f3fSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 15665f757f3fSDimitry Andric MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false); 15675f757f3fSDimitry Andric if (std::error_code EC = BufOrErr.getError()) 15685f757f3fSDimitry Andric return createFileError(ArchiveName, EC); 15695f757f3fSDimitry Andric 15705f757f3fSDimitry Andric ArchiveBuffers.push_back(std::move(*BufOrErr)); 15715f757f3fSDimitry Andric Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 15725f757f3fSDimitry Andric Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 15735f757f3fSDimitry Andric if (!LibOrErr) 15745f757f3fSDimitry Andric return LibOrErr.takeError(); 15755f757f3fSDimitry Andric 15765f757f3fSDimitry Andric auto Archive = std::move(*LibOrErr); 15775f757f3fSDimitry Andric 15785f757f3fSDimitry Andric Error ArchiveErr = Error::success(); 15795f757f3fSDimitry Andric auto ChildEnd = Archive->child_end(); 15805f757f3fSDimitry Andric 15815f757f3fSDimitry Andric /// Iterate over all bundled code object files in the input archive. 15825f757f3fSDimitry Andric for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 15835f757f3fSDimitry Andric ArchiveIter != ChildEnd; ++ArchiveIter) { 15845f757f3fSDimitry Andric if (ArchiveErr) 15855f757f3fSDimitry Andric return ArchiveErr; 15865f757f3fSDimitry Andric auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 15875f757f3fSDimitry Andric if (!ArchiveChildNameOrErr) 15885f757f3fSDimitry Andric return ArchiveChildNameOrErr.takeError(); 15895f757f3fSDimitry Andric 15905f757f3fSDimitry Andric auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 15915f757f3fSDimitry Andric if (!CodeObjectBufferRefOrErr) 15925f757f3fSDimitry Andric return CodeObjectBufferRefOrErr.takeError(); 15935f757f3fSDimitry Andric 15945f757f3fSDimitry Andric auto CodeObjectBuffer = 15955f757f3fSDimitry Andric MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 15965f757f3fSDimitry Andric 15975f757f3fSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 15985f757f3fSDimitry Andric CreateFileHandler(*CodeObjectBuffer, BundlerConfig); 15995f757f3fSDimitry Andric if (!FileHandlerOrErr) 16005f757f3fSDimitry Andric return FileHandlerOrErr.takeError(); 16015f757f3fSDimitry Andric 16025f757f3fSDimitry Andric std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 16035f757f3fSDimitry Andric assert(FileHandler); 16045f757f3fSDimitry Andric 16055f757f3fSDimitry Andric std::set<StringRef> BundleIds; 16065f757f3fSDimitry Andric auto CodeObjectFileError = 16075f757f3fSDimitry Andric FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds); 16085f757f3fSDimitry Andric if (CodeObjectFileError) 16095f757f3fSDimitry Andric return CodeObjectFileError; 16105f757f3fSDimitry Andric 16115f757f3fSDimitry Andric auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds); 16125f757f3fSDimitry Andric if (ConflictingArchs) { 16135f757f3fSDimitry Andric std::string ErrMsg = 16145f757f3fSDimitry Andric Twine("conflicting TargetIDs [" + ConflictingArchs.value().first + 16155f757f3fSDimitry Andric ", " + ConflictingArchs.value().second + "] found in " + 16165f757f3fSDimitry Andric ArchiveChildNameOrErr.get() + " of " + ArchiveName) 16175f757f3fSDimitry Andric .str(); 16185f757f3fSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 16195f757f3fSDimitry Andric } 16205f757f3fSDimitry Andric } 16215f757f3fSDimitry Andric 16225f757f3fSDimitry Andric return ArchiveErr; 16235f757f3fSDimitry Andric } 16245f757f3fSDimitry Andric 1625bdd1243dSDimitry Andric /// UnbundleArchive takes an archive file (".a") as input containing bundled 1626bdd1243dSDimitry Andric /// code object files, and a list of offload targets (not host), and extracts 1627bdd1243dSDimitry Andric /// the code objects into a new archive file for each offload target. Each 1628bdd1243dSDimitry Andric /// resulting archive file contains all code object files corresponding to that 1629bdd1243dSDimitry Andric /// particular offload target. The created archive file does not 1630bdd1243dSDimitry Andric /// contain an index of the symbols and code object files are named as 16315f757f3fSDimitry Andric /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'. 1632bdd1243dSDimitry Andric Error OffloadBundler::UnbundleArchive() { 1633bdd1243dSDimitry Andric std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 1634bdd1243dSDimitry Andric 1635bdd1243dSDimitry Andric /// Map of target names with list of object files that will form the device 1636bdd1243dSDimitry Andric /// specific archive for that target 1637bdd1243dSDimitry Andric StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; 1638bdd1243dSDimitry Andric 1639bdd1243dSDimitry Andric // Map of target names and output archive filenames 1640bdd1243dSDimitry Andric StringMap<StringRef> TargetOutputFileNameMap; 1641bdd1243dSDimitry Andric 1642bdd1243dSDimitry Andric auto Output = BundlerConfig.OutputFileNames.begin(); 1643bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1644bdd1243dSDimitry Andric TargetOutputFileNameMap[Target] = *Output; 1645bdd1243dSDimitry Andric ++Output; 1646bdd1243dSDimitry Andric } 1647bdd1243dSDimitry Andric 1648bdd1243dSDimitry Andric StringRef IFName = BundlerConfig.InputFileNames.front(); 1649bdd1243dSDimitry Andric 16505f757f3fSDimitry Andric if (BundlerConfig.CheckInputArchive) { 16515f757f3fSDimitry Andric // For a specific processor, a feature either shows up in all target IDs, or 16525f757f3fSDimitry Andric // does not show up in any target IDs. Otherwise the target ID combination 16535f757f3fSDimitry Andric // is invalid. 16545f757f3fSDimitry Andric auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig); 16555f757f3fSDimitry Andric if (ArchiveError) { 16565f757f3fSDimitry Andric return ArchiveError; 16575f757f3fSDimitry Andric } 16585f757f3fSDimitry Andric } 16595f757f3fSDimitry Andric 1660bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 1661bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(IFName, true, false); 1662bdd1243dSDimitry Andric if (std::error_code EC = BufOrErr.getError()) 1663bdd1243dSDimitry Andric return createFileError(BundlerConfig.InputFileNames.front(), EC); 1664bdd1243dSDimitry Andric 1665bdd1243dSDimitry Andric ArchiveBuffers.push_back(std::move(*BufOrErr)); 1666bdd1243dSDimitry Andric Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1667bdd1243dSDimitry Andric Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1668bdd1243dSDimitry Andric if (!LibOrErr) 1669bdd1243dSDimitry Andric return LibOrErr.takeError(); 1670bdd1243dSDimitry Andric 1671bdd1243dSDimitry Andric auto Archive = std::move(*LibOrErr); 1672bdd1243dSDimitry Andric 1673bdd1243dSDimitry Andric Error ArchiveErr = Error::success(); 1674bdd1243dSDimitry Andric auto ChildEnd = Archive->child_end(); 1675bdd1243dSDimitry Andric 1676bdd1243dSDimitry Andric /// Iterate over all bundled code object files in the input archive. 1677bdd1243dSDimitry Andric for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1678bdd1243dSDimitry Andric ArchiveIter != ChildEnd; ++ArchiveIter) { 1679bdd1243dSDimitry Andric if (ArchiveErr) 1680bdd1243dSDimitry Andric return ArchiveErr; 1681bdd1243dSDimitry Andric auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1682bdd1243dSDimitry Andric if (!ArchiveChildNameOrErr) 1683bdd1243dSDimitry Andric return ArchiveChildNameOrErr.takeError(); 1684bdd1243dSDimitry Andric 1685bdd1243dSDimitry Andric StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); 1686bdd1243dSDimitry Andric 1687bdd1243dSDimitry Andric auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1688bdd1243dSDimitry Andric if (!CodeObjectBufferRefOrErr) 1689bdd1243dSDimitry Andric return CodeObjectBufferRefOrErr.takeError(); 1690bdd1243dSDimitry Andric 16915f757f3fSDimitry Andric auto TempCodeObjectBuffer = 1692bdd1243dSDimitry Andric MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1693bdd1243dSDimitry Andric 16945f757f3fSDimitry Andric // Decompress the buffer if necessary. 16955f757f3fSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 16965f757f3fSDimitry Andric CompressedOffloadBundle::decompress(*TempCodeObjectBuffer, 16975f757f3fSDimitry Andric BundlerConfig.Verbose); 16985f757f3fSDimitry Andric if (!DecompressedBufferOrErr) 16995f757f3fSDimitry Andric return createStringError( 17005f757f3fSDimitry Andric inconvertibleErrorCode(), 17015f757f3fSDimitry Andric "Failed to decompress code object: " + 17025f757f3fSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError())); 17035f757f3fSDimitry Andric 17045f757f3fSDimitry Andric MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr; 17055f757f3fSDimitry Andric 1706bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 17075f757f3fSDimitry Andric CreateFileHandler(CodeObjectBuffer, BundlerConfig); 1708bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1709bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1710bdd1243dSDimitry Andric 1711bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1712bdd1243dSDimitry Andric assert(FileHandler && 1713bdd1243dSDimitry Andric "FileHandle creation failed for file in the archive!"); 1714bdd1243dSDimitry Andric 17155f757f3fSDimitry Andric if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer)) 1716bdd1243dSDimitry Andric return ReadErr; 1717bdd1243dSDimitry Andric 1718bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurBundleIDOrErr = 17195f757f3fSDimitry Andric FileHandler->ReadBundleStart(CodeObjectBuffer); 1720bdd1243dSDimitry Andric if (!CurBundleIDOrErr) 1721bdd1243dSDimitry Andric return CurBundleIDOrErr.takeError(); 1722bdd1243dSDimitry Andric 1723bdd1243dSDimitry Andric std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; 1724bdd1243dSDimitry Andric // No device code in this child, skip. 1725bdd1243dSDimitry Andric if (!OptionalCurBundleID) 1726bdd1243dSDimitry Andric continue; 1727bdd1243dSDimitry Andric StringRef CodeObject = *OptionalCurBundleID; 1728bdd1243dSDimitry Andric 1729bdd1243dSDimitry Andric // Process all bundle entries (CodeObjects) found in this child of input 1730bdd1243dSDimitry Andric // archive. 1731bdd1243dSDimitry Andric while (!CodeObject.empty()) { 1732bdd1243dSDimitry Andric SmallVector<StringRef> CompatibleTargets; 1733bdd1243dSDimitry Andric auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig); 1734*0fca6ea1SDimitry Andric if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets, 1735bdd1243dSDimitry Andric BundlerConfig)) { 1736bdd1243dSDimitry Andric std::string BundleData; 1737bdd1243dSDimitry Andric raw_string_ostream DataStream(BundleData); 17385f757f3fSDimitry Andric if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer)) 1739bdd1243dSDimitry Andric return Err; 1740bdd1243dSDimitry Andric 1741bdd1243dSDimitry Andric for (auto &CompatibleTarget : CompatibleTargets) { 1742bdd1243dSDimitry Andric SmallString<128> BundledObjectFileName; 1743bdd1243dSDimitry Andric BundledObjectFileName.assign(BundledObjectFile); 1744bdd1243dSDimitry Andric auto OutputBundleName = 1745bdd1243dSDimitry Andric Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + 1746bdd1243dSDimitry Andric CodeObject + 1747bdd1243dSDimitry Andric getDeviceLibraryFileName(BundledObjectFileName, 1748bdd1243dSDimitry Andric CodeObjectInfo.TargetID)) 1749bdd1243dSDimitry Andric .str(); 1750bdd1243dSDimitry Andric // Replace ':' in optional target feature list with '_' to ensure 1751bdd1243dSDimitry Andric // cross-platform validity. 1752bdd1243dSDimitry Andric std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', 1753bdd1243dSDimitry Andric '_'); 1754bdd1243dSDimitry Andric 1755bdd1243dSDimitry Andric std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy( 1756bdd1243dSDimitry Andric DataStream.str(), OutputBundleName); 1757bdd1243dSDimitry Andric ArchiveBuffers.push_back(std::move(MemBuf)); 1758bdd1243dSDimitry Andric llvm::MemoryBufferRef MemBufRef = 1759bdd1243dSDimitry Andric MemoryBufferRef(*(ArchiveBuffers.back())); 1760bdd1243dSDimitry Andric 1761bdd1243dSDimitry Andric // For inserting <CompatibleTarget, list<CodeObject>> entry in 1762bdd1243dSDimitry Andric // OutputArchivesMap. 176306c3fb27SDimitry Andric if (!OutputArchivesMap.contains(CompatibleTarget)) { 1764bdd1243dSDimitry Andric 1765bdd1243dSDimitry Andric std::vector<NewArchiveMember> ArchiveMembers; 1766bdd1243dSDimitry Andric ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); 1767bdd1243dSDimitry Andric OutputArchivesMap.insert_or_assign(CompatibleTarget, 1768bdd1243dSDimitry Andric std::move(ArchiveMembers)); 1769bdd1243dSDimitry Andric } else { 1770bdd1243dSDimitry Andric OutputArchivesMap[CompatibleTarget].push_back( 1771bdd1243dSDimitry Andric NewArchiveMember(MemBufRef)); 1772bdd1243dSDimitry Andric } 1773bdd1243dSDimitry Andric } 1774bdd1243dSDimitry Andric } 1775bdd1243dSDimitry Andric 17765f757f3fSDimitry Andric if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer)) 1777bdd1243dSDimitry Andric return Err; 1778bdd1243dSDimitry Andric 1779bdd1243dSDimitry Andric Expected<std::optional<StringRef>> NextTripleOrErr = 17805f757f3fSDimitry Andric FileHandler->ReadBundleStart(CodeObjectBuffer); 1781bdd1243dSDimitry Andric if (!NextTripleOrErr) 1782bdd1243dSDimitry Andric return NextTripleOrErr.takeError(); 1783bdd1243dSDimitry Andric 1784bdd1243dSDimitry Andric CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : ""; 1785bdd1243dSDimitry Andric } // End of processing of all bundle entries of this child of input archive. 1786bdd1243dSDimitry Andric } // End of while over children of input archive. 1787bdd1243dSDimitry Andric 1788bdd1243dSDimitry Andric assert(!ArchiveErr && "Error occurred while reading archive!"); 1789bdd1243dSDimitry Andric 1790bdd1243dSDimitry Andric /// Write out an archive for each target 1791bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1792bdd1243dSDimitry Andric StringRef FileName = TargetOutputFileNameMap[Target]; 1793bdd1243dSDimitry Andric StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = 1794bdd1243dSDimitry Andric OutputArchivesMap.find(Target); 1795bdd1243dSDimitry Andric if (CurArchiveMembers != OutputArchivesMap.end()) { 1796bdd1243dSDimitry Andric if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), 17975f757f3fSDimitry Andric SymtabWritingMode::NormalSymtab, 17985f757f3fSDimitry Andric getDefaultArchiveKindForHost(), true, 17995f757f3fSDimitry Andric false, nullptr)) 1800bdd1243dSDimitry Andric return WriteErr; 1801bdd1243dSDimitry Andric } else if (!BundlerConfig.AllowMissingBundles) { 1802bdd1243dSDimitry Andric std::string ErrMsg = 1803bdd1243dSDimitry Andric Twine("no compatible code object found for the target '" + Target + 1804bdd1243dSDimitry Andric "' in heterogeneous archive library: " + IFName) 1805bdd1243dSDimitry Andric .str(); 1806bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1807bdd1243dSDimitry Andric } else { // Create an empty archive file if no compatible code object is 1808bdd1243dSDimitry Andric // found and "allow-missing-bundles" is enabled. It ensures that 1809bdd1243dSDimitry Andric // the linker using output of this step doesn't complain about 1810bdd1243dSDimitry Andric // the missing input file. 1811bdd1243dSDimitry Andric std::vector<llvm::NewArchiveMember> EmptyArchive; 1812bdd1243dSDimitry Andric EmptyArchive.clear(); 18135f757f3fSDimitry Andric if (Error WriteErr = writeArchive( 18145f757f3fSDimitry Andric FileName, EmptyArchive, SymtabWritingMode::NormalSymtab, 18155f757f3fSDimitry Andric getDefaultArchiveKindForHost(), true, false, nullptr)) 1816bdd1243dSDimitry Andric return WriteErr; 1817bdd1243dSDimitry Andric } 1818bdd1243dSDimitry Andric } 1819bdd1243dSDimitry Andric 1820bdd1243dSDimitry Andric return Error::success(); 1821bdd1243dSDimitry Andric } 1822