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();
1015 llvm::raw_svector_ostream OS(FinalBuffer);
1017 OS.write(
reinterpret_cast<const char *
>(&Version),
sizeof(Version));
1018 OS.write(
reinterpret_cast<const char *
>(&CompressionMethod),
1019 sizeof(CompressionMethod));
1020 OS.write(
reinterpret_cast<const char *
>(&UncompressedSize),
1021 sizeof(UncompressedSize));
1022 OS.write(
reinterpret_cast<const char *
>(&TruncatedHash),
1023 sizeof(TruncatedHash));
1024 OS.write(
reinterpret_cast<const char *
>(CompressedBuffer.data()),
1025 CompressedBuffer.size());
1029 P.format == llvm::compression::Format::Zstd ?
"zstd" :
"zlib";
1030 double CompressionRate =
1031 static_cast<double>(UncompressedSize) / CompressedBuffer.size();
1032 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1033 double CompressionSpeedMBs =
1034 (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
1036 llvm::errs() <<
"Compressed bundle format version: " << Version <<
"\n"
1037 <<
"Compression method used: " << MethodUsed <<
"\n"
1038 <<
"Compression level: " <<
P.level <<
"\n"
1039 <<
"Binary size before compression: "
1041 <<
"Binary size after compression: "
1043 <<
"Compression rate: "
1044 << llvm::format(
"%.2lf", CompressionRate) <<
"\n"
1045 <<
"Compression ratio: "
1046 << llvm::format(
"%.2lf%%", 100.0 / CompressionRate) <<
"\n"
1047 <<
"Compression speed: "
1048 << llvm::format(
"%.2lf MB/s", CompressionSpeedMBs) <<
"\n"
1049 <<
"Truncated MD5 hash: "
1050 << llvm::format_hex(TruncatedHash, 16) <<
"\n";
1052 return llvm::MemoryBuffer::getMemBufferCopy(
1053 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1060 StringRef Blob = Input.getBuffer();
1062 if (Blob.size() < HeaderSize) {
1063 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1065 if (llvm::identify_magic(Blob) !=
1066 llvm::file_magic::offload_bundle_compressed) {
1068 llvm::errs() <<
"Uncompressed bundle.\n";
1069 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1072 uint16_t ThisVersion;
1073 uint16_t CompressionMethod;
1074 uint32_t UncompressedSize;
1075 uint64_t StoredHash;
1076 memcpy(&ThisVersion, Input.getBuffer().data() + MagicNumber.size(),
1078 memcpy(&CompressionMethod, Blob.data() + MagicSize + VersionFieldSize,
1080 memcpy(&UncompressedSize,
1081 Blob.data() + MagicSize + VersionFieldSize + MethodFieldSize,
1084 Blob.data() + MagicSize + VersionFieldSize + MethodFieldSize +
1088 llvm::compression::Format CompressionFormat;
1089 if (CompressionMethod ==
1090 static_cast<uint16_t
>(llvm::compression::Format::Zlib))
1091 CompressionFormat = llvm::compression::Format::Zlib;
1092 else if (CompressionMethod ==
1093 static_cast<uint16_t
>(llvm::compression::Format::Zstd))
1094 CompressionFormat = llvm::compression::Format::Zstd;
1096 return createStringError(inconvertibleErrorCode(),
1097 "Unknown compressing method");
1099 llvm::Timer DecompressTimer(
"Decompression Timer",
"Decompression time",
1102 DecompressTimer.startTimer();
1105 StringRef CompressedData = Blob.substr(HeaderSize);
1106 if (llvm::Error DecompressionError = llvm::compression::decompress(
1107 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
1108 DecompressedData, UncompressedSize))
1109 return createStringError(inconvertibleErrorCode(),
1110 "Could not decompress embedded file contents: " +
1111 llvm::toString(std::move(DecompressionError)));
1114 DecompressTimer.stopTimer();
1116 double DecompressionTimeSeconds =
1117 DecompressTimer.getTotalTime().getWallTime();
1120 llvm::Timer HashRecalcTimer(
"Hash Recalculation Timer",
1121 "Hash recalculation time",
1123 HashRecalcTimer.startTimer();
1125 llvm::MD5::MD5Result
Result;
1127 DecompressedData.size()));
1129 uint64_t RecalculatedHash =
Result.low();
1130 HashRecalcTimer.stopTimer();
1131 bool HashMatch = (StoredHash == RecalculatedHash);
1133 double CompressionRate =
1134 static_cast<double>(UncompressedSize) / CompressedData.size();
1135 double DecompressionSpeedMBs =
1136 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1138 llvm::errs() <<
"Compressed bundle format version: " << ThisVersion <<
"\n"
1139 <<
"Decompression method: "
1140 << (CompressionFormat == llvm::compression::Format::Zlib
1144 <<
"Size before decompression: "
1146 <<
"Size after decompression: "
1148 <<
"Compression rate: "
1149 << llvm::format(
"%.2lf", CompressionRate) <<
"\n"
1150 <<
"Compression ratio: "
1151 << llvm::format(
"%.2lf%%", 100.0 / CompressionRate) <<
"\n"
1152 <<
"Decompression speed: "
1153 << llvm::format(
"%.2lf MB/s", DecompressionSpeedMBs) <<
"\n"
1154 <<
"Stored hash: " << llvm::format_hex(StoredHash, 16) <<
"\n"
1155 <<
"Recalculated hash: "
1156 << llvm::format_hex(RecalculatedHash, 16) <<
"\n"
1157 <<
"Hashes match: " << (HashMatch ?
"Yes" :
"No") <<
"\n";
1160 return llvm::MemoryBuffer::getMemBufferCopy(
1161 llvm::toStringRef(DecompressedData));
1168 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1169 MemoryBuffer::getFileOrSTDIN(InputFileName);
1170 if (std::error_code EC = CodeOrErr.getError())
1171 return createFileError(InputFileName, EC);
1176 if (!DecompressedBufferOrErr)
1177 return createStringError(
1178 inconvertibleErrorCode(),
1179 "Failed to decompress input: " +
1180 llvm::toString(DecompressedBufferOrErr.takeError()));
1182 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1187 if (!FileHandlerOrErr)
1188 return FileHandlerOrErr.takeError();
1190 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1192 return FH->listBundleIDs(DecompressedInput);
1203 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1204 dbgs() <<
"Compatible: Exact match: \t[CodeObject: "
1205 << CodeObjectInfo.
str()
1206 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1214 "CodeObjectCompatibility",
1215 dbgs() <<
"Incompatible: Kind/Triple mismatch \t[CodeObject: "
1216 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1222 llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap;
1224 CodeObjectInfo.
Triple, CodeObjectInfo.
TargetID, &CodeObjectFeatureMap);
1229 if (!TargetProc || !CodeObjectProc ||
1230 CodeObjectProc.value() != TargetProc.value()) {
1231 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1232 dbgs() <<
"Incompatible: Processor mismatch \t[CodeObject: "
1233 << CodeObjectInfo.
str()
1234 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1240 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1241 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1242 dbgs() <<
"Incompatible: CodeObject has more features "
1243 "than target \t[CodeObject: "
1244 << CodeObjectInfo.
str()
1245 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1253 for (
const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1254 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1255 if (TargetFeature == TargetFeatureMap.end()) {
1257 "CodeObjectCompatibility",
1259 <<
"Incompatible: Value of CodeObject's non-ANY feature is "
1260 "not matching with Target feature's ANY value \t[CodeObject: "
1261 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1264 }
else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1266 "CodeObjectCompatibility",
1267 dbgs() <<
"Incompatible: Value of CodeObject's non-ANY feature is "
1268 "not matching with Target feature's non-ANY value "
1270 << CodeObjectInfo.
str()
1271 <<
"]\t:\t[Target: " <<
TargetInfo.str() <<
"]\n");
1281 "CodeObjectCompatibility",
1282 dbgs() <<
"Compatible: Target IDs are compatible \t[CodeObject: "
1283 << CodeObjectInfo.
str() <<
"]\t:\t[Target: " <<
TargetInfo.str()
1294 llvm::raw_svector_ostream BufferStream(Buffer);
1300 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1301 MemoryBuffer::getFileOrSTDIN(I);
1302 if (std::error_code EC = CodeOrErr.getError())
1303 return createFileError(I, EC);
1304 InputBuffers.emplace_back(std::move(*CodeOrErr));
1309 "Host input index undefined??");
1314 if (!FileHandlerOrErr)
1315 return FileHandlerOrErr.takeError();
1317 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1321 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1326 auto Input = InputBuffers.begin();
1328 if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1330 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1332 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1344 std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1345 llvm::MemoryBuffer::getMemBufferCopy(
1346 llvm::StringRef(Buffer.data(), Buffer.size()));
1351 if (
auto Error = CompressionResult.takeError())
1354 auto CompressedMemBuffer = std::move(CompressionResult.get());
1355 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1356 CompressedMemBuffer->getBufferEnd());
1358 CompressedBuffer = Buffer;
1360 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1362 return FH->finalizeOutputFile();
1368 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
1370 if (std::error_code EC = CodeOrErr.getError())
1376 if (!DecompressedBufferOrErr)
1377 return createStringError(
1378 inconvertibleErrorCode(),
1379 "Failed to decompress input: " +
1380 llvm::toString(DecompressedBufferOrErr.takeError()));
1382 MemoryBuffer &Input = **DecompressedBufferOrErr;
1387 if (!FileHandlerOrErr)
1388 return FileHandlerOrErr.takeError();
1390 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
1394 if (Error Err = FH->ReadHeader(Input))
1398 StringMap<StringRef> Worklist;
1401 Worklist[Triple] = *Output;
1407 bool FoundHostBundle =
false;
1408 while (!Worklist.empty()) {
1410 FH->ReadBundleStart(Input);
1411 if (!CurTripleOrErr)
1412 return CurTripleOrErr.takeError();
1415 if (!*CurTripleOrErr)
1418 StringRef CurTriple = **CurTripleOrErr;
1419 assert(!CurTriple.empty());
1421 auto Output = Worklist.begin();
1422 for (
auto E = Worklist.end(); Output != E; Output++) {
1430 if (Output == Worklist.end())
1434 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1436 return createFileError((*Output).second, EC);
1437 if (Error Err = FH->ReadBundle(OutputFile, Input))
1439 if (Error Err = FH->ReadBundleEnd(Input))
1441 Worklist.erase(Output);
1445 if (OffloadInfo.hasHostKind())
1446 FoundHostBundle =
true;
1450 std::string ErrMsg =
"Can't find bundles for";
1451 std::set<StringRef> Sorted;
1452 for (
auto &E : Worklist)
1453 Sorted.insert(E.first());
1455 unsigned Last = Sorted.size() - 1;
1456 for (
auto &E : Sorted) {
1457 if (I != 0 &&
Last > 1)
1460 if (I ==
Last && I != 0)
1465 return createStringError(inconvertibleErrorCode(), ErrMsg);
1471 for (
auto &E : Worklist) {
1473 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1475 return createFileError(E.second, EC);
1479 if (OffloadInfo.hasHostKind())
1480 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1482 return Error::success();
1489 return createStringError(inconvertibleErrorCode(),
1490 "Can't find bundle for the host target");
1493 for (
auto &E : Worklist) {
1495 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1497 return createFileError(E.second, EC);
1500 return Error::success();
1504 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1518 if (!CompatibleTargets.empty()) {
1519 DEBUG_WITH_TYPE(
"CodeObjectCompatibility",
1520 dbgs() <<
"CompatibleTargets list should be empty\n");
1526 CompatibleTargets.push_back(
Target);
1528 return !CompatibleTargets.empty();
1538 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1539 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1540 MemoryBuffer::getFileOrSTDIN(ArchiveName,
true,
false);
1541 if (std::error_code EC = BufOrErr.getError())
1542 return createFileError(ArchiveName, EC);
1544 ArchiveBuffers.push_back(std::move(*BufOrErr));
1546 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1548 return LibOrErr.takeError();
1550 auto Archive = std::move(*LibOrErr);
1552 Error ArchiveErr = Error::success();
1553 auto ChildEnd = Archive->child_end();
1556 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1557 ArchiveIter != ChildEnd; ++ArchiveIter) {
1560 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1561 if (!ArchiveChildNameOrErr)
1562 return ArchiveChildNameOrErr.takeError();
1564 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1565 if (!CodeObjectBufferRefOrErr)
1566 return CodeObjectBufferRefOrErr.takeError();
1568 auto CodeObjectBuffer =
1569 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1573 if (!FileHandlerOrErr)
1574 return FileHandlerOrErr.takeError();
1576 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1577 assert(FileHandler);
1579 std::set<StringRef> BundleIds;
1580 auto CodeObjectFileError =
1581 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1582 if (CodeObjectFileError)
1583 return CodeObjectFileError;
1586 if (ConflictingArchs) {
1587 std::string ErrMsg =
1588 Twine(
"conflicting TargetIDs [" + ConflictingArchs.value().first +
1589 ", " + ConflictingArchs.value().second +
"] found in " +
1590 ArchiveChildNameOrErr.get() +
" of " + ArchiveName)
1592 return createStringError(inconvertibleErrorCode(), ErrMsg);
1607 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
1611 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap;
1614 StringMap<StringRef> TargetOutputFileNameMap;
1618 TargetOutputFileNameMap[
Target] = *Output;
1630 return ArchiveError;
1634 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
1635 MemoryBuffer::getFileOrSTDIN(IFName,
true,
false);
1636 if (std::error_code EC = BufOrErr.getError())
1639 ArchiveBuffers.push_back(std::move(*BufOrErr));
1641 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1643 return LibOrErr.takeError();
1645 auto Archive = std::move(*LibOrErr);
1647 Error ArchiveErr = Error::success();
1648 auto ChildEnd = Archive->child_end();
1651 for (
auto ArchiveIter = Archive->child_begin(ArchiveErr);
1652 ArchiveIter != ChildEnd; ++ArchiveIter) {
1655 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1656 if (!ArchiveChildNameOrErr)
1657 return ArchiveChildNameOrErr.takeError();
1659 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1661 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1662 if (!CodeObjectBufferRefOrErr)
1663 return CodeObjectBufferRefOrErr.takeError();
1665 auto TempCodeObjectBuffer =
1666 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr,
false);
1672 if (!DecompressedBufferOrErr)
1673 return createStringError(
1674 inconvertibleErrorCode(),
1675 "Failed to decompress code object: " +
1676 llvm::toString(DecompressedBufferOrErr.takeError()));
1678 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1682 if (!FileHandlerOrErr)
1683 return FileHandlerOrErr.takeError();
1685 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr;
1686 assert(FileHandler &&
1687 "FileHandle creation failed for file in the archive!");
1689 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1693 FileHandler->ReadBundleStart(CodeObjectBuffer);
1694 if (!CurBundleIDOrErr)
1695 return CurBundleIDOrErr.takeError();
1697 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr;
1699 if (!OptionalCurBundleID)
1701 StringRef CodeObject = *OptionalCurBundleID;
1705 while (!CodeObject.empty()) {
1710 std::string BundleData;
1711 raw_string_ostream DataStream(BundleData);
1712 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1715 for (
auto &CompatibleTarget : CompatibleTargets) {
1717 BundledObjectFileName.assign(BundledObjectFile);
1718 auto OutputBundleName =
1719 Twine(llvm::sys::path::stem(BundledObjectFileName) +
"-" +
1722 CodeObjectInfo.TargetID))
1726 std::replace(OutputBundleName.begin(), OutputBundleName.end(),
':',
1729 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(
1730 DataStream.str(), OutputBundleName);
1731 ArchiveBuffers.push_back(std::move(MemBuf));
1732 llvm::MemoryBufferRef MemBufRef =
1733 MemoryBufferRef(*(ArchiveBuffers.back()));
1737 if (!OutputArchivesMap.contains(CompatibleTarget)) {
1739 std::vector<NewArchiveMember> ArchiveMembers;
1740 ArchiveMembers.push_back(NewArchiveMember(MemBufRef));
1741 OutputArchivesMap.insert_or_assign(CompatibleTarget,
1742 std::move(ArchiveMembers));
1744 OutputArchivesMap[CompatibleTarget].push_back(
1745 NewArchiveMember(MemBufRef));
1750 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1754 FileHandler->ReadBundleStart(CodeObjectBuffer);
1755 if (!NextTripleOrErr)
1756 return NextTripleOrErr.takeError();
1758 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr :
"";
1762 assert(!ArchiveErr &&
"Error occurred while reading archive!");
1767 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
1768 OutputArchivesMap.find(
Target);
1769 if (CurArchiveMembers != OutputArchivesMap.end()) {
1770 if (Error WriteErr = writeArchive(
FileName, CurArchiveMembers->getValue(),
1771 SymtabWritingMode::NormalSymtab,
1776 std::string ErrMsg =
1777 Twine(
"no compatible code object found for the target '" +
Target +
1778 "' in heterogeneous archive library: " + IFName)
1780 return createStringError(inconvertibleErrorCode(), ErrMsg);
1785 std::vector<llvm::NewArchiveMember> EmptyArchive;
1786 EmptyArchive.clear();
1787 if (Error WriteErr = writeArchive(
1788 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1794 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
'copyin' clause, allowed on Compute and Combined constructs, plus 'data', 'enter data',...
@ Device
'device' clause, allowed on the 'update' construct.
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.
YAML serialization mapping.
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