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 {
117 (
OffloadKind ==
"hip" && TargetOffloadKind ==
"hipv4") ||
118 (
OffloadKind ==
"hipv4" && TargetOffloadKind ==
"hip"))
122 bool HIPCompatibleWithOpenMP =
OffloadKind.starts_with_insensitive(
"hip") &&
123 TargetOffloadKind ==
"openmp";
124 bool OpenMPCompatibleWithHIP =
126 TargetOffloadKind.starts_with_insensitive(
"hip");
127 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
133 return !
Triple.str().empty() &&
Triple.getArch() != Triple::UnknownArch;
146 StringRef BundleFileName) {
147 if (
Device.contains(
"gfx"))
149 if (
Device.contains(
"sm_"))
151 return sys::path::extension(BundleFileName);
156 StringRef LibName = sys::path::stem(BundleFileName);
175 virtual ~FileHandler() {}
179 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
185 ReadBundleStart(MemoryBuffer &Input) = 0;
188 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
191 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
195 virtual Error WriteHeader(raw_ostream &OS,
196 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
200 virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0;
204 virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0;
207 virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
210 virtual Error finalizeOutputFile() {
return Error::success(); }
213 virtual Error listBundleIDs(MemoryBuffer &Input) {
214 if (Error Err = ReadHeader(Input))
216 return forEachBundle(Input, [&](
const BundleInfo &Info) -> Error {
217 llvm::outs() << Info.BundleID <<
'\n';
218 Error Err = listBundleIDsCallback(Input, Info);
221 return Error::success();
226 virtual Error getBundleIDs(MemoryBuffer &Input,
227 std::set<StringRef> &BundleIds) {
228 if (Error Err = ReadHeader(Input))
230 return forEachBundle(Input, [&](
const BundleInfo &Info) -> Error {
231 BundleIds.insert(Info.BundleID);
232 Error Err = listBundleIDsCallback(Input, Info);
235 return Error::success();
240 Error forEachBundle(MemoryBuffer &Input,
241 std::function<
Error(
const BundleInfo &)>
Func) {
244 ReadBundleStart(Input);
246 return CurTripleOrErr.takeError();
249 if (!*CurTripleOrErr)
252 StringRef CurTriple = **CurTripleOrErr;
253 assert(!CurTriple.empty());
255 BundleInfo Info{CurTriple};
256 if (Error Err =
Func(Info))
259 return Error::success();
263 virtual Error listBundleIDsCallback(MemoryBuffer &Input,
264 const BundleInfo &Info) {
265 return Error::success();
293static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer,
size_t pos) {
294 return llvm::support::endian::read64le(Buffer.data() + pos);
298static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
299 llvm::support::endian::write(OS, Val, llvm::endianness::little);
302class BinaryFileHandler final :
public FileHandler {
304 struct BinaryBundleInfo final :
public BundleInfo {
310 BinaryBundleInfo() {}
311 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
316 StringMap<BinaryBundleInfo> BundlesInfo;
319 StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
320 StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
323 std::string CurWriteBundleTarget;
332 ~BinaryFileHandler() final {}
334 Error ReadHeader(MemoryBuffer &Input)
final {
335 StringRef FC = Input.getBuffer();
338 CurBundleInfo = BundlesInfo.end();
342 if (ReadChars > FC.size())
343 return Error::success();
346 if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
347 return Error::success();
350 if (ReadChars + 8 > FC.size())
351 return Error::success();
353 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
357 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
360 if (ReadChars + 8 > FC.size())
361 return Error::success();
363 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
367 if (ReadChars + 8 > FC.size())
368 return Error::success();
370 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
374 if (ReadChars + 8 > FC.size())
375 return Error::success();
377 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
381 if (ReadChars + TripleSize > FC.size())
382 return Error::success();
384 StringRef Triple(&FC.data()[ReadChars], TripleSize);
385 ReadChars += TripleSize;
388 if (!Offset || Offset + Size > FC.size())
389 return Error::success();
391 assert(!BundlesInfo.contains(Triple) &&
"Triple is duplicated??");
392 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
395 CurBundleInfo = BundlesInfo.end();
396 NextBundleInfo = BundlesInfo.begin();
397 return Error::success();
401 ReadBundleStart(MemoryBuffer &Input)
final {
402 if (NextBundleInfo == BundlesInfo.end())
404 CurBundleInfo = NextBundleInfo++;
405 return CurBundleInfo->first();
408 Error ReadBundleEnd(MemoryBuffer &Input)
final {
409 assert(CurBundleInfo != BundlesInfo.end() &&
"Invalid reader info!");
410 return Error::success();
413 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
414 assert(CurBundleInfo != BundlesInfo.end() &&
"Invalid reader info!");
415 StringRef FC = Input.getBuffer();
416 OS.write(FC.data() + CurBundleInfo->second.Offset,
417 CurBundleInfo->second.Size);
418 return Error::success();
421 Error WriteHeader(raw_ostream &OS,
422 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)
final {
432 HeaderSize +=
T.size();
438 Write8byteIntegerToBuffer(OS, BundlerConfig.
TargetNames.size());
442 MemoryBuffer &MB = *Inputs[Idx++];
445 Write8byteIntegerToBuffer(OS, HeaderSize);
447 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
448 BundlesInfo[
T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
449 HeaderSize += MB.getBufferSize();
451 Write8byteIntegerToBuffer(OS,
T.size());
455 return Error::success();
458 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple)
final {
459 CurWriteBundleTarget = TargetTriple.str();
460 return Error::success();
463 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple)
final {
464 return Error::success();
467 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
468 auto BI = BundlesInfo[CurWriteBundleTarget];
471 size_t CurrentPos = OS.tell();
472 size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0;
473 for (
size_t I = 0; I < PaddingSize; ++I)
475 assert(OS.tell() == BI.Offset);
477 OS.write(Input.getBufferStart(), Input.getBufferSize());
479 return Error::success();
485class TempFileHandlerRAII {
487 ~TempFileHandlerRAII() {
488 for (
const auto &
File : Files)
489 sys::fs::remove(
File);
495 if (std::error_code EC =
496 sys::fs::createTemporaryFile(
"clang-offload-bundler",
"tmp",
File))
497 return createFileError(
File, EC);
498 Files.push_front(
File);
502 raw_fd_ostream OS(
File, EC);
504 return createFileError(
File, EC);
505 OS.write(Contents->data(), Contents->size());
507 return Files.front().str();
511 std::forward_list<SmallString<128u>> Files;
518class ObjectFileHandler final :
public FileHandler {
521 std::unique_ptr<ObjectFile> Obj;
524 StringRef getInputFileContents()
const {
return Obj->getData(); }
529 IsOffloadSection(SectionRef CurSection) {
532 return NameOrErr.takeError();
535 if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle)
543 unsigned NumberOfInputs = 0;
547 unsigned NumberOfProcessedInputs = 0;
550 section_iterator CurrentSection;
551 section_iterator NextSection;
558 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn,
560 : Obj(
std::move(ObjIn)), CurrentSection(Obj->section_begin()),
561 NextSection(Obj->section_begin()), BundlerConfig(BC) {}
563 ~ObjectFileHandler() final {}
565 Error ReadHeader(MemoryBuffer &Input)
final {
return Error::success(); }
568 ReadBundleStart(MemoryBuffer &Input)
final {
569 while (NextSection != Obj->section_end()) {
570 CurrentSection = NextSection;
576 IsOffloadSection(*CurrentSection);
578 return TripleOrErr.takeError();
580 return **TripleOrErr;
585 Error ReadBundleEnd(MemoryBuffer &Input)
final {
return Error::success(); }
587 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
590 return ContentOrErr.takeError();
591 StringRef Content = *ContentOrErr;
594 std::string ModifiedContent;
595 if (Content.size() == 1u && Content.front() == 0) {
596 auto HostBundleOrErr = getHostBundle(
597 StringRef(Input.getBufferStart(), Input.getBufferSize()));
598 if (!HostBundleOrErr)
599 return HostBundleOrErr.takeError();
601 ModifiedContent = std::move(*HostBundleOrErr);
602 Content = ModifiedContent;
605 OS.write(Content.data(), Content.size());
606 return Error::success();
609 Error WriteHeader(raw_ostream &OS,
610 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)
final {
612 "Host input index not defined.");
615 NumberOfInputs = Inputs.size();
616 return Error::success();
619 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple)
final {
620 ++NumberOfProcessedInputs;
621 return Error::success();
624 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple)
final {
625 return Error::success();
628 Error finalizeOutputFile() final {
629 assert(NumberOfProcessedInputs <= NumberOfInputs &&
630 "Processing more inputs that actually exist!");
632 "Host input index not defined.");
635 if (NumberOfProcessedInputs != NumberOfInputs)
636 return Error::success();
644 "llvm-objcopy path not specified");
647 TempFileHandlerRAII TempFiles;
651 BumpPtrAllocator Alloc;
652 StringSaver SS{Alloc};
655 for (
unsigned I = 0; I < NumberOfInputs; ++I) {
664 return TempFileOrErr.takeError();
665 InputFile = *TempFileOrErr;
668 ObjcopyArgs.push_back(
671 ObjcopyArgs.push_back(
673 BundlerConfig.
TargetNames[I] +
"=readonly,exclude"));
675 ObjcopyArgs.push_back(
"--");
676 ObjcopyArgs.push_back(
680 if (Error Err = executeObjcopy(BundlerConfig.
ObjcopyPath, ObjcopyArgs))
683 return Error::success();
686 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
687 return Error::success();
695 errs() <<
"\"" << Objcopy <<
"\"";
696 for (StringRef Arg : drop_begin(Args, 1))
697 errs() <<
" \"" << Arg <<
"\"";
700 if (sys::ExecuteAndWait(Objcopy, Args))
701 return createStringError(inconvertibleErrorCode(),
702 "'llvm-objcopy' tool failed");
704 return Error::success();
708 TempFileHandlerRAII TempFiles;
710 auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
711 if (!ModifiedObjPathOrErr)
712 return ModifiedObjPathOrErr.takeError();
713 StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
715 BumpPtrAllocator Alloc;
716 StringSaver SS{Alloc};
719 ObjcopyArgs.push_back(
"--regex");
720 ObjcopyArgs.push_back(
"--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
721 ObjcopyArgs.push_back(
"--");
723 StringRef ObjcopyInputFileName;
730 if (StringRef(BundlerConfig.
FilesType).starts_with(
"a")) {
731 auto InputFileOrErr =
734 return InputFileOrErr.takeError();
735 ObjcopyInputFileName = *InputFileOrErr;
739 ObjcopyArgs.push_back(ObjcopyInputFileName);
740 ObjcopyArgs.push_back(ModifiedObjPath);
742 if (Error Err = executeObjcopy(BundlerConfig.
ObjcopyPath, ObjcopyArgs))
743 return std::move(Err);
745 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
747 return createStringError(BufOrErr.getError(),
748 "Failed to read back the modified object file");
750 return BufOrErr->get()->getBuffer().str();
763class TextFileHandler final :
public FileHandler {
768 std::string BundleStartString;
771 std::string BundleEndString;
774 size_t ReadChars = 0u;
777 Error ReadHeader(MemoryBuffer &Input)
final {
return Error::success(); }
780 ReadBundleStart(MemoryBuffer &Input)
final {
781 StringRef FC = Input.getBuffer();
784 ReadChars = FC.find(BundleStartString, ReadChars);
785 if (ReadChars == FC.npos)
789 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
792 size_t TripleEnd = ReadChars = FC.find(
"\n", ReadChars);
793 if (TripleEnd == FC.npos)
799 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
802 Error ReadBundleEnd(MemoryBuffer &Input)
final {
803 StringRef FC = Input.getBuffer();
806 assert(FC[ReadChars] ==
'\n' &&
"The bundle should end with a new line.");
808 size_t TripleEnd = ReadChars = FC.find(
"\n", ReadChars + 1);
809 if (TripleEnd != FC.npos)
813 return Error::success();
816 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
817 StringRef FC = Input.getBuffer();
818 size_t BundleStart = ReadChars;
821 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
823 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
826 return Error::success();
829 Error WriteHeader(raw_ostream &OS,
830 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)
final {
831 return Error::success();
834 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple)
final {
835 OS << BundleStartString << TargetTriple <<
"\n";
836 return Error::success();
839 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple)
final {
840 OS << BundleEndString << TargetTriple <<
"\n";
841 return Error::success();
844 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input)
final {
845 OS << Input.getBuffer();
846 return Error::success();
850 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
857 Error listBundleIDsCallback(MemoryBuffer &Input,
858 const BundleInfo &Info)
final {
863 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
864 if (Error Err = ReadBundleEnd(Input))
866 return Error::success();
874static std::unique_ptr<FileHandler>
882 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
883 return std::make_unique<BinaryFileHandler>(BundlerConfig);
887 return std::make_unique<ObjectFileHandler>(
888 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())),
896 std::string FilesType = BundlerConfig.
FilesType;
898 if (FilesType ==
"i")
899 return std::make_unique<TextFileHandler>(
"//");
900 if (FilesType ==
"ii")
901 return std::make_unique<TextFileHandler>(
"//");
902 if (FilesType ==
"cui")
903 return std::make_unique<TextFileHandler>(
"//");
904 if (FilesType ==
"hipi")
905 return std::make_unique<TextFileHandler>(
"//");
908 if (FilesType ==
"d")
909 return std::make_unique<TextFileHandler>(
"#");
910 if (FilesType ==
"ll")
911 return std::make_unique<TextFileHandler>(
";");
912 if (FilesType ==
"bc")
913 return std::make_unique<BinaryFileHandler>(BundlerConfig);
914 if (FilesType ==
"s")
915 return std::make_unique<TextFileHandler>(
"#");
916 if (FilesType ==
"o")
918 if (FilesType ==
"a")
920 if (FilesType ==
"gch")
921 return std::make_unique<BinaryFileHandler>(BundlerConfig);
922 if (FilesType ==
"ast")
923 return std::make_unique<BinaryFileHandler>(BundlerConfig);
925 return createStringError(errc::invalid_argument,
926 "'" + FilesType +
"': invalid file type specified");
930 if (llvm::compression::zstd::isAvailable()) {
935 }
else if (llvm::compression::zlib::isAvailable()) {
941 auto IgnoreEnvVarOpt =
942 llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
943 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() ==
"1")
946 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_VERBOSE");
947 if (VerboseEnvVarOpt.has_value())
948 Verbose = VerboseEnvVarOpt.value() ==
"1";
950 auto CompressEnvVarOpt =
951 llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_COMPRESS");
952 if (CompressEnvVarOpt.has_value())
953 Compress = CompressEnvVarOpt.value() ==
"1";
955 auto CompressionLevelEnvVarOpt =
956 llvm::sys::Process::GetEnv(
"OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
957 if (CompressionLevelEnvVarOpt.has_value()) {
958 llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
960 if (!CompressionLevelStr.getAsInteger(10, Level))
964 <<
"Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
965 << CompressionLevelStr.str() <<
". Ignoring it.\n";
971 std::string Num = std::to_string(
Value);
972 int InsertPosition = Num.length() - 3;
973 while (InsertPosition > 0) {
974 Num.insert(InsertPosition,
",");
982 const llvm::MemoryBuffer &Input,
984 if (!llvm::compression::zstd::isAvailable() &&
985 !llvm::compression::zlib::isAvailable())
986 return createStringError(llvm::inconvertibleErrorCode(),
987 "Compression not supported");
989 llvm::Timer HashTimer(
"Hash Calculation Timer",
"Hash calculation time",
992 HashTimer.startTimer();
994 llvm::MD5::MD5Result
Result;
995 Hash.update(Input.getBuffer());
997 uint64_t TruncatedHash =
Result.low();
999 HashTimer.stopTimer();
1003 reinterpret_cast<const uint8_t *
>(Input.getBuffer().data()),
1004 Input.getBuffer().size());
1006 llvm::Timer CompressTimer(
"Compression Timer",
"Compression time",
1009 CompressTimer.startTimer();
1010 llvm::compression::compress(
P, BufferUint8, CompressedBuffer);
1012 CompressTimer.stopTimer();
1014 uint16_t CompressionMethod =
static_cast<uint16_t
>(
P.format);
1015 uint32_t UncompressedSize = Input.getBuffer().size();
1016 uint32_t TotalFileSize = MagicNumber.size() +
sizeof(TotalFileSize) +
1017 sizeof(Version) +
sizeof(CompressionMethod) +
1018 sizeof(UncompressedSize) +
sizeof(TruncatedHash) +
1019 CompressedBuffer.size();
1022 llvm::raw_svector_ostream OS(FinalBuffer);
1024 OS.write(
reinterpret_cast<const char *
>(&Version),
sizeof(Version));
1025 OS.write(
reinterpret_cast<const char *
>(&CompressionMethod),
1026 sizeof(CompressionMethod));
1027 OS.write(
reinterpret_cast<const char *
>(&TotalFileSize),
1028 sizeof(TotalFileSize));
1029 OS.write(
reinterpret_cast<const char *
>(&UncompressedSize),
1030 sizeof(UncompressedSize));
1031 OS.write(
reinterpret_cast<const char *
>(&TruncatedHash),
1032 sizeof(TruncatedHash));
1033 OS.write(
reinterpret_cast<const char *
>(CompressedBuffer.data()),
1034 CompressedBuffer.size());
1038 P.format == llvm::compression::Format::Zstd ?
"zstd" :
"zlib";
1039 double CompressionRate =
1040 static_cast<double>(UncompressedSize) / CompressedBuffer.size();
1041 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1042 double CompressionSpeedMBs =
1043 (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
1045 llvm::errs() <<
"Compressed bundle format version: " << Version <<
"\n"
1046 <<
"Total file size (including headers): "
1048 <<
"Compression method used: " << MethodUsed <<
"\n"
1049 <<
"Compression level: " <<
P.level <<
"\n"
1050 <<
"Binary size before compression: "
1052 <<
"Binary size after compression: "
1054 <<
"Compression rate: "
1055 << llvm::format(
"%.2lf", CompressionRate) <<
"\n"
1056 <<
"Compression ratio: "
1057 << llvm::format(
"%.2lf%%", 100.0 / CompressionRate) <<
"\n"
1058 <<
"Compression speed: "
1059 << llvm::format(
"%.2lf MB/s", CompressionSpeedMBs) <<
"\n"
1060 <<
"Truncated MD5 hash: "
1061 << llvm::format_hex(TruncatedHash, 16) <<
"\n";
1063 return llvm::MemoryBuffer::getMemBufferCopy(
1064 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1071 StringRef Blob = Input.getBuffer();
1073 if (Blob.size() < V1HeaderSize)
1074 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1076 if (llvm::identify_magic(Blob) !=
1077 llvm::file_magic::offload_bundle_compressed) {
1079 llvm::errs() <<
"Uncompressed bundle.\n";
1080 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1083 size_t CurrentOffset = MagicSize;
1085 uint16_t ThisVersion;
1086 memcpy(&ThisVersion, Blob.data() + CurrentOffset,
sizeof(uint16_t));
1087 CurrentOffset += VersionFieldSize;
1089 uint16_t CompressionMethod;
1090 memcpy(&CompressionMethod, Blob.data() + CurrentOffset,
sizeof(uint16_t));
1091 CurrentOffset += MethodFieldSize;
1093 uint32_t TotalFileSize;
1094 if (ThisVersion >= 2) {
1095 if (Blob.size() < V2HeaderSize)
1096 return createStringError(inconvertibleErrorCode(),
1097 "Compressed bundle header size too small");
1098 memcpy(&TotalFileSize, Blob.data() + CurrentOffset,
sizeof(uint32_t));
1099 CurrentOffset += FileSizeFieldSize;
1102 uint32_t UncompressedSize;
1103 memcpy(&UncompressedSize, Blob.data() + CurrentOffset,
sizeof(uint32_t));
1104 CurrentOffset += UncompressedSizeFieldSize;
1106 uint64_t StoredHash;
1107 memcpy(&StoredHash, Blob.data() + CurrentOffset,
sizeof(uint64_t));
1108 CurrentOffset += HashFieldSize;
1110 llvm::compression::Format CompressionFormat;
1111 if (CompressionMethod ==
1112 static_cast<uint16_t
>(llvm::compression::Format::Zlib))
1113 CompressionFormat = llvm::compression::Format::Zlib;
1114 else if (CompressionMethod ==
1115 static_cast<uint16_t
>(llvm::compression::Format::Zstd))
1116 CompressionFormat = llvm::compression::Format::Zstd;
1118 return createStringError(inconvertibleErrorCode(),
1119 "Unknown compressing method");
1121 llvm::Timer DecompressTimer(
"Decompression Timer",
"Decompression time",
1124 DecompressTimer.startTimer();
1127 StringRef CompressedData = Blob.substr(CurrentOffset);
1128 if (llvm::Error DecompressionError = llvm::compression::decompress(
1129 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
1130 DecompressedData, UncompressedSize))
1131 return createStringError(inconvertibleErrorCode(),
1132 "Could not decompress embedded file contents: " +
1133 llvm::toString(std::move(DecompressionError)));
1136 DecompressTimer.stopTimer();
1138 double DecompressionTimeSeconds =
1139 DecompressTimer.getTotalTime().getWallTime();
1142 llvm::Timer HashRecalcTimer(
"Hash Recalculation Timer",
1143 "Hash recalculation time",
1145 HashRecalcTimer.startTimer();
1147 llvm::MD5::MD5Result
Result;
1149 DecompressedData.size()));
1151 uint64_t RecalculatedHash =
Result.low();
1152 HashRecalcTimer.stopTimer();
1153 bool HashMatch = (StoredHash == RecalculatedHash);
1155 double CompressionRate =
1156 static_cast<double>(UncompressedSize) / CompressedData.size();
1157 double DecompressionSpeedMBs =
1158 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1160 llvm::errs() <<
"Compressed bundle format version: " << ThisVersion <<
"\n";
1161 if (ThisVersion >= 2)
1162 llvm::errs() <<
"Total file size (from header): "
1164 llvm::errs() <<
"Decompression method: "
1165 << (CompressionFormat == llvm::compression::Format::Zlib
1169 <<
"Size before decompression: "
1171 <<
"Size after decompression: "
1173 <<
"Compression rate: "
1174 << llvm::format(
"%.2lf", CompressionRate) <<
"\n"
1175 <<
"Compression ratio: "
1176 << llvm::format(
"%.2lf%%", 100.0 / CompressionRate) <<
"\n"
1177 <<
"Decompression speed: "
1178 << llvm::format(
"%.2lf MB/s", DecompressionSpeedMBs) <<
"\n"
1179 <<
"Stored hash: " << llvm::format_hex(StoredHash, 16) <<
"\n"
1180 <<
"Recalculated hash: "
1181 << llvm::format_hex(RecalculatedHash, 16) <<
"\n"
1182 <<
"Hashes match: " << (HashMatch ?
"Yes" :
"No") <<
"\n";
1185 return llvm::MemoryBuffer::getMemBufferCopy(
1186 llvm::toStringRef(DecompressedData));
1193 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1194 MemoryBuffer::getFileOrSTDIN(InputFileName);
1195 if (std::error_code EC = CodeOrErr.getError())
1196 return createFileError(InputFileName, EC);
1201 if (!DecompressedBufferOrErr)
1202 return createStringError(
1203 inconvertibleErrorCode(),
1204 "Failed to decompress input: " +
1205 llvm::toString(DecompressedBufferOrErr.takeError()));
1207 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1212 if (!FileHandlerOrErr)
1213 return FileHandlerOrErr.takeError();
1215 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1217 return FH->listBundleIDs(DecompressedInput);
1228 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1229 dbgs() <<
"Compatible: Exact match: \t[CodeObject: "
1230 << CodeObjectInfo.
str()
1231 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1239 "CodeObjectCompatibility",
1240 dbgs() <<
"Incompatible: Kind/Triple mismatch \t[CodeObject: "
1241 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1247 llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
1249 CodeObjectInfo.
Triple, CodeObjectInfo.
TargetID, &CodeObjectFeatureMap);
1254 if (!TargetProc || !CodeObjectProc ||
1255 CodeObjectProc.value() != TargetProc.value()) {
1256 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1257 dbgs() <<
"Incompatible: Processor mismatch \t[CodeObject: "
1258 << CodeObjectInfo.
str()
1259 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1265 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1266 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1267 dbgs() <<
"Incompatible: CodeObject has more features "
1268 "than target \t[CodeObject: "
1269 << CodeObjectInfo.
str()
1270 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1278 for (
const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1279 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1280 if (TargetFeature == TargetFeatureMap.end()) {
1282 "CodeObjectCompatibility",
1284 <<
"Incompatible: Value of CodeObject's non-ANY feature is "
1285 "not matching with Target feature's ANY value \t[CodeObject: "
1286 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1289 }
else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1291 "CodeObjectCompatibility",
1292 dbgs() <<
"Incompatible: Value of CodeObject's non-ANY feature is "
1293 "not matching with Target feature's non-ANY value "
1295 << CodeObjectInfo.
str()
1296 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1306 "CodeObjectCompatibility",
1307 dbgs() <<
"Compatible: Target IDs are compatible \t[CodeObject: "
1308 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1319 llvm::raw_svector_ostream BufferStream(Buffer);
1325 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1326 MemoryBuffer::getFileOrSTDIN(I);
1327 if (std::error_code EC = CodeOrErr.getError())
1328 return createFileError(I, EC);
1329 InputBuffers.emplace_back(std::move(*CodeOrErr));
1334 "Host input index undefined??");
1339 if (!FileHandlerOrErr)
1340 return FileHandlerOrErr.takeError();
1342 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1346 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1351 auto Input = InputBuffers.begin();
1353 if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1355 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1357 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1369 std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1370 llvm::MemoryBuffer::getMemBufferCopy(
1371 llvm::StringRef(Buffer.data(), Buffer.size()));
1376 if (
auto Error = CompressionResult.takeError())
1379 auto CompressedMemBuffer = std::move(CompressionResult.get());
1380 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1381 CompressedMemBuffer->getBufferEnd());
1383 CompressedBuffer = Buffer;
1385 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1387 return FH->finalizeOutputFile();
1393 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1395 if (std::error_code EC = CodeOrErr.getError())
1401 if (!DecompressedBufferOrErr)
1402 return createStringError(
1403 inconvertibleErrorCode(),
1404 "Failed to decompress input: " +
1405 llvm::toString(DecompressedBufferOrErr.takeError()));
1407 MemoryBuffer &Input = **DecompressedBufferOrErr;
1412 if (!FileHandlerOrErr)
1413 return FileHandlerOrErr.takeError();
1415 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1419 if (Error Err = FH->ReadHeader(Input))
1423 StringMap<StringRef> Worklist;
1426 Worklist[Triple] = *Output;
1432 bool FoundHostBundle =
false;
1433 while (!Worklist.empty()) {
1435 FH->ReadBundleStart(Input);
1436 if (!CurTripleOrErr)
1437 return CurTripleOrErr.takeError();
1440 if (!*CurTripleOrErr)
1443 StringRef CurTriple = **CurTripleOrErr;
1444 assert(!CurTriple.empty());
1446 auto Output = Worklist.begin();
1447 for (
auto E = Worklist.end(); Output != E; Output++) {
1455 if (Output == Worklist.end())
1459 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1461 return createFileError((*Output).second, EC);
1462 if (Error Err = FH->ReadBundle(OutputFile, Input))
1464 if (Error Err = FH->ReadBundleEnd(Input))
1466 Worklist.erase(Output);
1470 if (OffloadInfo.hasHostKind())
1471 FoundHostBundle =
true;
1475 std::string ErrMsg =
"Can't find bundles for";
1476 std::set<StringRef> Sorted;
1477 for (
auto &E : Worklist)
1478 Sorted.insert(E.first());
1480 unsigned Last = Sorted.size() - 1;
1481 for (
auto &E : Sorted) {
1482 if (I != 0 &&
Last > 1)
1485 if (I ==
Last && I != 0)
1490 return createStringError(inconvertibleErrorCode(), ErrMsg);
1496 for (
auto &E : Worklist) {
1498 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1500 return createFileError(E.second, EC);
1504 if (OffloadInfo.hasHostKind())
1505 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1507 return Error::success();
1514 return createStringError(inconvertibleErrorCode(),
1515 "Can't find bundle for the host target");
1518 for (
auto &E : Worklist) {
1520 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1522 return createFileError(E.second, EC);
1525 return Error::success();
1529 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1543 if (!CompatibleTargets.empty()) {
1544 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1545 dbgs() <<
"CompatibleTargets list should be empty\n");
1551 CompatibleTargets.push_back(
Target);
1553 return !CompatibleTargets.empty();
1563 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1564 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1565 MemoryBuffer::getFileOrSTDIN(ArchiveName,
true,
false);
1566 if (std::error_code EC = BufOrErr.getError())
1567 return createFileError(ArchiveName, EC);
1569 ArchiveBuffers.push_back(std::move(*BufOrErr));
1571 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1573 return LibOrErr.takeError();
1575 auto Archive = std::move(*LibOrErr);
1577 Error ArchiveErr = Error::success();
1578 auto ChildEnd = Archive->child_end();
1581 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1582 ArchiveIter != ChildEnd; ++ArchiveIter) {
1585 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1586 if (!ArchiveChildNameOrErr)
1587 return ArchiveChildNameOrErr.takeError();
1589 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1590 if (!CodeObjectBufferRefOrErr)
1591 return CodeObjectBufferRefOrErr.takeError();
1593 auto CodeObjectBuffer =
1594 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1598 if (!FileHandlerOrErr)
1599 return FileHandlerOrErr.takeError();
1601 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1602 assert(FileHandler);
1604 std::set<StringRef> BundleIds;
1605 auto CodeObjectFileError =
1606 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1607 if (CodeObjectFileError)
1608 return CodeObjectFileError;
1611 if (ConflictingArchs) {
1612 std::string ErrMsg =
1613 Twine(
"conflicting TargetIDs [" + ConflictingArchs.value().first +
1614 ", " + ConflictingArchs.value().second +
"] found in " +
1615 ArchiveChildNameOrErr.get() +
" of " + ArchiveName)
1617 return createStringError(inconvertibleErrorCode(), ErrMsg);
1632 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1636 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1639 StringMap<StringRef> TargetOutputFileNameMap;
1643 TargetOutputFileNameMap[
Target] = *Output;
1655 return ArchiveError;
1659 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1660 MemoryBuffer::getFileOrSTDIN(IFName,
true,
false);
1661 if (std::error_code EC = BufOrErr.getError())
1664 ArchiveBuffers.push_back(std::move(*BufOrErr));
1666 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1668 return LibOrErr.takeError();
1670 auto Archive = std::move(*LibOrErr);
1672 Error ArchiveErr = Error::success();
1673 auto ChildEnd = Archive->child_end();
1676 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1677 ArchiveIter != ChildEnd; ++ArchiveIter) {
1680 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1681 if (!ArchiveChildNameOrErr)
1682 return ArchiveChildNameOrErr.takeError();
1684 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1686 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1687 if (!CodeObjectBufferRefOrErr)
1688 return CodeObjectBufferRefOrErr.takeError();
1690 auto TempCodeObjectBuffer =
1691 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1697 if (!DecompressedBufferOrErr)
1698 return createStringError(
1699 inconvertibleErrorCode(),
1700 "Failed to decompress code object: " +
1701 llvm::toString(DecompressedBufferOrErr.takeError()));
1703 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1707 if (!FileHandlerOrErr)
1708 return FileHandlerOrErr.takeError();
1710 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1711 assert(FileHandler &&
1712 "FileHandle creation failed for file in the archive!");
1714 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1718 FileHandler->ReadBundleStart(CodeObjectBuffer);
1719 if (!CurBundleIDOrErr)
1720 return CurBundleIDOrErr.takeError();
1722 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1724 if (!OptionalCurBundleID)
1726 StringRef CodeObject = *OptionalCurBundleID;
1730 while (!CodeObject.empty()) {
1735 std::string BundleData;
1736 raw_string_ostream DataStream(BundleData);
1737 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1740 for (
auto &CompatibleTarget : CompatibleTargets) {
1742 BundledObjectFileName.assign(BundledObjectFile);
1743 auto OutputBundleName =
1744 Twine(llvm::sys::path::stem(BundledObjectFileName) +
"-" +
1747 CodeObjectInfo.TargetID))
1751 std::replace(OutputBundleName.begin(), OutputBundleName.end(),
':',
1754 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1755 DataStream.str(), OutputBundleName);
1756 ArchiveBuffers.push_back(std::move(MemBuf));
1757 llvm::MemoryBufferRef MemBufRef =
1758 MemoryBufferRef(*(ArchiveBuffers.back()));
1762 if (!OutputArchivesMap.contains(CompatibleTarget)) {
1764 std::vector<NewArchiveMember> ArchiveMembers;
1765 ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
1766 OutputArchivesMap.insert_or_assign(CompatibleTarget,
1767 std::move(ArchiveMembers));
1769 OutputArchivesMap[CompatibleTarget].push_back(
1770 NewArchiveMember(MemBufRef));
1775 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1779 FileHandler->ReadBundleStart(CodeObjectBuffer);
1780 if (!NextTripleOrErr)
1781 return NextTripleOrErr.takeError();
1783 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr :
"";
1787 assert(!ArchiveErr &&
"Error occurred while reading archive!");
1792 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1793 OutputArchivesMap.find(
Target);
1794 if (CurArchiveMembers != OutputArchivesMap.end()) {
1795 if (Error WriteErr = writeArchive(
FileName, CurArchiveMembers->getValue(),
1796 SymtabWritingMode::NormalSymtab,
1801 std::string ErrMsg =
1802 Twine(
"no compatible code object found for the target '" +
Target +
1803 "' in heterogeneous archive library: " + IFName)
1805 return createStringError(inconvertibleErrorCode(), ErrMsg);
1810 std::vector<llvm::NewArchiveMember> EmptyArchive;
1811 EmptyArchive.clear();
1812 if (Error WriteErr = writeArchive(
1813 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1819 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