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
);
163 /// Generic file handler interface.
172 virtual ~FileHandler() {}
174 /// Update the file handler with information from the header of the bundled
176 virtual Error
ReadHeader(MemoryBuffer
&Input
) = 0;
178 /// Read the marker of the next bundled to be read in the file. The bundle
179 /// name is returned if there is one in the file, or `std::nullopt` if there
180 /// are no more bundles to be read.
181 virtual Expected
<std::optional
<StringRef
>>
182 ReadBundleStart(MemoryBuffer
&Input
) = 0;
184 /// Read the marker that closes the current bundle.
185 virtual Error
ReadBundleEnd(MemoryBuffer
&Input
) = 0;
187 /// Read the current bundle and write the result into the stream \a OS.
188 virtual Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) = 0;
190 /// Write the header of the bundled file to \a OS based on the information
191 /// gathered from \a Inputs.
192 virtual Error
WriteHeader(raw_ostream
&OS
,
193 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) = 0;
195 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
197 virtual Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) = 0;
199 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
201 virtual Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) = 0;
203 /// Write the bundle from \a Input into \a OS.
204 virtual Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) = 0;
206 /// Finalize output file.
207 virtual Error
finalizeOutputFile() { return Error::success(); }
209 /// List bundle IDs in \a Input.
210 virtual Error
listBundleIDs(MemoryBuffer
&Input
) {
211 if (Error Err
= ReadHeader(Input
))
213 return forEachBundle(Input
, [&](const BundleInfo
&Info
) -> Error
{
214 llvm::outs() << Info
.BundleID
<< '\n';
215 Error Err
= listBundleIDsCallback(Input
, Info
);
218 return Error::success();
222 /// Get bundle IDs in \a Input in \a BundleIds.
223 virtual Error
getBundleIDs(MemoryBuffer
&Input
,
224 std::set
<StringRef
> &BundleIds
) {
225 if (Error Err
= ReadHeader(Input
))
227 return forEachBundle(Input
, [&](const BundleInfo
&Info
) -> Error
{
228 BundleIds
.insert(Info
.BundleID
);
229 Error Err
= listBundleIDsCallback(Input
, Info
);
232 return Error::success();
236 /// For each bundle in \a Input, do \a Func.
237 Error
forEachBundle(MemoryBuffer
&Input
,
238 std::function
<Error(const BundleInfo
&)> Func
) {
240 Expected
<std::optional
<StringRef
>> CurTripleOrErr
=
241 ReadBundleStart(Input
);
243 return CurTripleOrErr
.takeError();
246 if (!*CurTripleOrErr
)
249 StringRef CurTriple
= **CurTripleOrErr
;
250 assert(!CurTriple
.empty());
252 BundleInfo Info
{CurTriple
};
253 if (Error Err
= Func(Info
))
256 return Error::success();
260 virtual Error
listBundleIDsCallback(MemoryBuffer
&Input
,
261 const BundleInfo
&Info
) {
262 return Error::success();
266 /// Handler for binary files. The bundled file will have the following format
267 /// (all integers are stored in little-endian format):
269 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
271 /// NumberOfOffloadBundles (8-byte integer)
273 /// OffsetOfBundle1 (8-byte integer)
274 /// SizeOfBundle1 (8-byte integer)
275 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
276 /// TripleOfBundle1 (byte length defined before)
280 /// OffsetOfBundleN (8-byte integer)
281 /// SizeOfBundleN (8-byte integer)
282 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
283 /// TripleOfBundleN (byte length defined before)
289 /// Read 8-byte integers from a buffer in little-endian format.
290 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer
, size_t pos
) {
291 return llvm::support::endian::read64le(Buffer
.data() + pos
);
294 /// Write 8-byte integers to a buffer in little-endian format.
295 static void Write8byteIntegerToBuffer(raw_ostream
&OS
, uint64_t Val
) {
296 llvm::support::endian::write(OS
, Val
, llvm::endianness::little
);
299 class BinaryFileHandler final
: public FileHandler
{
300 /// Information about the bundles extracted from the header.
301 struct BinaryBundleInfo final
: public BundleInfo
{
302 /// Size of the bundle.
304 /// Offset at which the bundle starts in the bundled file.
305 uint64_t Offset
= 0u;
307 BinaryBundleInfo() {}
308 BinaryBundleInfo(uint64_t Size
, uint64_t Offset
)
309 : Size(Size
), Offset(Offset
) {}
312 /// Map between a triple and the corresponding bundle information.
313 StringMap
<BinaryBundleInfo
> BundlesInfo
;
315 /// Iterator for the bundle information that is being read.
316 StringMap
<BinaryBundleInfo
>::iterator CurBundleInfo
;
317 StringMap
<BinaryBundleInfo
>::iterator NextBundleInfo
;
319 /// Current bundle target to be written.
320 std::string CurWriteBundleTarget
;
322 /// Configuration options and arrays for this bundler job
323 const OffloadBundlerConfig
&BundlerConfig
;
326 // TODO: Add error checking from ClangOffloadBundler.cpp
327 BinaryFileHandler(const OffloadBundlerConfig
&BC
) : BundlerConfig(BC
) {}
329 ~BinaryFileHandler() final
{}
331 Error
ReadHeader(MemoryBuffer
&Input
) final
{
332 StringRef FC
= Input
.getBuffer();
334 // Initialize the current bundle with the end of the container.
335 CurBundleInfo
= BundlesInfo
.end();
337 // Check if buffer is smaller than magic string.
338 size_t ReadChars
= sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1;
339 if (ReadChars
> FC
.size())
340 return Error::success();
342 // Check if no magic was found.
343 if (llvm::identify_magic(FC
) != llvm::file_magic::offload_bundle
)
344 return Error::success();
346 // Read number of bundles.
347 if (ReadChars
+ 8 > FC
.size())
348 return Error::success();
350 uint64_t NumberOfBundles
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
353 // Read bundle offsets, sizes and triples.
354 for (uint64_t i
= 0; i
< NumberOfBundles
; ++i
) {
357 if (ReadChars
+ 8 > FC
.size())
358 return Error::success();
360 uint64_t Offset
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
364 if (ReadChars
+ 8 > FC
.size())
365 return Error::success();
367 uint64_t Size
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
371 if (ReadChars
+ 8 > FC
.size())
372 return Error::success();
374 uint64_t TripleSize
= Read8byteIntegerFromBuffer(FC
, ReadChars
);
378 if (ReadChars
+ TripleSize
> FC
.size())
379 return Error::success();
381 StringRef
Triple(&FC
.data()[ReadChars
], TripleSize
);
382 ReadChars
+= TripleSize
;
384 // Check if the offset and size make sense.
385 if (!Offset
|| Offset
+ Size
> FC
.size())
386 return Error::success();
388 assert(!BundlesInfo
.contains(Triple
) && "Triple is duplicated??");
389 BundlesInfo
[Triple
] = BinaryBundleInfo(Size
, Offset
);
391 // Set the iterator to where we will start to read.
392 CurBundleInfo
= BundlesInfo
.end();
393 NextBundleInfo
= BundlesInfo
.begin();
394 return Error::success();
397 Expected
<std::optional
<StringRef
>>
398 ReadBundleStart(MemoryBuffer
&Input
) final
{
399 if (NextBundleInfo
== BundlesInfo
.end())
401 CurBundleInfo
= NextBundleInfo
++;
402 return CurBundleInfo
->first();
405 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{
406 assert(CurBundleInfo
!= BundlesInfo
.end() && "Invalid reader info!");
407 return Error::success();
410 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
411 assert(CurBundleInfo
!= BundlesInfo
.end() && "Invalid reader info!");
412 StringRef FC
= Input
.getBuffer();
413 OS
.write(FC
.data() + CurBundleInfo
->second
.Offset
,
414 CurBundleInfo
->second
.Size
);
415 return Error::success();
418 Error
WriteHeader(raw_ostream
&OS
,
419 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
421 // Compute size of the header.
422 uint64_t HeaderSize
= 0;
424 HeaderSize
+= sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1;
425 HeaderSize
+= 8; // Number of Bundles
427 for (auto &T
: BundlerConfig
.TargetNames
) {
428 HeaderSize
+= 3 * 8; // Bundle offset, Size of bundle and size of triple.
429 HeaderSize
+= T
.size(); // The triple.
432 // Write to the buffer the header.
433 OS
<< OFFLOAD_BUNDLER_MAGIC_STR
;
435 Write8byteIntegerToBuffer(OS
, BundlerConfig
.TargetNames
.size());
438 for (auto &T
: BundlerConfig
.TargetNames
) {
439 MemoryBuffer
&MB
= *Inputs
[Idx
++];
440 HeaderSize
= alignTo(HeaderSize
, BundlerConfig
.BundleAlignment
);
442 Write8byteIntegerToBuffer(OS
, HeaderSize
);
443 // Size of the bundle (adds to the next bundle's offset)
444 Write8byteIntegerToBuffer(OS
, MB
.getBufferSize());
445 BundlesInfo
[T
] = BinaryBundleInfo(MB
.getBufferSize(), HeaderSize
);
446 HeaderSize
+= MB
.getBufferSize();
447 // Size of the triple
448 Write8byteIntegerToBuffer(OS
, T
.size());
452 return Error::success();
455 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
456 CurWriteBundleTarget
= TargetTriple
.str();
457 return Error::success();
460 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
461 return Error::success();
464 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
465 auto BI
= BundlesInfo
[CurWriteBundleTarget
];
467 // Pad with 0 to reach specified offset.
468 size_t CurrentPos
= OS
.tell();
469 size_t PaddingSize
= BI
.Offset
> CurrentPos
? BI
.Offset
- CurrentPos
: 0;
470 for (size_t I
= 0; I
< PaddingSize
; ++I
)
472 assert(OS
.tell() == BI
.Offset
);
474 OS
.write(Input
.getBufferStart(), Input
.getBufferSize());
476 return Error::success();
480 // This class implements a list of temporary files that are removed upon
481 // object destruction.
482 class TempFileHandlerRAII
{
484 ~TempFileHandlerRAII() {
485 for (const auto &File
: Files
)
486 sys::fs::remove(File
);
489 // Creates temporary file with given contents.
490 Expected
<StringRef
> Create(std::optional
<ArrayRef
<char>> Contents
) {
491 SmallString
<128u> File
;
492 if (std::error_code EC
=
493 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File
))
494 return createFileError(File
, EC
);
495 Files
.push_front(File
);
499 raw_fd_ostream
OS(File
, EC
);
501 return createFileError(File
, EC
);
502 OS
.write(Contents
->data(), Contents
->size());
504 return Files
.front().str();
508 std::forward_list
<SmallString
<128u>> Files
;
511 /// Handler for object files. The bundles are organized by sections with a
514 /// To unbundle, we just copy the contents of the designated section.
515 class ObjectFileHandler final
: public FileHandler
{
517 /// The object file we are currently dealing with.
518 std::unique_ptr
<ObjectFile
> Obj
;
520 /// Return the input file contents.
521 StringRef
getInputFileContents() const { return Obj
->getData(); }
523 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
525 static Expected
<std::optional
<StringRef
>>
526 IsOffloadSection(SectionRef CurSection
) {
527 Expected
<StringRef
> NameOrErr
= CurSection
.getName();
529 return NameOrErr
.takeError();
531 // If it does not start with the reserved suffix, just skip this section.
532 if (llvm::identify_magic(*NameOrErr
) != llvm::file_magic::offload_bundle
)
535 // Return the triple that is right after the reserved prefix.
536 return NameOrErr
->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR
) - 1);
539 /// Total number of inputs.
540 unsigned NumberOfInputs
= 0;
542 /// Total number of processed inputs, i.e, inputs that were already
543 /// read from the buffers.
544 unsigned NumberOfProcessedInputs
= 0;
546 /// Iterator of the current and next section.
547 section_iterator CurrentSection
;
548 section_iterator NextSection
;
550 /// Configuration options and arrays for this bundler job
551 const OffloadBundlerConfig
&BundlerConfig
;
554 // TODO: Add error checking from ClangOffloadBundler.cpp
555 ObjectFileHandler(std::unique_ptr
<ObjectFile
> ObjIn
,
556 const OffloadBundlerConfig
&BC
)
557 : Obj(std::move(ObjIn
)), CurrentSection(Obj
->section_begin()),
558 NextSection(Obj
->section_begin()), BundlerConfig(BC
) {}
560 ~ObjectFileHandler() final
{}
562 Error
ReadHeader(MemoryBuffer
&Input
) final
{ return Error::success(); }
564 Expected
<std::optional
<StringRef
>>
565 ReadBundleStart(MemoryBuffer
&Input
) final
{
566 while (NextSection
!= Obj
->section_end()) {
567 CurrentSection
= NextSection
;
570 // Check if the current section name starts with the reserved prefix. If
571 // so, return the triple.
572 Expected
<std::optional
<StringRef
>> TripleOrErr
=
573 IsOffloadSection(*CurrentSection
);
575 return TripleOrErr
.takeError();
577 return **TripleOrErr
;
582 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{ return Error::success(); }
584 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
585 Expected
<StringRef
> ContentOrErr
= CurrentSection
->getContents();
587 return ContentOrErr
.takeError();
588 StringRef Content
= *ContentOrErr
;
590 // Copy fat object contents to the output when extracting host bundle.
591 if (Content
.size() == 1u && Content
.front() == 0)
592 Content
= StringRef(Input
.getBufferStart(), Input
.getBufferSize());
594 OS
.write(Content
.data(), Content
.size());
595 return Error::success();
598 Error
WriteHeader(raw_ostream
&OS
,
599 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
600 assert(BundlerConfig
.HostInputIndex
!= ~0u &&
601 "Host input index not defined.");
603 // Record number of inputs.
604 NumberOfInputs
= Inputs
.size();
605 return Error::success();
608 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
609 ++NumberOfProcessedInputs
;
610 return Error::success();
613 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
614 return Error::success();
617 Error
finalizeOutputFile() final
{
618 assert(NumberOfProcessedInputs
<= NumberOfInputs
&&
619 "Processing more inputs that actually exist!");
620 assert(BundlerConfig
.HostInputIndex
!= ~0u &&
621 "Host input index not defined.");
623 // If this is not the last output, we don't have to do anything.
624 if (NumberOfProcessedInputs
!= NumberOfInputs
)
625 return Error::success();
627 // We will use llvm-objcopy to add target objects sections to the output
628 // fat object. These sections should have 'exclude' flag set which tells
629 // link editor to remove them from linker inputs when linking executable or
632 assert(BundlerConfig
.ObjcopyPath
!= "" &&
633 "llvm-objcopy path not specified");
635 // Temporary files that need to be removed.
636 TempFileHandlerRAII TempFiles
;
638 // Compose llvm-objcopy command line for add target objects' sections with
639 // appropriate flags.
640 BumpPtrAllocator Alloc
;
641 StringSaver SS
{Alloc
};
642 SmallVector
<StringRef
, 8u> ObjcopyArgs
{"llvm-objcopy"};
644 for (unsigned I
= 0; I
< NumberOfInputs
; ++I
) {
645 StringRef InputFile
= BundlerConfig
.InputFileNames
[I
];
646 if (I
== BundlerConfig
.HostInputIndex
) {
647 // Special handling for the host bundle. We do not need to add a
648 // standard bundle for the host object since we are going to use fat
649 // object as a host object. Therefore use dummy contents (one zero byte)
650 // when creating section for the host bundle.
651 Expected
<StringRef
> TempFileOrErr
= TempFiles
.Create(ArrayRef
<char>(0));
653 return TempFileOrErr
.takeError();
654 InputFile
= *TempFileOrErr
;
657 ObjcopyArgs
.push_back(
658 SS
.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR
+
659 BundlerConfig
.TargetNames
[I
] + "=" + InputFile
));
660 ObjcopyArgs
.push_back(
661 SS
.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR
+
662 BundlerConfig
.TargetNames
[I
] + "=readonly,exclude"));
664 ObjcopyArgs
.push_back("--");
665 ObjcopyArgs
.push_back(
666 BundlerConfig
.InputFileNames
[BundlerConfig
.HostInputIndex
]);
667 ObjcopyArgs
.push_back(BundlerConfig
.OutputFileNames
.front());
669 if (Error Err
= executeObjcopy(BundlerConfig
.ObjcopyPath
, ObjcopyArgs
))
672 return Error::success();
675 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
676 return Error::success();
680 Error
executeObjcopy(StringRef Objcopy
, ArrayRef
<StringRef
> Args
) {
681 // If the user asked for the commands to be printed out, we do that
682 // instead of executing it.
683 if (BundlerConfig
.PrintExternalCommands
) {
684 errs() << "\"" << Objcopy
<< "\"";
685 for (StringRef Arg
: drop_begin(Args
, 1))
686 errs() << " \"" << Arg
<< "\"";
689 if (sys::ExecuteAndWait(Objcopy
, Args
))
690 return createStringError(inconvertibleErrorCode(),
691 "'llvm-objcopy' tool failed");
693 return Error::success();
697 /// Handler for text files. The bundled file will have the following format.
699 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
701 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
703 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
705 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
706 class TextFileHandler final
: public FileHandler
{
707 /// String that begins a line comment.
710 /// String that initiates a bundle.
711 std::string BundleStartString
;
713 /// String that closes a bundle.
714 std::string BundleEndString
;
716 /// Number of chars read from input.
717 size_t ReadChars
= 0u;
720 Error
ReadHeader(MemoryBuffer
&Input
) final
{ return Error::success(); }
722 Expected
<std::optional
<StringRef
>>
723 ReadBundleStart(MemoryBuffer
&Input
) final
{
724 StringRef FC
= Input
.getBuffer();
726 // Find start of the bundle.
727 ReadChars
= FC
.find(BundleStartString
, ReadChars
);
728 if (ReadChars
== FC
.npos
)
731 // Get position of the triple.
732 size_t TripleStart
= ReadChars
= ReadChars
+ BundleStartString
.size();
734 // Get position that closes the triple.
735 size_t TripleEnd
= ReadChars
= FC
.find("\n", ReadChars
);
736 if (TripleEnd
== FC
.npos
)
739 // Next time we read after the new line.
742 return StringRef(&FC
.data()[TripleStart
], TripleEnd
- TripleStart
);
745 Error
ReadBundleEnd(MemoryBuffer
&Input
) final
{
746 StringRef FC
= Input
.getBuffer();
748 // Read up to the next new line.
749 assert(FC
[ReadChars
] == '\n' && "The bundle should end with a new line.");
751 size_t TripleEnd
= ReadChars
= FC
.find("\n", ReadChars
+ 1);
752 if (TripleEnd
!= FC
.npos
)
753 // Next time we read after the new line.
756 return Error::success();
759 Error
ReadBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
760 StringRef FC
= Input
.getBuffer();
761 size_t BundleStart
= ReadChars
;
763 // Find end of the bundle.
764 size_t BundleEnd
= ReadChars
= FC
.find(BundleEndString
, ReadChars
);
766 StringRef
Bundle(&FC
.data()[BundleStart
], BundleEnd
- BundleStart
);
769 return Error::success();
772 Error
WriteHeader(raw_ostream
&OS
,
773 ArrayRef
<std::unique_ptr
<MemoryBuffer
>> Inputs
) final
{
774 return Error::success();
777 Error
WriteBundleStart(raw_ostream
&OS
, StringRef TargetTriple
) final
{
778 OS
<< BundleStartString
<< TargetTriple
<< "\n";
779 return Error::success();
782 Error
WriteBundleEnd(raw_ostream
&OS
, StringRef TargetTriple
) final
{
783 OS
<< BundleEndString
<< TargetTriple
<< "\n";
784 return Error::success();
787 Error
WriteBundle(raw_ostream
&OS
, MemoryBuffer
&Input
) final
{
788 OS
<< Input
.getBuffer();
789 return Error::success();
793 TextFileHandler(StringRef Comment
) : Comment(Comment
), ReadChars(0) {
795 "\n" + Comment
.str() + " " OFFLOAD_BUNDLER_MAGIC_STR
"__START__ ";
797 "\n" + Comment
.str() + " " OFFLOAD_BUNDLER_MAGIC_STR
"__END__ ";
800 Error
listBundleIDsCallback(MemoryBuffer
&Input
,
801 const BundleInfo
&Info
) final
{
802 // TODO: To list bundle IDs in a bundled text file we need to go through
803 // all bundles. The format of bundled text file may need to include a
804 // header if the performance of listing bundle IDs of bundled text file is
806 ReadChars
= Input
.getBuffer().find(BundleEndString
, ReadChars
);
807 if (Error Err
= ReadBundleEnd(Input
))
809 return Error::success();
814 /// Return an appropriate object file handler. We use the specific object
815 /// handler if we know how to deal with that format, otherwise we use a default
816 /// binary file handler.
817 static std::unique_ptr
<FileHandler
>
818 CreateObjectFileHandler(MemoryBuffer
&FirstInput
,
819 const OffloadBundlerConfig
&BundlerConfig
) {
820 // Check if the input file format is one that we know how to deal with.
821 Expected
<std::unique_ptr
<Binary
>> BinaryOrErr
= createBinary(FirstInput
);
823 // We only support regular object files. If failed to open the input as a
824 // known binary or this is not an object file use the default binary handler.
825 if (errorToBool(BinaryOrErr
.takeError()) || !isa
<ObjectFile
>(*BinaryOrErr
))
826 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
828 // Otherwise create an object file handler. The handler will be owned by the
829 // client of this function.
830 return std::make_unique
<ObjectFileHandler
>(
831 std::unique_ptr
<ObjectFile
>(cast
<ObjectFile
>(BinaryOrErr
->release())),
835 /// Return an appropriate handler given the input files and options.
836 static Expected
<std::unique_ptr
<FileHandler
>>
837 CreateFileHandler(MemoryBuffer
&FirstInput
,
838 const OffloadBundlerConfig
&BundlerConfig
) {
839 std::string FilesType
= BundlerConfig
.FilesType
;
841 if (FilesType
== "i")
842 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
843 if (FilesType
== "ii")
844 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
845 if (FilesType
== "cui")
846 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
847 if (FilesType
== "hipi")
848 return std::make_unique
<TextFileHandler
>(/*Comment=*/"//");
849 // TODO: `.d` should be eventually removed once `-M` and its variants are
850 // handled properly in offload compilation.
851 if (FilesType
== "d")
852 return std::make_unique
<TextFileHandler
>(/*Comment=*/"#");
853 if (FilesType
== "ll")
854 return std::make_unique
<TextFileHandler
>(/*Comment=*/";");
855 if (FilesType
== "bc")
856 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
857 if (FilesType
== "s")
858 return std::make_unique
<TextFileHandler
>(/*Comment=*/"#");
859 if (FilesType
== "o")
860 return CreateObjectFileHandler(FirstInput
, BundlerConfig
);
861 if (FilesType
== "a")
862 return CreateObjectFileHandler(FirstInput
, BundlerConfig
);
863 if (FilesType
== "gch")
864 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
865 if (FilesType
== "ast")
866 return std::make_unique
<BinaryFileHandler
>(BundlerConfig
);
868 return createStringError(errc::invalid_argument
,
869 "'" + FilesType
+ "': invalid file type specified");
872 OffloadBundlerConfig::OffloadBundlerConfig() {
873 auto IgnoreEnvVarOpt
=
874 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
875 if (IgnoreEnvVarOpt
.has_value() && IgnoreEnvVarOpt
.value() == "1")
878 auto VerboseEnvVarOpt
= llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
879 if (VerboseEnvVarOpt
.has_value())
880 Verbose
= VerboseEnvVarOpt
.value() == "1";
882 auto CompressEnvVarOpt
=
883 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
884 if (CompressEnvVarOpt
.has_value())
885 Compress
= CompressEnvVarOpt
.value() == "1";
888 llvm::Expected
<std::unique_ptr
<llvm::MemoryBuffer
>>
889 CompressedOffloadBundle::compress(const llvm::MemoryBuffer
&Input
,
891 llvm::Timer
HashTimer("Hash Calculation Timer", "Hash calculation time",
892 ClangOffloadBundlerTimerGroup
);
894 HashTimer
.startTimer();
896 llvm::MD5::MD5Result Result
;
897 Hash
.update(Input
.getBuffer());
899 uint64_t TruncatedHash
= Result
.low();
901 HashTimer
.stopTimer();
903 SmallVector
<uint8_t, 0> CompressedBuffer
;
904 auto BufferUint8
= llvm::ArrayRef
<uint8_t>(
905 reinterpret_cast<const uint8_t *>(Input
.getBuffer().data()),
906 Input
.getBuffer().size());
908 llvm::compression::Format CompressionFormat
;
910 if (llvm::compression::zstd::isAvailable())
911 CompressionFormat
= llvm::compression::Format::Zstd
;
912 else if (llvm::compression::zlib::isAvailable())
913 CompressionFormat
= llvm::compression::Format::Zlib
;
915 return createStringError(llvm::inconvertibleErrorCode(),
916 "Compression not supported");
918 llvm::Timer
CompressTimer("Compression Timer", "Compression time",
919 ClangOffloadBundlerTimerGroup
);
921 CompressTimer
.startTimer();
922 llvm::compression::compress(CompressionFormat
, BufferUint8
, CompressedBuffer
);
924 CompressTimer
.stopTimer();
926 uint16_t CompressionMethod
= static_cast<uint16_t>(CompressionFormat
);
927 uint32_t UncompressedSize
= Input
.getBuffer().size();
929 SmallVector
<char, 0> FinalBuffer
;
930 llvm::raw_svector_ostream
OS(FinalBuffer
);
932 OS
.write(reinterpret_cast<const char *>(&Version
), sizeof(Version
));
933 OS
.write(reinterpret_cast<const char *>(&CompressionMethod
),
934 sizeof(CompressionMethod
));
935 OS
.write(reinterpret_cast<const char *>(&UncompressedSize
),
936 sizeof(UncompressedSize
));
937 OS
.write(reinterpret_cast<const char *>(&TruncatedHash
),
938 sizeof(TruncatedHash
));
939 OS
.write(reinterpret_cast<const char *>(CompressedBuffer
.data()),
940 CompressedBuffer
.size());
944 CompressionFormat
== llvm::compression::Format::Zstd
? "zstd" : "zlib";
945 llvm::errs() << "Compressed bundle format version: " << Version
<< "\n"
946 << "Compression method used: " << MethodUsed
<< "\n"
947 << "Binary size before compression: " << UncompressedSize
949 << "Binary size after compression: " << CompressedBuffer
.size()
951 << "Truncated MD5 hash: "
952 << llvm::format_hex(TruncatedHash
, 16) << "\n";
955 return llvm::MemoryBuffer::getMemBufferCopy(
956 llvm::StringRef(FinalBuffer
.data(), FinalBuffer
.size()));
959 llvm::Expected
<std::unique_ptr
<llvm::MemoryBuffer
>>
960 CompressedOffloadBundle::decompress(const llvm::MemoryBuffer
&Input
,
963 StringRef Blob
= Input
.getBuffer();
965 if (Blob
.size() < HeaderSize
) {
966 return llvm::MemoryBuffer::getMemBufferCopy(Blob
);
968 if (llvm::identify_magic(Blob
) !=
969 llvm::file_magic::offload_bundle_compressed
) {
971 llvm::errs() << "Uncompressed bundle.\n";
972 return llvm::MemoryBuffer::getMemBufferCopy(Blob
);
975 uint16_t ThisVersion
;
976 uint16_t CompressionMethod
;
977 uint32_t UncompressedSize
;
979 memcpy(&ThisVersion
, Input
.getBuffer().data() + MagicNumber
.size(),
981 memcpy(&CompressionMethod
, Blob
.data() + MagicSize
+ VersionFieldSize
,
983 memcpy(&UncompressedSize
,
984 Blob
.data() + MagicSize
+ VersionFieldSize
+ MethodFieldSize
,
987 Blob
.data() + MagicSize
+ VersionFieldSize
+ MethodFieldSize
+
991 llvm::compression::Format CompressionFormat
;
992 if (CompressionMethod
==
993 static_cast<uint16_t>(llvm::compression::Format::Zlib
))
994 CompressionFormat
= llvm::compression::Format::Zlib
;
995 else if (CompressionMethod
==
996 static_cast<uint16_t>(llvm::compression::Format::Zstd
))
997 CompressionFormat
= llvm::compression::Format::Zstd
;
999 return createStringError(inconvertibleErrorCode(),
1000 "Unknown compressing method");
1002 llvm::Timer
DecompressTimer("Decompression Timer", "Decompression time",
1003 ClangOffloadBundlerTimerGroup
);
1005 DecompressTimer
.startTimer();
1007 SmallVector
<uint8_t, 0> DecompressedData
;
1008 StringRef CompressedData
= Blob
.substr(HeaderSize
);
1009 if (llvm::Error DecompressionError
= llvm::compression::decompress(
1010 CompressionFormat
, llvm::arrayRefFromStringRef(CompressedData
),
1011 DecompressedData
, UncompressedSize
))
1012 return createStringError(inconvertibleErrorCode(),
1013 "Could not decompress embedded file contents: " +
1014 llvm::toString(std::move(DecompressionError
)));
1017 DecompressTimer
.stopTimer();
1019 // Recalculate MD5 hash
1020 llvm::Timer
HashRecalcTimer("Hash Recalculation Timer",
1021 "Hash recalculation time",
1022 ClangOffloadBundlerTimerGroup
);
1023 HashRecalcTimer
.startTimer();
1025 llvm::MD5::MD5Result Result
;
1026 Hash
.update(llvm::ArrayRef
<uint8_t>(DecompressedData
.data(),
1027 DecompressedData
.size()));
1029 uint64_t RecalculatedHash
= Result
.low();
1030 HashRecalcTimer
.stopTimer();
1031 bool HashMatch
= (StoredHash
== RecalculatedHash
);
1033 llvm::errs() << "Compressed bundle format version: " << ThisVersion
<< "\n"
1034 << "Decompression method: "
1035 << (CompressionFormat
== llvm::compression::Format::Zlib
1039 << "Size before decompression: " << CompressedData
.size()
1041 << "Size after decompression: " << UncompressedSize
1043 << "Stored hash: " << llvm::format_hex(StoredHash
, 16) << "\n"
1044 << "Recalculated hash: "
1045 << llvm::format_hex(RecalculatedHash
, 16) << "\n"
1046 << "Hashes match: " << (HashMatch
? "Yes" : "No") << "\n";
1049 return llvm::MemoryBuffer::getMemBufferCopy(
1050 llvm::toStringRef(DecompressedData
));
1053 // List bundle IDs. Return true if an error was found.
1054 Error
OffloadBundler::ListBundleIDsInFile(
1055 StringRef InputFileName
, const OffloadBundlerConfig
&BundlerConfig
) {
1057 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1058 MemoryBuffer::getFileOrSTDIN(InputFileName
);
1059 if (std::error_code EC
= CodeOrErr
.getError())
1060 return createFileError(InputFileName
, EC
);
1062 // Decompress the input if necessary.
1063 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1064 CompressedOffloadBundle::decompress(**CodeOrErr
, BundlerConfig
.Verbose
);
1065 if (!DecompressedBufferOrErr
)
1066 return createStringError(
1067 inconvertibleErrorCode(),
1068 "Failed to decompress input: " +
1069 llvm::toString(DecompressedBufferOrErr
.takeError()));
1071 MemoryBuffer
&DecompressedInput
= **DecompressedBufferOrErr
;
1073 // Select the right files handler.
1074 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1075 CreateFileHandler(DecompressedInput
, BundlerConfig
);
1076 if (!FileHandlerOrErr
)
1077 return FileHandlerOrErr
.takeError();
1079 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1081 return FH
->listBundleIDs(DecompressedInput
);
1084 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
1085 /// target \p TargetInfo.
1086 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id
1087 bool isCodeObjectCompatible(const OffloadTargetInfo
&CodeObjectInfo
,
1088 const OffloadTargetInfo
&TargetInfo
) {
1090 // Compatible in case of exact match.
1091 if (CodeObjectInfo
== TargetInfo
) {
1092 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1093 dbgs() << "Compatible: Exact match: \t[CodeObject: "
1094 << CodeObjectInfo
.str()
1095 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1099 // Incompatible if Kinds or Triples mismatch.
1100 if (!CodeObjectInfo
.isOffloadKindCompatible(TargetInfo
.OffloadKind
) ||
1101 !CodeObjectInfo
.Triple
.isCompatibleWith(TargetInfo
.Triple
)) {
1103 "CodeObjectCompatibility",
1104 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
1105 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
1110 // Incompatible if Processors mismatch.
1111 llvm::StringMap
<bool> CodeObjectFeatureMap
, TargetFeatureMap
;
1112 std::optional
<StringRef
> CodeObjectProc
= clang::parseTargetID(
1113 CodeObjectInfo
.Triple
, CodeObjectInfo
.TargetID
, &CodeObjectFeatureMap
);
1114 std::optional
<StringRef
> TargetProc
= clang::parseTargetID(
1115 TargetInfo
.Triple
, TargetInfo
.TargetID
, &TargetFeatureMap
);
1117 // Both TargetProc and CodeObjectProc can't be empty here.
1118 if (!TargetProc
|| !CodeObjectProc
||
1119 CodeObjectProc
.value() != TargetProc
.value()) {
1120 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1121 dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
1122 << CodeObjectInfo
.str()
1123 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1127 // Incompatible if CodeObject has more features than Target, irrespective of
1128 // type or sign of features.
1129 if (CodeObjectFeatureMap
.getNumItems() > TargetFeatureMap
.getNumItems()) {
1130 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1131 dbgs() << "Incompatible: CodeObject has more features "
1132 "than target \t[CodeObject: "
1133 << CodeObjectInfo
.str()
1134 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1138 // Compatible if each target feature specified by target is compatible with
1139 // target feature of code object. The target feature is compatible if the
1140 // code object does not specify it (meaning Any), or if it specifies it
1141 // with the same value (meaning On or Off).
1142 for (const auto &CodeObjectFeature
: CodeObjectFeatureMap
) {
1143 auto TargetFeature
= TargetFeatureMap
.find(CodeObjectFeature
.getKey());
1144 if (TargetFeature
== TargetFeatureMap
.end()) {
1146 "CodeObjectCompatibility",
1148 << "Incompatible: Value of CodeObject's non-ANY feature is "
1149 "not matching with Target feature's ANY value \t[CodeObject: "
1150 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
1153 } else if (TargetFeature
->getValue() != CodeObjectFeature
.getValue()) {
1155 "CodeObjectCompatibility",
1156 dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
1157 "not matching with Target feature's non-ANY value "
1159 << CodeObjectInfo
.str()
1160 << "]\t:\t[Target: " << TargetInfo
.str() << "]\n");
1165 // CodeObject is compatible if all features of Target are:
1166 // - either, present in the Code Object's features map with the same sign,
1167 // - or, the feature is missing from CodeObjects's features map i.e. it is
1170 "CodeObjectCompatibility",
1171 dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
1172 << CodeObjectInfo
.str() << "]\t:\t[Target: " << TargetInfo
.str()
1177 /// Bundle the files. Return true if an error was found.
1178 Error
OffloadBundler::BundleFiles() {
1181 // Create a buffer to hold the content before compressing.
1182 SmallVector
<char, 0> Buffer
;
1183 llvm::raw_svector_ostream
BufferStream(Buffer
);
1185 // Open input files.
1186 SmallVector
<std::unique_ptr
<MemoryBuffer
>, 8u> InputBuffers
;
1187 InputBuffers
.reserve(BundlerConfig
.InputFileNames
.size());
1188 for (auto &I
: BundlerConfig
.InputFileNames
) {
1189 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1190 MemoryBuffer::getFileOrSTDIN(I
);
1191 if (std::error_code EC
= CodeOrErr
.getError())
1192 return createFileError(I
, EC
);
1193 InputBuffers
.emplace_back(std::move(*CodeOrErr
));
1196 // Get the file handler. We use the host buffer as reference.
1197 assert((BundlerConfig
.HostInputIndex
!= ~0u || BundlerConfig
.AllowNoHost
) &&
1198 "Host input index undefined??");
1199 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
= CreateFileHandler(
1200 *InputBuffers
[BundlerConfig
.AllowNoHost
? 0
1201 : BundlerConfig
.HostInputIndex
],
1203 if (!FileHandlerOrErr
)
1204 return FileHandlerOrErr
.takeError();
1206 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1210 if (Error Err
= FH
->WriteHeader(BufferStream
, InputBuffers
))
1213 // Write all bundles along with the start/end markers. If an error was found
1214 // writing the end of the bundle component, abort the bundle writing.
1215 auto Input
= InputBuffers
.begin();
1216 for (auto &Triple
: BundlerConfig
.TargetNames
) {
1217 if (Error Err
= FH
->WriteBundleStart(BufferStream
, Triple
))
1219 if (Error Err
= FH
->WriteBundle(BufferStream
, **Input
))
1221 if (Error Err
= FH
->WriteBundleEnd(BufferStream
, Triple
))
1226 raw_fd_ostream
OutputFile(BundlerConfig
.OutputFileNames
.front(), EC
,
1229 return createFileError(BundlerConfig
.OutputFileNames
.front(), EC
);
1231 SmallVector
<char, 0> CompressedBuffer
;
1232 if (BundlerConfig
.Compress
) {
1233 std::unique_ptr
<llvm::MemoryBuffer
> BufferMemory
=
1234 llvm::MemoryBuffer::getMemBufferCopy(
1235 llvm::StringRef(Buffer
.data(), Buffer
.size()));
1236 auto CompressionResult
=
1237 CompressedOffloadBundle::compress(*BufferMemory
, BundlerConfig
.Verbose
);
1238 if (auto Error
= CompressionResult
.takeError())
1241 auto CompressedMemBuffer
= std::move(CompressionResult
.get());
1242 CompressedBuffer
.assign(CompressedMemBuffer
->getBufferStart(),
1243 CompressedMemBuffer
->getBufferEnd());
1245 CompressedBuffer
= Buffer
;
1247 OutputFile
.write(CompressedBuffer
.data(), CompressedBuffer
.size());
1249 return FH
->finalizeOutputFile();
1252 // Unbundle the files. Return true if an error was found.
1253 Error
OffloadBundler::UnbundleFiles() {
1255 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> CodeOrErr
=
1256 MemoryBuffer::getFileOrSTDIN(BundlerConfig
.InputFileNames
.front());
1257 if (std::error_code EC
= CodeOrErr
.getError())
1258 return createFileError(BundlerConfig
.InputFileNames
.front(), EC
);
1260 // Decompress the input if necessary.
1261 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1262 CompressedOffloadBundle::decompress(**CodeOrErr
, BundlerConfig
.Verbose
);
1263 if (!DecompressedBufferOrErr
)
1264 return createStringError(
1265 inconvertibleErrorCode(),
1266 "Failed to decompress input: " +
1267 llvm::toString(DecompressedBufferOrErr
.takeError()));
1269 MemoryBuffer
&Input
= **DecompressedBufferOrErr
;
1271 // Select the right files handler.
1272 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1273 CreateFileHandler(Input
, BundlerConfig
);
1274 if (!FileHandlerOrErr
)
1275 return FileHandlerOrErr
.takeError();
1277 std::unique_ptr
<FileHandler
> &FH
= *FileHandlerOrErr
;
1280 // Read the header of the bundled file.
1281 if (Error Err
= FH
->ReadHeader(Input
))
1284 // Create a work list that consist of the map triple/output file.
1285 StringMap
<StringRef
> Worklist
;
1286 auto Output
= BundlerConfig
.OutputFileNames
.begin();
1287 for (auto &Triple
: BundlerConfig
.TargetNames
) {
1288 Worklist
[Triple
] = *Output
;
1292 // Read all the bundles that are in the work list. If we find no bundles we
1293 // assume the file is meant for the host target.
1294 bool FoundHostBundle
= false;
1295 while (!Worklist
.empty()) {
1296 Expected
<std::optional
<StringRef
>> CurTripleOrErr
=
1297 FH
->ReadBundleStart(Input
);
1298 if (!CurTripleOrErr
)
1299 return CurTripleOrErr
.takeError();
1301 // We don't have more bundles.
1302 if (!*CurTripleOrErr
)
1305 StringRef CurTriple
= **CurTripleOrErr
;
1306 assert(!CurTriple
.empty());
1308 auto Output
= Worklist
.begin();
1309 for (auto E
= Worklist
.end(); Output
!= E
; Output
++) {
1310 if (isCodeObjectCompatible(
1311 OffloadTargetInfo(CurTriple
, BundlerConfig
),
1312 OffloadTargetInfo((*Output
).first(), BundlerConfig
))) {
1317 if (Output
== Worklist
.end())
1319 // Check if the output file can be opened and copy the bundle to it.
1321 raw_fd_ostream
OutputFile((*Output
).second
, EC
, sys::fs::OF_None
);
1323 return createFileError((*Output
).second
, EC
);
1324 if (Error Err
= FH
->ReadBundle(OutputFile
, Input
))
1326 if (Error Err
= FH
->ReadBundleEnd(Input
))
1328 Worklist
.erase(Output
);
1330 // Record if we found the host bundle.
1331 auto OffloadInfo
= OffloadTargetInfo(CurTriple
, BundlerConfig
);
1332 if (OffloadInfo
.hasHostKind())
1333 FoundHostBundle
= true;
1336 if (!BundlerConfig
.AllowMissingBundles
&& !Worklist
.empty()) {
1337 std::string ErrMsg
= "Can't find bundles for";
1338 std::set
<StringRef
> Sorted
;
1339 for (auto &E
: Worklist
)
1340 Sorted
.insert(E
.first());
1342 unsigned Last
= Sorted
.size() - 1;
1343 for (auto &E
: Sorted
) {
1344 if (I
!= 0 && Last
> 1)
1347 if (I
== Last
&& I
!= 0)
1352 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1355 // If no bundles were found, assume the input file is the host bundle and
1356 // create empty files for the remaining targets.
1357 if (Worklist
.size() == BundlerConfig
.TargetNames
.size()) {
1358 for (auto &E
: Worklist
) {
1360 raw_fd_ostream
OutputFile(E
.second
, EC
, sys::fs::OF_None
);
1362 return createFileError(E
.second
, EC
);
1364 // If this entry has a host kind, copy the input file to the output file.
1365 auto OffloadInfo
= OffloadTargetInfo(E
.getKey(), BundlerConfig
);
1366 if (OffloadInfo
.hasHostKind())
1367 OutputFile
.write(Input
.getBufferStart(), Input
.getBufferSize());
1369 return Error::success();
1372 // If we found elements, we emit an error if none of those were for the host
1373 // in case host bundle name was provided in command line.
1374 if (!(FoundHostBundle
|| BundlerConfig
.HostInputIndex
== ~0u ||
1375 BundlerConfig
.AllowMissingBundles
))
1376 return createStringError(inconvertibleErrorCode(),
1377 "Can't find bundle for the host target");
1379 // If we still have any elements in the worklist, create empty files for them.
1380 for (auto &E
: Worklist
) {
1382 raw_fd_ostream
OutputFile(E
.second
, EC
, sys::fs::OF_None
);
1384 return createFileError(E
.second
, EC
);
1387 return Error::success();
1390 static Archive::Kind
getDefaultArchiveKindForHost() {
1391 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1395 /// @brief Computes a list of targets among all given targets which are
1396 /// compatible with this code object
1397 /// @param [in] CodeObjectInfo Code Object
1398 /// @param [out] CompatibleTargets List of all compatible targets among all
1400 /// @return false, if no compatible target is found.
1402 getCompatibleOffloadTargets(OffloadTargetInfo
&CodeObjectInfo
,
1403 SmallVectorImpl
<StringRef
> &CompatibleTargets
,
1404 const OffloadBundlerConfig
&BundlerConfig
) {
1405 if (!CompatibleTargets
.empty()) {
1406 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1407 dbgs() << "CompatibleTargets list should be empty\n");
1410 for (auto &Target
: BundlerConfig
.TargetNames
) {
1411 auto TargetInfo
= OffloadTargetInfo(Target
, BundlerConfig
);
1412 if (isCodeObjectCompatible(CodeObjectInfo
, TargetInfo
))
1413 CompatibleTargets
.push_back(Target
);
1415 return !CompatibleTargets
.empty();
1418 // Check that each code object file in the input archive conforms to following
1419 // rule: for a specific processor, a feature either shows up in all target IDs,
1420 // or does not show up in any target IDs. Otherwise the target ID combination is
1423 CheckHeterogeneousArchive(StringRef ArchiveName
,
1424 const OffloadBundlerConfig
&BundlerConfig
) {
1425 std::vector
<std::unique_ptr
<MemoryBuffer
>> ArchiveBuffers
;
1426 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
1427 MemoryBuffer::getFileOrSTDIN(ArchiveName
, true, false);
1428 if (std::error_code EC
= BufOrErr
.getError())
1429 return createFileError(ArchiveName
, EC
);
1431 ArchiveBuffers
.push_back(std::move(*BufOrErr
));
1432 Expected
<std::unique_ptr
<llvm::object::Archive
>> LibOrErr
=
1433 Archive::create(ArchiveBuffers
.back()->getMemBufferRef());
1435 return LibOrErr
.takeError();
1437 auto Archive
= std::move(*LibOrErr
);
1439 Error ArchiveErr
= Error::success();
1440 auto ChildEnd
= Archive
->child_end();
1442 /// Iterate over all bundled code object files in the input archive.
1443 for (auto ArchiveIter
= Archive
->child_begin(ArchiveErr
);
1444 ArchiveIter
!= ChildEnd
; ++ArchiveIter
) {
1447 auto ArchiveChildNameOrErr
= (*ArchiveIter
).getName();
1448 if (!ArchiveChildNameOrErr
)
1449 return ArchiveChildNameOrErr
.takeError();
1451 auto CodeObjectBufferRefOrErr
= (*ArchiveIter
).getMemoryBufferRef();
1452 if (!CodeObjectBufferRefOrErr
)
1453 return CodeObjectBufferRefOrErr
.takeError();
1455 auto CodeObjectBuffer
=
1456 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr
, false);
1458 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1459 CreateFileHandler(*CodeObjectBuffer
, BundlerConfig
);
1460 if (!FileHandlerOrErr
)
1461 return FileHandlerOrErr
.takeError();
1463 std::unique_ptr
<FileHandler
> &FileHandler
= *FileHandlerOrErr
;
1464 assert(FileHandler
);
1466 std::set
<StringRef
> BundleIds
;
1467 auto CodeObjectFileError
=
1468 FileHandler
->getBundleIDs(*CodeObjectBuffer
, BundleIds
);
1469 if (CodeObjectFileError
)
1470 return CodeObjectFileError
;
1472 auto &&ConflictingArchs
= clang::getConflictTargetIDCombination(BundleIds
);
1473 if (ConflictingArchs
) {
1474 std::string ErrMsg
=
1475 Twine("conflicting TargetIDs [" + ConflictingArchs
.value().first
+
1476 ", " + ConflictingArchs
.value().second
+ "] found in " +
1477 ArchiveChildNameOrErr
.get() + " of " + ArchiveName
)
1479 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1486 /// UnbundleArchive takes an archive file (".a") as input containing bundled
1487 /// code object files, and a list of offload targets (not host), and extracts
1488 /// the code objects into a new archive file for each offload target. Each
1489 /// resulting archive file contains all code object files corresponding to that
1490 /// particular offload target. The created archive file does not
1491 /// contain an index of the symbols and code object files are named as
1492 /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'.
1493 Error
OffloadBundler::UnbundleArchive() {
1494 std::vector
<std::unique_ptr
<MemoryBuffer
>> ArchiveBuffers
;
1496 /// Map of target names with list of object files that will form the device
1497 /// specific archive for that target
1498 StringMap
<std::vector
<NewArchiveMember
>> OutputArchivesMap
;
1500 // Map of target names and output archive filenames
1501 StringMap
<StringRef
> TargetOutputFileNameMap
;
1503 auto Output
= BundlerConfig
.OutputFileNames
.begin();
1504 for (auto &Target
: BundlerConfig
.TargetNames
) {
1505 TargetOutputFileNameMap
[Target
] = *Output
;
1509 StringRef IFName
= BundlerConfig
.InputFileNames
.front();
1511 if (BundlerConfig
.CheckInputArchive
) {
1512 // For a specific processor, a feature either shows up in all target IDs, or
1513 // does not show up in any target IDs. Otherwise the target ID combination
1515 auto ArchiveError
= CheckHeterogeneousArchive(IFName
, BundlerConfig
);
1517 return ArchiveError
;
1521 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
1522 MemoryBuffer::getFileOrSTDIN(IFName
, true, false);
1523 if (std::error_code EC
= BufOrErr
.getError())
1524 return createFileError(BundlerConfig
.InputFileNames
.front(), EC
);
1526 ArchiveBuffers
.push_back(std::move(*BufOrErr
));
1527 Expected
<std::unique_ptr
<llvm::object::Archive
>> LibOrErr
=
1528 Archive::create(ArchiveBuffers
.back()->getMemBufferRef());
1530 return LibOrErr
.takeError();
1532 auto Archive
= std::move(*LibOrErr
);
1534 Error ArchiveErr
= Error::success();
1535 auto ChildEnd
= Archive
->child_end();
1537 /// Iterate over all bundled code object files in the input archive.
1538 for (auto ArchiveIter
= Archive
->child_begin(ArchiveErr
);
1539 ArchiveIter
!= ChildEnd
; ++ArchiveIter
) {
1542 auto ArchiveChildNameOrErr
= (*ArchiveIter
).getName();
1543 if (!ArchiveChildNameOrErr
)
1544 return ArchiveChildNameOrErr
.takeError();
1546 StringRef BundledObjectFile
= sys::path::filename(*ArchiveChildNameOrErr
);
1548 auto CodeObjectBufferRefOrErr
= (*ArchiveIter
).getMemoryBufferRef();
1549 if (!CodeObjectBufferRefOrErr
)
1550 return CodeObjectBufferRefOrErr
.takeError();
1552 auto TempCodeObjectBuffer
=
1553 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr
, false);
1555 // Decompress the buffer if necessary.
1556 Expected
<std::unique_ptr
<MemoryBuffer
>> DecompressedBufferOrErr
=
1557 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer
,
1558 BundlerConfig
.Verbose
);
1559 if (!DecompressedBufferOrErr
)
1560 return createStringError(
1561 inconvertibleErrorCode(),
1562 "Failed to decompress code object: " +
1563 llvm::toString(DecompressedBufferOrErr
.takeError()));
1565 MemoryBuffer
&CodeObjectBuffer
= **DecompressedBufferOrErr
;
1567 Expected
<std::unique_ptr
<FileHandler
>> FileHandlerOrErr
=
1568 CreateFileHandler(CodeObjectBuffer
, BundlerConfig
);
1569 if (!FileHandlerOrErr
)
1570 return FileHandlerOrErr
.takeError();
1572 std::unique_ptr
<FileHandler
> &FileHandler
= *FileHandlerOrErr
;
1573 assert(FileHandler
&&
1574 "FileHandle creation failed for file in the archive!");
1576 if (Error ReadErr
= FileHandler
->ReadHeader(CodeObjectBuffer
))
1579 Expected
<std::optional
<StringRef
>> CurBundleIDOrErr
=
1580 FileHandler
->ReadBundleStart(CodeObjectBuffer
);
1581 if (!CurBundleIDOrErr
)
1582 return CurBundleIDOrErr
.takeError();
1584 std::optional
<StringRef
> OptionalCurBundleID
= *CurBundleIDOrErr
;
1585 // No device code in this child, skip.
1586 if (!OptionalCurBundleID
)
1588 StringRef CodeObject
= *OptionalCurBundleID
;
1590 // Process all bundle entries (CodeObjects) found in this child of input
1592 while (!CodeObject
.empty()) {
1593 SmallVector
<StringRef
> CompatibleTargets
;
1594 auto CodeObjectInfo
= OffloadTargetInfo(CodeObject
, BundlerConfig
);
1595 if (CodeObjectInfo
.hasHostKind()) {
1596 // Do nothing, we don't extract host code yet.
1597 } else if (getCompatibleOffloadTargets(CodeObjectInfo
, CompatibleTargets
,
1599 std::string BundleData
;
1600 raw_string_ostream
DataStream(BundleData
);
1601 if (Error Err
= FileHandler
->ReadBundle(DataStream
, CodeObjectBuffer
))
1604 for (auto &CompatibleTarget
: CompatibleTargets
) {
1605 SmallString
<128> BundledObjectFileName
;
1606 BundledObjectFileName
.assign(BundledObjectFile
);
1607 auto OutputBundleName
=
1608 Twine(llvm::sys::path::stem(BundledObjectFileName
) + "-" +
1610 getDeviceLibraryFileName(BundledObjectFileName
,
1611 CodeObjectInfo
.TargetID
))
1613 // Replace ':' in optional target feature list with '_' to ensure
1614 // cross-platform validity.
1615 std::replace(OutputBundleName
.begin(), OutputBundleName
.end(), ':',
1618 std::unique_ptr
<MemoryBuffer
> MemBuf
= MemoryBuffer::getMemBufferCopy(
1619 DataStream
.str(), OutputBundleName
);
1620 ArchiveBuffers
.push_back(std::move(MemBuf
));
1621 llvm::MemoryBufferRef MemBufRef
=
1622 MemoryBufferRef(*(ArchiveBuffers
.back()));
1624 // For inserting <CompatibleTarget, list<CodeObject>> entry in
1625 // OutputArchivesMap.
1626 if (!OutputArchivesMap
.contains(CompatibleTarget
)) {
1628 std::vector
<NewArchiveMember
> ArchiveMembers
;
1629 ArchiveMembers
.push_back(NewArchiveMember(MemBufRef
));
1630 OutputArchivesMap
.insert_or_assign(CompatibleTarget
,
1631 std::move(ArchiveMembers
));
1633 OutputArchivesMap
[CompatibleTarget
].push_back(
1634 NewArchiveMember(MemBufRef
));
1639 if (Error Err
= FileHandler
->ReadBundleEnd(CodeObjectBuffer
))
1642 Expected
<std::optional
<StringRef
>> NextTripleOrErr
=
1643 FileHandler
->ReadBundleStart(CodeObjectBuffer
);
1644 if (!NextTripleOrErr
)
1645 return NextTripleOrErr
.takeError();
1647 CodeObject
= ((*NextTripleOrErr
).has_value()) ? **NextTripleOrErr
: "";
1648 } // End of processing of all bundle entries of this child of input archive.
1649 } // End of while over children of input archive.
1651 assert(!ArchiveErr
&& "Error occurred while reading archive!");
1653 /// Write out an archive for each target
1654 for (auto &Target
: BundlerConfig
.TargetNames
) {
1655 StringRef FileName
= TargetOutputFileNameMap
[Target
];
1656 StringMapIterator
<std::vector
<llvm::NewArchiveMember
>> CurArchiveMembers
=
1657 OutputArchivesMap
.find(Target
);
1658 if (CurArchiveMembers
!= OutputArchivesMap
.end()) {
1659 if (Error WriteErr
= writeArchive(FileName
, CurArchiveMembers
->getValue(),
1660 SymtabWritingMode::NormalSymtab
,
1661 getDefaultArchiveKindForHost(), true,
1664 } else if (!BundlerConfig
.AllowMissingBundles
) {
1665 std::string ErrMsg
=
1666 Twine("no compatible code object found for the target '" + Target
+
1667 "' in heterogeneous archive library: " + IFName
)
1669 return createStringError(inconvertibleErrorCode(), ErrMsg
);
1670 } else { // Create an empty archive file if no compatible code object is
1671 // found and "allow-missing-bundles" is enabled. It ensures that
1672 // the linker using output of this step doesn't complain about
1673 // the missing input file.
1674 std::vector
<llvm::NewArchiveMember
> EmptyArchive
;
1675 EmptyArchive
.clear();
1676 if (Error WriteErr
= writeArchive(
1677 FileName
, EmptyArchive
, SymtabWritingMode::NormalSymtab
,
1678 getDefaultArchiveKindForHost(), true, false, nullptr))
1683 return Error::success();