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 "llvm/ADT/ArrayRef.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/ADT/StringMap.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/BinaryFormat/Magic.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/Compression.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/EndianStream.h"
35 #include "llvm/Support/Errc.h"
36 #include "llvm/Support/Error.h"
37 #include "llvm/Support/ErrorOr.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/MD5.h"
40 #include "llvm/Support/MemoryBuffer.h"
41 #include "llvm/Support/Path.h"
42 #include "llvm/Support/Program.h"
43 #include "llvm/Support/Signals.h"
44 #include "llvm/Support/StringSaver.h"
45 #include "llvm/Support/Timer.h"
46 #include "llvm/Support/WithColor.h"
47 #include "llvm/Support/raw_ostream.h"
48 #include "llvm/TargetParser/Host.h"
49 #include "llvm/TargetParser/Triple.h"
54 #include <forward_list>
55 #include <llvm/Support/Process.h>
59 #include <system_error>
63 using namespace llvm::object
;
64 using namespace clang
;
66 static llvm::TimerGroup
67 ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group",
68 "Timer group for clang offload bundler");
70 /// Magic string that marks the existence of offloading data.
71 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
73 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target
,
74 const OffloadBundlerConfig
&BC
)
77 // TODO: Add error checking from ClangOffloadBundler.cpp
78 auto TargetFeatures
= Target
.split(':');
79 auto TripleOrGPU
= TargetFeatures
.first
.rsplit('-');
81 if (clang::StringToOffloadArch(TripleOrGPU
.second
) !=
82 clang::OffloadArch::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
) ||
117 (OffloadKind
== "hip" && TargetOffloadKind
== "hipv4") ||
118 (OffloadKind
== "hipv4" && TargetOffloadKind
== "hip"))
121 if (BundlerConfig
.HipOpenmpCompatible
) {
122 bool HIPCompatibleWithOpenMP
= OffloadKind
.starts_with_insensitive("hip") &&
123 TargetOffloadKind
== "openmp";
124 bool OpenMPCompatibleWithHIP
=
125 OffloadKind
== "openmp" &&
126 TargetOffloadKind
.starts_with_insensitive("hip");
127 return HIPCompatibleWithOpenMP
|| OpenMPCompatibleWithHIP
;
132 bool OffloadTargetInfo::isTripleValid() const {
133 return !Triple
.str().empty() && Triple
.getArch() != Triple::UnknownArch
;
136 bool OffloadTargetInfo::operator==(const OffloadTargetInfo
&Target
) const {
137 return OffloadKind
== Target
.OffloadKind
&&
138 Triple
.isCompatibleWith(Target
.Triple
) && TargetID
== Target
.TargetID
;
141 std::string
OffloadTargetInfo::str() const {
142 return Twine(OffloadKind
+ "-" + Triple
.str() + "-" + TargetID
).str();
145 static StringRef
getDeviceFileExtension(StringRef Device
,
146 StringRef BundleFileName
) {
147 if (Device
.contains("gfx"))
149 if (Device
.contains("sm_"))
151 return sys::path::extension(BundleFileName
);
154 static std::string
getDeviceLibraryFileName(StringRef BundleFileName
,
156 StringRef LibName
= sys::path::stem(BundleFileName
);
157 StringRef Extension
= getDeviceFileExtension(Device
, BundleFileName
);
166 /// Generic file handler interface.
175 virtual ~FileHandler() {}
177 /// Update the file handler with information from the header of the bundled
179 virtual Error
ReadHeader(MemoryBuffer
&Input
) = 0;
181 /// Read the marker of the next bundled to be read in the file. The bundle
182 /// name is returned if there is one in the file, or `std::nullopt` if there
183 /// are no more bundles to be read.
184 virtual Expected
<std::optional
<StringRef
>>
185 ReadBundleStart(MemoryBuffer
&Input
) = 0;
187 /// Read the marker that closes the current bundle.
188 virtual Error
ReadBundleEnd(MemoryBuffer
&Input
) = 0;
190 /// Read the current bundle and write the result into the stream \a OS.
191 virtual Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) = 0;
193 /// Write the header of the bundled file to \a OS based on the information
194 /// gathered from \a Inputs.
195 virtual Error
WriteHeader(raw_ostream
&OS
,
196 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) = 0;
198 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
200 virtual Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) = 0;
202 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
204 virtual Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) = 0;
206 /// Write the bundle from \a Input into \a OS.
207 virtual Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) = 0;
209 /// Finalize output file.
210 virtual Error
finalizeOutputFile() { return Error::success(); }
212 /// List bundle IDs in \a Input.
213 virtual Error
listBundleIDs(MemoryBuffer
&Input
) {
214 if (Error Err
= ReadHeader(Input
))
216 return forEachBundle(Input
, [&](const BundleInfo
&Info
) -> Error
{
217 llvm::outs() << Info
.BundleID
<< '\n';
218 Error Err
= listBundleIDsCallback(Input
, Info
);
221 return Error::success();
225 /// Get bundle IDs in \a Input in \a BundleIds.
226 virtual Error
getBundleIDs(MemoryBuffer
&Input
,
227 std::set
<StringRef
> &BundleIds
) {
228 if (Error Err
= ReadHeader(Input
))
230 return forEachBundle(Input
, [&](const BundleInfo
&Info
) -> Error
{
231 BundleIds
.insert(Info
.BundleID
);
232 Error Err
= listBundleIDsCallback(Input
, Info
);
235 return Error::success();
239 /// For each bundle in \a Input, do \a Func.
240 Error
forEachBundle(MemoryBuffer
&Input
,
241 std::function
<Error(const BundleInfo
&)> Func
) {
243 Expected
<std::optional
<StringRef
>> CurTripleOrErr
=
244 ReadBundleStart(Input
);
246 return CurTripleOrErr
.takeError();
249 if (!*CurTripleOrErr
)
252 StringRef CurTriple
= **CurTripleOrErr
;
253 assert(!CurTriple
.empty());
255 BundleInfo Info
{CurTriple
};
256 if (Error Err
= Func(Info
))
259 return Error::success();
263 virtual Error
listBundleIDsCallback(MemoryBuffer
&Input
,
264 const BundleInfo
&Info
) {
265 return Error::success();
269 /// Handler for binary files. The bundled file will have the following format
270 /// (all integers are stored in little-endian format):
272 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
274 /// NumberOfOffloadBundles (8-byte integer)
276 /// OffsetOfBundle1 (8-byte integer)
277 /// SizeOfBundle1 (8-byte integer)
278 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
279 /// TripleOfBundle1 (byte length defined before)
283 /// OffsetOfBundleN (8-byte integer)
284 /// SizeOfBundleN (8-byte integer)
285 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
286 /// TripleOfBundleN (byte length defined before)
292 /// Read 8-byte integers from a buffer in little-endian format.
293 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer
, size_t pos
) {
294 return llvm::support::endian::read64le(Buffer
.data() + pos
);
297 /// Write 8-byte integers to a buffer in little-endian format.
298 static void Write8byteIntegerToBuffer(raw_ostream
&OS
, uint64_t Val
) {
299 llvm::support::endian::write(OS
, Val
, llvm::endianness::little
);
302 class BinaryFileHandler final
: public FileHandler
{
303 /// Information about the bundles extracted from the header.
304 struct BinaryBundleInfo final
: public BundleInfo
{
305 /// Size of the bundle.
307 /// Offset at which the bundle starts in the bundled file.
308 uint64_t Offset
= 0u;
310 BinaryBundleInfo() {}
311 BinaryBundleInfo(uint64_t Size
, uint64_t Offset
)
312 : Size(Size
), Offset(Offset
) {}
315 /// Map between a triple and the corresponding bundle information.
316 StringMap
<BinaryBundleInfo
> BundlesInfo
;
318 /// Iterator for the bundle information that is being read.
319 StringMap
<BinaryBundleInfo
>::iterator CurBundleInfo
;
320 StringMap
<BinaryBundleInfo
>::iterator NextBundleInfo
;
322 /// Current bundle target to be written.
323 std::string CurWriteBundleTarget
;
325 /// Configuration options and arrays for this bundler job
326 const OffloadBundlerConfig
&BundlerConfig
;
329 // TODO: Add error checking from ClangOffloadBundler.cpp
330 BinaryFileHandler(const OffloadBundlerConfig
&BC
) : BundlerConfig(BC
) {}
332 ~BinaryFileHandler() final
{}
334 Error
ReadHeader(MemoryBuffer
&Input
) final
{
335 StringRef FC
= Input
.getBuffer();
337 // Initialize the current bundle with the end of the container.
338 CurBundleInfo
= BundlesInfo
.end();
340 // Check if buffer is smaller than magic string.
341 size_t ReadChars
= sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1;
342 if (ReadChars
> FC
.size())
343 return Error::success();
345 // Check if no magic was found.
346 if (llvm::identify_magic(FC
) != llvm::file_magic::offload_bundle
)
347 return Error::success();
349 // Read number of bundles.
350 if (ReadChars
+ 8 > FC
.size())
351 return Error::success();
353 uint64_t NumberOfBundles
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
356 // Read bundle offsets, sizes and triples.
357 for (uint64_t i
= 0; i
< NumberOfBundles
; ++i
) {
360 if (ReadChars
+ 8 > FC
.size())
361 return Error::success();
363 uint64_t Offset
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
367 if (ReadChars
+ 8 > FC
.size())
368 return Error::success();
370 uint64_t Size
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
374 if (ReadChars
+ 8 > FC
.size())
375 return Error::success();
377 uint64_t TripleSize
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
381 if (ReadChars
+ TripleSize
> FC
.size())
382 return Error::success();
384 StringRef
Triple(&FC
.data()[ReadChars
], TripleSize
);
385 ReadChars
+= TripleSize
;
387 // Check if the offset and size make sense.
388 if (!Offset
|| Offset
+ Size
> FC
.size())
389 return Error::success();
391 assert(!BundlesInfo
.contains(Triple
) && "Triple is duplicated??");
392 BundlesInfo
[Triple
] = BinaryBundleInfo(Size
, Offset
);
394 // Set the iterator to where we will start to read.
395 CurBundleInfo
= BundlesInfo
.end();
396 NextBundleInfo
= BundlesInfo
.begin();
397 return Error::success();
400 Expected
<std::optional
<StringRef
>>
401 ReadBundleStart(MemoryBuffer
&Input
) final
{
402 if (NextBundleInfo
== BundlesInfo
.end())
404 CurBundleInfo
= NextBundleInfo
++;
405 return CurBundleInfo
->first();
408 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{
409 assert(CurBundleInfo
!= BundlesInfo
.end() && "Invalid reader info!");
410 return Error::success();
413 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
414 assert(CurBundleInfo
!= BundlesInfo
.end() && "Invalid reader info!");
415 StringRef FC
= Input
.getBuffer();
416 OS
.write(FC
.data() + CurBundleInfo
->second
.Offset
,
417 CurBundleInfo
->second
.Size
);
418 return Error::success();
421 Error
WriteHeader(raw_ostream
&OS
,
422 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
424 // Compute size of the header.
425 uint64_t HeaderSize
= 0;
427 HeaderSize
+= sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1;
428 HeaderSize
+= 8; // Number of Bundles
430 for (auto &T
: BundlerConfig
.TargetNames
) {
431 HeaderSize
+= 3 * 8; // Bundle offset, Size of bundle and size of triple.
432 HeaderSize
+= T
.size(); // The triple.
435 // Write to the buffer the header.
436 OS
<< OFFLOAD_BUNDLER_MAGIC_STR
;
438 Write8byteIntegerToBuffer(OS
, BundlerConfig
.TargetNames
.size());
441 for (auto &T
: BundlerConfig
.TargetNames
) {
442 MemoryBuffer
&MB
= *Inputs
[Idx
++];
443 HeaderSize
= alignTo(HeaderSize
, BundlerConfig
.BundleAlignment
);
445 Write8byteIntegerToBuffer(OS
, HeaderSize
);
446 // Size of the bundle (adds to the next bundle's offset)
447 Write8byteIntegerToBuffer(OS
, MB
.getBufferSize());
448 BundlesInfo
[T
] = BinaryBundleInfo(MB
.getBufferSize(), HeaderSize
);
449 HeaderSize
+= MB
.getBufferSize();
450 // Size of the triple
451 Write8byteIntegerToBuffer(OS
, T
.size());
455 return Error::success();
458 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
459 CurWriteBundleTarget
= TargetTriple
.str();
460 return Error::success();
463 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
464 return Error::success();
467 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
468 auto BI
= BundlesInfo
[CurWriteBundleTarget
];
470 // Pad with 0 to reach specified offset.
471 size_t CurrentPos
= OS
.tell();
472 size_t PaddingSize
= BI
.Offset
> CurrentPos
? BI
.Offset
- CurrentPos
: 0;
473 for (size_t I
= 0; I
< PaddingSize
; ++I
)
475 assert(OS
.tell() == BI
.Offset
);
477 OS
.write(Input
.getBufferStart(), Input
.getBufferSize());
479 return Error::success();
483 // This class implements a list of temporary files that are removed upon
484 // object destruction.
485 class TempFileHandlerRAII
{
487 ~TempFileHandlerRAII() {
488 for (const auto &File
: Files
)
489 sys::fs::remove(File
);
492 // Creates temporary file with given 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
);
502 raw_fd_ostream
OS(File
, EC
);
504 return createFileError(File
, EC
);
505 OS
.write(Contents
->data(), Contents
->size());
507 return Files
.front().str();
511 std::forward_list
<SmallString
<128u>> Files
;
514 /// Handler for object files. The bundles are organized by sections with a
517 /// To unbundle, we just copy the contents of the designated section.
518 class ObjectFileHandler final
: public FileHandler
{
520 /// The object file we are currently dealing with.
521 std::unique_ptr
<ObjectFile
> Obj
;
523 /// Return the input file contents.
524 StringRef
getInputFileContents() const { return Obj
->getData(); }
526 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
528 static Expected
<std::optional
<StringRef
>>
529 IsOffloadSection(SectionRef CurSection
) {
530 Expected
<StringRef
> NameOrErr
= CurSection
.getName();
532 return NameOrErr
.takeError();
534 // If it does not start with the reserved suffix, just skip this section.
535 if (llvm::identify_magic(*NameOrErr
) != llvm::file_magic::offload_bundle
)
538 // Return the triple that is right after the reserved prefix.
539 return NameOrErr
->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1);
542 /// Total number of inputs.
543 unsigned NumberOfInputs
= 0;
545 /// Total number of processed inputs, i.e, inputs that were already
546 /// read from the buffers.
547 unsigned NumberOfProcessedInputs
= 0;
549 /// Iterator of the current and next section.
550 section_iterator CurrentSection
;
551 section_iterator NextSection
;
553 /// Configuration options and arrays for this bundler job
554 const OffloadBundlerConfig
&BundlerConfig
;
557 // TODO: Add error checking from ClangOffloadBundler.cpp
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
) {}
563 ~ObjectFileHandler() final
{}
565 Error
ReadHeader(MemoryBuffer
&Input
) final
{ return Error::success(); }
567 Expected
<std::optional
<StringRef
>>
568 ReadBundleStart(MemoryBuffer
&Input
) final
{
569 while (NextSection
!= Obj
->section_end()) {
570 CurrentSection
= NextSection
;
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
);
578 return TripleOrErr
.takeError();
580 return **TripleOrErr
;
585 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{ return Error::success(); }
587 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
588 Expected
<StringRef
> ContentOrErr
= CurrentSection
->getContents();
590 return ContentOrErr
.takeError();
591 StringRef Content
= *ContentOrErr
;
593 // Copy fat object contents to the output when extracting host bundle.
594 std::string ModifiedContent
;
595 if (Content
.size() == 1u && Content
.front() == 0) {
596 auto HostBundleOrErr
= getHostBundle(
597 StringRef(Input
.getBufferStart(), Input
.getBufferSize()));
598 if (!HostBundleOrErr
)
599 return HostBundleOrErr
.takeError();
601 ModifiedContent
= std::move(*HostBundleOrErr
);
602 Content
= ModifiedContent
;
605 OS
.write(Content
.data(), Content
.size());
606 return Error::success();
609 Error
WriteHeader(raw_ostream
&OS
,
610 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
611 assert(BundlerConfig
.HostInputIndex
!= ~0u &&
612 "Host input index not defined.");
614 // Record number of inputs.
615 NumberOfInputs
= Inputs
.size();
616 return Error::success();
619 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
620 ++NumberOfProcessedInputs
;
621 return Error::success();
624 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
625 return Error::success();
628 Error
finalizeOutputFile() final
{
629 assert(NumberOfProcessedInputs
<= NumberOfInputs
&&
630 "Processing more inputs that actually exist!");
631 assert(BundlerConfig
.HostInputIndex
!= ~0u &&
632 "Host input index not defined.");
634 // If this is not the last output, we don't have to do anything.
635 if (NumberOfProcessedInputs
!= NumberOfInputs
)
636 return Error::success();
638 // We will use llvm-objcopy to add target objects sections to the output
639 // fat object. These sections should have 'exclude' flag set which tells
640 // link editor to remove them from linker inputs when linking executable or
643 assert(BundlerConfig
.ObjcopyPath
!= "" &&
644 "llvm-objcopy path not specified");
646 // Temporary files that need to be removed.
647 TempFileHandlerRAII TempFiles
;
649 // Compose llvm-objcopy command line for add target objects' sections with
650 // appropriate flags.
651 BumpPtrAllocator Alloc
;
652 StringSaver SS
{Alloc
};
653 SmallVector
<StringRef
, 8u> ObjcopyArgs
{"llvm-objcopy"};
655 for (unsigned I
= 0; I
< NumberOfInputs
; ++I
) {
656 StringRef InputFile
= BundlerConfig
.InputFileNames
[I
];
657 if (I
== BundlerConfig
.HostInputIndex
) {
658 // Special handling for the host bundle. We do not need to add a
659 // standard bundle for the host object since we are going to use fat
660 // object as a host object. Therefore use dummy contents (one zero byte)
661 // when creating section for the host bundle.
662 Expected
<StringRef
> TempFileOrErr
= TempFiles
.Create(ArrayRef
<char>(0));
664 return TempFileOrErr
.takeError();
665 InputFile
= *TempFileOrErr
;
668 ObjcopyArgs
.push_back(
669 SS
.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR
+
670 BundlerConfig
.TargetNames
[I
] + "=" + InputFile
));
671 ObjcopyArgs
.push_back(
672 SS
.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR
+
673 BundlerConfig
.TargetNames
[I
] + "=readonly,exclude"));
675 ObjcopyArgs
.push_back("--");
676 ObjcopyArgs
.push_back(
677 BundlerConfig
.InputFileNames
[BundlerConfig
.HostInputIndex
]);
678 ObjcopyArgs
.push_back(BundlerConfig
.OutputFileNames
.front());
680 if (Error Err
= executeObjcopy(BundlerConfig
.ObjcopyPath
, ObjcopyArgs
))
683 return Error::success();
686 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
687 return Error::success();
691 Error
executeObjcopy(StringRef Objcopy
, ArrayRef
<StringRef
> Args
) {
692 // If the user asked for the commands to be printed out, we do that
693 // instead of executing it.
694 if (BundlerConfig
.PrintExternalCommands
) {
695 errs() << "\"" << Objcopy
<< "\"";
696 for (StringRef Arg
: drop_begin(Args
, 1))
697 errs() << " \"" << Arg
<< "\"";
700 if (sys::ExecuteAndWait(Objcopy
, Args
))
701 return createStringError(inconvertibleErrorCode(),
702 "'llvm-objcopy' tool failed");
704 return Error::success();
707 Expected
<std::string
> getHostBundle(StringRef Input
) {
708 TempFileHandlerRAII TempFiles
;
710 auto ModifiedObjPathOrErr
= TempFiles
.Create(std::nullopt
);
711 if (!ModifiedObjPathOrErr
)
712 return ModifiedObjPathOrErr
.takeError();
713 StringRef ModifiedObjPath
= *ModifiedObjPathOrErr
;
715 BumpPtrAllocator Alloc
;
716 StringSaver SS
{Alloc
};
717 SmallVector
<StringRef
, 16> ObjcopyArgs
{"llvm-objcopy"};
719 ObjcopyArgs
.push_back("--regex");
720 ObjcopyArgs
.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
721 ObjcopyArgs
.push_back("--");
723 StringRef ObjcopyInputFileName
;
724 // When unbundling an archive, the content of each object file in the
725 // archive is passed to this function by parameter Input, which is different
726 // from the content of the original input archive file, therefore it needs
727 // to be saved to a temporary file before passed to llvm-objcopy. Otherwise,
728 // Input is the same as the content of the original input file, therefore
729 // temporary file is not needed.
730 if (StringRef(BundlerConfig
.FilesType
).starts_with("a")) {
731 auto InputFileOrErr
=
732 TempFiles
.Create(ArrayRef
<char>(Input
.data(), Input
.size()));
734 return InputFileOrErr
.takeError();
735 ObjcopyInputFileName
= *InputFileOrErr
;
737 ObjcopyInputFileName
= BundlerConfig
.InputFileNames
.front();
739 ObjcopyArgs
.push_back(ObjcopyInputFileName
);
740 ObjcopyArgs
.push_back(ModifiedObjPath
);
742 if (Error Err
= executeObjcopy(BundlerConfig
.ObjcopyPath
, ObjcopyArgs
))
743 return std::move(Err
);
745 auto BufOrErr
= MemoryBuffer::getFile(ModifiedObjPath
);
747 return createStringError(BufOrErr
.getError(),
748 "Failed to read back the modified object file");
750 return BufOrErr
->get()->getBuffer().str();
754 /// Handler for text files. The bundled file will have the following format.
756 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
758 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
760 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
762 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
763 class TextFileHandler final
: public FileHandler
{
764 /// String that begins a line comment.
767 /// String that initiates a bundle.
768 std::string BundleStartString
;
770 /// String that closes a bundle.
771 std::string BundleEndString
;
773 /// Number of chars read from input.
774 size_t ReadChars
= 0u;
777 Error
ReadHeader(MemoryBuffer
&Input
) final
{ return Error::success(); }
779 Expected
<std::optional
<StringRef
>>
780 ReadBundleStart(MemoryBuffer
&Input
) final
{
781 StringRef FC
= Input
.getBuffer();
783 // Find start of the bundle.
784 ReadChars
= FC
.find(BundleStartString
, ReadChars
);
785 if (ReadChars
== FC
.npos
)
788 // Get position of the triple.
789 size_t TripleStart
= ReadChars
= ReadChars
+ BundleStartString
.size();
791 // Get position that closes the triple.
792 size_t TripleEnd
= ReadChars
= FC
.find("\n", ReadChars
);
793 if (TripleEnd
== FC
.npos
)
796 // Next time we read after the new line.
799 return StringRef(&FC
.data()[TripleStart
], TripleEnd
- TripleStart
);
802 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{
803 StringRef FC
= Input
.getBuffer();
805 // Read up to the next new line.
806 assert(FC
[ReadChars
] == '\n' && "The bundle should end with a new line.");
808 size_t TripleEnd
= ReadChars
= FC
.find("\n", ReadChars
+ 1);
809 if (TripleEnd
!= FC
.npos
)
810 // Next time we read after the new line.
813 return Error::success();
816 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
817 StringRef FC
= Input
.getBuffer();
818 size_t BundleStart
= ReadChars
;
820 // Find end of the bundle.
821 size_t BundleEnd
= ReadChars
= FC
.find(BundleEndString
, ReadChars
);
823 StringRef
Bundle(&FC
.data()[BundleStart
], BundleEnd
- BundleStart
);
826 return Error::success();
829 Error
WriteHeader(raw_ostream
&OS
,
830 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
831 return Error::success();
834 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
835 OS
<< BundleStartString
<< TargetTriple
<< "\n";
836 return Error::success();
839 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
840 OS
<< BundleEndString
<< TargetTriple
<< "\n";
841 return Error::success();
844 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
845 OS
<< Input
.getBuffer();
846 return Error::success();
850 TextFileHandler(StringRef Comment
) : Comment(Comment
), ReadChars(0) {
852 "\n" + Comment
.str() + " " OFFLOAD_BUNDLER_MAGIC_STR
"__START__ ";
854 "\n" + Comment
.str() + " " OFFLOAD_BUNDLER_MAGIC_STR
"__END__ ";
857 Error
listBundleIDsCallback(MemoryBuffer
&Input
,
858 const BundleInfo
&Info
) final
{
859 // TODO: To list bundle IDs in a bundled text file we need to go through
860 // all bundles. The format of bundled text file may need to include a
861 // header if the performance of listing bundle IDs of bundled text file is
863 ReadChars
= Input
.getBuffer().find(BundleEndString
, ReadChars
);
864 if (Error Err
= ReadBundleEnd(Input
))
866 return Error::success();
871 /// Return an appropriate object file handler. We use the specific object
872 /// handler if we know how to deal with that format, otherwise we use a default
873 /// binary file handler.
874 static std::unique_ptr
<FileHandler
>
875 CreateObjectFileHandler(MemoryBuffer
&FirstInput
,
876 const OffloadBundlerConfig
&BundlerConfig
) {
877 // Check if the input file format is one that we know how to deal with.
878 Expected
<std::unique_ptr
<Binary
>> BinaryOrErr
= createBinary(FirstInput
);
880 // We only support regular object files. If failed to open the input as a
881 // known binary or this is not an object file use the default binary handler.
882 if (errorToBool(BinaryOrErr
.takeError()) || !isa
<ObjectFile
>(*BinaryOrErr
))
883 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
885 // Otherwise create an object file handler. The handler will be owned by the
886 // client of this function.
887 return std::make_unique
<ObjectFileHandler
>(
888 std::unique_ptr
<ObjectFile
>(cast
<ObjectFile
>(BinaryOrErr
->release())),
892 /// Return an appropriate handler given the input files and options.
893 static Expected
<std::unique_ptr
<FileHandler
>>
894 CreateFileHandler(MemoryBuffer
&FirstInput
,
895 const OffloadBundlerConfig
&BundlerConfig
) {
896 std::string FilesType
= BundlerConfig
.FilesType
;
898 if (FilesType
== "i")
899 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
900 if (FilesType
== "ii")
901 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
902 if (FilesType
== "cui")
903 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
904 if (FilesType
== "hipi")
905 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
906 // TODO: `.d` should be eventually removed once `-M` and its variants are
907 // handled properly in offload compilation.
908 if (FilesType
== "d")
909 return std::make_unique
<TextFileHandler
>(/*Comment=*/"#");
910 if (FilesType
== "ll")
911 return std::make_unique
<TextFileHandler
>(/*Comment=*/";");
912 if (FilesType
== "bc")
913 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
914 if (FilesType
== "s")
915 return std::make_unique
<TextFileHandler
>(/*Comment=*/"#");
916 if (FilesType
== "o")
917 return CreateObjectFileHandler(FirstInput
, BundlerConfig
);
918 if (FilesType
== "a")
919 return CreateObjectFileHandler(FirstInput
, BundlerConfig
);
920 if (FilesType
== "gch")
921 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
922 if (FilesType
== "ast")
923 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
925 return createStringError(errc::invalid_argument
,
926 "'" + FilesType
+ "': invalid file type specified");
929 OffloadBundlerConfig::OffloadBundlerConfig() {
930 if (llvm::compression::zstd::isAvailable()) {
931 CompressionFormat
= llvm::compression::Format::Zstd
;
932 // Compression level 3 is usually sufficient for zstd since long distance
933 // matching is enabled.
934 CompressionLevel
= 3;
935 } else if (llvm::compression::zlib::isAvailable()) {
936 CompressionFormat
= llvm::compression::Format::Zlib
;
937 // Use default level for zlib since higher level does not have significant
939 CompressionLevel
= llvm::compression::zlib::DefaultCompression
;
941 auto IgnoreEnvVarOpt
=
942 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
943 if (IgnoreEnvVarOpt
.has_value() && IgnoreEnvVarOpt
.value() == "1")
946 auto VerboseEnvVarOpt
= llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
947 if (VerboseEnvVarOpt
.has_value())
948 Verbose
= VerboseEnvVarOpt
.value() == "1";
950 auto CompressEnvVarOpt
=
951 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
952 if (CompressEnvVarOpt
.has_value())
953 Compress
= CompressEnvVarOpt
.value() == "1";
955 auto CompressionLevelEnvVarOpt
=
956 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
957 if (CompressionLevelEnvVarOpt
.has_value()) {
958 llvm::StringRef CompressionLevelStr
= CompressionLevelEnvVarOpt
.value();
960 if (!CompressionLevelStr
.getAsInteger(10, Level
))
961 CompressionLevel
= Level
;
964 << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
965 << CompressionLevelStr
.str() << ". Ignoring it.\n";
969 // Utility function to format numbers with commas
970 static std::string
formatWithCommas(unsigned long long Value
) {
971 std::string Num
= std::to_string(Value
);
972 int InsertPosition
= Num
.length() - 3;
973 while (InsertPosition
> 0) {
974 Num
.insert(InsertPosition
, ",");
980 llvm::Expected
<std::unique_ptr
<llvm::MemoryBuffer
>>
981 CompressedOffloadBundle::compress(llvm::compression::Params P
,
982 const llvm::MemoryBuffer
&Input
,
984 if (!llvm::compression::zstd::isAvailable() &&
985 !llvm::compression::zlib::isAvailable())
986 return createStringError(llvm::inconvertibleErrorCode(),
987 "Compression not supported");
989 llvm::Timer
HashTimer("Hash Calculation Timer", "Hash calculation time",
990 ClangOffloadBundlerTimerGroup
);
992 HashTimer
.startTimer();
994 llvm::MD5::MD5Result Result
;
995 Hash
.update(Input
.getBuffer());
997 uint64_t TruncatedHash
= Result
.low();
999 HashTimer
.stopTimer();
1001 SmallVector
<uint8_t, 0> CompressedBuffer
;
1002 auto BufferUint8
= llvm::ArrayRef
<uint8_t>(
1003 reinterpret_cast<const uint8_t *>(Input
.getBuffer().data()),
1004 Input
.getBuffer().size());
1006 llvm::Timer
CompressTimer("Compression Timer", "Compression time",
1007 ClangOffloadBundlerTimerGroup
);
1009 CompressTimer
.startTimer();
1010 llvm::compression::compress(P
, BufferUint8
, CompressedBuffer
);
1012 CompressTimer
.stopTimer();
1014 uint16_t CompressionMethod
= static_cast<uint16_t>(P
.format
);
1015 uint32_t UncompressedSize
= Input
.getBuffer().size();
1016 uint32_t TotalFileSize
= MagicNumber
.size() + sizeof(TotalFileSize
) +
1017 sizeof(Version
) + sizeof(CompressionMethod
) +
1018 sizeof(UncompressedSize
) + sizeof(TruncatedHash
) +
1019 CompressedBuffer
.size();
1021 SmallVector
<char, 0> FinalBuffer
;
1022 llvm::raw_svector_ostream
OS(FinalBuffer
);
1024 OS
.write(reinterpret_cast<const char *>(&Version
), sizeof(Version
));
1025 OS
.write(reinterpret_cast<const char *>(&CompressionMethod
),
1026 sizeof(CompressionMethod
));
1027 OS
.write(reinterpret_cast<const char *>(&TotalFileSize
),
1028 sizeof(TotalFileSize
));
1029 OS
.write(reinterpret_cast<const char *>(&UncompressedSize
),
1030 sizeof(UncompressedSize
));
1031 OS
.write(reinterpret_cast<const char *>(&TruncatedHash
),
1032 sizeof(TruncatedHash
));
1033 OS
.write(reinterpret_cast<const char *>(CompressedBuffer
.data()),
1034 CompressedBuffer
.size());
1038 P
.format
== llvm::compression::Format::Zstd
? "zstd" : "zlib";
1039 double CompressionRate
=
1040 static_cast<double>(UncompressedSize
) / CompressedBuffer
.size();
1041 double CompressionTimeSeconds
= CompressTimer
.getTotalTime().getWallTime();
1042 double CompressionSpeedMBs
=
1043 (UncompressedSize
/ (1024.0 * 1024.0)) / CompressionTimeSeconds
;
1045 llvm::errs() << "Compressed bundle format version: " << Version
<< "\n"
1046 << "Total file size (including headers): "
1047 << formatWithCommas(TotalFileSize
) << " bytes\n"
1048 << "Compression method used: " << MethodUsed
<< "\n"
1049 << "Compression level: " << P
.level
<< "\n"
1050 << "Binary size before compression: "
1051 << formatWithCommas(UncompressedSize
) << " bytes\n"
1052 << "Binary size after compression: "
1053 << formatWithCommas(CompressedBuffer
.size()) << " bytes\n"
1054 << "Compression rate: "
1055 << llvm::format("%.2lf", CompressionRate
) << "\n"
1056 << "Compression ratio: "
1057 << llvm::format("%.2lf%%", 100.0 / CompressionRate
) << "\n"
1058 << "Compression speed: "
1059 << llvm::format("%.2lf MB/s", CompressionSpeedMBs
) << "\n"
1060 << "Truncated MD5 hash: "
1061 << llvm::format_hex(TruncatedHash
, 16) << "\n";
1063 return llvm::MemoryBuffer::getMemBufferCopy(
1064 llvm::StringRef(FinalBuffer
.data(), FinalBuffer
.size()));
1067 llvm::Expected
<std::unique_ptr
<llvm::MemoryBuffer
>>
1068 CompressedOffloadBundle::decompress(const llvm::MemoryBuffer
&Input
,
1071 StringRef Blob
= Input
.getBuffer();
1073 if (Blob
.size() < V1HeaderSize
)
1074 return llvm::MemoryBuffer::getMemBufferCopy(Blob
);
1076 if (llvm::identify_magic(Blob
) !=
1077 llvm::file_magic::offload_bundle_compressed
) {
1079 llvm::errs() << "Uncompressed bundle.\n";
1080 return llvm::MemoryBuffer::getMemBufferCopy(Blob
);
1083 size_t CurrentOffset
= MagicSize
;
1085 uint16_t ThisVersion
;
1086 memcpy(&ThisVersion
, Blob
.data() + CurrentOffset
, sizeof(uint16_t));
1087 CurrentOffset
+= VersionFieldSize
;
1089 uint16_t CompressionMethod
;
1090 memcpy(&CompressionMethod
, Blob
.data() + CurrentOffset
, sizeof(uint16_t));
1091 CurrentOffset
+= MethodFieldSize
;
1093 uint32_t TotalFileSize
;
1094 if (ThisVersion
>= 2) {
1095 if (Blob
.size() < V2HeaderSize
)
1096 return createStringError(inconvertibleErrorCode(),
1097 "Compressed bundle header size too small");
1098 memcpy(&TotalFileSize
, Blob
.data() + CurrentOffset
, sizeof(uint32_t));
1099 CurrentOffset
+= FileSizeFieldSize
;
1102 uint32_t UncompressedSize
;
1103 memcpy(&UncompressedSize
, Blob
.data() + CurrentOffset
, sizeof(uint32_t));
1104 CurrentOffset
+= UncompressedSizeFieldSize
;
1106 uint64_t StoredHash
;
1107 memcpy(&StoredHash
, Blob
.data() + CurrentOffset
, sizeof(uint64_t));
1108 CurrentOffset
+= HashFieldSize
;
1110 llvm::compression::Format CompressionFormat
;
1111 if (CompressionMethod
==
1112 static_cast<uint16_t>(llvm::compression::Format::Zlib
))
1113 CompressionFormat
= llvm::compression::Format::Zlib
;
1114 else if (CompressionMethod
==
1115 static_cast<uint16_t>(llvm::compression::Format::Zstd
))
1116 CompressionFormat
= llvm::compression::Format::Zstd
;
1118 return createStringError(inconvertibleErrorCode(),
1119 "Unknown compressing method");
1121 llvm::Timer
DecompressTimer("Decompression Timer", "Decompression time",
1122 ClangOffloadBundlerTimerGroup
);
1124 DecompressTimer
.startTimer();
1126 SmallVector
<uint8_t, 0> DecompressedData
;
1127 StringRef CompressedData
= Blob
.substr(CurrentOffset
);
1128 if (llvm::Error DecompressionError
= llvm::compression::decompress(
1129 CompressionFormat
, llvm::arrayRefFromStringRef(CompressedData
),
1130 DecompressedData
, UncompressedSize
))
1131 return createStringError(inconvertibleErrorCode(),
1132 "Could not decompress embedded file contents: " +
1133 llvm::toString(std::move(DecompressionError
)));
1136 DecompressTimer
.stopTimer();
1138 double DecompressionTimeSeconds
=
1139 DecompressTimer
.getTotalTime().getWallTime();
1141 // Recalculate MD5 hash for integrity check
1142 llvm::Timer
HashRecalcTimer("Hash Recalculation Timer",
1143 "Hash recalculation time",
1144 ClangOffloadBundlerTimerGroup
);
1145 HashRecalcTimer
.startTimer();
1147 llvm::MD5::MD5Result Result
;
1148 Hash
.update(llvm::ArrayRef
<uint8_t>(DecompressedData
.data(),
1149 DecompressedData
.size()));
1151 uint64_t RecalculatedHash
= Result
.low();
1152 HashRecalcTimer
.stopTimer();
1153 bool HashMatch
= (StoredHash
== RecalculatedHash
);
1155 double CompressionRate
=
1156 static_cast<double>(UncompressedSize
) / CompressedData
.size();
1157 double DecompressionSpeedMBs
=
1158 (UncompressedSize
/ (1024.0 * 1024.0)) / DecompressionTimeSeconds
;
1160 llvm::errs() << "Compressed bundle format version: " << ThisVersion
<< "\n";
1161 if (ThisVersion
>= 2)
1162 llvm::errs() << "Total file size (from header): "
1163 << formatWithCommas(TotalFileSize
) << " bytes\n";
1164 llvm::errs() << "Decompression method: "
1165 << (CompressionFormat
== llvm::compression::Format::Zlib
1169 << "Size before decompression: "
1170 << formatWithCommas(CompressedData
.size()) << " bytes\n"
1171 << "Size after decompression: "
1172 << formatWithCommas(UncompressedSize
) << " bytes\n"
1173 << "Compression rate: "
1174 << llvm::format("%.2lf", CompressionRate
) << "\n"
1175 << "Compression ratio: "
1176 << llvm::format("%.2lf%%", 100.0 / CompressionRate
) << "\n"
1177 << "Decompression speed: "
1178 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs
) << "\n"
1179 << "Stored hash: " << llvm::format_hex(StoredHash
, 16) << "\n"
1180 << "Recalculated hash: "
1181 << llvm::format_hex(RecalculatedHash
, 16) << "\n"
1182 << "Hashes match: " << (HashMatch
? "Yes" : "No") << "\n";
1185 return llvm::MemoryBuffer::getMemBufferCopy(
1186 llvm::toStringRef(DecompressedData
));
1189 // List bundle IDs. Return true if an error was found.
1190 Error
OffloadBundler::ListBundleIDsInFile(
1191 StringRef InputFileName
, const OffloadBundlerConfig
&BundlerConfig
) {
1193 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1194 MemoryBuffer::getFileOrSTDIN(InputFileName
, /*IsText=*/true);
1195 if (std::error_code EC
= CodeOrErr
.getError())
1196 return createFileError(InputFileName
, 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
&DecompressedInput
= **DecompressedBufferOrErr
;
1209 // Select the right files handler.
1210 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1211 CreateFileHandler(DecompressedInput
, BundlerConfig
);
1212 if (!FileHandlerOrErr
)
1213 return FileHandlerOrErr
.takeError();
1215 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1217 return FH
->listBundleIDs(DecompressedInput
);
1220 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
1221 /// target \p TargetInfo.
1222 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
1223 bool isCodeObjectCompatible(const OffloadTargetInfo
&CodeObjectInfo
,
1224 const OffloadTargetInfo
&TargetInfo
) {
1226 // Compatible in case of exact match.
1227 if (CodeObjectInfo
== TargetInfo
) {
1228 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1229 dbgs() << "Compatible: Exact match: \t[CodeObject: "
1230 << CodeObjectInfo
.str()
1231 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1235 // Incompatible if Kinds or Triples mismatch.
1236 if (!CodeObjectInfo
.isOffloadKindCompatible(TargetInfo
.OffloadKind
) ||
1237 !CodeObjectInfo
.Triple
.isCompatibleWith(TargetInfo
.Triple
)) {
1239 "CodeObjectCompatibility",
1240 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
1241 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
1246 // Incompatible if Processors mismatch.
1247 llvm::StringMap
<bool> CodeObjectFeatureMap
, TargetFeatureMap
;
1248 std::optional
<StringRef
> CodeObjectProc
= clang::parseTargetID(
1249 CodeObjectInfo
.Triple
, CodeObjectInfo
.TargetID
, &CodeObjectFeatureMap
);
1250 std::optional
<StringRef
> TargetProc
= clang::parseTargetID(
1251 TargetInfo
.Triple
, TargetInfo
.TargetID
, &TargetFeatureMap
);
1253 // Both TargetProc and CodeObjectProc can't be empty here.
1254 if (!TargetProc
|| !CodeObjectProc
||
1255 CodeObjectProc
.value() != TargetProc
.value()) {
1256 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1257 dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
1258 << CodeObjectInfo
.str()
1259 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1263 // Incompatible if CodeObject has more features than Target, irrespective of
1264 // type or sign of features.
1265 if (CodeObjectFeatureMap
.getNumItems() > TargetFeatureMap
.getNumItems()) {
1266 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1267 dbgs() << "Incompatible: CodeObject has more features "
1268 "than target \t[CodeObject: "
1269 << CodeObjectInfo
.str()
1270 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1274 // Compatible if each target feature specified by target is compatible with
1275 // target feature of code object. The target feature is compatible if the
1276 // code object does not specify it (meaning Any), or if it specifies it
1277 // with the same value (meaning On or Off).
1278 for (const auto &CodeObjectFeature
: CodeObjectFeatureMap
) {
1279 auto TargetFeature
= TargetFeatureMap
.find(CodeObjectFeature
.getKey());
1280 if (TargetFeature
== TargetFeatureMap
.end()) {
1282 "CodeObjectCompatibility",
1284 << "Incompatible: Value of CodeObject's non-ANY feature is "
1285 "not matching with Target feature's ANY value \t[CodeObject: "
1286 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
1289 } else if (TargetFeature
->getValue() != CodeObjectFeature
.getValue()) {
1291 "CodeObjectCompatibility",
1292 dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
1293 "not matching with Target feature's non-ANY value "
1295 << CodeObjectInfo
.str()
1296 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1301 // CodeObject is compatible if all features of Target are:
1302 // - either, present in the Code Object's features map with the same sign,
1303 // - or, the feature is missing from CodeObjects's features map i.e. it is
1306 "CodeObjectCompatibility",
1307 dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
1308 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
1313 /// Bundle the files. Return true if an error was found.
1314 Error
OffloadBundler::BundleFiles() {
1317 // Create a buffer to hold the content before compressing.
1318 SmallVector
<char, 0> Buffer
;
1319 llvm::raw_svector_ostream
BufferStream(Buffer
);
1321 // Open input files.
1322 SmallVector
<std::unique_ptr
<MemoryBuffer
>, 8u> InputBuffers
;
1323 InputBuffers
.reserve(BundlerConfig
.InputFileNames
.size());
1324 for (auto &I
: BundlerConfig
.InputFileNames
) {
1325 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1326 MemoryBuffer::getFileOrSTDIN(I
, /*IsText=*/true);
1327 if (std::error_code EC
= CodeOrErr
.getError())
1328 return createFileError(I
, EC
);
1329 InputBuffers
.emplace_back(std::move(*CodeOrErr
));
1332 // Get the file handler. We use the host buffer as reference.
1333 assert((BundlerConfig
.HostInputIndex
!= ~0u || BundlerConfig
.AllowNoHost
) &&
1334 "Host input index undefined??");
1335 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
= CreateFileHandler(
1336 *InputBuffers
[BundlerConfig
.AllowNoHost
? 0
1337 : BundlerConfig
.HostInputIndex
],
1339 if (!FileHandlerOrErr
)
1340 return FileHandlerOrErr
.takeError();
1342 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1346 if (Error Err
= FH
->WriteHeader(BufferStream
, InputBuffers
))
1349 // Write all bundles along with the start/end markers. If an error was found
1350 // writing the end of the bundle component, abort the bundle writing.
1351 auto Input
= InputBuffers
.begin();
1352 for (auto &Triple
: BundlerConfig
.TargetNames
) {
1353 if (Error Err
= FH
->WriteBundleStart(BufferStream
, Triple
))
1355 if (Error Err
= FH
->WriteBundle(BufferStream
, **Input
))
1357 if (Error Err
= FH
->WriteBundleEnd(BufferStream
, Triple
))
1362 raw_fd_ostream
OutputFile(BundlerConfig
.OutputFileNames
.front(), EC
,
1365 return createFileError(BundlerConfig
.OutputFileNames
.front(), EC
);
1367 SmallVector
<char, 0> CompressedBuffer
;
1368 if (BundlerConfig
.Compress
) {
1369 std::unique_ptr
<llvm::MemoryBuffer
> BufferMemory
=
1370 llvm::MemoryBuffer::getMemBufferCopy(
1371 llvm::StringRef(Buffer
.data(), Buffer
.size()));
1372 auto CompressionResult
= CompressedOffloadBundle::compress(
1373 {BundlerConfig
.CompressionFormat
, BundlerConfig
.CompressionLevel
,
1374 /*zstdEnableLdm=*/true},
1375 *BufferMemory
, BundlerConfig
.Verbose
);
1376 if (auto Error
= CompressionResult
.takeError())
1379 auto CompressedMemBuffer
= std::move(CompressionResult
.get());
1380 CompressedBuffer
.assign(CompressedMemBuffer
->getBufferStart(),
1381 CompressedMemBuffer
->getBufferEnd());
1383 CompressedBuffer
= Buffer
;
1385 OutputFile
.write(CompressedBuffer
.data(), CompressedBuffer
.size());
1387 return FH
->finalizeOutputFile();
1390 // Unbundle the files. Return true if an error was found.
1391 Error
OffloadBundler::UnbundleFiles() {
1393 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1394 MemoryBuffer::getFileOrSTDIN(BundlerConfig
.InputFileNames
.front(),
1396 if (std::error_code EC
= CodeOrErr
.getError())
1397 return createFileError(BundlerConfig
.InputFileNames
.front(), EC
);
1399 // Decompress the input if necessary.
1400 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1401 CompressedOffloadBundle::decompress(**CodeOrErr
, BundlerConfig
.Verbose
);
1402 if (!DecompressedBufferOrErr
)
1403 return createStringError(
1404 inconvertibleErrorCode(),
1405 "Failed to decompress input: " +
1406 llvm::toString(DecompressedBufferOrErr
.takeError()));
1408 MemoryBuffer
&Input
= **DecompressedBufferOrErr
;
1410 // Select the right files handler.
1411 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1412 CreateFileHandler(Input
, BundlerConfig
);
1413 if (!FileHandlerOrErr
)
1414 return FileHandlerOrErr
.takeError();
1416 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1419 // Read the header of the bundled file.
1420 if (Error Err
= FH
->ReadHeader(Input
))
1423 // Create a work list that consist of the map triple/output file.
1424 StringMap
<StringRef
> Worklist
;
1425 auto Output
= BundlerConfig
.OutputFileNames
.begin();
1426 for (auto &Triple
: BundlerConfig
.TargetNames
) {
1427 Worklist
[Triple
] = *Output
;
1431 // Read all the bundles that are in the work list. If we find no bundles we
1432 // assume the file is meant for the host target.
1433 bool FoundHostBundle
= false;
1434 while (!Worklist
.empty()) {
1435 Expected
<std::optional
<StringRef
>> CurTripleOrErr
=
1436 FH
->ReadBundleStart(Input
);
1437 if (!CurTripleOrErr
)
1438 return CurTripleOrErr
.takeError();
1440 // We don't have more bundles.
1441 if (!*CurTripleOrErr
)
1444 StringRef CurTriple
= **CurTripleOrErr
;
1445 assert(!CurTriple
.empty());
1447 auto Output
= Worklist
.begin();
1448 for (auto E
= Worklist
.end(); Output
!= E
; Output
++) {
1449 if (isCodeObjectCompatible(
1450 OffloadTargetInfo(CurTriple
, BundlerConfig
),
1451 OffloadTargetInfo((*Output
).first(), BundlerConfig
))) {
1456 if (Output
== Worklist
.end())
1458 // Check if the output file can be opened and copy the bundle to it.
1460 raw_fd_ostream
OutputFile((*Output
).second
, EC
, sys::fs::OF_None
);
1462 return createFileError((*Output
).second
, EC
);
1463 if (Error Err
= FH
->ReadBundle(OutputFile
, Input
))
1465 if (Error Err
= FH
->ReadBundleEnd(Input
))
1467 Worklist
.erase(Output
);
1469 // Record if we found the host bundle.
1470 auto OffloadInfo
= OffloadTargetInfo(CurTriple
, BundlerConfig
);
1471 if (OffloadInfo
.hasHostKind())
1472 FoundHostBundle
= true;
1475 if (!BundlerConfig
.AllowMissingBundles
&& !Worklist
.empty()) {
1476 std::string ErrMsg
= "Can't find bundles for";
1477 std::set
<StringRef
> Sorted
;
1478 for (auto &E
: Worklist
)
1479 Sorted
.insert(E
.first());
1481 unsigned Last
= Sorted
.size() - 1;
1482 for (auto &E
: Sorted
) {
1483 if (I
!= 0 && Last
> 1)
1486 if (I
== Last
&& I
!= 0)
1491 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1494 // If no bundles were found, assume the input file is the host bundle and
1495 // create empty files for the remaining targets.
1496 if (Worklist
.size() == BundlerConfig
.TargetNames
.size()) {
1497 for (auto &E
: Worklist
) {
1499 raw_fd_ostream
OutputFile(E
.second
, EC
, sys::fs::OF_None
);
1501 return createFileError(E
.second
, EC
);
1503 // If this entry has a host kind, copy the input file to the output file.
1504 auto OffloadInfo
= OffloadTargetInfo(E
.getKey(), BundlerConfig
);
1505 if (OffloadInfo
.hasHostKind())
1506 OutputFile
.write(Input
.getBufferStart(), Input
.getBufferSize());
1508 return Error::success();
1511 // If we found elements, we emit an error if none of those were for the host
1512 // in case host bundle name was provided in command line.
1513 if (!(FoundHostBundle
|| BundlerConfig
.HostInputIndex
== ~0u ||
1514 BundlerConfig
.AllowMissingBundles
))
1515 return createStringError(inconvertibleErrorCode(),
1516 "Can't find bundle for the host target");
1518 // If we still have any elements in the worklist, create empty files for them.
1519 for (auto &E
: Worklist
) {
1521 raw_fd_ostream
OutputFile(E
.second
, EC
, sys::fs::OF_None
);
1523 return createFileError(E
.second
, EC
);
1526 return Error::success();
1529 static Archive::Kind
getDefaultArchiveKindForHost() {
1530 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1534 /// @brief Computes a list of targets among all given targets which are
1535 /// compatible with this code object
1536 /// @param [in] CodeObjectInfo Code Object
1537 /// @param [out] CompatibleTargets List of all compatible targets among all
1539 /// @return false, if no compatible target is found.
1541 getCompatibleOffloadTargets(OffloadTargetInfo
&CodeObjectInfo
,
1542 SmallVectorImpl
<StringRef
> &CompatibleTargets
,
1543 const OffloadBundlerConfig
&BundlerConfig
) {
1544 if (!CompatibleTargets
.empty()) {
1545 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1546 dbgs() << "CompatibleTargets list should be empty\n");
1549 for (auto &Target
: BundlerConfig
.TargetNames
) {
1550 auto TargetInfo
= OffloadTargetInfo(Target
, BundlerConfig
);
1551 if (isCodeObjectCompatible(CodeObjectInfo
, TargetInfo
))
1552 CompatibleTargets
.push_back(Target
);
1554 return !CompatibleTargets
.empty();
1557 // Check that each code object file in the input archive conforms to following
1558 // rule: for a specific processor, a feature either shows up in all target IDs,
1559 // or does not show up in any target IDs. Otherwise the target ID combination is
1562 CheckHeterogeneousArchive(StringRef ArchiveName
,
1563 const OffloadBundlerConfig
&BundlerConfig
) {
1564 std::vector
<std::unique_ptr
<MemoryBuffer
>> ArchiveBuffers
;
1565 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
1566 MemoryBuffer::getFileOrSTDIN(ArchiveName
, true, false);
1567 if (std::error_code EC
= BufOrErr
.getError())
1568 return createFileError(ArchiveName
, EC
);
1570 ArchiveBuffers
.push_back(std::move(*BufOrErr
));
1571 Expected
<std::unique_ptr
<llvm::object::Archive
>> LibOrErr
=
1572 Archive::create(ArchiveBuffers
.back()->getMemBufferRef());
1574 return LibOrErr
.takeError();
1576 auto Archive
= std::move(*LibOrErr
);
1578 Error ArchiveErr
= Error::success();
1579 auto ChildEnd
= Archive
->child_end();
1581 /// Iterate over all bundled code object files in the input archive.
1582 for (auto ArchiveIter
= Archive
->child_begin(ArchiveErr
);
1583 ArchiveIter
!= ChildEnd
; ++ArchiveIter
) {
1586 auto ArchiveChildNameOrErr
= (*ArchiveIter
).getName();
1587 if (!ArchiveChildNameOrErr
)
1588 return ArchiveChildNameOrErr
.takeError();
1590 auto CodeObjectBufferRefOrErr
= (*ArchiveIter
).getMemoryBufferRef();
1591 if (!CodeObjectBufferRefOrErr
)
1592 return CodeObjectBufferRefOrErr
.takeError();
1594 auto CodeObjectBuffer
=
1595 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr
, false);
1597 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1598 CreateFileHandler(*CodeObjectBuffer
, BundlerConfig
);
1599 if (!FileHandlerOrErr
)
1600 return FileHandlerOrErr
.takeError();
1602 std::unique_ptr
<FileHandler
> &FileHandler
= *FileHandlerOrErr
;
1603 assert(FileHandler
);
1605 std::set
<StringRef
> BundleIds
;
1606 auto CodeObjectFileError
=
1607 FileHandler
->getBundleIDs(*CodeObjectBuffer
, BundleIds
);
1608 if (CodeObjectFileError
)
1609 return CodeObjectFileError
;
1611 auto &&ConflictingArchs
= clang::getConflictTargetIDCombination(BundleIds
);
1612 if (ConflictingArchs
) {
1613 std::string ErrMsg
=
1614 Twine("conflicting TargetIDs [" + ConflictingArchs
.value().first
+
1615 ", " + ConflictingArchs
.value().second
+ "] found in " +
1616 ArchiveChildNameOrErr
.get() + " of " + ArchiveName
)
1618 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1625 /// UnbundleArchive takes an archive file (".a") as input containing bundled
1626 /// code object files, and a list of offload targets (not host), and extracts
1627 /// the code objects into a new archive file for each offload target. Each
1628 /// resulting archive file contains all code object files corresponding to that
1629 /// particular offload target. The created archive file does not
1630 /// contain an index of the symbols and code object files are named as
1631 /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
1632 Error
OffloadBundler::UnbundleArchive() {
1633 std::vector
<std::unique_ptr
<MemoryBuffer
>> ArchiveBuffers
;
1635 /// Map of target names with list of object files that will form the device
1636 /// specific archive for that target
1637 StringMap
<std::vector
<NewArchiveMember
>> OutputArchivesMap
;
1639 // Map of target names and output archive filenames
1640 StringMap
<StringRef
> TargetOutputFileNameMap
;
1642 auto Output
= BundlerConfig
.OutputFileNames
.begin();
1643 for (auto &Target
: BundlerConfig
.TargetNames
) {
1644 TargetOutputFileNameMap
[Target
] = *Output
;
1648 StringRef IFName
= BundlerConfig
.InputFileNames
.front();
1650 if (BundlerConfig
.CheckInputArchive
) {
1651 // For a specific processor, a feature either shows up in all target IDs, or
1652 // does not show up in any target IDs. Otherwise the target ID combination
1654 auto ArchiveError
= CheckHeterogeneousArchive(IFName
, BundlerConfig
);
1656 return ArchiveError
;
1660 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
1661 MemoryBuffer::getFileOrSTDIN(IFName
, true, false);
1662 if (std::error_code EC
= BufOrErr
.getError())
1663 return createFileError(BundlerConfig
.InputFileNames
.front(), EC
);
1665 ArchiveBuffers
.push_back(std::move(*BufOrErr
));
1666 Expected
<std::unique_ptr
<llvm::object::Archive
>> LibOrErr
=
1667 Archive::create(ArchiveBuffers
.back()->getMemBufferRef());
1669 return LibOrErr
.takeError();
1671 auto Archive
= std::move(*LibOrErr
);
1673 Error ArchiveErr
= Error::success();
1674 auto ChildEnd
= Archive
->child_end();
1676 /// Iterate over all bundled code object files in the input archive.
1677 for (auto ArchiveIter
= Archive
->child_begin(ArchiveErr
);
1678 ArchiveIter
!= ChildEnd
; ++ArchiveIter
) {
1681 auto ArchiveChildNameOrErr
= (*ArchiveIter
).getName();
1682 if (!ArchiveChildNameOrErr
)
1683 return ArchiveChildNameOrErr
.takeError();
1685 StringRef BundledObjectFile
= sys::path::filename(*ArchiveChildNameOrErr
);
1687 auto CodeObjectBufferRefOrErr
= (*ArchiveIter
).getMemoryBufferRef();
1688 if (!CodeObjectBufferRefOrErr
)
1689 return CodeObjectBufferRefOrErr
.takeError();
1691 auto TempCodeObjectBuffer
=
1692 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr
, false);
1694 // Decompress the buffer if necessary.
1695 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1696 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer
,
1697 BundlerConfig
.Verbose
);
1698 if (!DecompressedBufferOrErr
)
1699 return createStringError(
1700 inconvertibleErrorCode(),
1701 "Failed to decompress code object: " +
1702 llvm::toString(DecompressedBufferOrErr
.takeError()));
1704 MemoryBuffer
&CodeObjectBuffer
= **DecompressedBufferOrErr
;
1706 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1707 CreateFileHandler(CodeObjectBuffer
, BundlerConfig
);
1708 if (!FileHandlerOrErr
)
1709 return FileHandlerOrErr
.takeError();
1711 std::unique_ptr
<FileHandler
> &FileHandler
= *FileHandlerOrErr
;
1712 assert(FileHandler
&&
1713 "FileHandle creation failed for file in the archive!");
1715 if (Error ReadErr
= FileHandler
->ReadHeader(CodeObjectBuffer
))
1718 Expected
<std::optional
<StringRef
>> CurBundleIDOrErr
=
1719 FileHandler
->ReadBundleStart(CodeObjectBuffer
);
1720 if (!CurBundleIDOrErr
)
1721 return CurBundleIDOrErr
.takeError();
1723 std::optional
<StringRef
> OptionalCurBundleID
= *CurBundleIDOrErr
;
1724 // No device code in this child, skip.
1725 if (!OptionalCurBundleID
)
1727 StringRef CodeObject
= *OptionalCurBundleID
;
1729 // Process all bundle entries (CodeObjects) found in this child of input
1731 while (!CodeObject
.empty()) {
1732 SmallVector
<StringRef
> CompatibleTargets
;
1733 auto CodeObjectInfo
= OffloadTargetInfo(CodeObject
, BundlerConfig
);
1734 if (getCompatibleOffloadTargets(CodeObjectInfo
, CompatibleTargets
,
1736 std::string BundleData
;
1737 raw_string_ostream
DataStream(BundleData
);
1738 if (Error Err
= FileHandler
->ReadBundle(DataStream
, CodeObjectBuffer
))
1741 for (auto &CompatibleTarget
: CompatibleTargets
) {
1742 SmallString
<128> BundledObjectFileName
;
1743 BundledObjectFileName
.assign(BundledObjectFile
);
1744 auto OutputBundleName
=
1745 Twine(llvm::sys::path::stem(BundledObjectFileName
) + "-" +
1747 getDeviceLibraryFileName(BundledObjectFileName
,
1748 CodeObjectInfo
.TargetID
))
1750 // Replace ':' in optional target feature list with '_' to ensure
1751 // cross-platform validity.
1752 std::replace(OutputBundleName
.begin(), OutputBundleName
.end(), ':',
1755 std::unique_ptr
<MemoryBuffer
> MemBuf
= MemoryBuffer::getMemBufferCopy(
1756 DataStream
.str(), OutputBundleName
);
1757 ArchiveBuffers
.push_back(std::move(MemBuf
));
1758 llvm::MemoryBufferRef MemBufRef
=
1759 MemoryBufferRef(*(ArchiveBuffers
.back()));
1761 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1762 // OutputArchivesMap.
1763 OutputArchivesMap
[CompatibleTarget
].push_back(
1764 NewArchiveMember(MemBufRef
));
1768 if (Error Err
= FileHandler
->ReadBundleEnd(CodeObjectBuffer
))
1771 Expected
<std::optional
<StringRef
>> NextTripleOrErr
=
1772 FileHandler
->ReadBundleStart(CodeObjectBuffer
);
1773 if (!NextTripleOrErr
)
1774 return NextTripleOrErr
.takeError();
1776 CodeObject
= ((*NextTripleOrErr
).has_value()) ? **NextTripleOrErr
: "";
1777 } // End of processing of all bundle entries of this child of input archive.
1778 } // End of while over children of input archive.
1780 assert(!ArchiveErr
&& "Error occurred while reading archive!");
1782 /// Write out an archive for each target
1783 for (auto &Target
: BundlerConfig
.TargetNames
) {
1784 StringRef FileName
= TargetOutputFileNameMap
[Target
];
1785 StringMapIterator
<std::vector
<llvm::NewArchiveMember
>> CurArchiveMembers
=
1786 OutputArchivesMap
.find(Target
);
1787 if (CurArchiveMembers
!= OutputArchivesMap
.end()) {
1788 if (Error WriteErr
= writeArchive(FileName
, CurArchiveMembers
->getValue(),
1789 SymtabWritingMode::NormalSymtab
,
1790 getDefaultArchiveKindForHost(), true,
1793 } else if (!BundlerConfig
.AllowMissingBundles
) {
1794 std::string ErrMsg
=
1795 Twine("no compatible code object found for the target '" + Target
+
1796 "' in heterogeneous archive library: " + IFName
)
1798 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1799 } else { // Create an empty archive file if no compatible code object is
1800 // found and "allow-missing-bundles" is enabled. It ensures that
1801 // the linker using output of this step doesn't complain about
1802 // the missing input file.
1803 std::vector
<llvm::NewArchiveMember
> EmptyArchive
;
1804 EmptyArchive
.clear();
1805 if (Error WriteErr
= writeArchive(
1806 FileName
, EmptyArchive
, SymtabWritingMode::NormalSymtab
,
1807 getDefaultArchiveKindForHost(), true, false, nullptr))
1812 return Error::success();