20#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/ADT/StringMap.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/BinaryFormat/Magic.h"
27#include "llvm/Object/Archive.h"
28#include "llvm/Object/ArchiveWriter.h"
29#include "llvm/Object/Binary.h"
30#include "llvm/Object/ObjectFile.h"
31#include "llvm/Support/Casting.h"
32#include "llvm/Support/Compression.h"
33#include "llvm/Support/Debug.h"
34#include "llvm/Support/EndianStream.h"
35#include "llvm/Support/Errc.h"
36#include "llvm/Support/Error.h"
37#include "llvm/Support/ErrorOr.h"
38#include "llvm/Support/FileSystem.h"
39#include "llvm/Support/MD5.h"
40#include "llvm/Support/MemoryBuffer.h"
41#include "llvm/Support/Path.h"
42#include "llvm/Support/Program.h"
43#include "llvm/Support/Signals.h"
44#include "llvm/Support/StringSaver.h"
45#include "llvm/Support/Timer.h"
46#include "llvm/Support/WithColor.h"
47#include "llvm/Support/raw_ostream.h"
48#include "llvm/TargetParser/Host.h"
49#include "llvm/TargetParser/Triple.h"
54#include <forward_list>
55#include <llvm/Support/Process.h>
59#include <system_error>
63using namespace llvm::object;
66static llvm::TimerGroup
68 "Timer group for clang offload bundler");
71#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
78 auto TargetFeatures =
Target.split(
':');
79 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,
true);
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,
true);
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 =
1396 if (std::error_code EC = CodeOrErr.getError())
1402 if (!DecompressedBufferOrErr)
1403 return createStringError(
1404 inconvertibleErrorCode(),
1405 "Failed to decompress input: " +
1406 llvm::toString(DecompressedBufferOrErr.takeError()));
1408 MemoryBuffer &Input = **DecompressedBufferOrErr;
1413 if (!FileHandlerOrErr)
1414 return FileHandlerOrErr.takeError();
1416 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1420 if (Error Err = FH->ReadHeader(Input))
1424 StringMap<StringRef> Worklist;
1427 Worklist[Triple] = *Output;
1433 bool FoundHostBundle =
false;
1434 while (!Worklist.empty()) {
1436 FH->ReadBundleStart(Input);
1437 if (!CurTripleOrErr)
1438 return CurTripleOrErr.takeError();
1441 if (!*CurTripleOrErr)
1444 StringRef CurTriple = **CurTripleOrErr;
1445 assert(!CurTriple.empty());
1447 auto Output = Worklist.begin();
1448 for (
auto E = Worklist.end(); Output !=
E; Output++) {
1456 if (Output == Worklist.end())
1460 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1462 return createFileError((*Output).second, EC);
1463 if (Error Err = FH->ReadBundle(OutputFile, Input))
1465 if (Error Err = FH->ReadBundleEnd(Input))
1467 Worklist.erase(Output);
1471 if (OffloadInfo.hasHostKind())
1472 FoundHostBundle =
true;
1476 std::string ErrMsg =
"Can't find bundles for";
1477 std::set<StringRef> Sorted;
1478 for (
auto &
E : Worklist)
1479 Sorted.insert(
E.first());
1481 unsigned Last = Sorted.size() - 1;
1482 for (
auto &
E : Sorted) {
1483 if (I != 0 &&
Last > 1)
1486 if (I ==
Last && I != 0)
1491 return createStringError(inconvertibleErrorCode(), ErrMsg);
1497 for (
auto &
E : Worklist) {
1499 raw_fd_ostream OutputFile(
E.second, EC, sys::fs::OF_None);
1501 return createFileError(
E.second, EC);
1505 if (OffloadInfo.hasHostKind())
1506 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1508 return Error::success();
1515 return createStringError(inconvertibleErrorCode(),
1516 "Can't find bundle for the host target");
1519 for (
auto &
E : Worklist) {
1521 raw_fd_ostream OutputFile(
E.second, EC, sys::fs::OF_None);
1523 return createFileError(
E.second, EC);
1526 return Error::success();
1530 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1544 if (!CompatibleTargets.empty()) {
1545 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1546 dbgs() <<
"CompatibleTargets list should be empty\n");
1552 CompatibleTargets.push_back(
Target);
1554 return !CompatibleTargets.empty();
1564 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1565 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1566 MemoryBuffer::getFileOrSTDIN(ArchiveName,
true,
false);
1567 if (std::error_code EC = BufOrErr.getError())
1568 return createFileError(ArchiveName, EC);
1570 ArchiveBuffers.push_back(std::move(*BufOrErr));
1572 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1574 return LibOrErr.takeError();
1576 auto Archive = std::move(*LibOrErr);
1578 Error ArchiveErr = Error::success();
1579 auto ChildEnd = Archive->child_end();
1582 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1583 ArchiveIter != ChildEnd; ++ArchiveIter) {
1586 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1587 if (!ArchiveChildNameOrErr)
1588 return ArchiveChildNameOrErr.takeError();
1590 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1591 if (!CodeObjectBufferRefOrErr)
1592 return CodeObjectBufferRefOrErr.takeError();
1594 auto CodeObjectBuffer =
1595 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1599 if (!FileHandlerOrErr)
1600 return FileHandlerOrErr.takeError();
1602 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1603 assert(FileHandler);
1605 std::set<StringRef> BundleIds;
1606 auto CodeObjectFileError =
1607 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1608 if (CodeObjectFileError)
1609 return CodeObjectFileError;
1612 if (ConflictingArchs) {
1613 std::string ErrMsg =
1614 Twine(
"conflicting TargetIDs [" + ConflictingArchs.value().first +
1615 ", " + ConflictingArchs.value().second +
"] found in " +
1616 ArchiveChildNameOrErr.get() +
" of " + ArchiveName)
1618 return createStringError(inconvertibleErrorCode(), ErrMsg);
1633 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1637 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1640 StringMap<StringRef> TargetOutputFileNameMap;
1644 TargetOutputFileNameMap[
Target] = *Output;
1656 return ArchiveError;
1660 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1661 MemoryBuffer::getFileOrSTDIN(IFName,
true,
false);
1662 if (std::error_code EC = BufOrErr.getError())
1665 ArchiveBuffers.push_back(std::move(*BufOrErr));
1667 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1669 return LibOrErr.takeError();
1671 auto Archive = std::move(*LibOrErr);
1673 Error ArchiveErr = Error::success();
1674 auto ChildEnd = Archive->child_end();
1677 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1678 ArchiveIter != ChildEnd; ++ArchiveIter) {
1681 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1682 if (!ArchiveChildNameOrErr)
1683 return ArchiveChildNameOrErr.takeError();
1685 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1687 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1688 if (!CodeObjectBufferRefOrErr)
1689 return CodeObjectBufferRefOrErr.takeError();
1691 auto TempCodeObjectBuffer =
1692 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1698 if (!DecompressedBufferOrErr)
1699 return createStringError(
1700 inconvertibleErrorCode(),
1701 "Failed to decompress code object: " +
1702 llvm::toString(DecompressedBufferOrErr.takeError()));
1704 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1708 if (!FileHandlerOrErr)
1709 return FileHandlerOrErr.takeError();
1711 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1712 assert(FileHandler &&
1713 "FileHandle creation failed for file in the archive!");
1715 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1719 FileHandler->ReadBundleStart(CodeObjectBuffer);
1720 if (!CurBundleIDOrErr)
1721 return CurBundleIDOrErr.takeError();
1723 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1725 if (!OptionalCurBundleID)
1727 StringRef CodeObject = *OptionalCurBundleID;
1731 while (!CodeObject.empty()) {
1736 std::string BundleData;
1737 raw_string_ostream DataStream(BundleData);
1738 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1741 for (
auto &CompatibleTarget : CompatibleTargets) {
1743 BundledObjectFileName.assign(BundledObjectFile);
1744 auto OutputBundleName =
1745 Twine(llvm::sys::path::stem(BundledObjectFileName) +
"-" +
1748 CodeObjectInfo.TargetID))
1752 std::replace(OutputBundleName.begin(), OutputBundleName.end(),
':',
1755 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1756 DataStream.str(), OutputBundleName);
1757 ArchiveBuffers.push_back(std::move(MemBuf));
1758 llvm::MemoryBufferRef MemBufRef =
1759 MemoryBufferRef(*(ArchiveBuffers.back()));
1763 OutputArchivesMap[CompatibleTarget].push_back(
1764 NewArchiveMember(MemBufRef));
1768 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1772 FileHandler->ReadBundleStart(CodeObjectBuffer);
1773 if (!NextTripleOrErr)
1774 return NextTripleOrErr.takeError();
1776 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr :
"";
1780 assert(!ArchiveErr &&
"Error occurred while reading archive!");
1785 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1786 OutputArchivesMap.find(
Target);
1787 if (CurArchiveMembers != OutputArchivesMap.end()) {
1788 if (Error WriteErr = writeArchive(
FileName, CurArchiveMembers->getValue(),
1789 SymtabWritingMode::NormalSymtab,
1794 std::string ErrMsg =
1795 Twine(
"no compatible code object found for the target '" +
Target +
1796 "' in heterogeneous archive library: " + IFName)
1798 return createStringError(inconvertibleErrorCode(), ErrMsg);
1803 std::vector<llvm::NewArchiveMember> EmptyArchive;
1804 EmptyArchive.clear();
1805 if (Error WriteErr = writeArchive(
1806 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1812 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...
__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.
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
The JSON file list parser is used to communicate input to InstallAPI.
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.
@ Create
'create' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
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 ...
@ Result
The result type of a method or function.
OffloadArch StringToOffloadArch(llvm::StringRef S)
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