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