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