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>
64using namespace llvm::object;
67static llvm::TimerGroup
69 "Timer group for clang offload bundler");
72#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
79 auto TargetFeatures =
Target.split(
':');
80 auto TripleOrGPU = TargetFeatures.first.rsplit(
'-');
83 auto KindTriple = TripleOrGPU.first.split(
'-');
87 llvm::Triple t = llvm::Triple(KindTriple.second);
88 this->
Triple = llvm::Triple(t.getArchName(), t.getVendorName(),
89 t.getOSName(), t.getEnvironmentName());
93 auto KindTriple = TargetFeatures.first.split(
'-');
97 llvm::Triple t = llvm::Triple(KindTriple.second);
98 this->
Triple = llvm::Triple(t.getArchName(), t.getVendorName(),
99 t.getOSName(), t.getEnvironmentName());
115 const StringRef TargetOffloadKind)
const {
119 bool HIPCompatibleWithOpenMP =
OffloadKind.starts_with_insensitive(
"hip") &&
120 TargetOffloadKind ==
"openmp";
121 bool OpenMPCompatibleWithHIP =
123 TargetOffloadKind.starts_with_insensitive(
"hip");
124 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
130 return !
Triple.str().empty() &&
Triple.getArch() != Triple::UnknownArch;
143 StringRef BundleFileName) {
144 if (
Device.contains(
"gfx"))
146 if (
Device.contains(
"sm_"))
148 return sys::path::extension(BundleFileName);
153 StringRef LibName = sys::path::stem(BundleFileName);
172 virtual ~FileHandler() {}
176 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
182 ReadBundleStart(MemoryBuffer &Input) = 0;
185 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
188 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
192 virtual Error WriteHeader(raw_ostream &OS,
193 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
197 virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0;
201 virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0;
204 virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
207 virtual Error finalizeOutputFile() {
return Error::success(); }
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();
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();
237 Error forEachBundle(MemoryBuffer &Input,
238 std::function<
Error(
const BundleInfo &)>
Func) {
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();
290static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer,
size_t pos) {
291 return llvm::support::endian::read64le(Buffer.data() + pos);
295static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
296 llvm::support::endian::write(OS, Val, llvm::endianness::little);
299class BinaryFileHandler final :
public FileHandler {
301 struct BinaryBundleInfo final :
public BundleInfo {
307 BinaryBundleInfo() {}
308 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
313 StringMap<BinaryBundleInfo> BundlesInfo;
316 StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
317 StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
320 std::string CurWriteBundleTarget;
329 ~BinaryFileHandler() final {}
331 Error ReadHeader(MemoryBuffer &Input)
final {
332 StringRef FC = Input.getBuffer();
335 CurBundleInfo = BundlesInfo.end();
339 if (ReadChars > FC.size())
340 return Error::success();
343 if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
344 return Error::success();
347 if (ReadChars + 8 > FC.size())
348 return Error::success();
350 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
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;
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);
392 CurBundleInfo = BundlesInfo.end();
393 NextBundleInfo = BundlesInfo.begin();
394 return Error::success();
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 {
429 HeaderSize +=
T.size();
435 Write8byteIntegerToBuffer(OS, BundlerConfig.
TargetNames.size());
439 MemoryBuffer &MB = *Inputs[Idx++];
442 Write8byteIntegerToBuffer(OS, HeaderSize);
444 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
445 BundlesInfo[
T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
446 HeaderSize += MB.getBufferSize();
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];
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();
482class TempFileHandlerRAII {
484 ~TempFileHandlerRAII() {
485 for (
const auto &
File : Files)
486 sys::fs::remove(
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;
515class ObjectFileHandler final :
public FileHandler {
518 std::unique_ptr<ObjectFile> Obj;
521 StringRef getInputFileContents()
const {
return Obj->getData(); }
526 IsOffloadSection(SectionRef CurSection) {
529 return NameOrErr.takeError();
532 if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle)
540 unsigned NumberOfInputs = 0;
544 unsigned NumberOfProcessedInputs = 0;
547 section_iterator CurrentSection;
548 section_iterator NextSection;
555 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
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(); }
565 ReadBundleStart(MemoryBuffer &Input)
final {
566 while (NextSection != Obj->section_end()) {
567 CurrentSection = NextSection;
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 {
587 return ContentOrErr.takeError();
588 StringRef Content = *ContentOrErr;
591 std::string ModifiedContent;
592 if (Content.size() == 1u && Content.front() == 0) {
593 auto HostBundleOrErr = getHostBundle(
594 StringRef(Input.getBufferStart(), Input.getBufferSize()));
595 if (!HostBundleOrErr)
596 return HostBundleOrErr.takeError();
598 ModifiedContent = std::move(*HostBundleOrErr);
599 Content = ModifiedContent;
602 OS.write(Content.data(), Content.size());
603 return Error::success();
606 Error WriteHeader(raw_ostream &OS,
607 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)
final {
609 "Host input index not defined.");
612 NumberOfInputs = Inputs.size();
613 return Error::success();
616 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple)
final {
617 ++NumberOfProcessedInputs;
618 return Error::success();
621 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple)
final {
622 return Error::success();
625 Error finalizeOutputFile() final {
626 assert(NumberOfProcessedInputs <= NumberOfInputs &&
627 "Processing more inputs that actually exist!");
629 "Host input index not defined.");
632 if (NumberOfProcessedInputs != NumberOfInputs)
633 return Error::success();
641 "llvm-objcopy path not specified");
644 TempFileHandlerRAII TempFiles;
648 BumpPtrAllocator Alloc;
649 StringSaver SS{Alloc};
652 for (
unsigned I = 0; I < NumberOfInputs; ++I) {
661 return TempFileOrErr.takeError();
662 InputFile = *TempFileOrErr;
665 ObjcopyArgs.push_back(
668 ObjcopyArgs.push_back(
670 BundlerConfig.
TargetNames[I] +
"=readonly,exclude"));
672 ObjcopyArgs.push_back(
"--");
673 ObjcopyArgs.push_back(
677 if (Error Err = executeObjcopy(BundlerConfig.
ObjcopyPath, ObjcopyArgs))
680 return Error::success();
683 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
684 return Error::success();
692 errs() <<
"\"" << Objcopy <<
"\"";
693 for (StringRef Arg : drop_begin(Args, 1))
694 errs() <<
" \"" << Arg <<
"\"";
697 if (sys::ExecuteAndWait(Objcopy, Args))
698 return createStringError(inconvertibleErrorCode(),
699 "'llvm-objcopy' tool failed");
701 return Error::success();
705 TempFileHandlerRAII TempFiles;
707 auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
708 if (!ModifiedObjPathOrErr)
709 return ModifiedObjPathOrErr.takeError();
710 StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
712 BumpPtrAllocator Alloc;
713 StringSaver SS{Alloc};
716 ObjcopyArgs.push_back(
"--regex");
717 ObjcopyArgs.push_back(
"--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
718 ObjcopyArgs.push_back(
"--");
720 StringRef ObjcopyInputFileName;
727 if (StringRef(BundlerConfig.
FilesType).starts_with(
"a")) {
728 auto InputFileOrErr =
731 return InputFileOrErr.takeError();
732 ObjcopyInputFileName = *InputFileOrErr;
736 ObjcopyArgs.push_back(ObjcopyInputFileName);
737 ObjcopyArgs.push_back(ModifiedObjPath);
739 if (Error Err = executeObjcopy(BundlerConfig.
ObjcopyPath, ObjcopyArgs))
740 return std::move(Err);
742 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
744 return createStringError(BufOrErr.getError(),
745 "Failed to read back the modified object file");
747 return BufOrErr->get()->getBuffer().str();
760class TextFileHandler final :
public FileHandler {
765 std::string BundleStartString;
768 std::string BundleEndString;
771 size_t ReadChars = 0u;
774 Error ReadHeader(MemoryBuffer &Input)
final {
return Error::success(); }
777 ReadBundleStart(MemoryBuffer &Input)
final {
778 StringRef FC = Input.getBuffer();
781 ReadChars = FC.find(BundleStartString, ReadChars);
782 if (ReadChars == FC.npos)
786 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
789 size_t TripleEnd = ReadChars = FC.find(
"\n", ReadChars);
790 if (TripleEnd == FC.npos)
796 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
799 Error ReadBundleEnd(MemoryBuffer &Input)
final {
800 StringRef FC = Input.getBuffer();
803 assert(FC[ReadChars] ==
'\n' &&
"The bundle should end with a new line.");
805 size_t TripleEnd = ReadChars = FC.find(
"\n", ReadChars + 1);
806 if (TripleEnd != FC.npos)
810 return Error::success();
813 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
814 StringRef FC = Input.getBuffer();
815 size_t BundleStart = ReadChars;
818 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
820 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
823 return Error::success();
826 Error WriteHeader(raw_ostream &OS,
827 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)
final {
828 return Error::success();
831 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple)
final {
832 OS << BundleStartString << TargetTriple <<
"\n";
833 return Error::success();
836 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple)
final {
837 OS << BundleEndString << TargetTriple <<
"\n";
838 return Error::success();
841 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
842 OS << Input.getBuffer();
843 return Error::success();
847 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
854 Error listBundleIDsCallback(MemoryBuffer &Input,
855 const BundleInfo &Info)
final {
860 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
861 if (Error Err = ReadBundleEnd(Input))
863 return Error::success();
871static std::unique_ptr<FileHandler>
879 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
880 return std::make_unique<BinaryFileHandler>(BundlerConfig);
884 return std::make_unique<ObjectFileHandler>(
885 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
893 std::string FilesType = BundlerConfig.
FilesType;
895 if (FilesType ==
"i")
896 return std::make_unique<TextFileHandler>(
"//");
897 if (FilesType ==
"ii")
898 return std::make_unique<TextFileHandler>(
"//");
899 if (FilesType ==
"cui")
900 return std::make_unique<TextFileHandler>(
"//");
901 if (FilesType ==
"hipi")
902 return std::make_unique<TextFileHandler>(
"//");
905 if (FilesType ==
"d")
906 return std::make_unique<TextFileHandler>(
"#");
907 if (FilesType ==
"ll")
908 return std::make_unique<TextFileHandler>(
";");
909 if (FilesType ==
"bc")
910 return std::make_unique<BinaryFileHandler>(BundlerConfig);
911 if (FilesType ==
"s")
912 return std::make_unique<TextFileHandler>(
"#");
913 if (FilesType ==
"o")
915 if (FilesType ==
"a")
917 if (FilesType ==
"gch")
918 return std::make_unique<BinaryFileHandler>(BundlerConfig);
919 if (FilesType ==
"ast")
920 return std::make_unique<BinaryFileHandler>(BundlerConfig);
922 return createStringError(errc::invalid_argument,
923 "'" + FilesType +
"': invalid file type specified");
927 if (llvm::compression::zstd::isAvailable()) {
932 }
else if (llvm::compression::zlib::isAvailable()) {
938 auto IgnoreEnvVarOpt =
939 llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
940 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() ==
"1")
943 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_VERBOSE");
944 if (VerboseEnvVarOpt.has_value())
945 Verbose = VerboseEnvVarOpt.value() ==
"1";
947 auto CompressEnvVarOpt =
948 llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_COMPRESS");
949 if (CompressEnvVarOpt.has_value())
950 Compress = CompressEnvVarOpt.value() ==
"1";
952 auto CompressionLevelEnvVarOpt =
953 llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
954 if (CompressionLevelEnvVarOpt.has_value()) {
955 llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
957 if (!CompressionLevelStr.getAsInteger(10, Level))
961 <<
"Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
962 << CompressionLevelStr.str() <<
". Ignoring it.\n";
968 std::string Num = std::to_string(
Value);
969 int InsertPosition = Num.length() - 3;
970 while (InsertPosition > 0) {
971 Num.insert(InsertPosition,
",");
979 const llvm::MemoryBuffer &Input,
981 if (!llvm::compression::zstd::isAvailable() &&
982 !llvm::compression::zlib::isAvailable())
983 return createStringError(llvm::inconvertibleErrorCode(),
984 "Compression not supported");
986 llvm::Timer HashTimer(
"Hash Calculation Timer",
"Hash calculation time",
989 HashTimer.startTimer();
991 llvm::MD5::MD5Result
Result;
992 Hash.update(Input.getBuffer());
994 uint64_t TruncatedHash =
Result.low();
996 HashTimer.stopTimer();
1000 reinterpret_cast<const uint8_t *
>(Input.getBuffer().data()),
1001 Input.getBuffer().size());
1003 llvm::Timer CompressTimer(
"Compression Timer",
"Compression time",
1006 CompressTimer.startTimer();
1007 llvm::compression::compress(
P, BufferUint8, CompressedBuffer);
1009 CompressTimer.stopTimer();
1011 uint16_t CompressionMethod =
static_cast<uint16_t
>(
P.format);
1012 uint32_t UncompressedSize = Input.getBuffer().size();
1013 uint32_t TotalFileSize = MagicNumber.size() +
sizeof(TotalFileSize) +
1014 sizeof(Version) +
sizeof(CompressionMethod) +
1015 sizeof(UncompressedSize) +
sizeof(TruncatedHash) +
1016 CompressedBuffer.size();
1019 llvm::raw_svector_ostream OS(FinalBuffer);
1021 OS.write(
reinterpret_cast<const char *
>(&Version),
sizeof(Version));
1022 OS.write(
reinterpret_cast<const char *
>(&CompressionMethod),
1023 sizeof(CompressionMethod));
1024 OS.write(
reinterpret_cast<const char *
>(&TotalFileSize),
1025 sizeof(TotalFileSize));
1026 OS.write(
reinterpret_cast<const char *
>(&UncompressedSize),
1027 sizeof(UncompressedSize));
1028 OS.write(
reinterpret_cast<const char *
>(&TruncatedHash),
1029 sizeof(TruncatedHash));
1030 OS.write(
reinterpret_cast<const char *
>(CompressedBuffer.data()),
1031 CompressedBuffer.size());
1035 P.format == llvm::compression::Format::Zstd ?
"zstd" :
"zlib";
1036 double CompressionRate =
1037 static_cast<double>(UncompressedSize) / CompressedBuffer.size();
1038 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1039 double CompressionSpeedMBs =
1040 (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
1042 llvm::errs() <<
"Compressed bundle format version: " << Version <<
"\n"
1043 <<
"Total file size (including headers): "
1045 <<
"Compression method used: " << MethodUsed <<
"\n"
1046 <<
"Compression level: " <<
P.level <<
"\n"
1047 <<
"Binary size before compression: "
1049 <<
"Binary size after compression: "
1051 <<
"Compression rate: "
1052 << llvm::format(
"%.2lf", CompressionRate) <<
"\n"
1053 <<
"Compression ratio: "
1054 << llvm::format(
"%.2lf%%", 100.0 / CompressionRate) <<
"\n"
1055 <<
"Compression speed: "
1056 << llvm::format(
"%.2lf MB/s", CompressionSpeedMBs) <<
"\n"
1057 <<
"Truncated MD5 hash: "
1058 << llvm::format_hex(TruncatedHash, 16) <<
"\n";
1060 return llvm::MemoryBuffer::getMemBufferCopy(
1061 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1068 StringRef Blob = Input.getBuffer();
1070 if (Blob.size() < V1HeaderSize)
1071 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1073 if (llvm::identify_magic(Blob) !=
1074 llvm::file_magic::offload_bundle_compressed) {
1076 llvm::errs() <<
"Uncompressed bundle.\n";
1077 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1080 size_t CurrentOffset = MagicSize;
1082 uint16_t ThisVersion;
1083 memcpy(&ThisVersion, Blob.data() + CurrentOffset,
sizeof(uint16_t));
1084 CurrentOffset += VersionFieldSize;
1086 uint16_t CompressionMethod;
1087 memcpy(&CompressionMethod, Blob.data() + CurrentOffset,
sizeof(uint16_t));
1088 CurrentOffset += MethodFieldSize;
1090 uint32_t TotalFileSize;
1091 if (ThisVersion >= 2) {
1092 if (Blob.size() < V2HeaderSize)
1093 return createStringError(inconvertibleErrorCode(),
1094 "Compressed bundle header size too small");
1095 memcpy(&TotalFileSize, Blob.data() + CurrentOffset,
sizeof(uint32_t));
1096 CurrentOffset += FileSizeFieldSize;
1099 uint32_t UncompressedSize;
1100 memcpy(&UncompressedSize, Blob.data() + CurrentOffset,
sizeof(uint32_t));
1101 CurrentOffset += UncompressedSizeFieldSize;
1103 uint64_t StoredHash;
1104 memcpy(&StoredHash, Blob.data() + CurrentOffset,
sizeof(uint64_t));
1105 CurrentOffset += HashFieldSize;
1107 llvm::compression::Format CompressionFormat;
1108 if (CompressionMethod ==
1109 static_cast<uint16_t
>(llvm::compression::Format::Zlib))
1110 CompressionFormat = llvm::compression::Format::Zlib;
1111 else if (CompressionMethod ==
1112 static_cast<uint16_t
>(llvm::compression::Format::Zstd))
1113 CompressionFormat = llvm::compression::Format::Zstd;
1115 return createStringError(inconvertibleErrorCode(),
1116 "Unknown compressing method");
1118 llvm::Timer DecompressTimer(
"Decompression Timer",
"Decompression time",
1121 DecompressTimer.startTimer();
1124 StringRef CompressedData = Blob.substr(CurrentOffset);
1125 if (llvm::Error DecompressionError = llvm::compression::decompress(
1126 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
1127 DecompressedData, UncompressedSize))
1128 return createStringError(inconvertibleErrorCode(),
1129 "Could not decompress embedded file contents: " +
1130 llvm::toString(std::move(DecompressionError)));
1133 DecompressTimer.stopTimer();
1135 double DecompressionTimeSeconds =
1136 DecompressTimer.getTotalTime().getWallTime();
1139 llvm::Timer HashRecalcTimer(
"Hash Recalculation Timer",
1140 "Hash recalculation time",
1142 HashRecalcTimer.startTimer();
1144 llvm::MD5::MD5Result
Result;
1146 DecompressedData.size()));
1148 uint64_t RecalculatedHash =
Result.low();
1149 HashRecalcTimer.stopTimer();
1150 bool HashMatch = (StoredHash == RecalculatedHash);
1152 double CompressionRate =
1153 static_cast<double>(UncompressedSize) / CompressedData.size();
1154 double DecompressionSpeedMBs =
1155 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1157 llvm::errs() <<
"Compressed bundle format version: " << ThisVersion <<
"\n";
1158 if (ThisVersion >= 2)
1159 llvm::errs() <<
"Total file size (from header): "
1161 llvm::errs() <<
"Decompression method: "
1162 << (CompressionFormat == llvm::compression::Format::Zlib
1166 <<
"Size before decompression: "
1168 <<
"Size after decompression: "
1170 <<
"Compression rate: "
1171 << llvm::format(
"%.2lf", CompressionRate) <<
"\n"
1172 <<
"Compression ratio: "
1173 << llvm::format(
"%.2lf%%", 100.0 / CompressionRate) <<
"\n"
1174 <<
"Decompression speed: "
1175 << llvm::format(
"%.2lf MB/s", DecompressionSpeedMBs) <<
"\n"
1176 <<
"Stored hash: " << llvm::format_hex(StoredHash, 16) <<
"\n"
1177 <<
"Recalculated hash: "
1178 << llvm::format_hex(RecalculatedHash, 16) <<
"\n"
1179 <<
"Hashes match: " << (HashMatch ?
"Yes" :
"No") <<
"\n";
1182 return llvm::MemoryBuffer::getMemBufferCopy(
1183 llvm::toStringRef(DecompressedData));
1190 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1191 MemoryBuffer::getFileOrSTDIN(InputFileName);
1192 if (std::error_code EC = CodeOrErr.getError())
1193 return createFileError(InputFileName, EC);
1198 if (!DecompressedBufferOrErr)
1199 return createStringError(
1200 inconvertibleErrorCode(),
1201 "Failed to decompress input: " +
1202 llvm::toString(DecompressedBufferOrErr.takeError()));
1204 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1209 if (!FileHandlerOrErr)
1210 return FileHandlerOrErr.takeError();
1212 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1214 return FH->listBundleIDs(DecompressedInput);
1225 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1226 dbgs() <<
"Compatible: Exact match: \t[CodeObject: "
1227 << CodeObjectInfo.
str()
1228 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1236 "CodeObjectCompatibility",
1237 dbgs() <<
"Incompatible: Kind/Triple mismatch \t[CodeObject: "
1238 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1244 llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
1246 CodeObjectInfo.
Triple, CodeObjectInfo.
TargetID, &CodeObjectFeatureMap);
1251 if (!TargetProc || !CodeObjectProc ||
1252 CodeObjectProc.value() != TargetProc.value()) {
1253 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1254 dbgs() <<
"Incompatible: Processor mismatch \t[CodeObject: "
1255 << CodeObjectInfo.
str()
1256 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1262 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1263 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1264 dbgs() <<
"Incompatible: CodeObject has more features "
1265 "than target \t[CodeObject: "
1266 << CodeObjectInfo.
str()
1267 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1275 for (
const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1276 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1277 if (TargetFeature == TargetFeatureMap.end()) {
1279 "CodeObjectCompatibility",
1281 <<
"Incompatible: Value of CodeObject's non-ANY feature is "
1282 "not matching with Target feature's ANY value \t[CodeObject: "
1283 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1286 }
else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1288 "CodeObjectCompatibility",
1289 dbgs() <<
"Incompatible: Value of CodeObject's non-ANY feature is "
1290 "not matching with Target feature's non-ANY value "
1292 << CodeObjectInfo.
str()
1293 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1303 "CodeObjectCompatibility",
1304 dbgs() <<
"Compatible: Target IDs are compatible \t[CodeObject: "
1305 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1316 llvm::raw_svector_ostream BufferStream(Buffer);
1322 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1323 MemoryBuffer::getFileOrSTDIN(I);
1324 if (std::error_code EC = CodeOrErr.getError())
1325 return createFileError(I, EC);
1326 InputBuffers.emplace_back(std::move(*CodeOrErr));
1331 "Host input index undefined??");
1336 if (!FileHandlerOrErr)
1337 return FileHandlerOrErr.takeError();
1339 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1343 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1348 auto Input = InputBuffers.begin();
1350 if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1352 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1354 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1366 std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1367 llvm::MemoryBuffer::getMemBufferCopy(
1368 llvm::StringRef(Buffer.data(), Buffer.size()));
1373 if (
auto Error = CompressionResult.takeError())
1376 auto CompressedMemBuffer = std::move(CompressionResult.get());
1377 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1378 CompressedMemBuffer->getBufferEnd());
1380 CompressedBuffer = Buffer;
1382 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1384 return FH->finalizeOutputFile();
1390 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1392 if (std::error_code EC = CodeOrErr.getError())
1398 if (!DecompressedBufferOrErr)
1399 return createStringError(
1400 inconvertibleErrorCode(),
1401 "Failed to decompress input: " +
1402 llvm::toString(DecompressedBufferOrErr.takeError()));
1404 MemoryBuffer &Input = **DecompressedBufferOrErr;
1409 if (!FileHandlerOrErr)
1410 return FileHandlerOrErr.takeError();
1412 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1416 if (Error Err = FH->ReadHeader(Input))
1420 StringMap<StringRef> Worklist;
1423 Worklist[Triple] = *Output;
1429 bool FoundHostBundle =
false;
1430 while (!Worklist.empty()) {
1432 FH->ReadBundleStart(Input);
1433 if (!CurTripleOrErr)
1434 return CurTripleOrErr.takeError();
1437 if (!*CurTripleOrErr)
1440 StringRef CurTriple = **CurTripleOrErr;
1441 assert(!CurTriple.empty());
1443 auto Output = Worklist.begin();
1444 for (
auto E = Worklist.end(); Output != E; Output++) {
1452 if (Output == Worklist.end())
1456 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1458 return createFileError((*Output).second, EC);
1459 if (Error Err = FH->ReadBundle(OutputFile, Input))
1461 if (Error Err = FH->ReadBundleEnd(Input))
1463 Worklist.erase(Output);
1467 if (OffloadInfo.hasHostKind())
1468 FoundHostBundle =
true;
1472 std::string ErrMsg =
"Can't find bundles for";
1473 std::set<StringRef> Sorted;
1474 for (
auto &E : Worklist)
1475 Sorted.insert(E.first());
1477 unsigned Last = Sorted.size() - 1;
1478 for (
auto &E : Sorted) {
1479 if (I != 0 &&
Last > 1)
1482 if (I ==
Last && I != 0)
1487 return createStringError(inconvertibleErrorCode(), ErrMsg);
1493 for (
auto &E : Worklist) {
1495 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1497 return createFileError(E.second, EC);
1501 if (OffloadInfo.hasHostKind())
1502 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1504 return Error::success();
1511 return createStringError(inconvertibleErrorCode(),
1512 "Can't find bundle for the host target");
1515 for (
auto &E : Worklist) {
1517 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1519 return createFileError(E.second, EC);
1522 return Error::success();
1526 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1540 if (!CompatibleTargets.empty()) {
1541 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1542 dbgs() <<
"CompatibleTargets list should be empty\n");
1548 CompatibleTargets.push_back(
Target);
1550 return !CompatibleTargets.empty();
1560 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1561 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1562 MemoryBuffer::getFileOrSTDIN(ArchiveName,
true,
false);
1563 if (std::error_code EC = BufOrErr.getError())
1564 return createFileError(ArchiveName, EC);
1566 ArchiveBuffers.push_back(std::move(*BufOrErr));
1568 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1570 return LibOrErr.takeError();
1572 auto Archive = std::move(*LibOrErr);
1574 Error ArchiveErr = Error::success();
1575 auto ChildEnd = Archive->child_end();
1578 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1579 ArchiveIter != ChildEnd; ++ArchiveIter) {
1582 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1583 if (!ArchiveChildNameOrErr)
1584 return ArchiveChildNameOrErr.takeError();
1586 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1587 if (!CodeObjectBufferRefOrErr)
1588 return CodeObjectBufferRefOrErr.takeError();
1590 auto CodeObjectBuffer =
1591 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1595 if (!FileHandlerOrErr)
1596 return FileHandlerOrErr.takeError();
1598 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1599 assert(FileHandler);
1601 std::set<StringRef> BundleIds;
1602 auto CodeObjectFileError =
1603 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1604 if (CodeObjectFileError)
1605 return CodeObjectFileError;
1608 if (ConflictingArchs) {
1609 std::string ErrMsg =
1610 Twine(
"conflicting TargetIDs [" + ConflictingArchs.value().first +
1611 ", " + ConflictingArchs.value().second +
"] found in " +
1612 ArchiveChildNameOrErr.get() +
" of " + ArchiveName)
1614 return createStringError(inconvertibleErrorCode(), ErrMsg);
1629 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1633 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1636 StringMap<StringRef> TargetOutputFileNameMap;
1640 TargetOutputFileNameMap[
Target] = *Output;
1652 return ArchiveError;
1656 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1657 MemoryBuffer::getFileOrSTDIN(IFName,
true,
false);
1658 if (std::error_code EC = BufOrErr.getError())
1661 ArchiveBuffers.push_back(std::move(*BufOrErr));
1663 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1665 return LibOrErr.takeError();
1667 auto Archive = std::move(*LibOrErr);
1669 Error ArchiveErr = Error::success();
1670 auto ChildEnd = Archive->child_end();
1673 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1674 ArchiveIter != ChildEnd; ++ArchiveIter) {
1677 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1678 if (!ArchiveChildNameOrErr)
1679 return ArchiveChildNameOrErr.takeError();
1681 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1683 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1684 if (!CodeObjectBufferRefOrErr)
1685 return CodeObjectBufferRefOrErr.takeError();
1687 auto TempCodeObjectBuffer =
1688 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1694 if (!DecompressedBufferOrErr)
1695 return createStringError(
1696 inconvertibleErrorCode(),
1697 "Failed to decompress code object: " +
1698 llvm::toString(DecompressedBufferOrErr.takeError()));
1700 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1704 if (!FileHandlerOrErr)
1705 return FileHandlerOrErr.takeError();
1707 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1708 assert(FileHandler &&
1709 "FileHandle creation failed for file in the archive!");
1711 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1715 FileHandler->ReadBundleStart(CodeObjectBuffer);
1716 if (!CurBundleIDOrErr)
1717 return CurBundleIDOrErr.takeError();
1719 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1721 if (!OptionalCurBundleID)
1723 StringRef CodeObject = *OptionalCurBundleID;
1727 while (!CodeObject.empty()) {
1732 std::string BundleData;
1733 raw_string_ostream DataStream(BundleData);
1734 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1737 for (
auto &CompatibleTarget : CompatibleTargets) {
1739 BundledObjectFileName.assign(BundledObjectFile);
1740 auto OutputBundleName =
1741 Twine(llvm::sys::path::stem(BundledObjectFileName) +
"-" +
1744 CodeObjectInfo.TargetID))
1748 std::replace(OutputBundleName.begin(), OutputBundleName.end(),
':',
1751 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1752 DataStream.str(), OutputBundleName);
1753 ArchiveBuffers.push_back(std::move(MemBuf));
1754 llvm::MemoryBufferRef MemBufRef =
1755 MemoryBufferRef(*(ArchiveBuffers.back()));
1759 if (!OutputArchivesMap.contains(CompatibleTarget)) {
1761 std::vector<NewArchiveMember> ArchiveMembers;
1762 ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
1763 OutputArchivesMap.insert_or_assign(CompatibleTarget,
1764 std::move(ArchiveMembers));
1766 OutputArchivesMap[CompatibleTarget].push_back(
1767 NewArchiveMember(MemBufRef));
1772 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1776 FileHandler->ReadBundleStart(CodeObjectBuffer);
1777 if (!NextTripleOrErr)
1778 return NextTripleOrErr.takeError();
1780 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr :
"";
1784 assert(!ArchiveErr &&
"Error occurred while reading archive!");
1789 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1790 OutputArchivesMap.find(
Target);
1791 if (CurArchiveMembers != OutputArchivesMap.end()) {
1792 if (Error WriteErr = writeArchive(
FileName, CurArchiveMembers->getValue(),
1793 SymtabWritingMode::NormalSymtab,
1798 std::string ErrMsg =
1799 Twine(
"no compatible code object found for the target '" +
Target +
1800 "' in heterogeneous archive library: " + IFName)
1802 return createStringError(inconvertibleErrorCode(), ErrMsg);
1807 std::vector<llvm::NewArchiveMember> EmptyArchive;
1808 EmptyArchive.clear();
1809 if (Error WriteErr = writeArchive(
1810 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1816 return Error::success();
llvm::MachO::Target Target
static std::string getDeviceLibraryFileName(StringRef BundleFileName, StringRef Device)
static StringRef getDeviceFileExtension(StringRef Device, StringRef BundleFileName)
static Expected< std::unique_ptr< FileHandler > > CreateFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate handler given the input files and options.
#define OFFLOAD_BUNDLER_MAGIC_STR
Magic string that marks the existence of offloading data.
bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, const OffloadTargetInfo &TargetInfo)
Checks if a code object CodeObjectInfo is compatible with a given target TargetInfo.
static llvm::TimerGroup ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group", "Timer group for clang offload bundler")
static Error CheckHeterogeneousArchive(StringRef ArchiveName, const OffloadBundlerConfig &BundlerConfig)
static std::unique_ptr< FileHandler > CreateObjectFileHandler(MemoryBuffer &FirstInput, const OffloadBundlerConfig &BundlerConfig)
Return an appropriate object file handler.
static Archive::Kind getDefaultArchiveKindForHost()
static std::string formatWithCommas(unsigned long long Value)
static bool getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, SmallVectorImpl< StringRef > &CompatibleTargets, const OffloadBundlerConfig &BundlerConfig)
Computes a list of targets among all given targets which are compatible with this code object.
This file defines an offload bundling API that bundles different files that relate with the same sour...
Defines version macros and version-related utility functions for Clang.
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > compress(llvm::compression::Params P, const llvm::MemoryBuffer &Input, bool Verbose=false)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > decompress(const llvm::MemoryBuffer &Input, bool Verbose=false)
llvm::compression::Format CompressionFormat
std::vector< std::string > OutputFileNames
std::vector< std::string > TargetNames
std::vector< std::string > InputFileNames
bool PrintExternalCommands
llvm::Error BundleFiles()
Bundle the files. Return true if an error was found.
llvm::Error UnbundleFiles()
llvm::Error UnbundleArchive()
UnbundleArchive takes an archive file (".a") as input containing bundled code object files,...
static llvm::Error ListBundleIDsInFile(llvm::StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig)
const OffloadBundlerConfig & BundlerConfig
Exposes information about the current target.
The JSON file list parser is used to communicate input to InstallAPI.
@ Create
'create' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
std::optional< llvm::StringRef > parseTargetID(const llvm::Triple &T, llvm::StringRef OffloadArch, llvm::StringMap< bool > *FeatureMap)
Parse a target ID to get processor and feature map.
std::optional< std::pair< llvm::StringRef, llvm::StringRef > > getConflictTargetIDCombination(const std::set< llvm::StringRef > &TargetIDs)
Get the conflicted pair of target IDs for a compilation or a bundled code object, assuming TargetIDs ...
CudaArch StringToCudaArch(llvm::StringRef S)
@ Result
The result type of a method or function.
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.
Obtain the offload kind, real machine triple, and an optional TargetID out of the target information ...
bool operator==(const OffloadTargetInfo &Target) const
bool isOffloadKindCompatible(const llvm::StringRef TargetOffloadKind) const
bool isTripleValid() const
OffloadTargetInfo(const llvm::StringRef Target, const OffloadBundlerConfig &BC)
llvm::StringRef OffloadKind
bool isOffloadKindValid() const
const OffloadBundlerConfig & BundlerConfig