1 //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===//
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
7 //===----------------------------------------------------------------------===//
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.
15 //===----------------------------------------------------------------------===//
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/StringExtras.h"
25 #include "llvm/ADT/StringMap.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/BinaryFormat/Magic.h"
28 #include "llvm/Object/Archive.h"
29 #include "llvm/Object/ArchiveWriter.h"
30 #include "llvm/Object/Binary.h"
31 #include "llvm/Object/ObjectFile.h"
32 #include "llvm/Support/Casting.h"
33 #include "llvm/Support/Compression.h"
34 #include "llvm/Support/Debug.h"
35 #include "llvm/Support/EndianStream.h"
36 #include "llvm/Support/Errc.h"
37 #include "llvm/Support/Error.h"
38 #include "llvm/Support/ErrorOr.h"
39 #include "llvm/Support/FileSystem.h"
40 #include "llvm/Support/MD5.h"
41 #include "llvm/Support/MemoryBuffer.h"
42 #include "llvm/Support/Path.h"
43 #include "llvm/Support/Program.h"
44 #include "llvm/Support/Signals.h"
45 #include "llvm/Support/StringSaver.h"
46 #include "llvm/Support/Timer.h"
47 #include "llvm/Support/WithColor.h"
48 #include "llvm/Support/raw_ostream.h"
49 #include "llvm/TargetParser/Host.h"
50 #include "llvm/TargetParser/Triple.h"
55 #include <forward_list>
56 #include <llvm/Support/Process.h>
60 #include <system_error>
64 using namespace llvm::object
;
65 using namespace clang
;
67 static llvm::TimerGroup
68 ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group",
69 "Timer group for clang offload bundler");
71 /// Magic string that marks the existence of offloading data.
72 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
74 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target
,
75 const OffloadBundlerConfig
&BC
)
78 // TODO: Add error checking from ClangOffloadBundler.cpp
79 auto TargetFeatures
= Target
.split(':');
80 auto TripleOrGPU
= TargetFeatures
.first
.rsplit('-');
82 if (clang::StringToCudaArch(TripleOrGPU
.second
) != clang::CudaArch::UNKNOWN
) {
83 auto KindTriple
= TripleOrGPU
.first
.split('-');
84 this->OffloadKind
= KindTriple
.first
;
86 // Enforce optional env field to standardize bundles
87 llvm::Triple t
= llvm::Triple(KindTriple
.second
);
88 this->Triple
= llvm::Triple(t
.getArchName(), t
.getVendorName(),
89 t
.getOSName(), t
.getEnvironmentName());
91 this->TargetID
= Target
.substr(Target
.find(TripleOrGPU
.second
));
93 auto KindTriple
= TargetFeatures
.first
.split('-');
94 this->OffloadKind
= KindTriple
.first
;
96 // Enforce optional env field to standardize bundles
97 llvm::Triple t
= llvm::Triple(KindTriple
.second
);
98 this->Triple
= llvm::Triple(t
.getArchName(), t
.getVendorName(),
99 t
.getOSName(), t
.getEnvironmentName());
105 bool OffloadTargetInfo::hasHostKind() const {
106 return this->OffloadKind
== "host";
109 bool OffloadTargetInfo::isOffloadKindValid() const {
110 return OffloadKind
== "host" || OffloadKind
== "openmp" ||
111 OffloadKind
== "hip" || OffloadKind
== "hipv4";
114 bool OffloadTargetInfo::isOffloadKindCompatible(
115 const StringRef TargetOffloadKind
) const {
116 if (OffloadKind
== TargetOffloadKind
)
118 if (BundlerConfig
.HipOpenmpCompatible
) {
119 bool HIPCompatibleWithOpenMP
= OffloadKind
.starts_with_insensitive("hip") &&
120 TargetOffloadKind
== "openmp";
121 bool OpenMPCompatibleWithHIP
=
122 OffloadKind
== "openmp" &&
123 TargetOffloadKind
.starts_with_insensitive("hip");
124 return HIPCompatibleWithOpenMP
|| OpenMPCompatibleWithHIP
;
129 bool OffloadTargetInfo::isTripleValid() const {
130 return !Triple
.str().empty() && Triple
.getArch() != Triple::UnknownArch
;
133 bool OffloadTargetInfo::operator==(const OffloadTargetInfo
&Target
) const {
134 return OffloadKind
== Target
.OffloadKind
&&
135 Triple
.isCompatibleWith(Target
.Triple
) && TargetID
== Target
.TargetID
;
138 std::string
OffloadTargetInfo::str() const {
139 return Twine(OffloadKind
+ "-" + Triple
.str() + "-" + TargetID
).str();
142 static StringRef
getDeviceFileExtension(StringRef Device
,
143 StringRef BundleFileName
) {
144 if (Device
.contains("gfx"))
146 if (Device
.contains("sm_"))
148 return sys::path::extension(BundleFileName
);
151 static std::string
getDeviceLibraryFileName(StringRef BundleFileName
,
153 StringRef LibName
= sys::path::stem(BundleFileName
);
154 StringRef Extension
= getDeviceFileExtension(Device
, BundleFileName
);
162 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
163 /// target \p TargetInfo.
164 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
165 bool isCodeObjectCompatible(const OffloadTargetInfo
&CodeObjectInfo
,
166 const OffloadTargetInfo
&TargetInfo
) {
168 // Compatible in case of exact match.
169 if (CodeObjectInfo
== TargetInfo
) {
170 DEBUG_WITH_TYPE("CodeObjectCompatibility",
171 dbgs() << "Compatible: Exact match: \t[CodeObject: "
172 << CodeObjectInfo
.str()
173 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
177 // Incompatible if Kinds or Triples mismatch.
178 if (!CodeObjectInfo
.isOffloadKindCompatible(TargetInfo
.OffloadKind
) ||
179 !CodeObjectInfo
.Triple
.isCompatibleWith(TargetInfo
.Triple
)) {
181 "CodeObjectCompatibility",
182 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
183 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
188 // Incompatible if target IDs are incompatible.
189 if (!clang::isCompatibleTargetID(CodeObjectInfo
.TargetID
,
190 TargetInfo
.TargetID
)) {
192 "CodeObjectCompatibility",
193 dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: "
194 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
200 "CodeObjectCompatibility",
201 dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: "
202 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
208 /// Generic file handler interface.
217 virtual ~FileHandler() {}
219 /// Update the file handler with information from the header of the bundled
221 virtual Error
ReadHeader(MemoryBuffer
&Input
) = 0;
223 /// Read the marker of the next bundled to be read in the file. The bundle
224 /// name is returned if there is one in the file, or `std::nullopt` if there
225 /// are no more bundles to be read.
226 virtual Expected
<std::optional
<StringRef
>>
227 ReadBundleStart(MemoryBuffer
&Input
) = 0;
229 /// Read the marker that closes the current bundle.
230 virtual Error
ReadBundleEnd(MemoryBuffer
&Input
) = 0;
232 /// Read the current bundle and write the result into the stream \a OS.
233 virtual Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) = 0;
235 /// Write the header of the bundled file to \a OS based on the information
236 /// gathered from \a Inputs.
237 virtual Error
WriteHeader(raw_ostream
&OS
,
238 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) = 0;
240 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
242 virtual Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) = 0;
244 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
246 virtual Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) = 0;
248 /// Write the bundle from \a Input into \a OS.
249 virtual Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) = 0;
251 /// Finalize output file.
252 virtual Error
finalizeOutputFile() { return Error::success(); }
254 /// List bundle IDs in \a Input.
255 virtual Error
listBundleIDs(MemoryBuffer
&Input
) {
256 if (Error Err
= ReadHeader(Input
))
258 return forEachBundle(Input
, [&](const BundleInfo
&Info
) -> Error
{
259 llvm::outs() << Info
.BundleID
<< '\n';
260 Error Err
= listBundleIDsCallback(Input
, Info
);
263 return Error::success();
267 /// For each bundle in \a Input, do \a Func.
268 Error
forEachBundle(MemoryBuffer
&Input
,
269 std::function
<Error(const BundleInfo
&)> Func
) {
271 Expected
<std::optional
<StringRef
>> CurTripleOrErr
=
272 ReadBundleStart(Input
);
274 return CurTripleOrErr
.takeError();
277 if (!*CurTripleOrErr
)
280 StringRef CurTriple
= **CurTripleOrErr
;
281 assert(!CurTriple
.empty());
283 BundleInfo Info
{CurTriple
};
284 if (Error Err
= Func(Info
))
287 return Error::success();
291 virtual Error
listBundleIDsCallback(MemoryBuffer
&Input
,
292 const BundleInfo
&Info
) {
293 return Error::success();
297 /// Handler for binary files. The bundled file will have the following format
298 /// (all integers are stored in little-endian format):
300 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
302 /// NumberOfOffloadBundles (8-byte integer)
304 /// OffsetOfBundle1 (8-byte integer)
305 /// SizeOfBundle1 (8-byte integer)
306 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
307 /// TripleOfBundle1 (byte length defined before)
311 /// OffsetOfBundleN (8-byte integer)
312 /// SizeOfBundleN (8-byte integer)
313 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
314 /// TripleOfBundleN (byte length defined before)
320 /// Read 8-byte integers from a buffer in little-endian format.
321 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer
, size_t pos
) {
322 return llvm::support::endian::read64le(Buffer
.data() + pos
);
325 /// Write 8-byte integers to a buffer in little-endian format.
326 static void Write8byteIntegerToBuffer(raw_ostream
&OS
, uint64_t Val
) {
327 llvm::support::endian::write(OS
, Val
, llvm::endianness::little
);
330 class BinaryFileHandler final
: public FileHandler
{
331 /// Information about the bundles extracted from the header.
332 struct BinaryBundleInfo final
: public BundleInfo
{
333 /// Size of the bundle.
335 /// Offset at which the bundle starts in the bundled file.
336 uint64_t Offset
= 0u;
338 BinaryBundleInfo() {}
339 BinaryBundleInfo(uint64_t Size
, uint64_t Offset
)
340 : Size(Size
), Offset(Offset
) {}
343 /// Map between a triple and the corresponding bundle information.
344 StringMap
<BinaryBundleInfo
> BundlesInfo
;
346 /// Iterator for the bundle information that is being read.
347 StringMap
<BinaryBundleInfo
>::iterator CurBundleInfo
;
348 StringMap
<BinaryBundleInfo
>::iterator NextBundleInfo
;
350 /// Current bundle target to be written.
351 std::string CurWriteBundleTarget
;
353 /// Configuration options and arrays for this bundler job
354 const OffloadBundlerConfig
&BundlerConfig
;
357 // TODO: Add error checking from ClangOffloadBundler.cpp
358 BinaryFileHandler(const OffloadBundlerConfig
&BC
) : BundlerConfig(BC
) {}
360 ~BinaryFileHandler() final
{}
362 Error
ReadHeader(MemoryBuffer
&Input
) final
{
363 StringRef FC
= Input
.getBuffer();
365 // Initialize the current bundle with the end of the container.
366 CurBundleInfo
= BundlesInfo
.end();
368 // Check if buffer is smaller than magic string.
369 size_t ReadChars
= sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1;
370 if (ReadChars
> FC
.size())
371 return Error::success();
373 // Check if no magic was found.
374 if (llvm::identify_magic(FC
) != llvm::file_magic::offload_bundle
)
375 return Error::success();
377 // Read number of bundles.
378 if (ReadChars
+ 8 > FC
.size())
379 return Error::success();
381 uint64_t NumberOfBundles
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
384 // Read bundle offsets, sizes and triples.
385 for (uint64_t i
= 0; i
< NumberOfBundles
; ++i
) {
388 if (ReadChars
+ 8 > FC
.size())
389 return Error::success();
391 uint64_t Offset
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
395 if (ReadChars
+ 8 > FC
.size())
396 return Error::success();
398 uint64_t Size
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
402 if (ReadChars
+ 8 > FC
.size())
403 return Error::success();
405 uint64_t TripleSize
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
409 if (ReadChars
+ TripleSize
> FC
.size())
410 return Error::success();
412 StringRef
Triple(&FC
.data()[ReadChars
], TripleSize
);
413 ReadChars
+= TripleSize
;
415 // Check if the offset and size make sense.
416 if (!Offset
|| Offset
+ Size
> FC
.size())
417 return Error::success();
419 assert(!BundlesInfo
.contains(Triple
) && "Triple is duplicated??");
420 BundlesInfo
[Triple
] = BinaryBundleInfo(Size
, Offset
);
422 // Set the iterator to where we will start to read.
423 CurBundleInfo
= BundlesInfo
.end();
424 NextBundleInfo
= BundlesInfo
.begin();
425 return Error::success();
428 Expected
<std::optional
<StringRef
>>
429 ReadBundleStart(MemoryBuffer
&Input
) final
{
430 if (NextBundleInfo
== BundlesInfo
.end())
432 CurBundleInfo
= NextBundleInfo
++;
433 return CurBundleInfo
->first();
436 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{
437 assert(CurBundleInfo
!= BundlesInfo
.end() && "Invalid reader info!");
438 return Error::success();
441 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
442 assert(CurBundleInfo
!= BundlesInfo
.end() && "Invalid reader info!");
443 StringRef FC
= Input
.getBuffer();
444 OS
.write(FC
.data() + CurBundleInfo
->second
.Offset
,
445 CurBundleInfo
->second
.Size
);
446 return Error::success();
449 Error
WriteHeader(raw_ostream
&OS
,
450 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
452 // Compute size of the header.
453 uint64_t HeaderSize
= 0;
455 HeaderSize
+= sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1;
456 HeaderSize
+= 8; // Number of Bundles
458 for (auto &T
: BundlerConfig
.TargetNames
) {
459 HeaderSize
+= 3 * 8; // Bundle offset, Size of bundle and size of triple.
460 HeaderSize
+= T
.size(); // The triple.
463 // Write to the buffer the header.
464 OS
<< OFFLOAD_BUNDLER_MAGIC_STR
;
466 Write8byteIntegerToBuffer(OS
, BundlerConfig
.TargetNames
.size());
469 for (auto &T
: BundlerConfig
.TargetNames
) {
470 MemoryBuffer
&MB
= *Inputs
[Idx
++];
471 HeaderSize
= alignTo(HeaderSize
, BundlerConfig
.BundleAlignment
);
473 Write8byteIntegerToBuffer(OS
, HeaderSize
);
474 // Size of the bundle (adds to the next bundle's offset)
475 Write8byteIntegerToBuffer(OS
, MB
.getBufferSize());
476 BundlesInfo
[T
] = BinaryBundleInfo(MB
.getBufferSize(), HeaderSize
);
477 HeaderSize
+= MB
.getBufferSize();
478 // Size of the triple
479 Write8byteIntegerToBuffer(OS
, T
.size());
483 return Error::success();
486 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
487 CurWriteBundleTarget
= TargetTriple
.str();
488 return Error::success();
491 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
492 return Error::success();
495 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
496 auto BI
= BundlesInfo
[CurWriteBundleTarget
];
498 // Pad with 0 to reach specified offset.
499 size_t CurrentPos
= OS
.tell();
500 size_t PaddingSize
= BI
.Offset
> CurrentPos
? BI
.Offset
- CurrentPos
: 0;
501 for (size_t I
= 0; I
< PaddingSize
; ++I
)
503 assert(OS
.tell() == BI
.Offset
);
505 OS
.write(Input
.getBufferStart(), Input
.getBufferSize());
507 return Error::success();
511 // This class implements a list of temporary files that are removed upon
512 // object destruction.
513 class TempFileHandlerRAII
{
515 ~TempFileHandlerRAII() {
516 for (const auto &File
: Files
)
517 sys::fs::remove(File
);
520 // Creates temporary file with given contents.
521 Expected
<StringRef
> Create(std::optional
<ArrayRef
<char>> Contents
) {
522 SmallString
<128u> File
;
523 if (std::error_code EC
=
524 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File
))
525 return createFileError(File
, EC
);
526 Files
.push_front(File
);
530 raw_fd_ostream
OS(File
, EC
);
532 return createFileError(File
, EC
);
533 OS
.write(Contents
->data(), Contents
->size());
535 return Files
.front().str();
539 std::forward_list
<SmallString
<128u>> Files
;
542 /// Handler for object files. The bundles are organized by sections with a
545 /// To unbundle, we just copy the contents of the designated section.
546 class ObjectFileHandler final
: public FileHandler
{
548 /// The object file we are currently dealing with.
549 std::unique_ptr
<ObjectFile
> Obj
;
551 /// Return the input file contents.
552 StringRef
getInputFileContents() const { return Obj
->getData(); }
554 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
556 static Expected
<std::optional
<StringRef
>>
557 IsOffloadSection(SectionRef CurSection
) {
558 Expected
<StringRef
> NameOrErr
= CurSection
.getName();
560 return NameOrErr
.takeError();
562 // If it does not start with the reserved suffix, just skip this section.
563 if (llvm::identify_magic(*NameOrErr
) != llvm::file_magic::offload_bundle
)
566 // Return the triple that is right after the reserved prefix.
567 return NameOrErr
->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1);
570 /// Total number of inputs.
571 unsigned NumberOfInputs
= 0;
573 /// Total number of processed inputs, i.e, inputs that were already
574 /// read from the buffers.
575 unsigned NumberOfProcessedInputs
= 0;
577 /// Iterator of the current and next section.
578 section_iterator CurrentSection
;
579 section_iterator NextSection
;
581 /// Configuration options and arrays for this bundler job
582 const OffloadBundlerConfig
&BundlerConfig
;
585 // TODO: Add error checking from ClangOffloadBundler.cpp
586 ObjectFileHandler(std::unique_ptr
<ObjectFile
> ObjIn
,
587 const OffloadBundlerConfig
&BC
)
588 : Obj(std::move(ObjIn
)), CurrentSection(Obj
->section_begin()),
589 NextSection(Obj
->section_begin()), BundlerConfig(BC
) {}
591 ~ObjectFileHandler() final
{}
593 Error
ReadHeader(MemoryBuffer
&Input
) final
{ return Error::success(); }
595 Expected
<std::optional
<StringRef
>>
596 ReadBundleStart(MemoryBuffer
&Input
) final
{
597 while (NextSection
!= Obj
->section_end()) {
598 CurrentSection
= NextSection
;
601 // Check if the current section name starts with the reserved prefix. If
602 // so, return the triple.
603 Expected
<std::optional
<StringRef
>> TripleOrErr
=
604 IsOffloadSection(*CurrentSection
);
606 return TripleOrErr
.takeError();
608 return **TripleOrErr
;
613 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{ return Error::success(); }
615 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
616 Expected
<StringRef
> ContentOrErr
= CurrentSection
->getContents();
618 return ContentOrErr
.takeError();
619 StringRef Content
= *ContentOrErr
;
621 // Copy fat object contents to the output when extracting host bundle.
622 if (Content
.size() == 1u && Content
.front() == 0)
623 Content
= StringRef(Input
.getBufferStart(), Input
.getBufferSize());
625 OS
.write(Content
.data(), Content
.size());
626 return Error::success();
629 Error
WriteHeader(raw_ostream
&OS
,
630 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
631 assert(BundlerConfig
.HostInputIndex
!= ~0u &&
632 "Host input index not defined.");
634 // Record number of inputs.
635 NumberOfInputs
= Inputs
.size();
636 return Error::success();
639 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
640 ++NumberOfProcessedInputs
;
641 return Error::success();
644 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
645 return Error::success();
648 Error
finalizeOutputFile() final
{
649 assert(NumberOfProcessedInputs
<= NumberOfInputs
&&
650 "Processing more inputs that actually exist!");
651 assert(BundlerConfig
.HostInputIndex
!= ~0u &&
652 "Host input index not defined.");
654 // If this is not the last output, we don't have to do anything.
655 if (NumberOfProcessedInputs
!= NumberOfInputs
)
656 return Error::success();
658 // We will use llvm-objcopy to add target objects sections to the output
659 // fat object. These sections should have 'exclude' flag set which tells
660 // link editor to remove them from linker inputs when linking executable or
663 assert(BundlerConfig
.ObjcopyPath
!= "" &&
664 "llvm-objcopy path not specified");
666 // Temporary files that need to be removed.
667 TempFileHandlerRAII TempFiles
;
669 // Compose llvm-objcopy command line for add target objects' sections with
670 // appropriate flags.
671 BumpPtrAllocator Alloc
;
672 StringSaver SS
{Alloc
};
673 SmallVector
<StringRef
, 8u> ObjcopyArgs
{"llvm-objcopy"};
675 for (unsigned I
= 0; I
< NumberOfInputs
; ++I
) {
676 StringRef InputFile
= BundlerConfig
.InputFileNames
[I
];
677 if (I
== BundlerConfig
.HostInputIndex
) {
678 // Special handling for the host bundle. We do not need to add a
679 // standard bundle for the host object since we are going to use fat
680 // object as a host object. Therefore use dummy contents (one zero byte)
681 // when creating section for the host bundle.
682 Expected
<StringRef
> TempFileOrErr
= TempFiles
.Create(ArrayRef
<char>(0));
684 return TempFileOrErr
.takeError();
685 InputFile
= *TempFileOrErr
;
688 ObjcopyArgs
.push_back(
689 SS
.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR
+
690 BundlerConfig
.TargetNames
[I
] + "=" + InputFile
));
691 ObjcopyArgs
.push_back(
692 SS
.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR
+
693 BundlerConfig
.TargetNames
[I
] + "=readonly,exclude"));
695 ObjcopyArgs
.push_back("--");
696 ObjcopyArgs
.push_back(
697 BundlerConfig
.InputFileNames
[BundlerConfig
.HostInputIndex
]);
698 ObjcopyArgs
.push_back(BundlerConfig
.OutputFileNames
.front());
700 if (Error Err
= executeObjcopy(BundlerConfig
.ObjcopyPath
, ObjcopyArgs
))
703 return Error::success();
706 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
707 return Error::success();
711 Error
executeObjcopy(StringRef Objcopy
, ArrayRef
<StringRef
> Args
) {
712 // If the user asked for the commands to be printed out, we do that
713 // instead of executing it.
714 if (BundlerConfig
.PrintExternalCommands
) {
715 errs() << "\"" << Objcopy
<< "\"";
716 for (StringRef Arg
: drop_begin(Args
, 1))
717 errs() << " \"" << Arg
<< "\"";
720 if (sys::ExecuteAndWait(Objcopy
, Args
))
721 return createStringError(inconvertibleErrorCode(),
722 "'llvm-objcopy' tool failed");
724 return Error::success();
728 /// Handler for text files. The bundled file will have the following format.
730 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
732 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
734 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
736 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
737 class TextFileHandler final
: public FileHandler
{
738 /// String that begins a line comment.
741 /// String that initiates a bundle.
742 std::string BundleStartString
;
744 /// String that closes a bundle.
745 std::string BundleEndString
;
747 /// Number of chars read from input.
748 size_t ReadChars
= 0u;
751 Error
ReadHeader(MemoryBuffer
&Input
) final
{ return Error::success(); }
753 Expected
<std::optional
<StringRef
>>
754 ReadBundleStart(MemoryBuffer
&Input
) final
{
755 StringRef FC
= Input
.getBuffer();
757 // Find start of the bundle.
758 ReadChars
= FC
.find(BundleStartString
, ReadChars
);
759 if (ReadChars
== FC
.npos
)
762 // Get position of the triple.
763 size_t TripleStart
= ReadChars
= ReadChars
+ BundleStartString
.size();
765 // Get position that closes the triple.
766 size_t TripleEnd
= ReadChars
= FC
.find("\n", ReadChars
);
767 if (TripleEnd
== FC
.npos
)
770 // Next time we read after the new line.
773 return StringRef(&FC
.data()[TripleStart
], TripleEnd
- TripleStart
);
776 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{
777 StringRef FC
= Input
.getBuffer();
779 // Read up to the next new line.
780 assert(FC
[ReadChars
] == '\n' && "The bundle should end with a new line.");
782 size_t TripleEnd
= ReadChars
= FC
.find("\n", ReadChars
+ 1);
783 if (TripleEnd
!= FC
.npos
)
784 // Next time we read after the new line.
787 return Error::success();
790 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
791 StringRef FC
= Input
.getBuffer();
792 size_t BundleStart
= ReadChars
;
794 // Find end of the bundle.
795 size_t BundleEnd
= ReadChars
= FC
.find(BundleEndString
, ReadChars
);
797 StringRef
Bundle(&FC
.data()[BundleStart
], BundleEnd
- BundleStart
);
800 return Error::success();
803 Error
WriteHeader(raw_ostream
&OS
,
804 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
805 return Error::success();
808 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
809 OS
<< BundleStartString
<< TargetTriple
<< "\n";
810 return Error::success();
813 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
814 OS
<< BundleEndString
<< TargetTriple
<< "\n";
815 return Error::success();
818 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
819 OS
<< Input
.getBuffer();
820 return Error::success();
824 TextFileHandler(StringRef Comment
) : Comment(Comment
), ReadChars(0) {
826 "\n" + Comment
.str() + " " OFFLOAD_BUNDLER_MAGIC_STR
"__START__ ";
828 "\n" + Comment
.str() + " " OFFLOAD_BUNDLER_MAGIC_STR
"__END__ ";
831 Error
listBundleIDsCallback(MemoryBuffer
&Input
,
832 const BundleInfo
&Info
) final
{
833 // TODO: To list bundle IDs in a bundled text file we need to go through
834 // all bundles. The format of bundled text file may need to include a
835 // header if the performance of listing bundle IDs of bundled text file is
837 ReadChars
= Input
.getBuffer().find(BundleEndString
, ReadChars
);
838 if (Error Err
= ReadBundleEnd(Input
))
840 return Error::success();
845 /// Return an appropriate object file handler. We use the specific object
846 /// handler if we know how to deal with that format, otherwise we use a default
847 /// binary file handler.
848 static std::unique_ptr
<FileHandler
>
849 CreateObjectFileHandler(MemoryBuffer
&FirstInput
,
850 const OffloadBundlerConfig
&BundlerConfig
) {
851 // Check if the input file format is one that we know how to deal with.
852 Expected
<std::unique_ptr
<Binary
>> BinaryOrErr
= createBinary(FirstInput
);
854 // We only support regular object files. If failed to open the input as a
855 // known binary or this is not an object file use the default binary handler.
856 if (errorToBool(BinaryOrErr
.takeError()) || !isa
<ObjectFile
>(*BinaryOrErr
))
857 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
859 // Otherwise create an object file handler. The handler will be owned by the
860 // client of this function.
861 return std::make_unique
<ObjectFileHandler
>(
862 std::unique_ptr
<ObjectFile
>(cast
<ObjectFile
>(BinaryOrErr
->release())),
866 /// Return an appropriate handler given the input files and options.
867 static Expected
<std::unique_ptr
<FileHandler
>>
868 CreateFileHandler(MemoryBuffer
&FirstInput
,
869 const OffloadBundlerConfig
&BundlerConfig
) {
870 std::string FilesType
= BundlerConfig
.FilesType
;
872 if (FilesType
== "i")
873 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
874 if (FilesType
== "ii")
875 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
876 if (FilesType
== "cui")
877 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
878 if (FilesType
== "hipi")
879 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
880 // TODO: `.d` should be eventually removed once `-M` and its variants are
881 // handled properly in offload compilation.
882 if (FilesType
== "d")
883 return std::make_unique
<TextFileHandler
>(/*Comment=*/"#");
884 if (FilesType
== "ll")
885 return std::make_unique
<TextFileHandler
>(/*Comment=*/";");
886 if (FilesType
== "bc")
887 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
888 if (FilesType
== "s")
889 return std::make_unique
<TextFileHandler
>(/*Comment=*/"#");
890 if (FilesType
== "o")
891 return CreateObjectFileHandler(FirstInput
, BundlerConfig
);
892 if (FilesType
== "a")
893 return CreateObjectFileHandler(FirstInput
, BundlerConfig
);
894 if (FilesType
== "gch")
895 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
896 if (FilesType
== "ast")
897 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
899 return createStringError(errc::invalid_argument
,
900 "'" + FilesType
+ "': invalid file type specified");
903 OffloadBundlerConfig::OffloadBundlerConfig() {
904 auto IgnoreEnvVarOpt
=
905 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
906 if (IgnoreEnvVarOpt
.has_value() && IgnoreEnvVarOpt
.value() == "1")
909 auto VerboseEnvVarOpt
= llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
910 if (VerboseEnvVarOpt
.has_value())
911 Verbose
= VerboseEnvVarOpt
.value() == "1";
913 auto CompressEnvVarOpt
=
914 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
915 if (CompressEnvVarOpt
.has_value())
916 Compress
= CompressEnvVarOpt
.value() == "1";
919 llvm::Expected
<std::unique_ptr
<llvm::MemoryBuffer
>>
920 CompressedOffloadBundle::compress(const llvm::MemoryBuffer
&Input
,
922 llvm::Timer
HashTimer("Hash Calculation Timer", "Hash calculation time",
923 ClangOffloadBundlerTimerGroup
);
925 HashTimer
.startTimer();
927 llvm::MD5::MD5Result Result
;
928 Hash
.update(Input
.getBuffer());
930 uint64_t TruncatedHash
= Result
.low();
932 HashTimer
.stopTimer();
934 SmallVector
<uint8_t, 0> CompressedBuffer
;
935 auto BufferUint8
= llvm::ArrayRef
<uint8_t>(
936 reinterpret_cast<const uint8_t *>(Input
.getBuffer().data()),
937 Input
.getBuffer().size());
939 llvm::compression::Format CompressionFormat
;
941 if (llvm::compression::zstd::isAvailable())
942 CompressionFormat
= llvm::compression::Format::Zstd
;
943 else if (llvm::compression::zlib::isAvailable())
944 CompressionFormat
= llvm::compression::Format::Zlib
;
946 return createStringError(llvm::inconvertibleErrorCode(),
947 "Compression not supported");
949 llvm::Timer
CompressTimer("Compression Timer", "Compression time",
950 ClangOffloadBundlerTimerGroup
);
952 CompressTimer
.startTimer();
953 llvm::compression::compress(CompressionFormat
, BufferUint8
, CompressedBuffer
);
955 CompressTimer
.stopTimer();
957 uint16_t CompressionMethod
= static_cast<uint16_t>(CompressionFormat
);
958 uint32_t UncompressedSize
= Input
.getBuffer().size();
960 SmallVector
<char, 0> FinalBuffer
;
961 llvm::raw_svector_ostream
OS(FinalBuffer
);
963 OS
.write(reinterpret_cast<const char *>(&Version
), sizeof(Version
));
964 OS
.write(reinterpret_cast<const char *>(&CompressionMethod
),
965 sizeof(CompressionMethod
));
966 OS
.write(reinterpret_cast<const char *>(&UncompressedSize
),
967 sizeof(UncompressedSize
));
968 OS
.write(reinterpret_cast<const char *>(&TruncatedHash
),
969 sizeof(TruncatedHash
));
970 OS
.write(reinterpret_cast<const char *>(CompressedBuffer
.data()),
971 CompressedBuffer
.size());
975 CompressionFormat
== llvm::compression::Format::Zstd
? "zstd" : "zlib";
976 llvm::errs() << "Compressed bundle format version: " << Version
<< "\n"
977 << "Compression method used: " << MethodUsed
<< "\n"
978 << "Binary size before compression: " << UncompressedSize
980 << "Binary size after compression: " << CompressedBuffer
.size()
982 << "Truncated MD5 hash: "
983 << llvm::format_hex(TruncatedHash
, 16) << "\n";
986 return llvm::MemoryBuffer::getMemBufferCopy(
987 llvm::StringRef(FinalBuffer
.data(), FinalBuffer
.size()));
990 llvm::Expected
<std::unique_ptr
<llvm::MemoryBuffer
>>
991 CompressedOffloadBundle::decompress(const llvm::MemoryBuffer
&Input
,
994 StringRef Blob
= Input
.getBuffer();
996 if (Blob
.size() < HeaderSize
) {
997 return llvm::MemoryBuffer::getMemBufferCopy(Blob
);
999 if (llvm::identify_magic(Blob
) !=
1000 llvm::file_magic::offload_bundle_compressed
) {
1002 llvm::errs() << "Uncompressed bundle.\n";
1003 return llvm::MemoryBuffer::getMemBufferCopy(Blob
);
1006 uint16_t ThisVersion
;
1007 uint16_t CompressionMethod
;
1008 uint32_t UncompressedSize
;
1009 uint64_t StoredHash
;
1010 memcpy(&ThisVersion
, Input
.getBuffer().data() + MagicNumber
.size(),
1012 memcpy(&CompressionMethod
, Blob
.data() + MagicSize
+ VersionFieldSize
,
1014 memcpy(&UncompressedSize
,
1015 Blob
.data() + MagicSize
+ VersionFieldSize
+ MethodFieldSize
,
1018 Blob
.data() + MagicSize
+ VersionFieldSize
+ MethodFieldSize
+
1022 llvm::compression::Format CompressionFormat
;
1023 if (CompressionMethod
==
1024 static_cast<uint16_t>(llvm::compression::Format::Zlib
))
1025 CompressionFormat
= llvm::compression::Format::Zlib
;
1026 else if (CompressionMethod
==
1027 static_cast<uint16_t>(llvm::compression::Format::Zstd
))
1028 CompressionFormat
= llvm::compression::Format::Zstd
;
1030 return createStringError(inconvertibleErrorCode(),
1031 "Unknown compressing method");
1033 llvm::Timer
DecompressTimer("Decompression Timer", "Decompression time",
1034 ClangOffloadBundlerTimerGroup
);
1036 DecompressTimer
.startTimer();
1038 SmallVector
<uint8_t, 0> DecompressedData
;
1039 StringRef CompressedData
= Blob
.substr(HeaderSize
);
1040 if (llvm::Error DecompressionError
= llvm::compression::decompress(
1041 CompressionFormat
, llvm::arrayRefFromStringRef(CompressedData
),
1042 DecompressedData
, UncompressedSize
))
1043 return createStringError(inconvertibleErrorCode(),
1044 "Could not decompress embedded file contents: " +
1045 llvm::toString(std::move(DecompressionError
)));
1048 DecompressTimer
.stopTimer();
1050 // Recalculate MD5 hash
1051 llvm::Timer
HashRecalcTimer("Hash Recalculation Timer",
1052 "Hash recalculation time",
1053 ClangOffloadBundlerTimerGroup
);
1054 HashRecalcTimer
.startTimer();
1056 llvm::MD5::MD5Result Result
;
1057 Hash
.update(llvm::ArrayRef
<uint8_t>(DecompressedData
.data(),
1058 DecompressedData
.size()));
1060 uint64_t RecalculatedHash
= Result
.low();
1061 HashRecalcTimer
.stopTimer();
1062 bool HashMatch
= (StoredHash
== RecalculatedHash
);
1064 llvm::errs() << "Compressed bundle format version: " << ThisVersion
<< "\n"
1065 << "Decompression method: "
1066 << (CompressionFormat
== llvm::compression::Format::Zlib
1070 << "Size before decompression: " << CompressedData
.size()
1072 << "Size after decompression: " << UncompressedSize
1074 << "Stored hash: " << llvm::format_hex(StoredHash
, 16) << "\n"
1075 << "Recalculated hash: "
1076 << llvm::format_hex(RecalculatedHash
, 16) << "\n"
1077 << "Hashes match: " << (HashMatch
? "Yes" : "No") << "\n";
1080 return llvm::MemoryBuffer::getMemBufferCopy(
1081 llvm::toStringRef(DecompressedData
));
1084 // List bundle IDs. Return true if an error was found.
1085 Error
OffloadBundler::ListBundleIDsInFile(
1086 StringRef InputFileName
, const OffloadBundlerConfig
&BundlerConfig
) {
1088 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1089 MemoryBuffer::getFileOrSTDIN(InputFileName
);
1090 if (std::error_code EC
= CodeOrErr
.getError())
1091 return createFileError(InputFileName
, EC
);
1093 // Decompress the input if necessary.
1094 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1095 CompressedOffloadBundle::decompress(**CodeOrErr
, BundlerConfig
.Verbose
);
1096 if (!DecompressedBufferOrErr
)
1097 return createStringError(
1098 inconvertibleErrorCode(),
1099 "Failed to decompress input: " +
1100 llvm::toString(DecompressedBufferOrErr
.takeError()));
1102 MemoryBuffer
&DecompressedInput
= **DecompressedBufferOrErr
;
1104 // Select the right files handler.
1105 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1106 CreateFileHandler(DecompressedInput
, BundlerConfig
);
1107 if (!FileHandlerOrErr
)
1108 return FileHandlerOrErr
.takeError();
1110 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1112 return FH
->listBundleIDs(DecompressedInput
);
1115 /// Bundle the files. Return true if an error was found.
1116 Error
OffloadBundler::BundleFiles() {
1119 // Create a buffer to hold the content before compressing.
1120 SmallVector
<char, 0> Buffer
;
1121 llvm::raw_svector_ostream
BufferStream(Buffer
);
1123 // Open input files.
1124 SmallVector
<std::unique_ptr
<MemoryBuffer
>, 8u> InputBuffers
;
1125 InputBuffers
.reserve(BundlerConfig
.InputFileNames
.size());
1126 for (auto &I
: BundlerConfig
.InputFileNames
) {
1127 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1128 MemoryBuffer::getFileOrSTDIN(I
);
1129 if (std::error_code EC
= CodeOrErr
.getError())
1130 return createFileError(I
, EC
);
1131 InputBuffers
.emplace_back(std::move(*CodeOrErr
));
1134 // Get the file handler. We use the host buffer as reference.
1135 assert((BundlerConfig
.HostInputIndex
!= ~0u || BundlerConfig
.AllowNoHost
) &&
1136 "Host input index undefined??");
1137 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
= CreateFileHandler(
1138 *InputBuffers
[BundlerConfig
.AllowNoHost
? 0
1139 : BundlerConfig
.HostInputIndex
],
1141 if (!FileHandlerOrErr
)
1142 return FileHandlerOrErr
.takeError();
1144 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1148 if (Error Err
= FH
->WriteHeader(BufferStream
, InputBuffers
))
1151 // Write all bundles along with the start/end markers. If an error was found
1152 // writing the end of the bundle component, abort the bundle writing.
1153 auto Input
= InputBuffers
.begin();
1154 for (auto &Triple
: BundlerConfig
.TargetNames
) {
1155 if (Error Err
= FH
->WriteBundleStart(BufferStream
, Triple
))
1157 if (Error Err
= FH
->WriteBundle(BufferStream
, **Input
))
1159 if (Error Err
= FH
->WriteBundleEnd(BufferStream
, Triple
))
1164 raw_fd_ostream
OutputFile(BundlerConfig
.OutputFileNames
.front(), EC
,
1167 return createFileError(BundlerConfig
.OutputFileNames
.front(), EC
);
1169 SmallVector
<char, 0> CompressedBuffer
;
1170 if (BundlerConfig
.Compress
) {
1171 std::unique_ptr
<llvm::MemoryBuffer
> BufferMemory
=
1172 llvm::MemoryBuffer::getMemBufferCopy(
1173 llvm::StringRef(Buffer
.data(), Buffer
.size()));
1174 auto CompressionResult
=
1175 CompressedOffloadBundle::compress(*BufferMemory
, BundlerConfig
.Verbose
);
1176 if (auto Error
= CompressionResult
.takeError())
1179 auto CompressedMemBuffer
= std::move(CompressionResult
.get());
1180 CompressedBuffer
.assign(CompressedMemBuffer
->getBufferStart(),
1181 CompressedMemBuffer
->getBufferEnd());
1183 CompressedBuffer
= Buffer
;
1185 OutputFile
.write(CompressedBuffer
.data(), CompressedBuffer
.size());
1187 return FH
->finalizeOutputFile();
1190 // Unbundle the files. Return true if an error was found.
1191 Error
OffloadBundler::UnbundleFiles() {
1193 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1194 MemoryBuffer::getFileOrSTDIN(BundlerConfig
.InputFileNames
.front());
1195 if (std::error_code EC
= CodeOrErr
.getError())
1196 return createFileError(BundlerConfig
.InputFileNames
.front(), EC
);
1198 // Decompress the input if necessary.
1199 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1200 CompressedOffloadBundle::decompress(**CodeOrErr
, BundlerConfig
.Verbose
);
1201 if (!DecompressedBufferOrErr
)
1202 return createStringError(
1203 inconvertibleErrorCode(),
1204 "Failed to decompress input: " +
1205 llvm::toString(DecompressedBufferOrErr
.takeError()));
1207 MemoryBuffer
&Input
= **DecompressedBufferOrErr
;
1209 // Select the right files handler.
1210 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1211 CreateFileHandler(Input
, BundlerConfig
);
1212 if (!FileHandlerOrErr
)
1213 return FileHandlerOrErr
.takeError();
1215 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1218 // Read the header of the bundled file.
1219 if (Error Err
= FH
->ReadHeader(Input
))
1222 // Create a work list that consist of the map triple/output file.
1223 StringMap
<StringRef
> Worklist
;
1224 auto Output
= BundlerConfig
.OutputFileNames
.begin();
1225 for (auto &Triple
: BundlerConfig
.TargetNames
) {
1226 Worklist
[Triple
] = *Output
;
1230 // Read all the bundles that are in the work list. If we find no bundles we
1231 // assume the file is meant for the host target.
1232 bool FoundHostBundle
= false;
1233 while (!Worklist
.empty()) {
1234 Expected
<std::optional
<StringRef
>> CurTripleOrErr
=
1235 FH
->ReadBundleStart(Input
);
1236 if (!CurTripleOrErr
)
1237 return CurTripleOrErr
.takeError();
1239 // We don't have more bundles.
1240 if (!*CurTripleOrErr
)
1243 StringRef CurTriple
= **CurTripleOrErr
;
1244 assert(!CurTriple
.empty());
1246 auto Output
= Worklist
.begin();
1247 for (auto E
= Worklist
.end(); Output
!= E
; Output
++) {
1248 if (isCodeObjectCompatible(
1249 OffloadTargetInfo(CurTriple
, BundlerConfig
),
1250 OffloadTargetInfo((*Output
).first(), BundlerConfig
))) {
1255 if (Output
== Worklist
.end())
1257 // Check if the output file can be opened and copy the bundle to it.
1259 raw_fd_ostream
OutputFile((*Output
).second
, EC
, sys::fs::OF_None
);
1261 return createFileError((*Output
).second
, EC
);
1262 if (Error Err
= FH
->ReadBundle(OutputFile
, Input
))
1264 if (Error Err
= FH
->ReadBundleEnd(Input
))
1266 Worklist
.erase(Output
);
1268 // Record if we found the host bundle.
1269 auto OffloadInfo
= OffloadTargetInfo(CurTriple
, BundlerConfig
);
1270 if (OffloadInfo
.hasHostKind())
1271 FoundHostBundle
= true;
1274 if (!BundlerConfig
.AllowMissingBundles
&& !Worklist
.empty()) {
1275 std::string ErrMsg
= "Can't find bundles for";
1276 std::set
<StringRef
> Sorted
;
1277 for (auto &E
: Worklist
)
1278 Sorted
.insert(E
.first());
1280 unsigned Last
= Sorted
.size() - 1;
1281 for (auto &E
: Sorted
) {
1282 if (I
!= 0 && Last
> 1)
1285 if (I
== Last
&& I
!= 0)
1290 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1293 // If no bundles were found, assume the input file is the host bundle and
1294 // create empty files for the remaining targets.
1295 if (Worklist
.size() == BundlerConfig
.TargetNames
.size()) {
1296 for (auto &E
: Worklist
) {
1298 raw_fd_ostream
OutputFile(E
.second
, EC
, sys::fs::OF_None
);
1300 return createFileError(E
.second
, EC
);
1302 // If this entry has a host kind, copy the input file to the output file.
1303 auto OffloadInfo
= OffloadTargetInfo(E
.getKey(), BundlerConfig
);
1304 if (OffloadInfo
.hasHostKind())
1305 OutputFile
.write(Input
.getBufferStart(), Input
.getBufferSize());
1307 return Error::success();
1310 // If we found elements, we emit an error if none of those were for the host
1311 // in case host bundle name was provided in command line.
1312 if (!(FoundHostBundle
|| BundlerConfig
.HostInputIndex
== ~0u ||
1313 BundlerConfig
.AllowMissingBundles
))
1314 return createStringError(inconvertibleErrorCode(),
1315 "Can't find bundle for the host target");
1317 // If we still have any elements in the worklist, create empty files for them.
1318 for (auto &E
: Worklist
) {
1320 raw_fd_ostream
OutputFile(E
.second
, EC
, sys::fs::OF_None
);
1322 return createFileError(E
.second
, EC
);
1325 return Error::success();
1328 static Archive::Kind
getDefaultArchiveKindForHost() {
1329 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1333 /// @brief Computes a list of targets among all given targets which are
1334 /// compatible with this code object
1335 /// @param [in] CodeObjectInfo Code Object
1336 /// @param [out] CompatibleTargets List of all compatible targets among all
1338 /// @return false, if no compatible target is found.
1340 getCompatibleOffloadTargets(OffloadTargetInfo
&CodeObjectInfo
,
1341 SmallVectorImpl
<StringRef
> &CompatibleTargets
,
1342 const OffloadBundlerConfig
&BundlerConfig
) {
1343 if (!CompatibleTargets
.empty()) {
1344 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1345 dbgs() << "CompatibleTargets list should be empty\n");
1348 for (auto &Target
: BundlerConfig
.TargetNames
) {
1349 auto TargetInfo
= OffloadTargetInfo(Target
, BundlerConfig
);
1350 if (isCodeObjectCompatible(CodeObjectInfo
, TargetInfo
))
1351 CompatibleTargets
.push_back(Target
);
1353 return !CompatibleTargets
.empty();
1356 /// UnbundleArchive takes an archive file (".a") as input containing bundled
1357 /// code object files, and a list of offload targets (not host), and extracts
1358 /// the code objects into a new archive file for each offload target. Each
1359 /// resulting archive file contains all code object files corresponding to that
1360 /// particular offload target. The created archive file does not
1361 /// contain an index of the symbols and code object files are named as
1362 /// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'.
1363 Error
OffloadBundler::UnbundleArchive() {
1364 std::vector
<std::unique_ptr
<MemoryBuffer
>> ArchiveBuffers
;
1366 /// Map of target names with list of object files that will form the device
1367 /// specific archive for that target
1368 StringMap
<std::vector
<NewArchiveMember
>> OutputArchivesMap
;
1370 // Map of target names and output archive filenames
1371 StringMap
<StringRef
> TargetOutputFileNameMap
;
1373 auto Output
= BundlerConfig
.OutputFileNames
.begin();
1374 for (auto &Target
: BundlerConfig
.TargetNames
) {
1375 TargetOutputFileNameMap
[Target
] = *Output
;
1379 StringRef IFName
= BundlerConfig
.InputFileNames
.front();
1381 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
1382 MemoryBuffer::getFileOrSTDIN(IFName
, true, false);
1383 if (std::error_code EC
= BufOrErr
.getError())
1384 return createFileError(BundlerConfig
.InputFileNames
.front(), EC
);
1386 ArchiveBuffers
.push_back(std::move(*BufOrErr
));
1387 Expected
<std::unique_ptr
<llvm::object::Archive
>> LibOrErr
=
1388 Archive::create(ArchiveBuffers
.back()->getMemBufferRef());
1390 return LibOrErr
.takeError();
1392 auto Archive
= std::move(*LibOrErr
);
1394 Error ArchiveErr
= Error::success();
1395 auto ChildEnd
= Archive
->child_end();
1397 /// Iterate over all bundled code object files in the input archive.
1398 for (auto ArchiveIter
= Archive
->child_begin(ArchiveErr
);
1399 ArchiveIter
!= ChildEnd
; ++ArchiveIter
) {
1402 auto ArchiveChildNameOrErr
= (*ArchiveIter
).getName();
1403 if (!ArchiveChildNameOrErr
)
1404 return ArchiveChildNameOrErr
.takeError();
1406 StringRef BundledObjectFile
= sys::path::filename(*ArchiveChildNameOrErr
);
1408 auto CodeObjectBufferRefOrErr
= (*ArchiveIter
).getMemoryBufferRef();
1409 if (!CodeObjectBufferRefOrErr
)
1410 return CodeObjectBufferRefOrErr
.takeError();
1412 auto TempCodeObjectBuffer
=
1413 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr
, false);
1415 // Decompress the buffer if necessary.
1416 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1417 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer
,
1418 BundlerConfig
.Verbose
);
1419 if (!DecompressedBufferOrErr
)
1420 return createStringError(
1421 inconvertibleErrorCode(),
1422 "Failed to decompress code object: " +
1423 llvm::toString(DecompressedBufferOrErr
.takeError()));
1425 MemoryBuffer
&CodeObjectBuffer
= **DecompressedBufferOrErr
;
1427 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1428 CreateFileHandler(CodeObjectBuffer
, BundlerConfig
);
1429 if (!FileHandlerOrErr
)
1430 return FileHandlerOrErr
.takeError();
1432 std::unique_ptr
<FileHandler
> &FileHandler
= *FileHandlerOrErr
;
1433 assert(FileHandler
&&
1434 "FileHandle creation failed for file in the archive!");
1436 if (Error ReadErr
= FileHandler
->ReadHeader(CodeObjectBuffer
))
1439 Expected
<std::optional
<StringRef
>> CurBundleIDOrErr
=
1440 FileHandler
->ReadBundleStart(CodeObjectBuffer
);
1441 if (!CurBundleIDOrErr
)
1442 return CurBundleIDOrErr
.takeError();
1444 std::optional
<StringRef
> OptionalCurBundleID
= *CurBundleIDOrErr
;
1445 // No device code in this child, skip.
1446 if (!OptionalCurBundleID
)
1448 StringRef CodeObject
= *OptionalCurBundleID
;
1450 // Process all bundle entries (CodeObjects) found in this child of input
1452 while (!CodeObject
.empty()) {
1453 SmallVector
<StringRef
> CompatibleTargets
;
1454 auto CodeObjectInfo
= OffloadTargetInfo(CodeObject
, BundlerConfig
);
1455 if (CodeObjectInfo
.hasHostKind()) {
1456 // Do nothing, we don't extract host code yet.
1457 } else if (getCompatibleOffloadTargets(CodeObjectInfo
, CompatibleTargets
,
1459 std::string BundleData
;
1460 raw_string_ostream
DataStream(BundleData
);
1461 if (Error Err
= FileHandler
->ReadBundle(DataStream
, CodeObjectBuffer
))
1464 for (auto &CompatibleTarget
: CompatibleTargets
) {
1465 SmallString
<128> BundledObjectFileName
;
1466 BundledObjectFileName
.assign(BundledObjectFile
);
1467 auto OutputBundleName
=
1468 Twine(llvm::sys::path::stem(BundledObjectFileName
) + "-" +
1470 getDeviceLibraryFileName(BundledObjectFileName
,
1471 CodeObjectInfo
.TargetID
))
1473 // Replace ':' in optional target feature list with '_' to ensure
1474 // cross-platform validity.
1475 std::replace(OutputBundleName
.begin(), OutputBundleName
.end(), ':',
1478 std::unique_ptr
<MemoryBuffer
> MemBuf
= MemoryBuffer::getMemBufferCopy(
1479 DataStream
.str(), OutputBundleName
);
1480 ArchiveBuffers
.push_back(std::move(MemBuf
));
1481 llvm::MemoryBufferRef MemBufRef
=
1482 MemoryBufferRef(*(ArchiveBuffers
.back()));
1484 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1485 // OutputArchivesMap.
1486 if (!OutputArchivesMap
.contains(CompatibleTarget
)) {
1488 std::vector
<NewArchiveMember
> ArchiveMembers
;
1489 ArchiveMembers
.push_back(NewArchiveMember(MemBufRef
));
1490 OutputArchivesMap
.insert_or_assign(CompatibleTarget
,
1491 std::move(ArchiveMembers
));
1493 OutputArchivesMap
[CompatibleTarget
].push_back(
1494 NewArchiveMember(MemBufRef
));
1499 if (Error Err
= FileHandler
->ReadBundleEnd(CodeObjectBuffer
))
1502 Expected
<std::optional
<StringRef
>> NextTripleOrErr
=
1503 FileHandler
->ReadBundleStart(CodeObjectBuffer
);
1504 if (!NextTripleOrErr
)
1505 return NextTripleOrErr
.takeError();
1507 CodeObject
= ((*NextTripleOrErr
).has_value()) ? **NextTripleOrErr
: "";
1508 } // End of processing of all bundle entries of this child of input archive.
1509 } // End of while over children of input archive.
1511 assert(!ArchiveErr
&& "Error occurred while reading archive!");
1513 /// Write out an archive for each target
1514 for (auto &Target
: BundlerConfig
.TargetNames
) {
1515 StringRef FileName
= TargetOutputFileNameMap
[Target
];
1516 StringMapIterator
<std::vector
<llvm::NewArchiveMember
>> CurArchiveMembers
=
1517 OutputArchivesMap
.find(Target
);
1518 if (CurArchiveMembers
!= OutputArchivesMap
.end()) {
1519 if (Error WriteErr
= writeArchive(FileName
, CurArchiveMembers
->getValue(),
1520 SymtabWritingMode::NormalSymtab
,
1521 getDefaultArchiveKindForHost(), true,
1524 } else if (!BundlerConfig
.AllowMissingBundles
) {
1525 std::string ErrMsg
=
1526 Twine("no compatible code object found for the target '" + Target
+
1527 "' in heterogeneous archive library: " + IFName
)
1529 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1530 } else { // Create an empty archive file if no compatible code object is
1531 // found and "allow-missing-bundles" is enabled. It ensures that
1532 // the linker using output of this step doesn't complain about
1533 // the missing input file.
1534 std::vector
<llvm::NewArchiveMember
> EmptyArchive
;
1535 EmptyArchive
.clear();
1536 if (Error WriteErr
= writeArchive(
1537 FileName
, EmptyArchive
, SymtabWritingMode::NormalSymtab
,
1538 getDefaultArchiveKindForHost(), true, false, nullptr))
1543 return Error::success();