1 //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file implements an offload bundling API that bundles different files
11 /// that relate with the same source code but different targets into a single
12 /// one. Also the implements the opposite functionality, i.e. unbundle files
13 /// previous created by this API.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #include "clang/Driver/OffloadBundler.h"
18 #include "clang/Basic/Cuda.h"
19 #include "clang/Basic/TargetID.h"
20 #include "clang/Basic/Version.h"
21 #include "llvm/ADT/ArrayRef.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/ADT/StringMap.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/ADT/Triple.h"
27 #include "llvm/Object/Archive.h"
28 #include "llvm/Object/ArchiveWriter.h"
29 #include "llvm/Object/Binary.h"
30 #include "llvm/Object/ObjectFile.h"
31 #include "llvm/Support/Casting.h"
32 #include "llvm/Support/Debug.h"
33 #include "llvm/Support/EndianStream.h"
34 #include "llvm/Support/Errc.h"
35 #include "llvm/Support/Error.h"
36 #include "llvm/Support/ErrorOr.h"
37 #include "llvm/Support/FileSystem.h"
38 #include "llvm/Support/Host.h"
39 #include "llvm/Support/MemoryBuffer.h"
40 #include "llvm/Support/Path.h"
41 #include "llvm/Support/Program.h"
42 #include "llvm/Support/Signals.h"
43 #include "llvm/Support/StringSaver.h"
44 #include "llvm/Support/WithColor.h"
45 #include "llvm/Support/raw_ostream.h"
46 #include <algorithm>
47 #include <cassert>
48 #include <cstddef>
49 #include <cstdint>
50 #include <forward_list>
51 #include <memory>
52 #include <set>
53 #include <string>
54 #include <system_error>
55 #include <utility>
56
57 using namespace llvm;
58 using namespace llvm::object;
59 using namespace clang;
60
61 /// Magic string that marks the existence of offloading data.
62 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
63
OffloadTargetInfo(const StringRef Target,const OffloadBundlerConfig & BC)64 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
65 const OffloadBundlerConfig &BC)
66 : BundlerConfig(BC) {
67
68 // TODO: Add error checking from ClangOffloadBundler.cpp
69 auto TargetFeatures = Target.split(':');
70 auto TripleOrGPU = TargetFeatures.first.rsplit('-');
71
72 if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) {
73 auto KindTriple = TripleOrGPU.first.split('-');
74 this->OffloadKind = KindTriple.first;
75 this->Triple = llvm::Triple(KindTriple.second);
76 this->TargetID = Target.substr(Target.find(TripleOrGPU.second));
77 } else {
78 auto KindTriple = TargetFeatures.first.split('-');
79 this->OffloadKind = KindTriple.first;
80 this->Triple = llvm::Triple(KindTriple.second);
81 this->TargetID = "";
82 }
83 }
84
hasHostKind() const85 bool OffloadTargetInfo::hasHostKind() const {
86 return this->OffloadKind == "host";
87 }
88
isOffloadKindValid() const89 bool OffloadTargetInfo::isOffloadKindValid() const {
90 return OffloadKind == "host" || OffloadKind == "openmp" ||
91 OffloadKind == "hip" || OffloadKind == "hipv4";
92 }
93
isOffloadKindCompatible(const StringRef TargetOffloadKind) const94 bool OffloadTargetInfo::isOffloadKindCompatible(
95 const StringRef TargetOffloadKind) const {
96 if (OffloadKind == TargetOffloadKind)
97 return true;
98 if (BundlerConfig.HipOpenmpCompatible) {
99 bool HIPCompatibleWithOpenMP = OffloadKind.startswith_insensitive("hip") &&
100 TargetOffloadKind == "openmp";
101 bool OpenMPCompatibleWithHIP =
102 OffloadKind == "openmp" &&
103 TargetOffloadKind.startswith_insensitive("hip");
104 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
105 }
106 return false;
107 }
108
isTripleValid() const109 bool OffloadTargetInfo::isTripleValid() const {
110 return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch;
111 }
112
operator ==(const OffloadTargetInfo & Target) const113 bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const {
114 return OffloadKind == Target.OffloadKind &&
115 Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID;
116 }
117
str() const118 std::string OffloadTargetInfo::str() const {
119 return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str();
120 }
121
getDeviceFileExtension(StringRef Device,StringRef BundleFileName)122 static StringRef getDeviceFileExtension(StringRef Device,
123 StringRef BundleFileName) {
124 if (Device.contains("gfx"))
125 return ".bc";
126 if (Device.contains("sm_"))
127 return ".cubin";
128 return sys::path::extension(BundleFileName);
129 }
130
getDeviceLibraryFileName(StringRef BundleFileName,StringRef Device)131 static std::string getDeviceLibraryFileName(StringRef BundleFileName,
132 StringRef Device) {
133 StringRef LibName = sys::path::stem(BundleFileName);
134 StringRef Extension = getDeviceFileExtension(Device, BundleFileName);
135
136 std::string Result;
137 Result += LibName;
138 Result += Extension;
139 return Result;
140 }
141
142 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
143 /// target \p TargetInfo.
144 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
isCodeObjectCompatible(const OffloadTargetInfo & CodeObjectInfo,const OffloadTargetInfo & TargetInfo)145 bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo,
146 const OffloadTargetInfo &TargetInfo) {
147
148 // Compatible in case of exact match.
149 if (CodeObjectInfo == TargetInfo) {
150 DEBUG_WITH_TYPE("CodeObjectCompatibility",
151 dbgs() << "Compatible: Exact match: \t[CodeObject: "
152 << CodeObjectInfo.str()
153 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
154 return true;
155 }
156
157 // Incompatible if Kinds or Triples mismatch.
158 if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) ||
159 !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
160 DEBUG_WITH_TYPE(
161 "CodeObjectCompatibility",
162 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
163 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
164 << "]\n");
165 return false;
166 }
167
168 // Incompatible if target IDs are incompatible.
169 if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID,
170 TargetInfo.TargetID)) {
171 DEBUG_WITH_TYPE(
172 "CodeObjectCompatibility",
173 dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: "
174 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
175 << "]\n");
176 return false;
177 }
178
179 DEBUG_WITH_TYPE(
180 "CodeObjectCompatibility",
181 dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: "
182 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
183 << "]\n");
184 return true;
185 }
186
187 namespace {
188 /// Generic file handler interface.
189 class FileHandler {
190 public:
191 struct BundleInfo {
192 StringRef BundleID;
193 };
194
FileHandler()195 FileHandler() {}
196
~FileHandler()197 virtual ~FileHandler() {}
198
199 /// Update the file handler with information from the header of the bundled
200 /// file.
201 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
202
203 /// Read the marker of the next bundled to be read in the file. The bundle
204 /// name is returned if there is one in the file, or `std::nullopt` if there
205 /// are no more bundles to be read.
206 virtual Expected<std::optional<StringRef>>
207 ReadBundleStart(MemoryBuffer &Input) = 0;
208
209 /// Read the marker that closes the current bundle.
210 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
211
212 /// Read the current bundle and write the result into the stream \a OS.
213 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
214
215 /// Write the header of the bundled file to \a OS based on the information
216 /// gathered from \a Inputs.
217 virtual Error WriteHeader(raw_fd_ostream &OS,
218 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
219
220 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
221 /// \a OS.
222 virtual Error WriteBundleStart(raw_fd_ostream &OS,
223 StringRef TargetTriple) = 0;
224
225 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
226 /// OS.
227 virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0;
228
229 /// Write the bundle from \a Input into \a OS.
230 virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
231
232 /// List bundle IDs in \a Input.
listBundleIDs(MemoryBuffer & Input)233 virtual Error listBundleIDs(MemoryBuffer &Input) {
234 if (Error Err = ReadHeader(Input))
235 return Err;
236 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
237 llvm::outs() << Info.BundleID << '\n';
238 Error Err = listBundleIDsCallback(Input, Info);
239 if (Err)
240 return Err;
241 return Error::success();
242 });
243 }
244
245 /// For each bundle in \a Input, do \a Func.
forEachBundle(MemoryBuffer & Input,std::function<Error (const BundleInfo &)> Func)246 Error forEachBundle(MemoryBuffer &Input,
247 std::function<Error(const BundleInfo &)> Func) {
248 while (true) {
249 Expected<std::optional<StringRef>> CurTripleOrErr =
250 ReadBundleStart(Input);
251 if (!CurTripleOrErr)
252 return CurTripleOrErr.takeError();
253
254 // No more bundles.
255 if (!*CurTripleOrErr)
256 break;
257
258 StringRef CurTriple = **CurTripleOrErr;
259 assert(!CurTriple.empty());
260
261 BundleInfo Info{CurTriple};
262 if (Error Err = Func(Info))
263 return Err;
264 }
265 return Error::success();
266 }
267
268 protected:
listBundleIDsCallback(MemoryBuffer & Input,const BundleInfo & Info)269 virtual Error listBundleIDsCallback(MemoryBuffer &Input,
270 const BundleInfo &Info) {
271 return Error::success();
272 }
273 };
274
275 /// Handler for binary files. The bundled file will have the following format
276 /// (all integers are stored in little-endian format):
277 ///
278 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
279 ///
280 /// NumberOfOffloadBundles (8-byte integer)
281 ///
282 /// OffsetOfBundle1 (8-byte integer)
283 /// SizeOfBundle1 (8-byte integer)
284 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
285 /// TripleOfBundle1 (byte length defined before)
286 ///
287 /// ...
288 ///
289 /// OffsetOfBundleN (8-byte integer)
290 /// SizeOfBundleN (8-byte integer)
291 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
292 /// TripleOfBundleN (byte length defined before)
293 ///
294 /// Bundle1
295 /// ...
296 /// BundleN
297
298 /// Read 8-byte integers from a buffer in little-endian format.
Read8byteIntegerFromBuffer(StringRef Buffer,size_t pos)299 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
300 return llvm::support::endian::read64le(Buffer.data() + pos);
301 }
302
303 /// Write 8-byte integers to a buffer in little-endian format.
Write8byteIntegerToBuffer(raw_fd_ostream & OS,uint64_t Val)304 static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) {
305 llvm::support::endian::write(OS, Val, llvm::support::little);
306 }
307
308 class BinaryFileHandler final : public FileHandler {
309 /// Information about the bundles extracted from the header.
310 struct BinaryBundleInfo final : public BundleInfo {
311 /// Size of the bundle.
312 uint64_t Size = 0u;
313 /// Offset at which the bundle starts in the bundled file.
314 uint64_t Offset = 0u;
315
BinaryBundleInfo__anon363a5eb30111::BinaryFileHandler::BinaryBundleInfo316 BinaryBundleInfo() {}
BinaryBundleInfo__anon363a5eb30111::BinaryFileHandler::BinaryBundleInfo317 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
318 : Size(Size), Offset(Offset) {}
319 };
320
321 /// Map between a triple and the corresponding bundle information.
322 StringMap<BinaryBundleInfo> BundlesInfo;
323
324 /// Iterator for the bundle information that is being read.
325 StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
326 StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
327
328 /// Current bundle target to be written.
329 std::string CurWriteBundleTarget;
330
331 /// Configuration options and arrays for this bundler job
332 const OffloadBundlerConfig &BundlerConfig;
333
334 public:
335 // TODO: Add error checking from ClangOffloadBundler.cpp
BinaryFileHandler(const OffloadBundlerConfig & BC)336 BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {}
337
~BinaryFileHandler()338 ~BinaryFileHandler() final {}
339
ReadHeader(MemoryBuffer & Input)340 Error ReadHeader(MemoryBuffer &Input) final {
341 StringRef FC = Input.getBuffer();
342
343 // Initialize the current bundle with the end of the container.
344 CurBundleInfo = BundlesInfo.end();
345
346 // Check if buffer is smaller than magic string.
347 size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
348 if (ReadChars > FC.size())
349 return Error::success();
350
351 // Check if no magic was found.
352 StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
353 if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR))
354 return Error::success();
355
356 // Read number of bundles.
357 if (ReadChars + 8 > FC.size())
358 return Error::success();
359
360 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
361 ReadChars += 8;
362
363 // Read bundle offsets, sizes and triples.
364 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
365
366 // Read offset.
367 if (ReadChars + 8 > FC.size())
368 return Error::success();
369
370 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
371 ReadChars += 8;
372
373 // Read size.
374 if (ReadChars + 8 > FC.size())
375 return Error::success();
376
377 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
378 ReadChars += 8;
379
380 // Read triple size.
381 if (ReadChars + 8 > FC.size())
382 return Error::success();
383
384 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
385 ReadChars += 8;
386
387 // Read triple.
388 if (ReadChars + TripleSize > FC.size())
389 return Error::success();
390
391 StringRef Triple(&FC.data()[ReadChars], TripleSize);
392 ReadChars += TripleSize;
393
394 // Check if the offset and size make sense.
395 if (!Offset || Offset + Size > FC.size())
396 return Error::success();
397
398 assert(BundlesInfo.find(Triple) == BundlesInfo.end() &&
399 "Triple is duplicated??");
400 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
401 }
402 // Set the iterator to where we will start to read.
403 CurBundleInfo = BundlesInfo.end();
404 NextBundleInfo = BundlesInfo.begin();
405 return Error::success();
406 }
407
408 Expected<std::optional<StringRef>>
ReadBundleStart(MemoryBuffer & Input)409 ReadBundleStart(MemoryBuffer &Input) final {
410 if (NextBundleInfo == BundlesInfo.end())
411 return std::nullopt;
412 CurBundleInfo = NextBundleInfo++;
413 return CurBundleInfo->first();
414 }
415
ReadBundleEnd(MemoryBuffer & Input)416 Error ReadBundleEnd(MemoryBuffer &Input) final {
417 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
418 return Error::success();
419 }
420
ReadBundle(raw_ostream & OS,MemoryBuffer & Input)421 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
422 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
423 StringRef FC = Input.getBuffer();
424 OS.write(FC.data() + CurBundleInfo->second.Offset,
425 CurBundleInfo->second.Size);
426 return Error::success();
427 }
428
WriteHeader(raw_fd_ostream & OS,ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)429 Error WriteHeader(raw_fd_ostream &OS,
430 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
431
432 // Compute size of the header.
433 uint64_t HeaderSize = 0;
434
435 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
436 HeaderSize += 8; // Number of Bundles
437
438 for (auto &T : BundlerConfig.TargetNames) {
439 HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
440 HeaderSize += T.size(); // The triple.
441 }
442
443 // Write to the buffer the header.
444 OS << OFFLOAD_BUNDLER_MAGIC_STR;
445
446 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
447
448 unsigned Idx = 0;
449 for (auto &T : BundlerConfig.TargetNames) {
450 MemoryBuffer &MB = *Inputs[Idx++];
451 HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
452 // Bundle offset.
453 Write8byteIntegerToBuffer(OS, HeaderSize);
454 // Size of the bundle (adds to the next bundle's offset)
455 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
456 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
457 HeaderSize += MB.getBufferSize();
458 // Size of the triple
459 Write8byteIntegerToBuffer(OS, T.size());
460 // Triple
461 OS << T;
462 }
463 return Error::success();
464 }
465
WriteBundleStart(raw_fd_ostream & OS,StringRef TargetTriple)466 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
467 CurWriteBundleTarget = TargetTriple.str();
468 return Error::success();
469 }
470
WriteBundleEnd(raw_fd_ostream & OS,StringRef TargetTriple)471 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
472 return Error::success();
473 }
474
WriteBundle(raw_fd_ostream & OS,MemoryBuffer & Input)475 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
476 auto BI = BundlesInfo[CurWriteBundleTarget];
477 OS.seek(BI.Offset);
478 OS.write(Input.getBufferStart(), Input.getBufferSize());
479 return Error::success();
480 }
481 };
482
483 // This class implements a list of temporary files that are removed upon
484 // object destruction.
485 class TempFileHandlerRAII {
486 public:
~TempFileHandlerRAII()487 ~TempFileHandlerRAII() {
488 for (const auto &File : Files)
489 sys::fs::remove(File);
490 }
491
492 // Creates temporary file with given contents.
Create(std::optional<ArrayRef<char>> Contents)493 Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) {
494 SmallString<128u> File;
495 if (std::error_code EC =
496 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
497 return createFileError(File, EC);
498 Files.push_front(File);
499
500 if (Contents) {
501 std::error_code EC;
502 raw_fd_ostream OS(File, EC);
503 if (EC)
504 return createFileError(File, EC);
505 OS.write(Contents->data(), Contents->size());
506 }
507 return Files.front().str();
508 }
509
510 private:
511 std::forward_list<SmallString<128u>> Files;
512 };
513
514 /// Handler for object files. The bundles are organized by sections with a
515 /// designated name.
516 ///
517 /// To unbundle, we just copy the contents of the designated section.
518 class ObjectFileHandler final : public FileHandler {
519
520 /// The object file we are currently dealing with.
521 std::unique_ptr<ObjectFile> Obj;
522
523 /// Return the input file contents.
getInputFileContents() const524 StringRef getInputFileContents() const { return Obj->getData(); }
525
526 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
527 /// section.
528 static Expected<std::optional<StringRef>>
IsOffloadSection(SectionRef CurSection)529 IsOffloadSection(SectionRef CurSection) {
530 Expected<StringRef> NameOrErr = CurSection.getName();
531 if (!NameOrErr)
532 return NameOrErr.takeError();
533
534 // If it does not start with the reserved suffix, just skip this section.
535 if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR))
536 return std::nullopt;
537
538 // Return the triple that is right after the reserved prefix.
539 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
540 }
541
542 /// Total number of inputs.
543 unsigned NumberOfInputs = 0;
544
545 /// Total number of processed inputs, i.e, inputs that were already
546 /// read from the buffers.
547 unsigned NumberOfProcessedInputs = 0;
548
549 /// Iterator of the current and next section.
550 section_iterator CurrentSection;
551 section_iterator NextSection;
552
553 /// Configuration options and arrays for this bundler job
554 const OffloadBundlerConfig &BundlerConfig;
555
556 public:
557 // TODO: Add error checking from ClangOffloadBundler.cpp
ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,const OffloadBundlerConfig & BC)558 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
559 const OffloadBundlerConfig &BC)
560 : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()),
561 NextSection(Obj->section_begin()), BundlerConfig(BC) {}
562
~ObjectFileHandler()563 ~ObjectFileHandler() final {}
564
ReadHeader(MemoryBuffer & Input)565 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
566
567 Expected<std::optional<StringRef>>
ReadBundleStart(MemoryBuffer & Input)568 ReadBundleStart(MemoryBuffer &Input) final {
569 while (NextSection != Obj->section_end()) {
570 CurrentSection = NextSection;
571 ++NextSection;
572
573 // Check if the current section name starts with the reserved prefix. If
574 // so, return the triple.
575 Expected<std::optional<StringRef>> TripleOrErr =
576 IsOffloadSection(*CurrentSection);
577 if (!TripleOrErr)
578 return TripleOrErr.takeError();
579 if (*TripleOrErr)
580 return **TripleOrErr;
581 }
582 return std::nullopt;
583 }
584
ReadBundleEnd(MemoryBuffer & Input)585 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
586
ReadBundle(raw_ostream & OS,MemoryBuffer & Input)587 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
588 Expected<StringRef> ContentOrErr = CurrentSection->getContents();
589 if (!ContentOrErr)
590 return ContentOrErr.takeError();
591 StringRef Content = *ContentOrErr;
592
593 // Copy fat object contents to the output when extracting host bundle.
594 if (Content.size() == 1u && Content.front() == 0)
595 Content = StringRef(Input.getBufferStart(), Input.getBufferSize());
596
597 OS.write(Content.data(), Content.size());
598 return Error::success();
599 }
600
WriteHeader(raw_fd_ostream & OS,ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)601 Error WriteHeader(raw_fd_ostream &OS,
602 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
603 assert(BundlerConfig.HostInputIndex != ~0u &&
604 "Host input index not defined.");
605
606 // Record number of inputs.
607 NumberOfInputs = Inputs.size();
608 return Error::success();
609 }
610
WriteBundleStart(raw_fd_ostream & OS,StringRef TargetTriple)611 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
612 ++NumberOfProcessedInputs;
613 return Error::success();
614 }
615
WriteBundleEnd(raw_fd_ostream & OS,StringRef TargetTriple)616 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
617 assert(NumberOfProcessedInputs <= NumberOfInputs &&
618 "Processing more inputs that actually exist!");
619 assert(BundlerConfig.HostInputIndex != ~0u &&
620 "Host input index not defined.");
621
622 // If this is not the last output, we don't have to do anything.
623 if (NumberOfProcessedInputs != NumberOfInputs)
624 return Error::success();
625
626 // We will use llvm-objcopy to add target objects sections to the output
627 // fat object. These sections should have 'exclude' flag set which tells
628 // link editor to remove them from linker inputs when linking executable or
629 // shared library.
630
631 assert(BundlerConfig.ObjcopyPath != "" &&
632 "llvm-objcopy path not specified");
633
634 // We write to the output file directly. So, we close it and use the name
635 // to pass down to llvm-objcopy.
636 OS.close();
637
638 // Temporary files that need to be removed.
639 TempFileHandlerRAII TempFiles;
640
641 // Compose llvm-objcopy command line for add target objects' sections with
642 // appropriate flags.
643 BumpPtrAllocator Alloc;
644 StringSaver SS{Alloc};
645 SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
646
647 for (unsigned I = 0; I < NumberOfInputs; ++I) {
648 StringRef InputFile = BundlerConfig.InputFileNames[I];
649 if (I == BundlerConfig.HostInputIndex) {
650 // Special handling for the host bundle. We do not need to add a
651 // standard bundle for the host object since we are going to use fat
652 // object as a host object. Therefore use dummy contents (one zero byte)
653 // when creating section for the host bundle.
654 Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0));
655 if (!TempFileOrErr)
656 return TempFileOrErr.takeError();
657 InputFile = *TempFileOrErr;
658 }
659
660 ObjcopyArgs.push_back(
661 SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR +
662 BundlerConfig.TargetNames[I] + "=" + InputFile));
663 ObjcopyArgs.push_back(
664 SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR +
665 BundlerConfig.TargetNames[I] + "=readonly,exclude"));
666 }
667 ObjcopyArgs.push_back("--");
668 ObjcopyArgs.push_back(
669 BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]);
670 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
671
672 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
673 return Err;
674
675 return Error::success();
676 }
677
WriteBundle(raw_fd_ostream & OS,MemoryBuffer & Input)678 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
679 return Error::success();
680 }
681
682 private:
executeObjcopy(StringRef Objcopy,ArrayRef<StringRef> Args)683 Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) {
684 // If the user asked for the commands to be printed out, we do that
685 // instead of executing it.
686 if (BundlerConfig.PrintExternalCommands) {
687 errs() << "\"" << Objcopy << "\"";
688 for (StringRef Arg : drop_begin(Args, 1))
689 errs() << " \"" << Arg << "\"";
690 errs() << "\n";
691 } else {
692 if (sys::ExecuteAndWait(Objcopy, Args))
693 return createStringError(inconvertibleErrorCode(),
694 "'llvm-objcopy' tool failed");
695 }
696 return Error::success();
697 }
698 };
699
700 /// Handler for text files. The bundled file will have the following format.
701 ///
702 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
703 /// Bundle 1
704 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
705 /// ...
706 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
707 /// Bundle N
708 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
709 class TextFileHandler final : public FileHandler {
710 /// String that begins a line comment.
711 StringRef Comment;
712
713 /// String that initiates a bundle.
714 std::string BundleStartString;
715
716 /// String that closes a bundle.
717 std::string BundleEndString;
718
719 /// Number of chars read from input.
720 size_t ReadChars = 0u;
721
722 protected:
ReadHeader(MemoryBuffer & Input)723 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
724
725 Expected<std::optional<StringRef>>
ReadBundleStart(MemoryBuffer & Input)726 ReadBundleStart(MemoryBuffer &Input) final {
727 StringRef FC = Input.getBuffer();
728
729 // Find start of the bundle.
730 ReadChars = FC.find(BundleStartString, ReadChars);
731 if (ReadChars == FC.npos)
732 return std::nullopt;
733
734 // Get position of the triple.
735 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
736
737 // Get position that closes the triple.
738 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
739 if (TripleEnd == FC.npos)
740 return std::nullopt;
741
742 // Next time we read after the new line.
743 ++ReadChars;
744
745 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
746 }
747
ReadBundleEnd(MemoryBuffer & Input)748 Error ReadBundleEnd(MemoryBuffer &Input) final {
749 StringRef FC = Input.getBuffer();
750
751 // Read up to the next new line.
752 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
753
754 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
755 if (TripleEnd != FC.npos)
756 // Next time we read after the new line.
757 ++ReadChars;
758
759 return Error::success();
760 }
761
ReadBundle(raw_ostream & OS,MemoryBuffer & Input)762 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
763 StringRef FC = Input.getBuffer();
764 size_t BundleStart = ReadChars;
765
766 // Find end of the bundle.
767 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
768
769 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
770 OS << Bundle;
771
772 return Error::success();
773 }
774
WriteHeader(raw_fd_ostream & OS,ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)775 Error WriteHeader(raw_fd_ostream &OS,
776 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
777 return Error::success();
778 }
779
WriteBundleStart(raw_fd_ostream & OS,StringRef TargetTriple)780 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
781 OS << BundleStartString << TargetTriple << "\n";
782 return Error::success();
783 }
784
WriteBundleEnd(raw_fd_ostream & OS,StringRef TargetTriple)785 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
786 OS << BundleEndString << TargetTriple << "\n";
787 return Error::success();
788 }
789
WriteBundle(raw_fd_ostream & OS,MemoryBuffer & Input)790 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
791 OS << Input.getBuffer();
792 return Error::success();
793 }
794
795 public:
TextFileHandler(StringRef Comment)796 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
797 BundleStartString =
798 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
799 BundleEndString =
800 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
801 }
802
listBundleIDsCallback(MemoryBuffer & Input,const BundleInfo & Info)803 Error listBundleIDsCallback(MemoryBuffer &Input,
804 const BundleInfo &Info) final {
805 // TODO: To list bundle IDs in a bundled text file we need to go through
806 // all bundles. The format of bundled text file may need to include a
807 // header if the performance of listing bundle IDs of bundled text file is
808 // important.
809 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
810 if (Error Err = ReadBundleEnd(Input))
811 return Err;
812 return Error::success();
813 }
814 };
815 } // namespace
816
817 /// Return an appropriate object file handler. We use the specific object
818 /// handler if we know how to deal with that format, otherwise we use a default
819 /// binary file handler.
820 static std::unique_ptr<FileHandler>
CreateObjectFileHandler(MemoryBuffer & FirstInput,const OffloadBundlerConfig & BundlerConfig)821 CreateObjectFileHandler(MemoryBuffer &FirstInput,
822 const OffloadBundlerConfig &BundlerConfig) {
823 // Check if the input file format is one that we know how to deal with.
824 Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
825
826 // We only support regular object files. If failed to open the input as a
827 // known binary or this is not an object file use the default binary handler.
828 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
829 return std::make_unique<BinaryFileHandler>(BundlerConfig);
830
831 // Otherwise create an object file handler. The handler will be owned by the
832 // client of this function.
833 return std::make_unique<ObjectFileHandler>(
834 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
835 BundlerConfig);
836 }
837
838 /// Return an appropriate handler given the input files and options.
839 static Expected<std::unique_ptr<FileHandler>>
CreateFileHandler(MemoryBuffer & FirstInput,const OffloadBundlerConfig & BundlerConfig)840 CreateFileHandler(MemoryBuffer &FirstInput,
841 const OffloadBundlerConfig &BundlerConfig) {
842 std::string FilesType = BundlerConfig.FilesType;
843
844 if (FilesType == "i")
845 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
846 if (FilesType == "ii")
847 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
848 if (FilesType == "cui")
849 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
850 if (FilesType == "hipi")
851 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
852 // TODO: `.d` should be eventually removed once `-M` and its variants are
853 // handled properly in offload compilation.
854 if (FilesType == "d")
855 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
856 if (FilesType == "ll")
857 return std::make_unique<TextFileHandler>(/*Comment=*/";");
858 if (FilesType == "bc")
859 return std::make_unique<BinaryFileHandler>(BundlerConfig);
860 if (FilesType == "s")
861 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
862 if (FilesType == "o")
863 return CreateObjectFileHandler(FirstInput, BundlerConfig);
864 if (FilesType == "a")
865 return CreateObjectFileHandler(FirstInput, BundlerConfig);
866 if (FilesType == "gch")
867 return std::make_unique<BinaryFileHandler>(BundlerConfig);
868 if (FilesType == "ast")
869 return std::make_unique<BinaryFileHandler>(BundlerConfig);
870
871 return createStringError(errc::invalid_argument,
872 "'" + FilesType + "': invalid file type specified");
873 }
874
875 // List bundle IDs. Return true if an error was found.
ListBundleIDsInFile(StringRef InputFileName,const OffloadBundlerConfig & BundlerConfig)876 Error OffloadBundler::ListBundleIDsInFile(
877 StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
878 // Open Input file.
879 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
880 MemoryBuffer::getFileOrSTDIN(InputFileName);
881 if (std::error_code EC = CodeOrErr.getError())
882 return createFileError(InputFileName, EC);
883
884 MemoryBuffer &Input = **CodeOrErr;
885
886 // Select the right files handler.
887 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
888 CreateFileHandler(Input, BundlerConfig);
889 if (!FileHandlerOrErr)
890 return FileHandlerOrErr.takeError();
891
892 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
893 assert(FH);
894 return FH->listBundleIDs(Input);
895 }
896
897 /// Bundle the files. Return true if an error was found.
BundleFiles()898 Error OffloadBundler::BundleFiles() {
899 std::error_code EC;
900
901 // Create output file.
902 raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC,
903 sys::fs::OF_None);
904 if (EC)
905 return createFileError(BundlerConfig.OutputFileNames.front(), EC);
906
907 // Open input files.
908 SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers;
909 InputBuffers.reserve(BundlerConfig.InputFileNames.size());
910 for (auto &I : BundlerConfig.InputFileNames) {
911 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
912 MemoryBuffer::getFileOrSTDIN(I);
913 if (std::error_code EC = CodeOrErr.getError())
914 return createFileError(I, EC);
915 InputBuffers.emplace_back(std::move(*CodeOrErr));
916 }
917
918 // Get the file handler. We use the host buffer as reference.
919 assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) &&
920 "Host input index undefined??");
921 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler(
922 *InputBuffers[BundlerConfig.AllowNoHost ? 0
923 : BundlerConfig.HostInputIndex],
924 BundlerConfig);
925 if (!FileHandlerOrErr)
926 return FileHandlerOrErr.takeError();
927
928 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
929 assert(FH);
930
931 // Write header.
932 if (Error Err = FH->WriteHeader(OutputFile, InputBuffers))
933 return Err;
934
935 // Write all bundles along with the start/end markers. If an error was found
936 // writing the end of the bundle component, abort the bundle writing.
937 auto Input = InputBuffers.begin();
938 for (auto &Triple : BundlerConfig.TargetNames) {
939 if (Error Err = FH->WriteBundleStart(OutputFile, Triple))
940 return Err;
941 if (Error Err = FH->WriteBundle(OutputFile, **Input))
942 return Err;
943 if (Error Err = FH->WriteBundleEnd(OutputFile, Triple))
944 return Err;
945 ++Input;
946 }
947 return Error::success();
948 }
949
950 // Unbundle the files. Return true if an error was found.
UnbundleFiles()951 Error OffloadBundler::UnbundleFiles() {
952 // Open Input file.
953 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
954 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front());
955 if (std::error_code EC = CodeOrErr.getError())
956 return createFileError(BundlerConfig.InputFileNames.front(), EC);
957
958 MemoryBuffer &Input = **CodeOrErr;
959
960 // Select the right files handler.
961 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
962 CreateFileHandler(Input, BundlerConfig);
963 if (!FileHandlerOrErr)
964 return FileHandlerOrErr.takeError();
965
966 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
967 assert(FH);
968
969 // Read the header of the bundled file.
970 if (Error Err = FH->ReadHeader(Input))
971 return Err;
972
973 // Create a work list that consist of the map triple/output file.
974 StringMap<StringRef> Worklist;
975 auto Output = BundlerConfig.OutputFileNames.begin();
976 for (auto &Triple : BundlerConfig.TargetNames) {
977 Worklist[Triple] = *Output;
978 ++Output;
979 }
980
981 // Read all the bundles that are in the work list. If we find no bundles we
982 // assume the file is meant for the host target.
983 bool FoundHostBundle = false;
984 while (!Worklist.empty()) {
985 Expected<std::optional<StringRef>> CurTripleOrErr =
986 FH->ReadBundleStart(Input);
987 if (!CurTripleOrErr)
988 return CurTripleOrErr.takeError();
989
990 // We don't have more bundles.
991 if (!*CurTripleOrErr)
992 break;
993
994 StringRef CurTriple = **CurTripleOrErr;
995 assert(!CurTriple.empty());
996
997 auto Output = Worklist.begin();
998 for (auto E = Worklist.end(); Output != E; Output++) {
999 if (isCodeObjectCompatible(
1000 OffloadTargetInfo(CurTriple, BundlerConfig),
1001 OffloadTargetInfo((*Output).first(), BundlerConfig))) {
1002 break;
1003 }
1004 }
1005
1006 if (Output == Worklist.end())
1007 continue;
1008 // Check if the output file can be opened and copy the bundle to it.
1009 std::error_code EC;
1010 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1011 if (EC)
1012 return createFileError((*Output).second, EC);
1013 if (Error Err = FH->ReadBundle(OutputFile, Input))
1014 return Err;
1015 if (Error Err = FH->ReadBundleEnd(Input))
1016 return Err;
1017 Worklist.erase(Output);
1018
1019 // Record if we found the host bundle.
1020 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig);
1021 if (OffloadInfo.hasHostKind())
1022 FoundHostBundle = true;
1023 }
1024
1025 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) {
1026 std::string ErrMsg = "Can't find bundles for";
1027 std::set<StringRef> Sorted;
1028 for (auto &E : Worklist)
1029 Sorted.insert(E.first());
1030 unsigned I = 0;
1031 unsigned Last = Sorted.size() - 1;
1032 for (auto &E : Sorted) {
1033 if (I != 0 && Last > 1)
1034 ErrMsg += ",";
1035 ErrMsg += " ";
1036 if (I == Last && I != 0)
1037 ErrMsg += "and ";
1038 ErrMsg += E.str();
1039 ++I;
1040 }
1041 return createStringError(inconvertibleErrorCode(), ErrMsg);
1042 }
1043
1044 // If no bundles were found, assume the input file is the host bundle and
1045 // create empty files for the remaining targets.
1046 if (Worklist.size() == BundlerConfig.TargetNames.size()) {
1047 for (auto &E : Worklist) {
1048 std::error_code EC;
1049 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1050 if (EC)
1051 return createFileError(E.second, EC);
1052
1053 // If this entry has a host kind, copy the input file to the output file.
1054 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig);
1055 if (OffloadInfo.hasHostKind())
1056 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1057 }
1058 return Error::success();
1059 }
1060
1061 // If we found elements, we emit an error if none of those were for the host
1062 // in case host bundle name was provided in command line.
1063 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u ||
1064 BundlerConfig.AllowMissingBundles))
1065 return createStringError(inconvertibleErrorCode(),
1066 "Can't find bundle for the host target");
1067
1068 // If we still have any elements in the worklist, create empty files for them.
1069 for (auto &E : Worklist) {
1070 std::error_code EC;
1071 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1072 if (EC)
1073 return createFileError(E.second, EC);
1074 }
1075
1076 return Error::success();
1077 }
1078
getDefaultArchiveKindForHost()1079 static Archive::Kind getDefaultArchiveKindForHost() {
1080 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1081 : Archive::K_GNU;
1082 }
1083
1084 /// @brief Computes a list of targets among all given targets which are
1085 /// compatible with this code object
1086 /// @param [in] CodeObjectInfo Code Object
1087 /// @param [out] CompatibleTargets List of all compatible targets among all
1088 /// given targets
1089 /// @return false, if no compatible target is found.
1090 static bool
getCompatibleOffloadTargets(OffloadTargetInfo & CodeObjectInfo,SmallVectorImpl<StringRef> & CompatibleTargets,const OffloadBundlerConfig & BundlerConfig)1091 getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo,
1092 SmallVectorImpl<StringRef> &CompatibleTargets,
1093 const OffloadBundlerConfig &BundlerConfig) {
1094 if (!CompatibleTargets.empty()) {
1095 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1096 dbgs() << "CompatibleTargets list should be empty\n");
1097 return false;
1098 }
1099 for (auto &Target : BundlerConfig.TargetNames) {
1100 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig);
1101 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo))
1102 CompatibleTargets.push_back(Target);
1103 }
1104 return !CompatibleTargets.empty();
1105 }
1106
1107 /// UnbundleArchive takes an archive file (".a") as input containing bundled
1108 /// code object files, and a list of offload targets (not host), and extracts
1109 /// the code objects into a new archive file for each offload target. Each
1110 /// resulting archive file contains all code object files corresponding to that
1111 /// particular offload target. The created archive file does not
1112 /// contain an index of the symbols and code object files are named as
1113 /// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'.
UnbundleArchive()1114 Error OffloadBundler::UnbundleArchive() {
1115 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1116
1117 /// Map of target names with list of object files that will form the device
1118 /// specific archive for that target
1119 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1120
1121 // Map of target names and output archive filenames
1122 StringMap<StringRef> TargetOutputFileNameMap;
1123
1124 auto Output = BundlerConfig.OutputFileNames.begin();
1125 for (auto &Target : BundlerConfig.TargetNames) {
1126 TargetOutputFileNameMap[Target] = *Output;
1127 ++Output;
1128 }
1129
1130 StringRef IFName = BundlerConfig.InputFileNames.front();
1131
1132 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1133 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1134 if (std::error_code EC = BufOrErr.getError())
1135 return createFileError(BundlerConfig.InputFileNames.front(), EC);
1136
1137 ArchiveBuffers.push_back(std::move(*BufOrErr));
1138 Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
1139 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1140 if (!LibOrErr)
1141 return LibOrErr.takeError();
1142
1143 auto Archive = std::move(*LibOrErr);
1144
1145 Error ArchiveErr = Error::success();
1146 auto ChildEnd = Archive->child_end();
1147
1148 /// Iterate over all bundled code object files in the input archive.
1149 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1150 ArchiveIter != ChildEnd; ++ArchiveIter) {
1151 if (ArchiveErr)
1152 return ArchiveErr;
1153 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1154 if (!ArchiveChildNameOrErr)
1155 return ArchiveChildNameOrErr.takeError();
1156
1157 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1158
1159 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1160 if (!CodeObjectBufferRefOrErr)
1161 return CodeObjectBufferRefOrErr.takeError();
1162
1163 auto CodeObjectBuffer =
1164 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1165
1166 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
1167 CreateFileHandler(*CodeObjectBuffer, BundlerConfig);
1168 if (!FileHandlerOrErr)
1169 return FileHandlerOrErr.takeError();
1170
1171 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1172 assert(FileHandler &&
1173 "FileHandle creation failed for file in the archive!");
1174
1175 if (Error ReadErr = FileHandler->ReadHeader(*CodeObjectBuffer))
1176 return ReadErr;
1177
1178 Expected<std::optional<StringRef>> CurBundleIDOrErr =
1179 FileHandler->ReadBundleStart(*CodeObjectBuffer);
1180 if (!CurBundleIDOrErr)
1181 return CurBundleIDOrErr.takeError();
1182
1183 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1184 // No device code in this child, skip.
1185 if (!OptionalCurBundleID)
1186 continue;
1187 StringRef CodeObject = *OptionalCurBundleID;
1188
1189 // Process all bundle entries (CodeObjects) found in this child of input
1190 // archive.
1191 while (!CodeObject.empty()) {
1192 SmallVector<StringRef> CompatibleTargets;
1193 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig);
1194 if (CodeObjectInfo.hasHostKind()) {
1195 // Do nothing, we don't extract host code yet.
1196 } else if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets,
1197 BundlerConfig)) {
1198 std::string BundleData;
1199 raw_string_ostream DataStream(BundleData);
1200 if (Error Err = FileHandler->ReadBundle(DataStream, *CodeObjectBuffer))
1201 return Err;
1202
1203 for (auto &CompatibleTarget : CompatibleTargets) {
1204 SmallString<128> BundledObjectFileName;
1205 BundledObjectFileName.assign(BundledObjectFile);
1206 auto OutputBundleName =
1207 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1208 CodeObject +
1209 getDeviceLibraryFileName(BundledObjectFileName,
1210 CodeObjectInfo.TargetID))
1211 .str();
1212 // Replace ':' in optional target feature list with '_' to ensure
1213 // cross-platform validity.
1214 std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
1215 '_');
1216
1217 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1218 DataStream.str(), OutputBundleName);
1219 ArchiveBuffers.push_back(std::move(MemBuf));
1220 llvm::MemoryBufferRef MemBufRef =
1221 MemoryBufferRef(*(ArchiveBuffers.back()));
1222
1223 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1224 // OutputArchivesMap.
1225 if (OutputArchivesMap.find(CompatibleTarget) ==
1226 OutputArchivesMap.end()) {
1227
1228 std::vector<NewArchiveMember> ArchiveMembers;
1229 ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
1230 OutputArchivesMap.insert_or_assign(CompatibleTarget,
1231 std::move(ArchiveMembers));
1232 } else {
1233 OutputArchivesMap[CompatibleTarget].push_back(
1234 NewArchiveMember(MemBufRef));
1235 }
1236 }
1237 }
1238
1239 if (Error Err = FileHandler->ReadBundleEnd(*CodeObjectBuffer))
1240 return Err;
1241
1242 Expected<std::optional<StringRef>> NextTripleOrErr =
1243 FileHandler->ReadBundleStart(*CodeObjectBuffer);
1244 if (!NextTripleOrErr)
1245 return NextTripleOrErr.takeError();
1246
1247 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
1248 } // End of processing of all bundle entries of this child of input archive.
1249 } // End of while over children of input archive.
1250
1251 assert(!ArchiveErr && "Error occurred while reading archive!");
1252
1253 /// Write out an archive for each target
1254 for (auto &Target : BundlerConfig.TargetNames) {
1255 StringRef FileName = TargetOutputFileNameMap[Target];
1256 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1257 OutputArchivesMap.find(Target);
1258 if (CurArchiveMembers != OutputArchivesMap.end()) {
1259 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1260 true, getDefaultArchiveKindForHost(),
1261 true, false, nullptr))
1262 return WriteErr;
1263 } else if (!BundlerConfig.AllowMissingBundles) {
1264 std::string ErrMsg =
1265 Twine("no compatible code object found for the target '" + Target +
1266 "' in heterogeneous archive library: " + IFName)
1267 .str();
1268 return createStringError(inconvertibleErrorCode(), ErrMsg);
1269 } else { // Create an empty archive file if no compatible code object is
1270 // found and "allow-missing-bundles" is enabled. It ensures that
1271 // the linker using output of this step doesn't complain about
1272 // the missing input file.
1273 std::vector<llvm::NewArchiveMember> EmptyArchive;
1274 EmptyArchive.clear();
1275 if (Error WriteErr = writeArchive(FileName, EmptyArchive, true,
1276 getDefaultArchiveKindForHost(), true,
1277 false, nullptr))
1278 return WriteErr;
1279 }
1280 }
1281
1282 return Error::success();
1283 }
1284