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" 24*5f757f3fSDimitry Andric #include "llvm/ADT/StringExtras.h" 25bdd1243dSDimitry Andric #include "llvm/ADT/StringMap.h" 26bdd1243dSDimitry Andric #include "llvm/ADT/StringRef.h" 27*5f757f3fSDimitry 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" 33*5f757f3fSDimitry 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" 40*5f757f3fSDimitry 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" 46*5f757f3fSDimitry 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> 56*5f757f3fSDimitry 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 67*5f757f3fSDimitry Andric static llvm::TimerGroup 68*5f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group", 69*5f757f3fSDimitry Andric "Timer group for clang offload bundler"); 70*5f757f3fSDimitry 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 82bdd1243dSDimitry Andric if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) { 83bdd1243dSDimitry Andric auto KindTriple = TripleOrGPU.first.split('-'); 84bdd1243dSDimitry Andric this->OffloadKind = KindTriple.first; 8506c3fb27SDimitry Andric 8606c3fb27SDimitry Andric // Enforce optional env field to standardize bundles 8706c3fb27SDimitry Andric llvm::Triple t = llvm::Triple(KindTriple.second); 8806c3fb27SDimitry Andric this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(), 8906c3fb27SDimitry Andric t.getOSName(), t.getEnvironmentName()); 9006c3fb27SDimitry Andric 91bdd1243dSDimitry Andric this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); 92bdd1243dSDimitry Andric } else { 93bdd1243dSDimitry Andric auto KindTriple = TargetFeatures.first.split('-'); 94bdd1243dSDimitry Andric this->OffloadKind = KindTriple.first; 9506c3fb27SDimitry Andric 9606c3fb27SDimitry Andric // Enforce optional env field to standardize bundles 9706c3fb27SDimitry Andric llvm::Triple t = llvm::Triple(KindTriple.second); 9806c3fb27SDimitry Andric this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(), 9906c3fb27SDimitry Andric t.getOSName(), t.getEnvironmentName()); 10006c3fb27SDimitry Andric 101bdd1243dSDimitry Andric this->TargetID = ""; 102bdd1243dSDimitry Andric } 103bdd1243dSDimitry Andric } 104bdd1243dSDimitry Andric 105bdd1243dSDimitry Andric bool OffloadTargetInfo::hasHostKind() const { 106bdd1243dSDimitry Andric return this->OffloadKind == "host"; 107bdd1243dSDimitry Andric } 108bdd1243dSDimitry Andric 109bdd1243dSDimitry Andric bool OffloadTargetInfo::isOffloadKindValid() const { 110bdd1243dSDimitry Andric return OffloadKind == "host" || OffloadKind == "openmp" || 111bdd1243dSDimitry Andric OffloadKind == "hip" || OffloadKind == "hipv4"; 112bdd1243dSDimitry Andric } 113bdd1243dSDimitry Andric 114bdd1243dSDimitry Andric bool OffloadTargetInfo::isOffloadKindCompatible( 115bdd1243dSDimitry Andric const StringRef TargetOffloadKind) const { 116bdd1243dSDimitry Andric if (OffloadKind == TargetOffloadKind) 117bdd1243dSDimitry Andric return true; 118bdd1243dSDimitry Andric if (BundlerConfig.HipOpenmpCompatible) { 11906c3fb27SDimitry Andric bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") && 120bdd1243dSDimitry Andric TargetOffloadKind == "openmp"; 121bdd1243dSDimitry Andric bool OpenMPCompatibleWithHIP = 122bdd1243dSDimitry Andric OffloadKind == "openmp" && 12306c3fb27SDimitry Andric TargetOffloadKind.starts_with_insensitive("hip"); 124bdd1243dSDimitry Andric return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP; 125bdd1243dSDimitry Andric } 126bdd1243dSDimitry Andric return false; 127bdd1243dSDimitry Andric } 128bdd1243dSDimitry Andric 129bdd1243dSDimitry Andric bool OffloadTargetInfo::isTripleValid() const { 130bdd1243dSDimitry Andric return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; 131bdd1243dSDimitry Andric } 132bdd1243dSDimitry Andric 133bdd1243dSDimitry Andric bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { 134bdd1243dSDimitry Andric return OffloadKind == Target.OffloadKind && 135bdd1243dSDimitry Andric Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; 136bdd1243dSDimitry Andric } 137bdd1243dSDimitry Andric 138bdd1243dSDimitry Andric std::string OffloadTargetInfo::str() const { 139bdd1243dSDimitry Andric return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); 140bdd1243dSDimitry Andric } 141bdd1243dSDimitry Andric 142bdd1243dSDimitry Andric static StringRef getDeviceFileExtension(StringRef Device, 143bdd1243dSDimitry Andric StringRef BundleFileName) { 144bdd1243dSDimitry Andric if (Device.contains("gfx")) 145bdd1243dSDimitry Andric return ".bc"; 146bdd1243dSDimitry Andric if (Device.contains("sm_")) 147bdd1243dSDimitry Andric return ".cubin"; 148bdd1243dSDimitry Andric return sys::path::extension(BundleFileName); 149bdd1243dSDimitry Andric } 150bdd1243dSDimitry Andric 151bdd1243dSDimitry Andric static std::string getDeviceLibraryFileName(StringRef BundleFileName, 152bdd1243dSDimitry Andric StringRef Device) { 153bdd1243dSDimitry Andric StringRef LibName = sys::path::stem(BundleFileName); 154bdd1243dSDimitry Andric StringRef Extension = getDeviceFileExtension(Device, BundleFileName); 155bdd1243dSDimitry Andric 156bdd1243dSDimitry Andric std::string Result; 157bdd1243dSDimitry Andric Result += LibName; 158bdd1243dSDimitry Andric Result += Extension; 159bdd1243dSDimitry Andric return Result; 160bdd1243dSDimitry Andric } 161bdd1243dSDimitry Andric 162bdd1243dSDimitry Andric namespace { 163bdd1243dSDimitry Andric /// Generic file handler interface. 164bdd1243dSDimitry Andric class FileHandler { 165bdd1243dSDimitry Andric public: 166bdd1243dSDimitry Andric struct BundleInfo { 167bdd1243dSDimitry Andric StringRef BundleID; 168bdd1243dSDimitry Andric }; 169bdd1243dSDimitry Andric 170bdd1243dSDimitry Andric FileHandler() {} 171bdd1243dSDimitry Andric 172bdd1243dSDimitry Andric virtual ~FileHandler() {} 173bdd1243dSDimitry Andric 174bdd1243dSDimitry Andric /// Update the file handler with information from the header of the bundled 175bdd1243dSDimitry Andric /// file. 176bdd1243dSDimitry Andric virtual Error ReadHeader(MemoryBuffer &Input) = 0; 177bdd1243dSDimitry Andric 178bdd1243dSDimitry Andric /// Read the marker of the next bundled to be read in the file. The bundle 179bdd1243dSDimitry Andric /// name is returned if there is one in the file, or `std::nullopt` if there 180bdd1243dSDimitry Andric /// are no more bundles to be read. 181bdd1243dSDimitry Andric virtual Expected<std::optional<StringRef>> 182bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) = 0; 183bdd1243dSDimitry Andric 184bdd1243dSDimitry Andric /// Read the marker that closes the current bundle. 185bdd1243dSDimitry Andric virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; 186bdd1243dSDimitry Andric 187bdd1243dSDimitry Andric /// Read the current bundle and write the result into the stream \a OS. 188bdd1243dSDimitry Andric virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 189bdd1243dSDimitry Andric 190bdd1243dSDimitry Andric /// Write the header of the bundled file to \a OS based on the information 191bdd1243dSDimitry Andric /// gathered from \a Inputs. 192*5f757f3fSDimitry Andric virtual Error WriteHeader(raw_ostream &OS, 193bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; 194bdd1243dSDimitry Andric 195bdd1243dSDimitry Andric /// Write the marker that initiates a bundle for the triple \a TargetTriple to 196bdd1243dSDimitry Andric /// \a OS. 197*5f757f3fSDimitry Andric virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0; 198bdd1243dSDimitry Andric 199bdd1243dSDimitry Andric /// Write the marker that closes a bundle for the triple \a TargetTriple to \a 200bdd1243dSDimitry Andric /// OS. 201*5f757f3fSDimitry Andric virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0; 202bdd1243dSDimitry Andric 203bdd1243dSDimitry Andric /// Write the bundle from \a Input into \a OS. 204*5f757f3fSDimitry Andric virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 205*5f757f3fSDimitry Andric 206*5f757f3fSDimitry Andric /// Finalize output file. 207*5f757f3fSDimitry Andric virtual Error finalizeOutputFile() { return Error::success(); } 208bdd1243dSDimitry Andric 209bdd1243dSDimitry Andric /// List bundle IDs in \a Input. 210bdd1243dSDimitry Andric virtual Error listBundleIDs(MemoryBuffer &Input) { 211bdd1243dSDimitry Andric if (Error Err = ReadHeader(Input)) 212bdd1243dSDimitry Andric return Err; 213bdd1243dSDimitry Andric return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 214bdd1243dSDimitry Andric llvm::outs() << Info.BundleID << '\n'; 215bdd1243dSDimitry Andric Error Err = listBundleIDsCallback(Input, Info); 216bdd1243dSDimitry Andric if (Err) 217bdd1243dSDimitry Andric return Err; 218bdd1243dSDimitry Andric return Error::success(); 219bdd1243dSDimitry Andric }); 220bdd1243dSDimitry Andric } 221bdd1243dSDimitry Andric 222*5f757f3fSDimitry Andric /// Get bundle IDs in \a Input in \a BundleIds. 223*5f757f3fSDimitry Andric virtual Error getBundleIDs(MemoryBuffer &Input, 224*5f757f3fSDimitry Andric std::set<StringRef> &BundleIds) { 225*5f757f3fSDimitry Andric if (Error Err = ReadHeader(Input)) 226*5f757f3fSDimitry Andric return Err; 227*5f757f3fSDimitry Andric return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 228*5f757f3fSDimitry Andric BundleIds.insert(Info.BundleID); 229*5f757f3fSDimitry Andric Error Err = listBundleIDsCallback(Input, Info); 230*5f757f3fSDimitry Andric if (Err) 231*5f757f3fSDimitry Andric return Err; 232*5f757f3fSDimitry Andric return Error::success(); 233*5f757f3fSDimitry Andric }); 234*5f757f3fSDimitry Andric } 235*5f757f3fSDimitry Andric 236bdd1243dSDimitry Andric /// For each bundle in \a Input, do \a Func. 237bdd1243dSDimitry Andric Error forEachBundle(MemoryBuffer &Input, 238bdd1243dSDimitry Andric std::function<Error(const BundleInfo &)> Func) { 239bdd1243dSDimitry Andric while (true) { 240bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurTripleOrErr = 241bdd1243dSDimitry Andric ReadBundleStart(Input); 242bdd1243dSDimitry Andric if (!CurTripleOrErr) 243bdd1243dSDimitry Andric return CurTripleOrErr.takeError(); 244bdd1243dSDimitry Andric 245bdd1243dSDimitry Andric // No more bundles. 246bdd1243dSDimitry Andric if (!*CurTripleOrErr) 247bdd1243dSDimitry Andric break; 248bdd1243dSDimitry Andric 249bdd1243dSDimitry Andric StringRef CurTriple = **CurTripleOrErr; 250bdd1243dSDimitry Andric assert(!CurTriple.empty()); 251bdd1243dSDimitry Andric 252bdd1243dSDimitry Andric BundleInfo Info{CurTriple}; 253bdd1243dSDimitry Andric if (Error Err = Func(Info)) 254bdd1243dSDimitry Andric return Err; 255bdd1243dSDimitry Andric } 256bdd1243dSDimitry Andric return Error::success(); 257bdd1243dSDimitry Andric } 258bdd1243dSDimitry Andric 259bdd1243dSDimitry Andric protected: 260bdd1243dSDimitry Andric virtual Error listBundleIDsCallback(MemoryBuffer &Input, 261bdd1243dSDimitry Andric const BundleInfo &Info) { 262bdd1243dSDimitry Andric return Error::success(); 263bdd1243dSDimitry Andric } 264bdd1243dSDimitry Andric }; 265bdd1243dSDimitry Andric 266bdd1243dSDimitry Andric /// Handler for binary files. The bundled file will have the following format 267bdd1243dSDimitry Andric /// (all integers are stored in little-endian format): 268bdd1243dSDimitry Andric /// 269bdd1243dSDimitry Andric /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) 270bdd1243dSDimitry Andric /// 271bdd1243dSDimitry Andric /// NumberOfOffloadBundles (8-byte integer) 272bdd1243dSDimitry Andric /// 273bdd1243dSDimitry Andric /// OffsetOfBundle1 (8-byte integer) 274bdd1243dSDimitry Andric /// SizeOfBundle1 (8-byte integer) 275bdd1243dSDimitry Andric /// NumberOfBytesInTripleOfBundle1 (8-byte integer) 276bdd1243dSDimitry Andric /// TripleOfBundle1 (byte length defined before) 277bdd1243dSDimitry Andric /// 278bdd1243dSDimitry Andric /// ... 279bdd1243dSDimitry Andric /// 280bdd1243dSDimitry Andric /// OffsetOfBundleN (8-byte integer) 281bdd1243dSDimitry Andric /// SizeOfBundleN (8-byte integer) 282bdd1243dSDimitry Andric /// NumberOfBytesInTripleOfBundleN (8-byte integer) 283bdd1243dSDimitry Andric /// TripleOfBundleN (byte length defined before) 284bdd1243dSDimitry Andric /// 285bdd1243dSDimitry Andric /// Bundle1 286bdd1243dSDimitry Andric /// ... 287bdd1243dSDimitry Andric /// BundleN 288bdd1243dSDimitry Andric 289bdd1243dSDimitry Andric /// Read 8-byte integers from a buffer in little-endian format. 290bdd1243dSDimitry Andric static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { 291bdd1243dSDimitry Andric return llvm::support::endian::read64le(Buffer.data() + pos); 292bdd1243dSDimitry Andric } 293bdd1243dSDimitry Andric 294bdd1243dSDimitry Andric /// Write 8-byte integers to a buffer in little-endian format. 295*5f757f3fSDimitry Andric static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) { 296*5f757f3fSDimitry Andric llvm::support::endian::write(OS, Val, llvm::endianness::little); 297bdd1243dSDimitry Andric } 298bdd1243dSDimitry Andric 299bdd1243dSDimitry Andric class BinaryFileHandler final : public FileHandler { 300bdd1243dSDimitry Andric /// Information about the bundles extracted from the header. 301bdd1243dSDimitry Andric struct BinaryBundleInfo final : public BundleInfo { 302bdd1243dSDimitry Andric /// Size of the bundle. 303bdd1243dSDimitry Andric uint64_t Size = 0u; 304bdd1243dSDimitry Andric /// Offset at which the bundle starts in the bundled file. 305bdd1243dSDimitry Andric uint64_t Offset = 0u; 306bdd1243dSDimitry Andric 307bdd1243dSDimitry Andric BinaryBundleInfo() {} 308bdd1243dSDimitry Andric BinaryBundleInfo(uint64_t Size, uint64_t Offset) 309bdd1243dSDimitry Andric : Size(Size), Offset(Offset) {} 310bdd1243dSDimitry Andric }; 311bdd1243dSDimitry Andric 312bdd1243dSDimitry Andric /// Map between a triple and the corresponding bundle information. 313bdd1243dSDimitry Andric StringMap<BinaryBundleInfo> BundlesInfo; 314bdd1243dSDimitry Andric 315bdd1243dSDimitry Andric /// Iterator for the bundle information that is being read. 316bdd1243dSDimitry Andric StringMap<BinaryBundleInfo>::iterator CurBundleInfo; 317bdd1243dSDimitry Andric StringMap<BinaryBundleInfo>::iterator NextBundleInfo; 318bdd1243dSDimitry Andric 319bdd1243dSDimitry Andric /// Current bundle target to be written. 320bdd1243dSDimitry Andric std::string CurWriteBundleTarget; 321bdd1243dSDimitry Andric 322bdd1243dSDimitry Andric /// Configuration options and arrays for this bundler job 323bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig; 324bdd1243dSDimitry Andric 325bdd1243dSDimitry Andric public: 326bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 327bdd1243dSDimitry Andric BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {} 328bdd1243dSDimitry Andric 329bdd1243dSDimitry Andric ~BinaryFileHandler() final {} 330bdd1243dSDimitry Andric 331bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { 332bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 333bdd1243dSDimitry Andric 334bdd1243dSDimitry Andric // Initialize the current bundle with the end of the container. 335bdd1243dSDimitry Andric CurBundleInfo = BundlesInfo.end(); 336bdd1243dSDimitry Andric 337bdd1243dSDimitry Andric // Check if buffer is smaller than magic string. 338bdd1243dSDimitry Andric size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 339bdd1243dSDimitry Andric if (ReadChars > FC.size()) 340bdd1243dSDimitry Andric return Error::success(); 341bdd1243dSDimitry Andric 342bdd1243dSDimitry Andric // Check if no magic was found. 343*5f757f3fSDimitry Andric if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle) 344bdd1243dSDimitry Andric return Error::success(); 345bdd1243dSDimitry Andric 346bdd1243dSDimitry Andric // Read number of bundles. 347bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 348bdd1243dSDimitry Andric return Error::success(); 349bdd1243dSDimitry Andric 350bdd1243dSDimitry Andric uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); 351bdd1243dSDimitry Andric ReadChars += 8; 352bdd1243dSDimitry Andric 353bdd1243dSDimitry Andric // Read bundle offsets, sizes and triples. 354bdd1243dSDimitry Andric for (uint64_t i = 0; i < NumberOfBundles; ++i) { 355bdd1243dSDimitry Andric 356bdd1243dSDimitry Andric // Read offset. 357bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 358bdd1243dSDimitry Andric return Error::success(); 359bdd1243dSDimitry Andric 360bdd1243dSDimitry Andric uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); 361bdd1243dSDimitry Andric ReadChars += 8; 362bdd1243dSDimitry Andric 363bdd1243dSDimitry Andric // Read size. 364bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 365bdd1243dSDimitry Andric return Error::success(); 366bdd1243dSDimitry Andric 367bdd1243dSDimitry Andric uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); 368bdd1243dSDimitry Andric ReadChars += 8; 369bdd1243dSDimitry Andric 370bdd1243dSDimitry Andric // Read triple size. 371bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 372bdd1243dSDimitry Andric return Error::success(); 373bdd1243dSDimitry Andric 374bdd1243dSDimitry Andric uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); 375bdd1243dSDimitry Andric ReadChars += 8; 376bdd1243dSDimitry Andric 377bdd1243dSDimitry Andric // Read triple. 378bdd1243dSDimitry Andric if (ReadChars + TripleSize > FC.size()) 379bdd1243dSDimitry Andric return Error::success(); 380bdd1243dSDimitry Andric 381bdd1243dSDimitry Andric StringRef Triple(&FC.data()[ReadChars], TripleSize); 382bdd1243dSDimitry Andric ReadChars += TripleSize; 383bdd1243dSDimitry Andric 384bdd1243dSDimitry Andric // Check if the offset and size make sense. 385bdd1243dSDimitry Andric if (!Offset || Offset + Size > FC.size()) 386bdd1243dSDimitry Andric return Error::success(); 387bdd1243dSDimitry Andric 38806c3fb27SDimitry Andric assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??"); 389bdd1243dSDimitry Andric BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); 390bdd1243dSDimitry Andric } 391bdd1243dSDimitry Andric // Set the iterator to where we will start to read. 392bdd1243dSDimitry Andric CurBundleInfo = BundlesInfo.end(); 393bdd1243dSDimitry Andric NextBundleInfo = BundlesInfo.begin(); 394bdd1243dSDimitry Andric return Error::success(); 395bdd1243dSDimitry Andric } 396bdd1243dSDimitry Andric 397bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 398bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 399bdd1243dSDimitry Andric if (NextBundleInfo == BundlesInfo.end()) 400bdd1243dSDimitry Andric return std::nullopt; 401bdd1243dSDimitry Andric CurBundleInfo = NextBundleInfo++; 402bdd1243dSDimitry Andric return CurBundleInfo->first(); 403bdd1243dSDimitry Andric } 404bdd1243dSDimitry Andric 405bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { 406bdd1243dSDimitry Andric assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 407bdd1243dSDimitry Andric return Error::success(); 408bdd1243dSDimitry Andric } 409bdd1243dSDimitry Andric 410bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 411bdd1243dSDimitry Andric assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 412bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 413bdd1243dSDimitry Andric OS.write(FC.data() + CurBundleInfo->second.Offset, 414bdd1243dSDimitry Andric CurBundleInfo->second.Size); 415bdd1243dSDimitry Andric return Error::success(); 416bdd1243dSDimitry Andric } 417bdd1243dSDimitry Andric 418*5f757f3fSDimitry Andric Error WriteHeader(raw_ostream &OS, 419bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 420bdd1243dSDimitry Andric 421bdd1243dSDimitry Andric // Compute size of the header. 422bdd1243dSDimitry Andric uint64_t HeaderSize = 0; 423bdd1243dSDimitry Andric 424bdd1243dSDimitry Andric HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 425bdd1243dSDimitry Andric HeaderSize += 8; // Number of Bundles 426bdd1243dSDimitry Andric 427bdd1243dSDimitry Andric for (auto &T : BundlerConfig.TargetNames) { 428bdd1243dSDimitry Andric HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. 429bdd1243dSDimitry Andric HeaderSize += T.size(); // The triple. 430bdd1243dSDimitry Andric } 431bdd1243dSDimitry Andric 432bdd1243dSDimitry Andric // Write to the buffer the header. 433bdd1243dSDimitry Andric OS << OFFLOAD_BUNDLER_MAGIC_STR; 434bdd1243dSDimitry Andric 435bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size()); 436bdd1243dSDimitry Andric 437bdd1243dSDimitry Andric unsigned Idx = 0; 438bdd1243dSDimitry Andric for (auto &T : BundlerConfig.TargetNames) { 439bdd1243dSDimitry Andric MemoryBuffer &MB = *Inputs[Idx++]; 440bdd1243dSDimitry Andric HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment); 441bdd1243dSDimitry Andric // Bundle offset. 442bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, HeaderSize); 443bdd1243dSDimitry Andric // Size of the bundle (adds to the next bundle's offset) 444bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, MB.getBufferSize()); 445bdd1243dSDimitry Andric BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); 446bdd1243dSDimitry Andric HeaderSize += MB.getBufferSize(); 447bdd1243dSDimitry Andric // Size of the triple 448bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, T.size()); 449bdd1243dSDimitry Andric // Triple 450bdd1243dSDimitry Andric OS << T; 451bdd1243dSDimitry Andric } 452bdd1243dSDimitry Andric return Error::success(); 453bdd1243dSDimitry Andric } 454bdd1243dSDimitry Andric 455*5f757f3fSDimitry Andric Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 456bdd1243dSDimitry Andric CurWriteBundleTarget = TargetTriple.str(); 457bdd1243dSDimitry Andric return Error::success(); 458bdd1243dSDimitry Andric } 459bdd1243dSDimitry Andric 460*5f757f3fSDimitry Andric Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 461bdd1243dSDimitry Andric return Error::success(); 462bdd1243dSDimitry Andric } 463bdd1243dSDimitry Andric 464*5f757f3fSDimitry Andric Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 465bdd1243dSDimitry Andric auto BI = BundlesInfo[CurWriteBundleTarget]; 466*5f757f3fSDimitry Andric 467*5f757f3fSDimitry Andric // Pad with 0 to reach specified offset. 468*5f757f3fSDimitry Andric size_t CurrentPos = OS.tell(); 469*5f757f3fSDimitry Andric size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0; 470*5f757f3fSDimitry Andric for (size_t I = 0; I < PaddingSize; ++I) 471*5f757f3fSDimitry Andric OS.write('\0'); 472*5f757f3fSDimitry Andric assert(OS.tell() == BI.Offset); 473*5f757f3fSDimitry Andric 474bdd1243dSDimitry Andric OS.write(Input.getBufferStart(), Input.getBufferSize()); 475*5f757f3fSDimitry Andric 476bdd1243dSDimitry Andric return Error::success(); 477bdd1243dSDimitry Andric } 478bdd1243dSDimitry Andric }; 479bdd1243dSDimitry Andric 480bdd1243dSDimitry Andric // This class implements a list of temporary files that are removed upon 481bdd1243dSDimitry Andric // object destruction. 482bdd1243dSDimitry Andric class TempFileHandlerRAII { 483bdd1243dSDimitry Andric public: 484bdd1243dSDimitry Andric ~TempFileHandlerRAII() { 485bdd1243dSDimitry Andric for (const auto &File : Files) 486bdd1243dSDimitry Andric sys::fs::remove(File); 487bdd1243dSDimitry Andric } 488bdd1243dSDimitry Andric 489bdd1243dSDimitry Andric // Creates temporary file with given contents. 490bdd1243dSDimitry Andric Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) { 491bdd1243dSDimitry Andric SmallString<128u> File; 492bdd1243dSDimitry Andric if (std::error_code EC = 493bdd1243dSDimitry Andric sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) 494bdd1243dSDimitry Andric return createFileError(File, EC); 495bdd1243dSDimitry Andric Files.push_front(File); 496bdd1243dSDimitry Andric 497bdd1243dSDimitry Andric if (Contents) { 498bdd1243dSDimitry Andric std::error_code EC; 499bdd1243dSDimitry Andric raw_fd_ostream OS(File, EC); 500bdd1243dSDimitry Andric if (EC) 501bdd1243dSDimitry Andric return createFileError(File, EC); 502bdd1243dSDimitry Andric OS.write(Contents->data(), Contents->size()); 503bdd1243dSDimitry Andric } 504bdd1243dSDimitry Andric return Files.front().str(); 505bdd1243dSDimitry Andric } 506bdd1243dSDimitry Andric 507bdd1243dSDimitry Andric private: 508bdd1243dSDimitry Andric std::forward_list<SmallString<128u>> Files; 509bdd1243dSDimitry Andric }; 510bdd1243dSDimitry Andric 511bdd1243dSDimitry Andric /// Handler for object files. The bundles are organized by sections with a 512bdd1243dSDimitry Andric /// designated name. 513bdd1243dSDimitry Andric /// 514bdd1243dSDimitry Andric /// To unbundle, we just copy the contents of the designated section. 515bdd1243dSDimitry Andric class ObjectFileHandler final : public FileHandler { 516bdd1243dSDimitry Andric 517bdd1243dSDimitry Andric /// The object file we are currently dealing with. 518bdd1243dSDimitry Andric std::unique_ptr<ObjectFile> Obj; 519bdd1243dSDimitry Andric 520bdd1243dSDimitry Andric /// Return the input file contents. 521bdd1243dSDimitry Andric StringRef getInputFileContents() const { return Obj->getData(); } 522bdd1243dSDimitry Andric 523bdd1243dSDimitry Andric /// Return bundle name (<kind>-<triple>) if the provided section is an offload 524bdd1243dSDimitry Andric /// section. 525bdd1243dSDimitry Andric static Expected<std::optional<StringRef>> 526bdd1243dSDimitry Andric IsOffloadSection(SectionRef CurSection) { 527bdd1243dSDimitry Andric Expected<StringRef> NameOrErr = CurSection.getName(); 528bdd1243dSDimitry Andric if (!NameOrErr) 529bdd1243dSDimitry Andric return NameOrErr.takeError(); 530bdd1243dSDimitry Andric 531bdd1243dSDimitry Andric // If it does not start with the reserved suffix, just skip this section. 532*5f757f3fSDimitry Andric if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle) 533bdd1243dSDimitry Andric return std::nullopt; 534bdd1243dSDimitry Andric 535bdd1243dSDimitry Andric // Return the triple that is right after the reserved prefix. 536bdd1243dSDimitry Andric return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 537bdd1243dSDimitry Andric } 538bdd1243dSDimitry Andric 539bdd1243dSDimitry Andric /// Total number of inputs. 540bdd1243dSDimitry Andric unsigned NumberOfInputs = 0; 541bdd1243dSDimitry Andric 542bdd1243dSDimitry Andric /// Total number of processed inputs, i.e, inputs that were already 543bdd1243dSDimitry Andric /// read from the buffers. 544bdd1243dSDimitry Andric unsigned NumberOfProcessedInputs = 0; 545bdd1243dSDimitry Andric 546bdd1243dSDimitry Andric /// Iterator of the current and next section. 547bdd1243dSDimitry Andric section_iterator CurrentSection; 548bdd1243dSDimitry Andric section_iterator NextSection; 549bdd1243dSDimitry Andric 550bdd1243dSDimitry Andric /// Configuration options and arrays for this bundler job 551bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig; 552bdd1243dSDimitry Andric 553bdd1243dSDimitry Andric public: 554bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 555bdd1243dSDimitry Andric ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn, 556bdd1243dSDimitry Andric const OffloadBundlerConfig &BC) 557bdd1243dSDimitry Andric : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()), 558bdd1243dSDimitry Andric NextSection(Obj->section_begin()), BundlerConfig(BC) {} 559bdd1243dSDimitry Andric 560bdd1243dSDimitry Andric ~ObjectFileHandler() final {} 561bdd1243dSDimitry Andric 562bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 563bdd1243dSDimitry Andric 564bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 565bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 566bdd1243dSDimitry Andric while (NextSection != Obj->section_end()) { 567bdd1243dSDimitry Andric CurrentSection = NextSection; 568bdd1243dSDimitry Andric ++NextSection; 569bdd1243dSDimitry Andric 570bdd1243dSDimitry Andric // Check if the current section name starts with the reserved prefix. If 571bdd1243dSDimitry Andric // so, return the triple. 572bdd1243dSDimitry Andric Expected<std::optional<StringRef>> TripleOrErr = 573bdd1243dSDimitry Andric IsOffloadSection(*CurrentSection); 574bdd1243dSDimitry Andric if (!TripleOrErr) 575bdd1243dSDimitry Andric return TripleOrErr.takeError(); 576bdd1243dSDimitry Andric if (*TripleOrErr) 577bdd1243dSDimitry Andric return **TripleOrErr; 578bdd1243dSDimitry Andric } 579bdd1243dSDimitry Andric return std::nullopt; 580bdd1243dSDimitry Andric } 581bdd1243dSDimitry Andric 582bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 583bdd1243dSDimitry Andric 584bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 585bdd1243dSDimitry Andric Expected<StringRef> ContentOrErr = CurrentSection->getContents(); 586bdd1243dSDimitry Andric if (!ContentOrErr) 587bdd1243dSDimitry Andric return ContentOrErr.takeError(); 588bdd1243dSDimitry Andric StringRef Content = *ContentOrErr; 589bdd1243dSDimitry Andric 590bdd1243dSDimitry Andric // Copy fat object contents to the output when extracting host bundle. 591bdd1243dSDimitry Andric if (Content.size() == 1u && Content.front() == 0) 592bdd1243dSDimitry Andric Content = StringRef(Input.getBufferStart(), Input.getBufferSize()); 593bdd1243dSDimitry Andric 594bdd1243dSDimitry Andric OS.write(Content.data(), Content.size()); 595bdd1243dSDimitry Andric return Error::success(); 596bdd1243dSDimitry Andric } 597bdd1243dSDimitry Andric 598*5f757f3fSDimitry Andric Error WriteHeader(raw_ostream &OS, 599bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 600bdd1243dSDimitry Andric assert(BundlerConfig.HostInputIndex != ~0u && 601bdd1243dSDimitry Andric "Host input index not defined."); 602bdd1243dSDimitry Andric 603bdd1243dSDimitry Andric // Record number of inputs. 604bdd1243dSDimitry Andric NumberOfInputs = Inputs.size(); 605bdd1243dSDimitry Andric return Error::success(); 606bdd1243dSDimitry Andric } 607bdd1243dSDimitry Andric 608*5f757f3fSDimitry Andric Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 609bdd1243dSDimitry Andric ++NumberOfProcessedInputs; 610bdd1243dSDimitry Andric return Error::success(); 611bdd1243dSDimitry Andric } 612bdd1243dSDimitry Andric 613*5f757f3fSDimitry Andric Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 614*5f757f3fSDimitry Andric return Error::success(); 615*5f757f3fSDimitry Andric } 616*5f757f3fSDimitry Andric 617*5f757f3fSDimitry Andric Error finalizeOutputFile() final { 618bdd1243dSDimitry Andric assert(NumberOfProcessedInputs <= NumberOfInputs && 619bdd1243dSDimitry Andric "Processing more inputs that actually exist!"); 620bdd1243dSDimitry Andric assert(BundlerConfig.HostInputIndex != ~0u && 621bdd1243dSDimitry Andric "Host input index not defined."); 622bdd1243dSDimitry Andric 623bdd1243dSDimitry Andric // If this is not the last output, we don't have to do anything. 624bdd1243dSDimitry Andric if (NumberOfProcessedInputs != NumberOfInputs) 625bdd1243dSDimitry Andric return Error::success(); 626bdd1243dSDimitry Andric 627bdd1243dSDimitry Andric // We will use llvm-objcopy to add target objects sections to the output 628bdd1243dSDimitry Andric // fat object. These sections should have 'exclude' flag set which tells 629bdd1243dSDimitry Andric // link editor to remove them from linker inputs when linking executable or 630bdd1243dSDimitry Andric // shared library. 631bdd1243dSDimitry Andric 632bdd1243dSDimitry Andric assert(BundlerConfig.ObjcopyPath != "" && 633bdd1243dSDimitry Andric "llvm-objcopy path not specified"); 634bdd1243dSDimitry Andric 635bdd1243dSDimitry Andric // Temporary files that need to be removed. 636bdd1243dSDimitry Andric TempFileHandlerRAII TempFiles; 637bdd1243dSDimitry Andric 638bdd1243dSDimitry Andric // Compose llvm-objcopy command line for add target objects' sections with 639bdd1243dSDimitry Andric // appropriate flags. 640bdd1243dSDimitry Andric BumpPtrAllocator Alloc; 641bdd1243dSDimitry Andric StringSaver SS{Alloc}; 642bdd1243dSDimitry Andric SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; 643bdd1243dSDimitry Andric 644bdd1243dSDimitry Andric for (unsigned I = 0; I < NumberOfInputs; ++I) { 645bdd1243dSDimitry Andric StringRef InputFile = BundlerConfig.InputFileNames[I]; 646bdd1243dSDimitry Andric if (I == BundlerConfig.HostInputIndex) { 647bdd1243dSDimitry Andric // Special handling for the host bundle. We do not need to add a 648bdd1243dSDimitry Andric // standard bundle for the host object since we are going to use fat 649bdd1243dSDimitry Andric // object as a host object. Therefore use dummy contents (one zero byte) 650bdd1243dSDimitry Andric // when creating section for the host bundle. 651bdd1243dSDimitry Andric Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); 652bdd1243dSDimitry Andric if (!TempFileOrErr) 653bdd1243dSDimitry Andric return TempFileOrErr.takeError(); 654bdd1243dSDimitry Andric InputFile = *TempFileOrErr; 655bdd1243dSDimitry Andric } 656bdd1243dSDimitry Andric 657bdd1243dSDimitry Andric ObjcopyArgs.push_back( 658bdd1243dSDimitry Andric SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR + 659bdd1243dSDimitry Andric BundlerConfig.TargetNames[I] + "=" + InputFile)); 660bdd1243dSDimitry Andric ObjcopyArgs.push_back( 661bdd1243dSDimitry Andric SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR + 662bdd1243dSDimitry Andric BundlerConfig.TargetNames[I] + "=readonly,exclude")); 663bdd1243dSDimitry Andric } 664bdd1243dSDimitry Andric ObjcopyArgs.push_back("--"); 665bdd1243dSDimitry Andric ObjcopyArgs.push_back( 666bdd1243dSDimitry Andric BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]); 667bdd1243dSDimitry Andric ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front()); 668bdd1243dSDimitry Andric 669bdd1243dSDimitry Andric if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 670bdd1243dSDimitry Andric return Err; 671bdd1243dSDimitry Andric 672bdd1243dSDimitry Andric return Error::success(); 673bdd1243dSDimitry Andric } 674bdd1243dSDimitry Andric 675*5f757f3fSDimitry Andric Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 676bdd1243dSDimitry Andric return Error::success(); 677bdd1243dSDimitry Andric } 678bdd1243dSDimitry Andric 679bdd1243dSDimitry Andric private: 680bdd1243dSDimitry Andric Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { 681bdd1243dSDimitry Andric // If the user asked for the commands to be printed out, we do that 682bdd1243dSDimitry Andric // instead of executing it. 683bdd1243dSDimitry Andric if (BundlerConfig.PrintExternalCommands) { 684bdd1243dSDimitry Andric errs() << "\"" << Objcopy << "\""; 685bdd1243dSDimitry Andric for (StringRef Arg : drop_begin(Args, 1)) 686bdd1243dSDimitry Andric errs() << " \"" << Arg << "\""; 687bdd1243dSDimitry Andric errs() << "\n"; 688bdd1243dSDimitry Andric } else { 689bdd1243dSDimitry Andric if (sys::ExecuteAndWait(Objcopy, Args)) 690bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), 691bdd1243dSDimitry Andric "'llvm-objcopy' tool failed"); 692bdd1243dSDimitry Andric } 693bdd1243dSDimitry Andric return Error::success(); 694bdd1243dSDimitry Andric } 695bdd1243dSDimitry Andric }; 696bdd1243dSDimitry Andric 697bdd1243dSDimitry Andric /// Handler for text files. The bundled file will have the following format. 698bdd1243dSDimitry Andric /// 699bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 700bdd1243dSDimitry Andric /// Bundle 1 701bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 702bdd1243dSDimitry Andric /// ... 703bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 704bdd1243dSDimitry Andric /// Bundle N 705bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 706bdd1243dSDimitry Andric class TextFileHandler final : public FileHandler { 707bdd1243dSDimitry Andric /// String that begins a line comment. 708bdd1243dSDimitry Andric StringRef Comment; 709bdd1243dSDimitry Andric 710bdd1243dSDimitry Andric /// String that initiates a bundle. 711bdd1243dSDimitry Andric std::string BundleStartString; 712bdd1243dSDimitry Andric 713bdd1243dSDimitry Andric /// String that closes a bundle. 714bdd1243dSDimitry Andric std::string BundleEndString; 715bdd1243dSDimitry Andric 716bdd1243dSDimitry Andric /// Number of chars read from input. 717bdd1243dSDimitry Andric size_t ReadChars = 0u; 718bdd1243dSDimitry Andric 719bdd1243dSDimitry Andric protected: 720bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 721bdd1243dSDimitry Andric 722bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 723bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 724bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 725bdd1243dSDimitry Andric 726bdd1243dSDimitry Andric // Find start of the bundle. 727bdd1243dSDimitry Andric ReadChars = FC.find(BundleStartString, ReadChars); 728bdd1243dSDimitry Andric if (ReadChars == FC.npos) 729bdd1243dSDimitry Andric return std::nullopt; 730bdd1243dSDimitry Andric 731bdd1243dSDimitry Andric // Get position of the triple. 732bdd1243dSDimitry Andric size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); 733bdd1243dSDimitry Andric 734bdd1243dSDimitry Andric // Get position that closes the triple. 735bdd1243dSDimitry Andric size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); 736bdd1243dSDimitry Andric if (TripleEnd == FC.npos) 737bdd1243dSDimitry Andric return std::nullopt; 738bdd1243dSDimitry Andric 739bdd1243dSDimitry Andric // Next time we read after the new line. 740bdd1243dSDimitry Andric ++ReadChars; 741bdd1243dSDimitry Andric 742bdd1243dSDimitry Andric return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); 743bdd1243dSDimitry Andric } 744bdd1243dSDimitry Andric 745bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { 746bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 747bdd1243dSDimitry Andric 748bdd1243dSDimitry Andric // Read up to the next new line. 749bdd1243dSDimitry Andric assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); 750bdd1243dSDimitry Andric 751bdd1243dSDimitry Andric size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); 752bdd1243dSDimitry Andric if (TripleEnd != FC.npos) 753bdd1243dSDimitry Andric // Next time we read after the new line. 754bdd1243dSDimitry Andric ++ReadChars; 755bdd1243dSDimitry Andric 756bdd1243dSDimitry Andric return Error::success(); 757bdd1243dSDimitry Andric } 758bdd1243dSDimitry Andric 759bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 760bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 761bdd1243dSDimitry Andric size_t BundleStart = ReadChars; 762bdd1243dSDimitry Andric 763bdd1243dSDimitry Andric // Find end of the bundle. 764bdd1243dSDimitry Andric size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); 765bdd1243dSDimitry Andric 766bdd1243dSDimitry Andric StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); 767bdd1243dSDimitry Andric OS << Bundle; 768bdd1243dSDimitry Andric 769bdd1243dSDimitry Andric return Error::success(); 770bdd1243dSDimitry Andric } 771bdd1243dSDimitry Andric 772*5f757f3fSDimitry Andric Error WriteHeader(raw_ostream &OS, 773bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 774bdd1243dSDimitry Andric return Error::success(); 775bdd1243dSDimitry Andric } 776bdd1243dSDimitry Andric 777*5f757f3fSDimitry Andric Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 778bdd1243dSDimitry Andric OS << BundleStartString << TargetTriple << "\n"; 779bdd1243dSDimitry Andric return Error::success(); 780bdd1243dSDimitry Andric } 781bdd1243dSDimitry Andric 782*5f757f3fSDimitry Andric Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 783bdd1243dSDimitry Andric OS << BundleEndString << TargetTriple << "\n"; 784bdd1243dSDimitry Andric return Error::success(); 785bdd1243dSDimitry Andric } 786bdd1243dSDimitry Andric 787*5f757f3fSDimitry Andric Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 788bdd1243dSDimitry Andric OS << Input.getBuffer(); 789bdd1243dSDimitry Andric return Error::success(); 790bdd1243dSDimitry Andric } 791bdd1243dSDimitry Andric 792bdd1243dSDimitry Andric public: 793bdd1243dSDimitry Andric TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) { 794bdd1243dSDimitry Andric BundleStartString = 795bdd1243dSDimitry Andric "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; 796bdd1243dSDimitry Andric BundleEndString = 797bdd1243dSDimitry Andric "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; 798bdd1243dSDimitry Andric } 799bdd1243dSDimitry Andric 800bdd1243dSDimitry Andric Error listBundleIDsCallback(MemoryBuffer &Input, 801bdd1243dSDimitry Andric const BundleInfo &Info) final { 802bdd1243dSDimitry Andric // TODO: To list bundle IDs in a bundled text file we need to go through 803bdd1243dSDimitry Andric // all bundles. The format of bundled text file may need to include a 804bdd1243dSDimitry Andric // header if the performance of listing bundle IDs of bundled text file is 805bdd1243dSDimitry Andric // important. 806bdd1243dSDimitry Andric ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); 807bdd1243dSDimitry Andric if (Error Err = ReadBundleEnd(Input)) 808bdd1243dSDimitry Andric return Err; 809bdd1243dSDimitry Andric return Error::success(); 810bdd1243dSDimitry Andric } 811bdd1243dSDimitry Andric }; 812bdd1243dSDimitry Andric } // namespace 813bdd1243dSDimitry Andric 814bdd1243dSDimitry Andric /// Return an appropriate object file handler. We use the specific object 815bdd1243dSDimitry Andric /// handler if we know how to deal with that format, otherwise we use a default 816bdd1243dSDimitry Andric /// binary file handler. 817bdd1243dSDimitry Andric static std::unique_ptr<FileHandler> 818bdd1243dSDimitry Andric CreateObjectFileHandler(MemoryBuffer &FirstInput, 819bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 820bdd1243dSDimitry Andric // Check if the input file format is one that we know how to deal with. 821bdd1243dSDimitry Andric Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); 822bdd1243dSDimitry Andric 823bdd1243dSDimitry Andric // We only support regular object files. If failed to open the input as a 824bdd1243dSDimitry Andric // known binary or this is not an object file use the default binary handler. 825bdd1243dSDimitry Andric if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) 826bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 827bdd1243dSDimitry Andric 828bdd1243dSDimitry Andric // Otherwise create an object file handler. The handler will be owned by the 829bdd1243dSDimitry Andric // client of this function. 830bdd1243dSDimitry Andric return std::make_unique<ObjectFileHandler>( 831bdd1243dSDimitry Andric std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())), 832bdd1243dSDimitry Andric BundlerConfig); 833bdd1243dSDimitry Andric } 834bdd1243dSDimitry Andric 835bdd1243dSDimitry Andric /// Return an appropriate handler given the input files and options. 836bdd1243dSDimitry Andric static Expected<std::unique_ptr<FileHandler>> 837bdd1243dSDimitry Andric CreateFileHandler(MemoryBuffer &FirstInput, 838bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 839bdd1243dSDimitry Andric std::string FilesType = BundlerConfig.FilesType; 840bdd1243dSDimitry Andric 841bdd1243dSDimitry Andric if (FilesType == "i") 842bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 843bdd1243dSDimitry Andric if (FilesType == "ii") 844bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 845bdd1243dSDimitry Andric if (FilesType == "cui") 846bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 847bdd1243dSDimitry Andric if (FilesType == "hipi") 848bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 849bdd1243dSDimitry Andric // TODO: `.d` should be eventually removed once `-M` and its variants are 850bdd1243dSDimitry Andric // handled properly in offload compilation. 851bdd1243dSDimitry Andric if (FilesType == "d") 852bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 853bdd1243dSDimitry Andric if (FilesType == "ll") 854bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/";"); 855bdd1243dSDimitry Andric if (FilesType == "bc") 856bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 857bdd1243dSDimitry Andric if (FilesType == "s") 858bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 859bdd1243dSDimitry Andric if (FilesType == "o") 860bdd1243dSDimitry Andric return CreateObjectFileHandler(FirstInput, BundlerConfig); 861bdd1243dSDimitry Andric if (FilesType == "a") 862bdd1243dSDimitry Andric return CreateObjectFileHandler(FirstInput, BundlerConfig); 863bdd1243dSDimitry Andric if (FilesType == "gch") 864bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 865bdd1243dSDimitry Andric if (FilesType == "ast") 866bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 867bdd1243dSDimitry Andric 868bdd1243dSDimitry Andric return createStringError(errc::invalid_argument, 869bdd1243dSDimitry Andric "'" + FilesType + "': invalid file type specified"); 870bdd1243dSDimitry Andric } 871bdd1243dSDimitry Andric 872*5f757f3fSDimitry Andric OffloadBundlerConfig::OffloadBundlerConfig() { 873*5f757f3fSDimitry Andric auto IgnoreEnvVarOpt = 874*5f757f3fSDimitry Andric llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR"); 875*5f757f3fSDimitry Andric if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1") 876*5f757f3fSDimitry Andric return; 877*5f757f3fSDimitry Andric 878*5f757f3fSDimitry Andric auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE"); 879*5f757f3fSDimitry Andric if (VerboseEnvVarOpt.has_value()) 880*5f757f3fSDimitry Andric Verbose = VerboseEnvVarOpt.value() == "1"; 881*5f757f3fSDimitry Andric 882*5f757f3fSDimitry Andric auto CompressEnvVarOpt = 883*5f757f3fSDimitry Andric llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS"); 884*5f757f3fSDimitry Andric if (CompressEnvVarOpt.has_value()) 885*5f757f3fSDimitry Andric Compress = CompressEnvVarOpt.value() == "1"; 886*5f757f3fSDimitry Andric } 887*5f757f3fSDimitry Andric 888*5f757f3fSDimitry Andric llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> 889*5f757f3fSDimitry Andric CompressedOffloadBundle::compress(const llvm::MemoryBuffer &Input, 890*5f757f3fSDimitry Andric bool Verbose) { 891*5f757f3fSDimitry Andric llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time", 892*5f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 893*5f757f3fSDimitry Andric if (Verbose) 894*5f757f3fSDimitry Andric HashTimer.startTimer(); 895*5f757f3fSDimitry Andric llvm::MD5 Hash; 896*5f757f3fSDimitry Andric llvm::MD5::MD5Result Result; 897*5f757f3fSDimitry Andric Hash.update(Input.getBuffer()); 898*5f757f3fSDimitry Andric Hash.final(Result); 899*5f757f3fSDimitry Andric uint64_t TruncatedHash = Result.low(); 900*5f757f3fSDimitry Andric if (Verbose) 901*5f757f3fSDimitry Andric HashTimer.stopTimer(); 902*5f757f3fSDimitry Andric 903*5f757f3fSDimitry Andric SmallVector<uint8_t, 0> CompressedBuffer; 904*5f757f3fSDimitry Andric auto BufferUint8 = llvm::ArrayRef<uint8_t>( 905*5f757f3fSDimitry Andric reinterpret_cast<const uint8_t *>(Input.getBuffer().data()), 906*5f757f3fSDimitry Andric Input.getBuffer().size()); 907*5f757f3fSDimitry Andric 908*5f757f3fSDimitry Andric llvm::compression::Format CompressionFormat; 909*5f757f3fSDimitry Andric 910*5f757f3fSDimitry Andric if (llvm::compression::zstd::isAvailable()) 911*5f757f3fSDimitry Andric CompressionFormat = llvm::compression::Format::Zstd; 912*5f757f3fSDimitry Andric else if (llvm::compression::zlib::isAvailable()) 913*5f757f3fSDimitry Andric CompressionFormat = llvm::compression::Format::Zlib; 914*5f757f3fSDimitry Andric else 915*5f757f3fSDimitry Andric return createStringError(llvm::inconvertibleErrorCode(), 916*5f757f3fSDimitry Andric "Compression not supported"); 917*5f757f3fSDimitry Andric 918*5f757f3fSDimitry Andric llvm::Timer CompressTimer("Compression Timer", "Compression time", 919*5f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 920*5f757f3fSDimitry Andric if (Verbose) 921*5f757f3fSDimitry Andric CompressTimer.startTimer(); 922*5f757f3fSDimitry Andric llvm::compression::compress(CompressionFormat, BufferUint8, CompressedBuffer); 923*5f757f3fSDimitry Andric if (Verbose) 924*5f757f3fSDimitry Andric CompressTimer.stopTimer(); 925*5f757f3fSDimitry Andric 926*5f757f3fSDimitry Andric uint16_t CompressionMethod = static_cast<uint16_t>(CompressionFormat); 927*5f757f3fSDimitry Andric uint32_t UncompressedSize = Input.getBuffer().size(); 928*5f757f3fSDimitry Andric 929*5f757f3fSDimitry Andric SmallVector<char, 0> FinalBuffer; 930*5f757f3fSDimitry Andric llvm::raw_svector_ostream OS(FinalBuffer); 931*5f757f3fSDimitry Andric OS << MagicNumber; 932*5f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version)); 933*5f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&CompressionMethod), 934*5f757f3fSDimitry Andric sizeof(CompressionMethod)); 935*5f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&UncompressedSize), 936*5f757f3fSDimitry Andric sizeof(UncompressedSize)); 937*5f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(&TruncatedHash), 938*5f757f3fSDimitry Andric sizeof(TruncatedHash)); 939*5f757f3fSDimitry Andric OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()), 940*5f757f3fSDimitry Andric CompressedBuffer.size()); 941*5f757f3fSDimitry Andric 942*5f757f3fSDimitry Andric if (Verbose) { 943*5f757f3fSDimitry Andric auto MethodUsed = 944*5f757f3fSDimitry Andric CompressionFormat == llvm::compression::Format::Zstd ? "zstd" : "zlib"; 945*5f757f3fSDimitry Andric llvm::errs() << "Compressed bundle format version: " << Version << "\n" 946*5f757f3fSDimitry Andric << "Compression method used: " << MethodUsed << "\n" 947*5f757f3fSDimitry Andric << "Binary size before compression: " << UncompressedSize 948*5f757f3fSDimitry Andric << " bytes\n" 949*5f757f3fSDimitry Andric << "Binary size after compression: " << CompressedBuffer.size() 950*5f757f3fSDimitry Andric << " bytes\n" 951*5f757f3fSDimitry Andric << "Truncated MD5 hash: " 952*5f757f3fSDimitry Andric << llvm::format_hex(TruncatedHash, 16) << "\n"; 953*5f757f3fSDimitry Andric } 954*5f757f3fSDimitry Andric 955*5f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy( 956*5f757f3fSDimitry Andric llvm::StringRef(FinalBuffer.data(), FinalBuffer.size())); 957*5f757f3fSDimitry Andric } 958*5f757f3fSDimitry Andric 959*5f757f3fSDimitry Andric llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> 960*5f757f3fSDimitry Andric CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input, 961*5f757f3fSDimitry Andric bool Verbose) { 962*5f757f3fSDimitry Andric 963*5f757f3fSDimitry Andric StringRef Blob = Input.getBuffer(); 964*5f757f3fSDimitry Andric 965*5f757f3fSDimitry Andric if (Blob.size() < HeaderSize) { 966*5f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Blob); 967*5f757f3fSDimitry Andric } 968*5f757f3fSDimitry Andric if (llvm::identify_magic(Blob) != 969*5f757f3fSDimitry Andric llvm::file_magic::offload_bundle_compressed) { 970*5f757f3fSDimitry Andric if (Verbose) 971*5f757f3fSDimitry Andric llvm::errs() << "Uncompressed bundle.\n"; 972*5f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Blob); 973*5f757f3fSDimitry Andric } 974*5f757f3fSDimitry Andric 975*5f757f3fSDimitry Andric uint16_t ThisVersion; 976*5f757f3fSDimitry Andric uint16_t CompressionMethod; 977*5f757f3fSDimitry Andric uint32_t UncompressedSize; 978*5f757f3fSDimitry Andric uint64_t StoredHash; 979*5f757f3fSDimitry Andric memcpy(&ThisVersion, Input.getBuffer().data() + MagicNumber.size(), 980*5f757f3fSDimitry Andric sizeof(uint16_t)); 981*5f757f3fSDimitry Andric memcpy(&CompressionMethod, Blob.data() + MagicSize + VersionFieldSize, 982*5f757f3fSDimitry Andric sizeof(uint16_t)); 983*5f757f3fSDimitry Andric memcpy(&UncompressedSize, 984*5f757f3fSDimitry Andric Blob.data() + MagicSize + VersionFieldSize + MethodFieldSize, 985*5f757f3fSDimitry Andric sizeof(uint32_t)); 986*5f757f3fSDimitry Andric memcpy(&StoredHash, 987*5f757f3fSDimitry Andric Blob.data() + MagicSize + VersionFieldSize + MethodFieldSize + 988*5f757f3fSDimitry Andric SizeFieldSize, 989*5f757f3fSDimitry Andric sizeof(uint64_t)); 990*5f757f3fSDimitry Andric 991*5f757f3fSDimitry Andric llvm::compression::Format CompressionFormat; 992*5f757f3fSDimitry Andric if (CompressionMethod == 993*5f757f3fSDimitry Andric static_cast<uint16_t>(llvm::compression::Format::Zlib)) 994*5f757f3fSDimitry Andric CompressionFormat = llvm::compression::Format::Zlib; 995*5f757f3fSDimitry Andric else if (CompressionMethod == 996*5f757f3fSDimitry Andric static_cast<uint16_t>(llvm::compression::Format::Zstd)) 997*5f757f3fSDimitry Andric CompressionFormat = llvm::compression::Format::Zstd; 998*5f757f3fSDimitry Andric else 999*5f757f3fSDimitry Andric return createStringError(inconvertibleErrorCode(), 1000*5f757f3fSDimitry Andric "Unknown compressing method"); 1001*5f757f3fSDimitry Andric 1002*5f757f3fSDimitry Andric llvm::Timer DecompressTimer("Decompression Timer", "Decompression time", 1003*5f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 1004*5f757f3fSDimitry Andric if (Verbose) 1005*5f757f3fSDimitry Andric DecompressTimer.startTimer(); 1006*5f757f3fSDimitry Andric 1007*5f757f3fSDimitry Andric SmallVector<uint8_t, 0> DecompressedData; 1008*5f757f3fSDimitry Andric StringRef CompressedData = Blob.substr(HeaderSize); 1009*5f757f3fSDimitry Andric if (llvm::Error DecompressionError = llvm::compression::decompress( 1010*5f757f3fSDimitry Andric CompressionFormat, llvm::arrayRefFromStringRef(CompressedData), 1011*5f757f3fSDimitry Andric DecompressedData, UncompressedSize)) 1012*5f757f3fSDimitry Andric return createStringError(inconvertibleErrorCode(), 1013*5f757f3fSDimitry Andric "Could not decompress embedded file contents: " + 1014*5f757f3fSDimitry Andric llvm::toString(std::move(DecompressionError))); 1015*5f757f3fSDimitry Andric 1016*5f757f3fSDimitry Andric if (Verbose) { 1017*5f757f3fSDimitry Andric DecompressTimer.stopTimer(); 1018*5f757f3fSDimitry Andric 1019*5f757f3fSDimitry Andric // Recalculate MD5 hash 1020*5f757f3fSDimitry Andric llvm::Timer HashRecalcTimer("Hash Recalculation Timer", 1021*5f757f3fSDimitry Andric "Hash recalculation time", 1022*5f757f3fSDimitry Andric ClangOffloadBundlerTimerGroup); 1023*5f757f3fSDimitry Andric HashRecalcTimer.startTimer(); 1024*5f757f3fSDimitry Andric llvm::MD5 Hash; 1025*5f757f3fSDimitry Andric llvm::MD5::MD5Result Result; 1026*5f757f3fSDimitry Andric Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData.data(), 1027*5f757f3fSDimitry Andric DecompressedData.size())); 1028*5f757f3fSDimitry Andric Hash.final(Result); 1029*5f757f3fSDimitry Andric uint64_t RecalculatedHash = Result.low(); 1030*5f757f3fSDimitry Andric HashRecalcTimer.stopTimer(); 1031*5f757f3fSDimitry Andric bool HashMatch = (StoredHash == RecalculatedHash); 1032*5f757f3fSDimitry Andric 1033*5f757f3fSDimitry Andric llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n" 1034*5f757f3fSDimitry Andric << "Decompression method: " 1035*5f757f3fSDimitry Andric << (CompressionFormat == llvm::compression::Format::Zlib 1036*5f757f3fSDimitry Andric ? "zlib" 1037*5f757f3fSDimitry Andric : "zstd") 1038*5f757f3fSDimitry Andric << "\n" 1039*5f757f3fSDimitry Andric << "Size before decompression: " << CompressedData.size() 1040*5f757f3fSDimitry Andric << " bytes\n" 1041*5f757f3fSDimitry Andric << "Size after decompression: " << UncompressedSize 1042*5f757f3fSDimitry Andric << " bytes\n" 1043*5f757f3fSDimitry Andric << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n" 1044*5f757f3fSDimitry Andric << "Recalculated hash: " 1045*5f757f3fSDimitry Andric << llvm::format_hex(RecalculatedHash, 16) << "\n" 1046*5f757f3fSDimitry Andric << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n"; 1047*5f757f3fSDimitry Andric } 1048*5f757f3fSDimitry Andric 1049*5f757f3fSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy( 1050*5f757f3fSDimitry Andric llvm::toStringRef(DecompressedData)); 1051*5f757f3fSDimitry Andric } 1052*5f757f3fSDimitry Andric 1053bdd1243dSDimitry Andric // List bundle IDs. Return true if an error was found. 1054bdd1243dSDimitry Andric Error OffloadBundler::ListBundleIDsInFile( 1055bdd1243dSDimitry Andric StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) { 1056bdd1243dSDimitry Andric // Open Input file. 1057bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1058bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(InputFileName); 1059bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 1060bdd1243dSDimitry Andric return createFileError(InputFileName, EC); 1061bdd1243dSDimitry Andric 1062*5f757f3fSDimitry Andric // Decompress the input if necessary. 1063*5f757f3fSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 1064*5f757f3fSDimitry Andric CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose); 1065*5f757f3fSDimitry Andric if (!DecompressedBufferOrErr) 1066*5f757f3fSDimitry Andric return createStringError( 1067*5f757f3fSDimitry Andric inconvertibleErrorCode(), 1068*5f757f3fSDimitry Andric "Failed to decompress input: " + 1069*5f757f3fSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError())); 1070*5f757f3fSDimitry Andric 1071*5f757f3fSDimitry Andric MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr; 1072bdd1243dSDimitry Andric 1073bdd1243dSDimitry Andric // Select the right files handler. 1074bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1075*5f757f3fSDimitry Andric CreateFileHandler(DecompressedInput, BundlerConfig); 1076bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1077bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1078bdd1243dSDimitry Andric 1079bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1080bdd1243dSDimitry Andric assert(FH); 1081*5f757f3fSDimitry Andric return FH->listBundleIDs(DecompressedInput); 1082*5f757f3fSDimitry Andric } 1083*5f757f3fSDimitry Andric 1084*5f757f3fSDimitry Andric /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given 1085*5f757f3fSDimitry Andric /// target \p TargetInfo. 1086*5f757f3fSDimitry Andric /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id 1087*5f757f3fSDimitry Andric bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, 1088*5f757f3fSDimitry Andric const OffloadTargetInfo &TargetInfo) { 1089*5f757f3fSDimitry Andric 1090*5f757f3fSDimitry Andric // Compatible in case of exact match. 1091*5f757f3fSDimitry Andric if (CodeObjectInfo == TargetInfo) { 1092*5f757f3fSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 1093*5f757f3fSDimitry Andric dbgs() << "Compatible: Exact match: \t[CodeObject: " 1094*5f757f3fSDimitry Andric << CodeObjectInfo.str() 1095*5f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1096*5f757f3fSDimitry Andric return true; 1097*5f757f3fSDimitry Andric } 1098*5f757f3fSDimitry Andric 1099*5f757f3fSDimitry Andric // Incompatible if Kinds or Triples mismatch. 1100*5f757f3fSDimitry Andric if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || 1101*5f757f3fSDimitry Andric !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { 1102*5f757f3fSDimitry Andric DEBUG_WITH_TYPE( 1103*5f757f3fSDimitry Andric "CodeObjectCompatibility", 1104*5f757f3fSDimitry Andric dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " 1105*5f757f3fSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 1106*5f757f3fSDimitry Andric << "]\n"); 1107*5f757f3fSDimitry Andric return false; 1108*5f757f3fSDimitry Andric } 1109*5f757f3fSDimitry Andric 1110*5f757f3fSDimitry Andric // Incompatible if Processors mismatch. 1111*5f757f3fSDimitry Andric llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap; 1112*5f757f3fSDimitry Andric std::optional<StringRef> CodeObjectProc = clang::parseTargetID( 1113*5f757f3fSDimitry Andric CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap); 1114*5f757f3fSDimitry Andric std::optional<StringRef> TargetProc = clang::parseTargetID( 1115*5f757f3fSDimitry Andric TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap); 1116*5f757f3fSDimitry Andric 1117*5f757f3fSDimitry Andric // Both TargetProc and CodeObjectProc can't be empty here. 1118*5f757f3fSDimitry Andric if (!TargetProc || !CodeObjectProc || 1119*5f757f3fSDimitry Andric CodeObjectProc.value() != TargetProc.value()) { 1120*5f757f3fSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 1121*5f757f3fSDimitry Andric dbgs() << "Incompatible: Processor mismatch \t[CodeObject: " 1122*5f757f3fSDimitry Andric << CodeObjectInfo.str() 1123*5f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1124*5f757f3fSDimitry Andric return false; 1125*5f757f3fSDimitry Andric } 1126*5f757f3fSDimitry Andric 1127*5f757f3fSDimitry Andric // Incompatible if CodeObject has more features than Target, irrespective of 1128*5f757f3fSDimitry Andric // type or sign of features. 1129*5f757f3fSDimitry Andric if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) { 1130*5f757f3fSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 1131*5f757f3fSDimitry Andric dbgs() << "Incompatible: CodeObject has more features " 1132*5f757f3fSDimitry Andric "than target \t[CodeObject: " 1133*5f757f3fSDimitry Andric << CodeObjectInfo.str() 1134*5f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1135*5f757f3fSDimitry Andric return false; 1136*5f757f3fSDimitry Andric } 1137*5f757f3fSDimitry Andric 1138*5f757f3fSDimitry Andric // Compatible if each target feature specified by target is compatible with 1139*5f757f3fSDimitry Andric // target feature of code object. The target feature is compatible if the 1140*5f757f3fSDimitry Andric // code object does not specify it (meaning Any), or if it specifies it 1141*5f757f3fSDimitry Andric // with the same value (meaning On or Off). 1142*5f757f3fSDimitry Andric for (const auto &CodeObjectFeature : CodeObjectFeatureMap) { 1143*5f757f3fSDimitry Andric auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey()); 1144*5f757f3fSDimitry Andric if (TargetFeature == TargetFeatureMap.end()) { 1145*5f757f3fSDimitry Andric DEBUG_WITH_TYPE( 1146*5f757f3fSDimitry Andric "CodeObjectCompatibility", 1147*5f757f3fSDimitry Andric dbgs() 1148*5f757f3fSDimitry Andric << "Incompatible: Value of CodeObject's non-ANY feature is " 1149*5f757f3fSDimitry Andric "not matching with Target feature's ANY value \t[CodeObject: " 1150*5f757f3fSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 1151*5f757f3fSDimitry Andric << "]\n"); 1152*5f757f3fSDimitry Andric return false; 1153*5f757f3fSDimitry Andric } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) { 1154*5f757f3fSDimitry Andric DEBUG_WITH_TYPE( 1155*5f757f3fSDimitry Andric "CodeObjectCompatibility", 1156*5f757f3fSDimitry Andric dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is " 1157*5f757f3fSDimitry Andric "not matching with Target feature's non-ANY value " 1158*5f757f3fSDimitry Andric "\t[CodeObject: " 1159*5f757f3fSDimitry Andric << CodeObjectInfo.str() 1160*5f757f3fSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1161*5f757f3fSDimitry Andric return false; 1162*5f757f3fSDimitry Andric } 1163*5f757f3fSDimitry Andric } 1164*5f757f3fSDimitry Andric 1165*5f757f3fSDimitry Andric // CodeObject is compatible if all features of Target are: 1166*5f757f3fSDimitry Andric // - either, present in the Code Object's features map with the same sign, 1167*5f757f3fSDimitry Andric // - or, the feature is missing from CodeObjects's features map i.e. it is 1168*5f757f3fSDimitry Andric // set to ANY 1169*5f757f3fSDimitry Andric DEBUG_WITH_TYPE( 1170*5f757f3fSDimitry Andric "CodeObjectCompatibility", 1171*5f757f3fSDimitry Andric dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: " 1172*5f757f3fSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 1173*5f757f3fSDimitry Andric << "]\n"); 1174*5f757f3fSDimitry Andric return true; 1175bdd1243dSDimitry Andric } 1176bdd1243dSDimitry Andric 1177bdd1243dSDimitry Andric /// Bundle the files. Return true if an error was found. 1178bdd1243dSDimitry Andric Error OffloadBundler::BundleFiles() { 1179bdd1243dSDimitry Andric std::error_code EC; 1180bdd1243dSDimitry Andric 1181*5f757f3fSDimitry Andric // Create a buffer to hold the content before compressing. 1182*5f757f3fSDimitry Andric SmallVector<char, 0> Buffer; 1183*5f757f3fSDimitry Andric llvm::raw_svector_ostream BufferStream(Buffer); 1184bdd1243dSDimitry Andric 1185bdd1243dSDimitry Andric // Open input files. 1186bdd1243dSDimitry Andric SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; 1187bdd1243dSDimitry Andric InputBuffers.reserve(BundlerConfig.InputFileNames.size()); 1188bdd1243dSDimitry Andric for (auto &I : BundlerConfig.InputFileNames) { 1189bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1190bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(I); 1191bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 1192bdd1243dSDimitry Andric return createFileError(I, EC); 1193bdd1243dSDimitry Andric InputBuffers.emplace_back(std::move(*CodeOrErr)); 1194bdd1243dSDimitry Andric } 1195bdd1243dSDimitry Andric 1196bdd1243dSDimitry Andric // Get the file handler. We use the host buffer as reference. 1197bdd1243dSDimitry Andric assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) && 1198bdd1243dSDimitry Andric "Host input index undefined??"); 1199bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler( 1200bdd1243dSDimitry Andric *InputBuffers[BundlerConfig.AllowNoHost ? 0 1201bdd1243dSDimitry Andric : BundlerConfig.HostInputIndex], 1202bdd1243dSDimitry Andric BundlerConfig); 1203bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1204bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1205bdd1243dSDimitry Andric 1206bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1207bdd1243dSDimitry Andric assert(FH); 1208bdd1243dSDimitry Andric 1209bdd1243dSDimitry Andric // Write header. 1210*5f757f3fSDimitry Andric if (Error Err = FH->WriteHeader(BufferStream, InputBuffers)) 1211bdd1243dSDimitry Andric return Err; 1212bdd1243dSDimitry Andric 1213bdd1243dSDimitry Andric // Write all bundles along with the start/end markers. If an error was found 1214bdd1243dSDimitry Andric // writing the end of the bundle component, abort the bundle writing. 1215bdd1243dSDimitry Andric auto Input = InputBuffers.begin(); 1216bdd1243dSDimitry Andric for (auto &Triple : BundlerConfig.TargetNames) { 1217*5f757f3fSDimitry Andric if (Error Err = FH->WriteBundleStart(BufferStream, Triple)) 1218bdd1243dSDimitry Andric return Err; 1219*5f757f3fSDimitry Andric if (Error Err = FH->WriteBundle(BufferStream, **Input)) 1220bdd1243dSDimitry Andric return Err; 1221*5f757f3fSDimitry Andric if (Error Err = FH->WriteBundleEnd(BufferStream, Triple)) 1222bdd1243dSDimitry Andric return Err; 1223bdd1243dSDimitry Andric ++Input; 1224bdd1243dSDimitry Andric } 1225*5f757f3fSDimitry Andric 1226*5f757f3fSDimitry Andric raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC, 1227*5f757f3fSDimitry Andric sys::fs::OF_None); 1228*5f757f3fSDimitry Andric if (EC) 1229*5f757f3fSDimitry Andric return createFileError(BundlerConfig.OutputFileNames.front(), EC); 1230*5f757f3fSDimitry Andric 1231*5f757f3fSDimitry Andric SmallVector<char, 0> CompressedBuffer; 1232*5f757f3fSDimitry Andric if (BundlerConfig.Compress) { 1233*5f757f3fSDimitry Andric std::unique_ptr<llvm::MemoryBuffer> BufferMemory = 1234*5f757f3fSDimitry Andric llvm::MemoryBuffer::getMemBufferCopy( 1235*5f757f3fSDimitry Andric llvm::StringRef(Buffer.data(), Buffer.size())); 1236*5f757f3fSDimitry Andric auto CompressionResult = 1237*5f757f3fSDimitry Andric CompressedOffloadBundle::compress(*BufferMemory, BundlerConfig.Verbose); 1238*5f757f3fSDimitry Andric if (auto Error = CompressionResult.takeError()) 1239*5f757f3fSDimitry Andric return Error; 1240*5f757f3fSDimitry Andric 1241*5f757f3fSDimitry Andric auto CompressedMemBuffer = std::move(CompressionResult.get()); 1242*5f757f3fSDimitry Andric CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(), 1243*5f757f3fSDimitry Andric CompressedMemBuffer->getBufferEnd()); 1244*5f757f3fSDimitry Andric } else 1245*5f757f3fSDimitry Andric CompressedBuffer = Buffer; 1246*5f757f3fSDimitry Andric 1247*5f757f3fSDimitry Andric OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size()); 1248*5f757f3fSDimitry Andric 1249*5f757f3fSDimitry Andric return FH->finalizeOutputFile(); 1250bdd1243dSDimitry Andric } 1251bdd1243dSDimitry Andric 1252bdd1243dSDimitry Andric // Unbundle the files. Return true if an error was found. 1253bdd1243dSDimitry Andric Error OffloadBundler::UnbundleFiles() { 1254bdd1243dSDimitry Andric // Open Input file. 1255bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1256bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front()); 1257bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 1258bdd1243dSDimitry Andric return createFileError(BundlerConfig.InputFileNames.front(), EC); 1259bdd1243dSDimitry Andric 1260*5f757f3fSDimitry Andric // Decompress the input if necessary. 1261*5f757f3fSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 1262*5f757f3fSDimitry Andric CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose); 1263*5f757f3fSDimitry Andric if (!DecompressedBufferOrErr) 1264*5f757f3fSDimitry Andric return createStringError( 1265*5f757f3fSDimitry Andric inconvertibleErrorCode(), 1266*5f757f3fSDimitry Andric "Failed to decompress input: " + 1267*5f757f3fSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError())); 1268*5f757f3fSDimitry Andric 1269*5f757f3fSDimitry Andric MemoryBuffer &Input = **DecompressedBufferOrErr; 1270bdd1243dSDimitry Andric 1271bdd1243dSDimitry Andric // Select the right files handler. 1272bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1273bdd1243dSDimitry Andric CreateFileHandler(Input, BundlerConfig); 1274bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1275bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1276bdd1243dSDimitry Andric 1277bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1278bdd1243dSDimitry Andric assert(FH); 1279bdd1243dSDimitry Andric 1280bdd1243dSDimitry Andric // Read the header of the bundled file. 1281bdd1243dSDimitry Andric if (Error Err = FH->ReadHeader(Input)) 1282bdd1243dSDimitry Andric return Err; 1283bdd1243dSDimitry Andric 1284bdd1243dSDimitry Andric // Create a work list that consist of the map triple/output file. 1285bdd1243dSDimitry Andric StringMap<StringRef> Worklist; 1286bdd1243dSDimitry Andric auto Output = BundlerConfig.OutputFileNames.begin(); 1287bdd1243dSDimitry Andric for (auto &Triple : BundlerConfig.TargetNames) { 1288bdd1243dSDimitry Andric Worklist[Triple] = *Output; 1289bdd1243dSDimitry Andric ++Output; 1290bdd1243dSDimitry Andric } 1291bdd1243dSDimitry Andric 1292bdd1243dSDimitry Andric // Read all the bundles that are in the work list. If we find no bundles we 1293bdd1243dSDimitry Andric // assume the file is meant for the host target. 1294bdd1243dSDimitry Andric bool FoundHostBundle = false; 1295bdd1243dSDimitry Andric while (!Worklist.empty()) { 1296bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurTripleOrErr = 1297bdd1243dSDimitry Andric FH->ReadBundleStart(Input); 1298bdd1243dSDimitry Andric if (!CurTripleOrErr) 1299bdd1243dSDimitry Andric return CurTripleOrErr.takeError(); 1300bdd1243dSDimitry Andric 1301bdd1243dSDimitry Andric // We don't have more bundles. 1302bdd1243dSDimitry Andric if (!*CurTripleOrErr) 1303bdd1243dSDimitry Andric break; 1304bdd1243dSDimitry Andric 1305bdd1243dSDimitry Andric StringRef CurTriple = **CurTripleOrErr; 1306bdd1243dSDimitry Andric assert(!CurTriple.empty()); 1307bdd1243dSDimitry Andric 1308bdd1243dSDimitry Andric auto Output = Worklist.begin(); 1309bdd1243dSDimitry Andric for (auto E = Worklist.end(); Output != E; Output++) { 1310bdd1243dSDimitry Andric if (isCodeObjectCompatible( 1311bdd1243dSDimitry Andric OffloadTargetInfo(CurTriple, BundlerConfig), 1312bdd1243dSDimitry Andric OffloadTargetInfo((*Output).first(), BundlerConfig))) { 1313bdd1243dSDimitry Andric break; 1314bdd1243dSDimitry Andric } 1315bdd1243dSDimitry Andric } 1316bdd1243dSDimitry Andric 1317bdd1243dSDimitry Andric if (Output == Worklist.end()) 1318bdd1243dSDimitry Andric continue; 1319bdd1243dSDimitry Andric // Check if the output file can be opened and copy the bundle to it. 1320bdd1243dSDimitry Andric std::error_code EC; 1321bdd1243dSDimitry Andric raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); 1322bdd1243dSDimitry Andric if (EC) 1323bdd1243dSDimitry Andric return createFileError((*Output).second, EC); 1324bdd1243dSDimitry Andric if (Error Err = FH->ReadBundle(OutputFile, Input)) 1325bdd1243dSDimitry Andric return Err; 1326bdd1243dSDimitry Andric if (Error Err = FH->ReadBundleEnd(Input)) 1327bdd1243dSDimitry Andric return Err; 1328bdd1243dSDimitry Andric Worklist.erase(Output); 1329bdd1243dSDimitry Andric 1330bdd1243dSDimitry Andric // Record if we found the host bundle. 1331bdd1243dSDimitry Andric auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig); 1332bdd1243dSDimitry Andric if (OffloadInfo.hasHostKind()) 1333bdd1243dSDimitry Andric FoundHostBundle = true; 1334bdd1243dSDimitry Andric } 1335bdd1243dSDimitry Andric 1336bdd1243dSDimitry Andric if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) { 1337bdd1243dSDimitry Andric std::string ErrMsg = "Can't find bundles for"; 1338bdd1243dSDimitry Andric std::set<StringRef> Sorted; 1339bdd1243dSDimitry Andric for (auto &E : Worklist) 1340bdd1243dSDimitry Andric Sorted.insert(E.first()); 1341bdd1243dSDimitry Andric unsigned I = 0; 1342bdd1243dSDimitry Andric unsigned Last = Sorted.size() - 1; 1343bdd1243dSDimitry Andric for (auto &E : Sorted) { 1344bdd1243dSDimitry Andric if (I != 0 && Last > 1) 1345bdd1243dSDimitry Andric ErrMsg += ","; 1346bdd1243dSDimitry Andric ErrMsg += " "; 1347bdd1243dSDimitry Andric if (I == Last && I != 0) 1348bdd1243dSDimitry Andric ErrMsg += "and "; 1349bdd1243dSDimitry Andric ErrMsg += E.str(); 1350bdd1243dSDimitry Andric ++I; 1351bdd1243dSDimitry Andric } 1352bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1353bdd1243dSDimitry Andric } 1354bdd1243dSDimitry Andric 1355bdd1243dSDimitry Andric // If no bundles were found, assume the input file is the host bundle and 1356bdd1243dSDimitry Andric // create empty files for the remaining targets. 1357bdd1243dSDimitry Andric if (Worklist.size() == BundlerConfig.TargetNames.size()) { 1358bdd1243dSDimitry Andric for (auto &E : Worklist) { 1359bdd1243dSDimitry Andric std::error_code EC; 1360bdd1243dSDimitry Andric raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1361bdd1243dSDimitry Andric if (EC) 1362bdd1243dSDimitry Andric return createFileError(E.second, EC); 1363bdd1243dSDimitry Andric 1364bdd1243dSDimitry Andric // If this entry has a host kind, copy the input file to the output file. 1365bdd1243dSDimitry Andric auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig); 1366bdd1243dSDimitry Andric if (OffloadInfo.hasHostKind()) 1367bdd1243dSDimitry Andric OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); 1368bdd1243dSDimitry Andric } 1369bdd1243dSDimitry Andric return Error::success(); 1370bdd1243dSDimitry Andric } 1371bdd1243dSDimitry Andric 1372bdd1243dSDimitry Andric // If we found elements, we emit an error if none of those were for the host 1373bdd1243dSDimitry Andric // in case host bundle name was provided in command line. 1374bdd1243dSDimitry Andric if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u || 1375bdd1243dSDimitry Andric BundlerConfig.AllowMissingBundles)) 1376bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), 1377bdd1243dSDimitry Andric "Can't find bundle for the host target"); 1378bdd1243dSDimitry Andric 1379bdd1243dSDimitry Andric // If we still have any elements in the worklist, create empty files for them. 1380bdd1243dSDimitry Andric for (auto &E : Worklist) { 1381bdd1243dSDimitry Andric std::error_code EC; 1382bdd1243dSDimitry Andric raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1383bdd1243dSDimitry Andric if (EC) 1384bdd1243dSDimitry Andric return createFileError(E.second, EC); 1385bdd1243dSDimitry Andric } 1386bdd1243dSDimitry Andric 1387bdd1243dSDimitry Andric return Error::success(); 1388bdd1243dSDimitry Andric } 1389bdd1243dSDimitry Andric 1390bdd1243dSDimitry Andric static Archive::Kind getDefaultArchiveKindForHost() { 1391bdd1243dSDimitry Andric return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN 1392bdd1243dSDimitry Andric : Archive::K_GNU; 1393bdd1243dSDimitry Andric } 1394bdd1243dSDimitry Andric 1395bdd1243dSDimitry Andric /// @brief Computes a list of targets among all given targets which are 1396bdd1243dSDimitry Andric /// compatible with this code object 1397bdd1243dSDimitry Andric /// @param [in] CodeObjectInfo Code Object 1398bdd1243dSDimitry Andric /// @param [out] CompatibleTargets List of all compatible targets among all 1399bdd1243dSDimitry Andric /// given targets 1400bdd1243dSDimitry Andric /// @return false, if no compatible target is found. 1401bdd1243dSDimitry Andric static bool 1402bdd1243dSDimitry Andric getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, 1403bdd1243dSDimitry Andric SmallVectorImpl<StringRef> &CompatibleTargets, 1404bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 1405bdd1243dSDimitry Andric if (!CompatibleTargets.empty()) { 1406bdd1243dSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 1407bdd1243dSDimitry Andric dbgs() << "CompatibleTargets list should be empty\n"); 1408bdd1243dSDimitry Andric return false; 1409bdd1243dSDimitry Andric } 1410bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1411bdd1243dSDimitry Andric auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig); 1412bdd1243dSDimitry Andric if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) 1413bdd1243dSDimitry Andric CompatibleTargets.push_back(Target); 1414bdd1243dSDimitry Andric } 1415bdd1243dSDimitry Andric return !CompatibleTargets.empty(); 1416bdd1243dSDimitry Andric } 1417bdd1243dSDimitry Andric 1418*5f757f3fSDimitry Andric // Check that each code object file in the input archive conforms to following 1419*5f757f3fSDimitry Andric // rule: for a specific processor, a feature either shows up in all target IDs, 1420*5f757f3fSDimitry Andric // or does not show up in any target IDs. Otherwise the target ID combination is 1421*5f757f3fSDimitry Andric // invalid. 1422*5f757f3fSDimitry Andric static Error 1423*5f757f3fSDimitry Andric CheckHeterogeneousArchive(StringRef ArchiveName, 1424*5f757f3fSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 1425*5f757f3fSDimitry Andric std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 1426*5f757f3fSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 1427*5f757f3fSDimitry Andric MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false); 1428*5f757f3fSDimitry Andric if (std::error_code EC = BufOrErr.getError()) 1429*5f757f3fSDimitry Andric return createFileError(ArchiveName, EC); 1430*5f757f3fSDimitry Andric 1431*5f757f3fSDimitry Andric ArchiveBuffers.push_back(std::move(*BufOrErr)); 1432*5f757f3fSDimitry Andric Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1433*5f757f3fSDimitry Andric Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1434*5f757f3fSDimitry Andric if (!LibOrErr) 1435*5f757f3fSDimitry Andric return LibOrErr.takeError(); 1436*5f757f3fSDimitry Andric 1437*5f757f3fSDimitry Andric auto Archive = std::move(*LibOrErr); 1438*5f757f3fSDimitry Andric 1439*5f757f3fSDimitry Andric Error ArchiveErr = Error::success(); 1440*5f757f3fSDimitry Andric auto ChildEnd = Archive->child_end(); 1441*5f757f3fSDimitry Andric 1442*5f757f3fSDimitry Andric /// Iterate over all bundled code object files in the input archive. 1443*5f757f3fSDimitry Andric for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1444*5f757f3fSDimitry Andric ArchiveIter != ChildEnd; ++ArchiveIter) { 1445*5f757f3fSDimitry Andric if (ArchiveErr) 1446*5f757f3fSDimitry Andric return ArchiveErr; 1447*5f757f3fSDimitry Andric auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1448*5f757f3fSDimitry Andric if (!ArchiveChildNameOrErr) 1449*5f757f3fSDimitry Andric return ArchiveChildNameOrErr.takeError(); 1450*5f757f3fSDimitry Andric 1451*5f757f3fSDimitry Andric auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1452*5f757f3fSDimitry Andric if (!CodeObjectBufferRefOrErr) 1453*5f757f3fSDimitry Andric return CodeObjectBufferRefOrErr.takeError(); 1454*5f757f3fSDimitry Andric 1455*5f757f3fSDimitry Andric auto CodeObjectBuffer = 1456*5f757f3fSDimitry Andric MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1457*5f757f3fSDimitry Andric 1458*5f757f3fSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1459*5f757f3fSDimitry Andric CreateFileHandler(*CodeObjectBuffer, BundlerConfig); 1460*5f757f3fSDimitry Andric if (!FileHandlerOrErr) 1461*5f757f3fSDimitry Andric return FileHandlerOrErr.takeError(); 1462*5f757f3fSDimitry Andric 1463*5f757f3fSDimitry Andric std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1464*5f757f3fSDimitry Andric assert(FileHandler); 1465*5f757f3fSDimitry Andric 1466*5f757f3fSDimitry Andric std::set<StringRef> BundleIds; 1467*5f757f3fSDimitry Andric auto CodeObjectFileError = 1468*5f757f3fSDimitry Andric FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds); 1469*5f757f3fSDimitry Andric if (CodeObjectFileError) 1470*5f757f3fSDimitry Andric return CodeObjectFileError; 1471*5f757f3fSDimitry Andric 1472*5f757f3fSDimitry Andric auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds); 1473*5f757f3fSDimitry Andric if (ConflictingArchs) { 1474*5f757f3fSDimitry Andric std::string ErrMsg = 1475*5f757f3fSDimitry Andric Twine("conflicting TargetIDs [" + ConflictingArchs.value().first + 1476*5f757f3fSDimitry Andric ", " + ConflictingArchs.value().second + "] found in " + 1477*5f757f3fSDimitry Andric ArchiveChildNameOrErr.get() + " of " + ArchiveName) 1478*5f757f3fSDimitry Andric .str(); 1479*5f757f3fSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1480*5f757f3fSDimitry Andric } 1481*5f757f3fSDimitry Andric } 1482*5f757f3fSDimitry Andric 1483*5f757f3fSDimitry Andric return ArchiveErr; 1484*5f757f3fSDimitry Andric } 1485*5f757f3fSDimitry Andric 1486bdd1243dSDimitry Andric /// UnbundleArchive takes an archive file (".a") as input containing bundled 1487bdd1243dSDimitry Andric /// code object files, and a list of offload targets (not host), and extracts 1488bdd1243dSDimitry Andric /// the code objects into a new archive file for each offload target. Each 1489bdd1243dSDimitry Andric /// resulting archive file contains all code object files corresponding to that 1490bdd1243dSDimitry Andric /// particular offload target. The created archive file does not 1491bdd1243dSDimitry Andric /// contain an index of the symbols and code object files are named as 1492*5f757f3fSDimitry Andric /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'. 1493bdd1243dSDimitry Andric Error OffloadBundler::UnbundleArchive() { 1494bdd1243dSDimitry Andric std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 1495bdd1243dSDimitry Andric 1496bdd1243dSDimitry Andric /// Map of target names with list of object files that will form the device 1497bdd1243dSDimitry Andric /// specific archive for that target 1498bdd1243dSDimitry Andric StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; 1499bdd1243dSDimitry Andric 1500bdd1243dSDimitry Andric // Map of target names and output archive filenames 1501bdd1243dSDimitry Andric StringMap<StringRef> TargetOutputFileNameMap; 1502bdd1243dSDimitry Andric 1503bdd1243dSDimitry Andric auto Output = BundlerConfig.OutputFileNames.begin(); 1504bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1505bdd1243dSDimitry Andric TargetOutputFileNameMap[Target] = *Output; 1506bdd1243dSDimitry Andric ++Output; 1507bdd1243dSDimitry Andric } 1508bdd1243dSDimitry Andric 1509bdd1243dSDimitry Andric StringRef IFName = BundlerConfig.InputFileNames.front(); 1510bdd1243dSDimitry Andric 1511*5f757f3fSDimitry Andric if (BundlerConfig.CheckInputArchive) { 1512*5f757f3fSDimitry Andric // For a specific processor, a feature either shows up in all target IDs, or 1513*5f757f3fSDimitry Andric // does not show up in any target IDs. Otherwise the target ID combination 1514*5f757f3fSDimitry Andric // is invalid. 1515*5f757f3fSDimitry Andric auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig); 1516*5f757f3fSDimitry Andric if (ArchiveError) { 1517*5f757f3fSDimitry Andric return ArchiveError; 1518*5f757f3fSDimitry Andric } 1519*5f757f3fSDimitry Andric } 1520*5f757f3fSDimitry Andric 1521bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 1522bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(IFName, true, false); 1523bdd1243dSDimitry Andric if (std::error_code EC = BufOrErr.getError()) 1524bdd1243dSDimitry Andric return createFileError(BundlerConfig.InputFileNames.front(), EC); 1525bdd1243dSDimitry Andric 1526bdd1243dSDimitry Andric ArchiveBuffers.push_back(std::move(*BufOrErr)); 1527bdd1243dSDimitry Andric Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1528bdd1243dSDimitry Andric Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1529bdd1243dSDimitry Andric if (!LibOrErr) 1530bdd1243dSDimitry Andric return LibOrErr.takeError(); 1531bdd1243dSDimitry Andric 1532bdd1243dSDimitry Andric auto Archive = std::move(*LibOrErr); 1533bdd1243dSDimitry Andric 1534bdd1243dSDimitry Andric Error ArchiveErr = Error::success(); 1535bdd1243dSDimitry Andric auto ChildEnd = Archive->child_end(); 1536bdd1243dSDimitry Andric 1537bdd1243dSDimitry Andric /// Iterate over all bundled code object files in the input archive. 1538bdd1243dSDimitry Andric for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1539bdd1243dSDimitry Andric ArchiveIter != ChildEnd; ++ArchiveIter) { 1540bdd1243dSDimitry Andric if (ArchiveErr) 1541bdd1243dSDimitry Andric return ArchiveErr; 1542bdd1243dSDimitry Andric auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1543bdd1243dSDimitry Andric if (!ArchiveChildNameOrErr) 1544bdd1243dSDimitry Andric return ArchiveChildNameOrErr.takeError(); 1545bdd1243dSDimitry Andric 1546bdd1243dSDimitry Andric StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); 1547bdd1243dSDimitry Andric 1548bdd1243dSDimitry Andric auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1549bdd1243dSDimitry Andric if (!CodeObjectBufferRefOrErr) 1550bdd1243dSDimitry Andric return CodeObjectBufferRefOrErr.takeError(); 1551bdd1243dSDimitry Andric 1552*5f757f3fSDimitry Andric auto TempCodeObjectBuffer = 1553bdd1243dSDimitry Andric MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1554bdd1243dSDimitry Andric 1555*5f757f3fSDimitry Andric // Decompress the buffer if necessary. 1556*5f757f3fSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 1557*5f757f3fSDimitry Andric CompressedOffloadBundle::decompress(*TempCodeObjectBuffer, 1558*5f757f3fSDimitry Andric BundlerConfig.Verbose); 1559*5f757f3fSDimitry Andric if (!DecompressedBufferOrErr) 1560*5f757f3fSDimitry Andric return createStringError( 1561*5f757f3fSDimitry Andric inconvertibleErrorCode(), 1562*5f757f3fSDimitry Andric "Failed to decompress code object: " + 1563*5f757f3fSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError())); 1564*5f757f3fSDimitry Andric 1565*5f757f3fSDimitry Andric MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr; 1566*5f757f3fSDimitry Andric 1567bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1568*5f757f3fSDimitry Andric CreateFileHandler(CodeObjectBuffer, BundlerConfig); 1569bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1570bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1571bdd1243dSDimitry Andric 1572bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1573bdd1243dSDimitry Andric assert(FileHandler && 1574bdd1243dSDimitry Andric "FileHandle creation failed for file in the archive!"); 1575bdd1243dSDimitry Andric 1576*5f757f3fSDimitry Andric if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer)) 1577bdd1243dSDimitry Andric return ReadErr; 1578bdd1243dSDimitry Andric 1579bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurBundleIDOrErr = 1580*5f757f3fSDimitry Andric FileHandler->ReadBundleStart(CodeObjectBuffer); 1581bdd1243dSDimitry Andric if (!CurBundleIDOrErr) 1582bdd1243dSDimitry Andric return CurBundleIDOrErr.takeError(); 1583bdd1243dSDimitry Andric 1584bdd1243dSDimitry Andric std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; 1585bdd1243dSDimitry Andric // No device code in this child, skip. 1586bdd1243dSDimitry Andric if (!OptionalCurBundleID) 1587bdd1243dSDimitry Andric continue; 1588bdd1243dSDimitry Andric StringRef CodeObject = *OptionalCurBundleID; 1589bdd1243dSDimitry Andric 1590bdd1243dSDimitry Andric // Process all bundle entries (CodeObjects) found in this child of input 1591bdd1243dSDimitry Andric // archive. 1592bdd1243dSDimitry Andric while (!CodeObject.empty()) { 1593bdd1243dSDimitry Andric SmallVector<StringRef> CompatibleTargets; 1594bdd1243dSDimitry Andric auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig); 1595bdd1243dSDimitry Andric if (CodeObjectInfo.hasHostKind()) { 1596bdd1243dSDimitry Andric // Do nothing, we don't extract host code yet. 1597bdd1243dSDimitry Andric } else if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets, 1598bdd1243dSDimitry Andric BundlerConfig)) { 1599bdd1243dSDimitry Andric std::string BundleData; 1600bdd1243dSDimitry Andric raw_string_ostream DataStream(BundleData); 1601*5f757f3fSDimitry Andric if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer)) 1602bdd1243dSDimitry Andric return Err; 1603bdd1243dSDimitry Andric 1604bdd1243dSDimitry Andric for (auto &CompatibleTarget : CompatibleTargets) { 1605bdd1243dSDimitry Andric SmallString<128> BundledObjectFileName; 1606bdd1243dSDimitry Andric BundledObjectFileName.assign(BundledObjectFile); 1607bdd1243dSDimitry Andric auto OutputBundleName = 1608bdd1243dSDimitry Andric Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + 1609bdd1243dSDimitry Andric CodeObject + 1610bdd1243dSDimitry Andric getDeviceLibraryFileName(BundledObjectFileName, 1611bdd1243dSDimitry Andric CodeObjectInfo.TargetID)) 1612bdd1243dSDimitry Andric .str(); 1613bdd1243dSDimitry Andric // Replace ':' in optional target feature list with '_' to ensure 1614bdd1243dSDimitry Andric // cross-platform validity. 1615bdd1243dSDimitry Andric std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', 1616bdd1243dSDimitry Andric '_'); 1617bdd1243dSDimitry Andric 1618bdd1243dSDimitry Andric std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy( 1619bdd1243dSDimitry Andric DataStream.str(), OutputBundleName); 1620bdd1243dSDimitry Andric ArchiveBuffers.push_back(std::move(MemBuf)); 1621bdd1243dSDimitry Andric llvm::MemoryBufferRef MemBufRef = 1622bdd1243dSDimitry Andric MemoryBufferRef(*(ArchiveBuffers.back())); 1623bdd1243dSDimitry Andric 1624bdd1243dSDimitry Andric // For inserting <CompatibleTarget, list<CodeObject>> entry in 1625bdd1243dSDimitry Andric // OutputArchivesMap. 162606c3fb27SDimitry Andric if (!OutputArchivesMap.contains(CompatibleTarget)) { 1627bdd1243dSDimitry Andric 1628bdd1243dSDimitry Andric std::vector<NewArchiveMember> ArchiveMembers; 1629bdd1243dSDimitry Andric ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); 1630bdd1243dSDimitry Andric OutputArchivesMap.insert_or_assign(CompatibleTarget, 1631bdd1243dSDimitry Andric std::move(ArchiveMembers)); 1632bdd1243dSDimitry Andric } else { 1633bdd1243dSDimitry Andric OutputArchivesMap[CompatibleTarget].push_back( 1634bdd1243dSDimitry Andric NewArchiveMember(MemBufRef)); 1635bdd1243dSDimitry Andric } 1636bdd1243dSDimitry Andric } 1637bdd1243dSDimitry Andric } 1638bdd1243dSDimitry Andric 1639*5f757f3fSDimitry Andric if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer)) 1640bdd1243dSDimitry Andric return Err; 1641bdd1243dSDimitry Andric 1642bdd1243dSDimitry Andric Expected<std::optional<StringRef>> NextTripleOrErr = 1643*5f757f3fSDimitry Andric FileHandler->ReadBundleStart(CodeObjectBuffer); 1644bdd1243dSDimitry Andric if (!NextTripleOrErr) 1645bdd1243dSDimitry Andric return NextTripleOrErr.takeError(); 1646bdd1243dSDimitry Andric 1647bdd1243dSDimitry Andric CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : ""; 1648bdd1243dSDimitry Andric } // End of processing of all bundle entries of this child of input archive. 1649bdd1243dSDimitry Andric } // End of while over children of input archive. 1650bdd1243dSDimitry Andric 1651bdd1243dSDimitry Andric assert(!ArchiveErr && "Error occurred while reading archive!"); 1652bdd1243dSDimitry Andric 1653bdd1243dSDimitry Andric /// Write out an archive for each target 1654bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1655bdd1243dSDimitry Andric StringRef FileName = TargetOutputFileNameMap[Target]; 1656bdd1243dSDimitry Andric StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = 1657bdd1243dSDimitry Andric OutputArchivesMap.find(Target); 1658bdd1243dSDimitry Andric if (CurArchiveMembers != OutputArchivesMap.end()) { 1659bdd1243dSDimitry Andric if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), 1660*5f757f3fSDimitry Andric SymtabWritingMode::NormalSymtab, 1661*5f757f3fSDimitry Andric getDefaultArchiveKindForHost(), true, 1662*5f757f3fSDimitry Andric false, nullptr)) 1663bdd1243dSDimitry Andric return WriteErr; 1664bdd1243dSDimitry Andric } else if (!BundlerConfig.AllowMissingBundles) { 1665bdd1243dSDimitry Andric std::string ErrMsg = 1666bdd1243dSDimitry Andric Twine("no compatible code object found for the target '" + Target + 1667bdd1243dSDimitry Andric "' in heterogeneous archive library: " + IFName) 1668bdd1243dSDimitry Andric .str(); 1669bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1670bdd1243dSDimitry Andric } else { // Create an empty archive file if no compatible code object is 1671bdd1243dSDimitry Andric // found and "allow-missing-bundles" is enabled. It ensures that 1672bdd1243dSDimitry Andric // the linker using output of this step doesn't complain about 1673bdd1243dSDimitry Andric // the missing input file. 1674bdd1243dSDimitry Andric std::vector<llvm::NewArchiveMember> EmptyArchive; 1675bdd1243dSDimitry Andric EmptyArchive.clear(); 1676*5f757f3fSDimitry Andric if (Error WriteErr = writeArchive( 1677*5f757f3fSDimitry Andric FileName, EmptyArchive, SymtabWritingMode::NormalSymtab, 1678*5f757f3fSDimitry Andric getDefaultArchiveKindForHost(), true, false, nullptr)) 1679bdd1243dSDimitry Andric return WriteErr; 1680bdd1243dSDimitry Andric } 1681bdd1243dSDimitry Andric } 1682bdd1243dSDimitry Andric 1683bdd1243dSDimitry Andric return Error::success(); 1684bdd1243dSDimitry Andric } 1685