clang: lib/Driver/OffloadBundler.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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/ManagedStatic.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"
51#include
52#include
53#include
54#include
55#include <forward_list>
56#include <llvm/Support/Process.h>
57#include
58#include
59#include
60#include <system_error>
61#include
62
63using namespace llvm;
64using namespace llvm::object;
65using namespace clang;
66
67namespace {
68struct CreateClangOffloadBundlerTimerGroup {
69 static void *call() {
70 return new TimerGroup("Clang Offload Bundler Timer Group",
71 "Timer group for clang offload bundler");
72 }
73};
74}
75static llvm::ManagedStatic<llvm::TimerGroup,
76 CreateClangOffloadBundlerTimerGroup>
78
79
80#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
81
84 : BundlerConfig(BC) {
85
86
87 auto TargetFeatures = Target.split(':');
88 auto TripleOrGPU = TargetFeatures.first.rsplit('-');
89
92 auto KindTriple = TripleOrGPU.first.split('-');
94
95
96 llvm::Triple t = llvm::Triple(KindTriple.second);
97 this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(),
98 t.getOSName(), t.getEnvironmentName());
99
100 this->TargetID = Target.substr(Target.find(TripleOrGPU.second));
101 } else {
102 auto KindTriple = TargetFeatures.first.split('-');
104
105
106 llvm::Triple t = llvm::Triple(KindTriple.second);
107 this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(),
108 t.getOSName(), t.getEnvironmentName());
109
111 }
112}
113
116}
117
121}
122
124 const StringRef TargetOffloadKind) const {
125 if ((OffloadKind == TargetOffloadKind) ||
126 (OffloadKind == "hip" && TargetOffloadKind == "hipv4") ||
127 (OffloadKind == "hipv4" && TargetOffloadKind == "hip"))
128 return true;
129
131 bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") &&
132 TargetOffloadKind == "openmp";
133 bool OpenMPCompatibleWithHIP =
135 TargetOffloadKind.starts_with_insensitive("hip");
136 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP;
137 }
138 return false;
139}
140
142 return .str().empty() && Triple.getArch() != Triple::UnknownArch;
143}
144
148}
149
152}
153
155 StringRef BundleFileName) {
156 if (Device.contains("gfx"))
157 return ".bc";
158 if (Device.contains("sm_"))
159 return ".cubin";
160 return sys::path::extension(BundleFileName);
161}
162
165 StringRef LibName = sys::path::stem(BundleFileName);
167
172}
173
174namespace {
175
176class FileHandler {
177public:
178 struct BundleInfo {
179 StringRef BundleID;
180 };
181
182 FileHandler() {}
183
184 virtual ~FileHandler() {}
185
186
187
188 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
189
190
191
192
194 ReadBundleStart(MemoryBuffer &Input) = 0;
195
196
197 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
198
199
200 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
201
202
203
204 virtual Error WriteHeader(raw_ostream &OS,
205 ArrayRef<std::unique_ptr> Inputs) = 0;
206
207
208
209 virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0;
210
211
212
213 virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0;
214
215
216 virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
217
218
219 virtual Error finalizeOutputFile() { return Error::success(); }
220
221
222 virtual Error listBundleIDs(MemoryBuffer &Input) {
223 if (Error Err = ReadHeader(Input))
224 return Err;
225 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
226 llvm::outs() << Info.BundleID << '\n';
227 Error Err = listBundleIDsCallback(Input, Info);
228 if (Err)
229 return Err;
230 return Error::success();
231 });
232 }
233
234
235 virtual Error getBundleIDs(MemoryBuffer &Input,
236 std::set &BundleIds) {
237 if (Error Err = ReadHeader(Input))
238 return Err;
239 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
240 BundleIds.insert(Info.BundleID);
241 Error Err = listBundleIDsCallback(Input, Info);
242 if (Err)
243 return Err;
244 return Error::success();
245 });
246 }
247
248
249 Error forEachBundle(MemoryBuffer &Input,
250 std::function<Error(const BundleInfo &)> Func) {
251 while (true) {
253 ReadBundleStart(Input);
254 if (!CurTripleOrErr)
255 return CurTripleOrErr.takeError();
256
257
258 if (!*CurTripleOrErr)
259 break;
260
261 StringRef CurTriple = **CurTripleOrErr;
262 assert(!CurTriple.empty());
263
264 BundleInfo Info{CurTriple};
265 if (Error Err = Func(Info))
266 return Err;
267 }
268 return Error::success();
269 }
270
271protected:
272 virtual Error listBundleIDsCallback(MemoryBuffer &Input,
273 const BundleInfo &Info) {
274 return Error::success();
275 }
276};
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
303 return llvm::support::endian::read64le(Buffer.data() + pos);
304}
305
306
307static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) {
308 llvm::support::endian::write(OS, Val, llvm::endianness::little);
309}
310
311class BinaryFileHandler final : public FileHandler {
312
313 struct BinaryBundleInfo final : public BundleInfo {
314
316
318
319 BinaryBundleInfo() {}
320 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
321 : Size(Size), Offset(Offset) {}
322 };
323
324
325 StringMap BundlesInfo;
326
327
328 StringMap::iterator CurBundleInfo;
329 StringMap::iterator NextBundleInfo;
330
331
332 std::string CurWriteBundleTarget;
333
334
336
337public:
338
340
341 ~BinaryFileHandler() final {}
342
343 Error ReadHeader(MemoryBuffer &Input) final {
344 StringRef FC = Input.getBuffer();
345
346
347 CurBundleInfo = BundlesInfo.end();
348
349
351 if (ReadChars > FC.size())
352 return Error::success();
353
354
355 if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle)
356 return Error::success();
357
358
359 if (ReadChars + 8 > FC.size())
360 return Error::success();
361
362 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
363 ReadChars += 8;
364
365
366 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
367
368
369 if (ReadChars + 8 > FC.size())
370 return Error::success();
371
372 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
373 ReadChars += 8;
374
375
376 if (ReadChars + 8 > FC.size())
377 return Error::success();
378
379 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
380 ReadChars += 8;
381
382
383 if (ReadChars + 8 > FC.size())
384 return Error::success();
385
386 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
387 ReadChars += 8;
388
389
390 if (ReadChars + TripleSize > FC.size())
391 return Error::success();
392
393 StringRef Triple(&FC.data()[ReadChars], TripleSize);
394 ReadChars += TripleSize;
395
396
397 if (!Offset || Offset + Size > FC.size())
398 return Error::success();
399
400 assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??");
401 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
402 }
403
404 CurBundleInfo = BundlesInfo.end();
405 NextBundleInfo = BundlesInfo.begin();
406 return Error::success();
407 }
408
410 ReadBundleStart(MemoryBuffer &Input) final {
411 if (NextBundleInfo == BundlesInfo.end())
412 return std::nullopt;
413 CurBundleInfo = NextBundleInfo++;
414 return CurBundleInfo->first();
415 }
416
417 Error ReadBundleEnd(MemoryBuffer &Input) final {
418 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
419 return Error::success();
420 }
421
422 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
423 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
424 StringRef FC = Input.getBuffer();
425 OS.write(FC.data() + CurBundleInfo->second.Offset,
426 CurBundleInfo->second.Size);
427 return Error::success();
428 }
429
430 Error WriteHeader(raw_ostream &OS,
431 ArrayRef<std::unique_ptr> Inputs) final {
432
433
435
437 HeaderSize += 8;
438
440 HeaderSize += 3 * 8;
441 HeaderSize += T.size();
442 }
443
444
446
447 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size());
448
449 unsigned Idx = 0;
451 MemoryBuffer &MB = *Inputs[Idx++];
452 HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment);
453
454 Write8byteIntegerToBuffer(OS, HeaderSize);
455
456 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
457 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
458 HeaderSize += MB.getBufferSize();
459
460 Write8byteIntegerToBuffer(OS, T.size());
461
462 OS << T;
463 }
464 return Error::success();
465 }
466
467 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
468 CurWriteBundleTarget = TargetTriple.str();
469 return Error::success();
470 }
471
472 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
473 return Error::success();
474 }
475
476 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
477 auto BI = BundlesInfo[CurWriteBundleTarget];
478
479
480 size_t CurrentPos = OS.tell();
481 size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0;
482 for (size_t I = 0; I < PaddingSize; ++I)
483 OS.write('\0');
484 assert(OS.tell() == BI.Offset);
485
486 OS.write(Input.getBufferStart(), Input.getBufferSize());
487
488 return Error::success();
489 }
490};
491
492
493
494class TempFileHandlerRAII {
495public:
496 ~TempFileHandlerRAII() {
497 for (const auto &File : Files)
498 sys::fs::remove(File);
499 }
500
501
504 if (std::error_code EC =
505 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
506 return createFileError(File, EC);
507 Files.push_front(File);
508
509 if (Contents) {
510 std::error_code EC;
511 raw_fd_ostream OS(File, EC);
512 if (EC)
513 return createFileError(File, EC);
514 OS.write(Contents->data(), Contents->size());
515 }
516 return Files.front().str();
517 }
518
519private:
520 std::forward_list<SmallString<128u>> Files;
521};
522
523
524
525
526
527class ObjectFileHandler final : public FileHandler {
528
529
530 std::unique_ptr Obj;
531
532
533 StringRef getInputFileContents() const { return Obj->getData(); }
534
535
536
538 IsOffloadSection(SectionRef CurSection) {
540 if (!NameOrErr)
541 return NameOrErr.takeError();
542
543
544 if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle)
545 return std::nullopt;
546
547
549 }
550
551
552 unsigned NumberOfInputs = 0;
553
554
555
556 unsigned NumberOfProcessedInputs = 0;
557
558
559 section_iterator CurrentSection;
560 section_iterator NextSection;
561
562
564
565public:
566
567 ObjectFileHandler(std::unique_ptr ObjIn,
569 : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()),
570 NextSection(Obj->section_begin()), BundlerConfig(BC) {}
571
572 ~ObjectFileHandler() final {}
573
574 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
575
577 ReadBundleStart(MemoryBuffer &Input) final {
578 while (NextSection != Obj->section_end()) {
579 CurrentSection = NextSection;
580 ++NextSection;
581
582
583
585 IsOffloadSection(*CurrentSection);
586 if (!TripleOrErr)
587 return TripleOrErr.takeError();
588 if (*TripleOrErr)
589 return **TripleOrErr;
590 }
591 return std::nullopt;
592 }
593
594 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
595
596 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
598 if (!ContentOrErr)
599 return ContentOrErr.takeError();
600 StringRef Content = *ContentOrErr;
601
602
603 std::string ModifiedContent;
604 if (Content.size() == 1u && Content.front() == 0) {
605 auto HostBundleOrErr = getHostBundle(
606 StringRef(Input.getBufferStart(), Input.getBufferSize()));
607 if (!HostBundleOrErr)
608 return HostBundleOrErr.takeError();
609
610 ModifiedContent = std::move(*HostBundleOrErr);
611 Content = ModifiedContent;
612 }
613
614 OS.write(Content.data(), Content.size());
615 return Error::success();
616 }
617
618 Error WriteHeader(raw_ostream &OS,
619 ArrayRef<std::unique_ptr> Inputs) final {
621 "Host input index not defined.");
622
623
624 NumberOfInputs = Inputs.size();
625 return Error::success();
626 }
627
628 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
629 ++NumberOfProcessedInputs;
630 return Error::success();
631 }
632
633 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
634 return Error::success();
635 }
636
637 Error finalizeOutputFile() final {
638 assert(NumberOfProcessedInputs <= NumberOfInputs &&
639 "Processing more inputs that actually exist!");
641 "Host input index not defined.");
642
643
644 if (NumberOfProcessedInputs != NumberOfInputs)
645 return Error::success();
646
647
648
649
650
651
652 assert(BundlerConfig.ObjcopyPath != "" &&
653 "llvm-objcopy path not specified");
654
655
656 TempFileHandlerRAII TempFiles;
657
658
659
660 BumpPtrAllocator Alloc;
661 StringSaver SS{Alloc};
663
664 for (unsigned I = 0; I < NumberOfInputs; ++I) {
665 StringRef InputFile = BundlerConfig.InputFileNames[I];
667
668
669
670
672 if (!TempFileOrErr)
673 return TempFileOrErr.takeError();
674 InputFile = *TempFileOrErr;
675 }
676
677 ObjcopyArgs.push_back(
679 BundlerConfig.TargetNames[I] + "=" + InputFile));
680 ObjcopyArgs.push_back(
682 BundlerConfig.TargetNames[I] + "=readonly,exclude"));
683 }
684 ObjcopyArgs.push_back("--");
685 ObjcopyArgs.push_back(
687 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front());
688
689 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
690 return Err;
691
692 return Error::success();
693 }
694
695 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
696 return Error::success();
697 }
698
699private:
701
702
704 errs() << "\"" << Objcopy << "\"";
705 for (StringRef Arg : drop_begin(Args, 1))
706 errs() << " \"" << Arg << "\"";
707 errs() << "\n";
708 } else {
709 if (sys::ExecuteAndWait(Objcopy, Args))
710 return createStringError(inconvertibleErrorCode(),
711 "'llvm-objcopy' tool failed");
712 }
713 return Error::success();
714 }
715
717 TempFileHandlerRAII TempFiles;
718
719 auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt);
720 if (!ModifiedObjPathOrErr)
721 return ModifiedObjPathOrErr.takeError();
722 StringRef ModifiedObjPath = *ModifiedObjPathOrErr;
723
724 BumpPtrAllocator Alloc;
725 StringSaver SS{Alloc};
727
728 ObjcopyArgs.push_back("--regex");
729 ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*");
730 ObjcopyArgs.push_back("--");
731
732 StringRef ObjcopyInputFileName;
733
734
735
736
737
738
739 if (StringRef(BundlerConfig.FilesType).starts_with("a")) {
740 auto InputFileOrErr =
741 TempFiles.Create(ArrayRef(Input.data(), Input.size()));
742 if (!InputFileOrErr)
743 return InputFileOrErr.takeError();
744 ObjcopyInputFileName = *InputFileOrErr;
745 } else
746 ObjcopyInputFileName = BundlerConfig.InputFileNames.front();
747
748 ObjcopyArgs.push_back(ObjcopyInputFileName);
749 ObjcopyArgs.push_back(ModifiedObjPath);
750
751 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs))
752 return std::move(Err);
753
754 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath);
755 if (!BufOrErr)
756 return createStringError(BufOrErr.getError(),
757 "Failed to read back the modified object file");
758
759 return BufOrErr->get()->getBuffer().str();
760 }
761};
762
763
764
765
766
767
768
769
770
771
772class TextFileHandler final : public FileHandler {
773
774 StringRef Comment;
775
776
777 std::string BundleStartString;
778
779
780 std::string BundleEndString;
781
782
783 size_t ReadChars = 0u;
784
785protected:
786 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
787
789 ReadBundleStart(MemoryBuffer &Input) final {
790 StringRef FC = Input.getBuffer();
791
792
793 ReadChars = FC.find(BundleStartString, ReadChars);
794 if (ReadChars == FC.npos)
795 return std::nullopt;
796
797
798 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
799
800
801 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
802 if (TripleEnd == FC.npos)
803 return std::nullopt;
804
805
806 ++ReadChars;
807
808 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
809 }
810
811 Error ReadBundleEnd(MemoryBuffer &Input) final {
812 StringRef FC = Input.getBuffer();
813
814
815 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
816
817 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
818 if (TripleEnd != FC.npos)
819
820 ++ReadChars;
821
822 return Error::success();
823 }
824
825 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
826 StringRef FC = Input.getBuffer();
827 size_t BundleStart = ReadChars;
828
829
830 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
831
832 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
833 OS << Bundle;
834
835 return Error::success();
836 }
837
838 Error WriteHeader(raw_ostream &OS,
839 ArrayRef<std::unique_ptr> Inputs) final {
840 return Error::success();
841 }
842
843 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final {
844 OS << BundleStartString << TargetTriple << "\n";
845 return Error::success();
846 }
847
848 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final {
849 OS << BundleEndString << TargetTriple << "\n";
850 return Error::success();
851 }
852
853 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final {
854 OS << Input.getBuffer();
855 return Error::success();
856 }
857
858public:
859 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) {
860 BundleStartString =
862 BundleEndString =
864 }
865
866 Error listBundleIDsCallback(MemoryBuffer &Input,
867 const BundleInfo &Info) final {
868
869
870
871
872 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
873 if (Error Err = ReadBundleEnd(Input))
874 return Err;
875 return Error::success();
876 }
877};
878}
879
880
881
882
883static std::unique_ptr
886
888
889
890
891 if (errorToBool(BinaryOrErr.takeError()) || !isa(*BinaryOrErr))
892 return std::make_unique(BundlerConfig);
893
894
895
896 return std::make_unique(
897 std::unique_ptr(cast(BinaryOrErr->release())),
898 BundlerConfig);
899}
900
901
905 std::string FilesType = BundlerConfig.FilesType;
906
907 if (FilesType == "i")
908 return std::make_unique("//");
909 if (FilesType == "ii")
910 return std::make_unique("//");
911 if (FilesType == "cui")
912 return std::make_unique("//");
913 if (FilesType == "hipi")
914 return std::make_unique("//");
915
916
917 if (FilesType == "d")
918 return std::make_unique("#");
919 if (FilesType == "ll")
920 return std::make_unique(";");
921 if (FilesType == "bc")
922 return std::make_unique(BundlerConfig);
923 if (FilesType == "s")
924 return std::make_unique("#");
925 if (FilesType == "o")
927 if (FilesType == "a")
929 if (FilesType == "gch")
930 return std::make_unique(BundlerConfig);
931 if (FilesType == "ast")
932 return std::make_unique(BundlerConfig);
933
934 return createStringError(errc::invalid_argument,
935 "'" + FilesType + "': invalid file type specified");
936}
937
940 if (llvm::compression::zstd::isAvailable()) {
942
943
945 } else if (llvm::compression::zlib::isAvailable()) {
947
948
949 CompressionLevel = llvm::compression::zlib::DefaultCompression;
950 }
951 auto IgnoreEnvVarOpt =
952 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR");
953 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1")
954 return;
955 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE");
956 if (VerboseEnvVarOpt.has_value())
957 Verbose = VerboseEnvVarOpt.value() == "1";
958 auto CompressEnvVarOpt =
959 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS");
960 if (CompressEnvVarOpt.has_value())
961 Compress = CompressEnvVarOpt.value() == "1";
962 auto CompressionLevelEnvVarOpt =
963 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL");
964 if (CompressionLevelEnvVarOpt.has_value()) {
965 llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value();
966 int Level;
967 if (!CompressionLevelStr.getAsInteger(10, Level))
969 else
970 llvm::errs()
971 << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
972 << CompressionLevelStr.str() << ". Ignoring it.\n";
973 }
974 auto CompressedBundleFormatVersionOpt =
975 llvm::sys::Process::GetEnv("COMPRESSED_BUNDLE_FORMAT_VERSION");
976 if (CompressedBundleFormatVersionOpt.has_value()) {
977 llvm::StringRef VersionStr = CompressedBundleFormatVersionOpt.value();
978 uint16_t Version;
979 if (!VersionStr.getAsInteger(10, Version)) {
980 if (Version >= 2 && Version <= 3)
982 else
983 llvm::errs()
984 << "Warning: Invalid value for COMPRESSED_BUNDLE_FORMAT_VERSION: "
985 << VersionStr.str()
986 << ". Valid values are 2 or 3. Using default version "
988 } else
989 llvm::errs()
990 << "Warning: Invalid value for COMPRESSED_BUNDLE_FORMAT_VERSION: "
991 << VersionStr.str() << ". Using default version "
993 }
994}
995
996
998 std::string Num = std::to_string(Value);
999 int InsertPosition = Num.length() - 3;
1000 while (InsertPosition > 0) {
1001 Num.insert(InsertPosition, ",");
1002 InsertPosition -= 3;
1003 }
1004 return Num;
1005}
1006
1009 const llvm::MemoryBuffer &Input,
1010 uint16_t Version, bool Verbose) {
1011 if (!llvm::compression::zstd::isAvailable() &&
1012 !llvm::compression::zlib::isAvailable())
1013 return createStringError(llvm::inconvertibleErrorCode(),
1014 "Compression not supported");
1015 llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
1017 if (Verbose)
1018 HashTimer.startTimer();
1019 llvm::MD5 Hash;
1020 llvm::MD5::MD5Result Result;
1021 Hash.update(Input.getBuffer());
1023 uint64_t TruncatedHash = Result.low();
1024 if (Verbose)
1025 HashTimer.stopTimer();
1026
1029 reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
1030 Input.getBuffer().size());
1031 llvm::Timer CompressTimer("Compression Timer", "Compression time",
1033 if (Verbose)
1034 CompressTimer.startTimer();
1035 llvm::compression::compress(P, BufferUint8, CompressedBuffer);
1036 if (Verbose)
1037 CompressTimer.stopTimer();
1038
1039 uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
1040
1041
1042 uint64_t UncompressedSize64 = Input.getBuffer().size();
1043 uint64_t TotalFileSize64;
1044
1045
1046 if (Version == 2) {
1047
1048 if (UncompressedSize64 > std::numeric_limits<uint32_t>::max())
1049 return createStringError(llvm::inconvertibleErrorCode(),
1050 "Uncompressed size exceeds version 2 limit");
1051 if ((MagicNumber.size() + sizeof(uint32_t) + sizeof(Version) +
1052 sizeof(CompressionMethod) + sizeof(uint32_t) + sizeof(TruncatedHash) +
1053 CompressedBuffer.size()) > std::numeric_limits<uint32_t>::max())
1054 return createStringError(llvm::inconvertibleErrorCode(),
1055 "Total file size exceeds version 2 limit");
1056
1057 TotalFileSize64 = MagicNumber.size() + sizeof(uint32_t) + sizeof(Version) +
1058 sizeof(CompressionMethod) + sizeof(uint32_t) +
1059 sizeof(TruncatedHash) + CompressedBuffer.size();
1060 } else {
1061 TotalFileSize64 = MagicNumber.size() + sizeof(uint64_t) + sizeof(Version) +
1062 sizeof(CompressionMethod) + sizeof(uint64_t) +
1063 sizeof(TruncatedHash) + CompressedBuffer.size();
1064 }
1065
1067 llvm::raw_svector_ostream OS(FinalBuffer);
1068 OS << MagicNumber;
1069 OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
1070 OS.write(reinterpret_cast<const char *>(&CompressionMethod),
1071 sizeof(CompressionMethod));
1072
1073
1074 if (Version == 2) {
1075 uint32_t TotalFileSize32 = static_cast<uint32_t>(TotalFileSize64);
1076 uint32_t UncompressedSize32 = static_cast<uint32_t>(UncompressedSize64);
1077 OS.write(reinterpret_cast<const char *>(&TotalFileSize32),
1078 sizeof(TotalFileSize32));
1079 OS.write(reinterpret_cast<const char *>(&UncompressedSize32),
1080 sizeof(UncompressedSize32));
1081 } else {
1082 OS.write(reinterpret_cast<const char *>(&TotalFileSize64),
1083 sizeof(TotalFileSize64));
1084 OS.write(reinterpret_cast<const char *>(&UncompressedSize64),
1085 sizeof(UncompressedSize64));
1086 }
1087
1088 OS.write(reinterpret_cast<const char *>(&TruncatedHash),
1089 sizeof(TruncatedHash));
1090 OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
1091 CompressedBuffer.size());
1092
1093 if (Verbose) {
1094 auto MethodUsed =
1095 P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
1096 double CompressionRate =
1097 static_cast<double>(UncompressedSize64) / CompressedBuffer.size();
1098 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
1099 double CompressionSpeedMBs =
1100 (UncompressedSize64 / (1024.0 * 1024.0)) / CompressionTimeSeconds;
1101 llvm::errs() << "Compressed bundle format version: " << Version << "\n"
1102 << "Total file size (including headers): "
1104 << "Compression method used: " << MethodUsed << "\n"
1105 << "Compression level: " << P.level << "\n"
1106 << "Binary size before compression: "
1108 << "Binary size after compression: "
1110 << "Compression rate: "
1111 << llvm::format("%.2lf", CompressionRate) << "\n"
1112 << "Compression ratio: "
1113 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1114 << "Compression speed: "
1115 << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
1116 << "Truncated MD5 hash: "
1117 << llvm::format_hex(TruncatedHash, 16) << "\n";
1118 }
1119
1120 return llvm::MemoryBuffer::getMemBufferCopy(
1121 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
1122}
1123
1126 bool Verbose) {
1127 StringRef Blob = Input.getBuffer();
1128
1129
1130 if (Blob.size() < V1HeaderSize)
1131 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1132
1133 if (llvm::identify_magic(Blob) !=
1134 llvm::file_magic::offload_bundle_compressed) {
1135 if (Verbose)
1136 llvm::errs() << "Uncompressed bundle.\n";
1137 return llvm::MemoryBuffer::getMemBufferCopy(Blob);
1138 }
1139
1140 size_t CurrentOffset = MagicSize;
1141
1142
1143 uint16_t ThisVersion;
1144 memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
1145 CurrentOffset += VersionFieldSize;
1146
1147
1148 if (ThisVersion >= 2 && ThisVersion <= 3) {
1149 size_t RequiredSize = (ThisVersion == 2) ? V2HeaderSize : V3HeaderSize;
1150 if (Blob.size() < RequiredSize)
1151 return createStringError(inconvertibleErrorCode(),
1152 "Compressed bundle header size too small");
1153 }
1154
1155
1156 uint16_t CompressionMethod;
1157 memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
1158 CurrentOffset += MethodFieldSize;
1159
1160
1161 uint64_t TotalFileSize = 0;
1162 if (ThisVersion >= 2) {
1163 if (ThisVersion == 2) {
1164 uint32_t TotalFileSize32;
1165 memcpy(&TotalFileSize32, Blob.data() + CurrentOffset, sizeof(uint32_t));
1166 TotalFileSize = TotalFileSize32;
1167 CurrentOffset += FileSizeFieldSizeV2;
1168 } else {
1169 memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint64_t));
1170 CurrentOffset += FileSizeFieldSizeV3;
1171 }
1172 }
1173
1174
1175 uint64_t UncompressedSize = 0;
1176 if (ThisVersion <= 2) {
1177 uint32_t UncompressedSize32;
1178 memcpy(&UncompressedSize32, Blob.data() + CurrentOffset, sizeof(uint32_t));
1179 UncompressedSize = UncompressedSize32;
1180 CurrentOffset += UncompressedSizeFieldSizeV2;
1181 } else {
1182 memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint64_t));
1183 CurrentOffset += UncompressedSizeFieldSizeV3;
1184 }
1185
1186
1187 uint64_t StoredHash;
1188 memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
1189 CurrentOffset += HashFieldSize;
1190
1191
1192 llvm::compression::Format CompressionFormat;
1193 if (CompressionMethod ==
1194 static_cast<uint16_t>(llvm::compression::Format::Zlib))
1195 CompressionFormat = llvm::compression::Format::Zlib;
1196 else if (CompressionMethod ==
1197 static_cast<uint16_t>(llvm::compression::Format::Zstd))
1198 CompressionFormat = llvm::compression::Format::Zstd;
1199 else
1200 return createStringError(inconvertibleErrorCode(),
1201 "Unknown compressing method");
1202
1203 llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
1205 if (Verbose)
1206 DecompressTimer.startTimer();
1207
1209 StringRef CompressedData = Blob.substr(CurrentOffset);
1210 if (llvm::Error DecompressionError = llvm::compression::decompress(
1211 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
1212 DecompressedData, UncompressedSize))
1213 return createStringError(inconvertibleErrorCode(),
1214 "Could not decompress embedded file contents: " +
1215 llvm::toString(std::move(DecompressionError)));
1216
1217 if (Verbose) {
1218 DecompressTimer.stopTimer();
1219
1220 double DecompressionTimeSeconds =
1221 DecompressTimer.getTotalTime().getWallTime();
1222
1223
1224 llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
1225 "Hash recalculation time",
1227 HashRecalcTimer.startTimer();
1228 llvm::MD5 Hash;
1229 llvm::MD5::MD5Result Result;
1231 DecompressedData.size()));
1233 uint64_t RecalculatedHash = Result.low();
1234 HashRecalcTimer.stopTimer();
1235 bool HashMatch = (StoredHash == RecalculatedHash);
1236
1237 double CompressionRate =
1238 static_cast<double>(UncompressedSize) / CompressedData.size();
1239 double DecompressionSpeedMBs =
1240 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
1241
1242 llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
1243 if (ThisVersion >= 2)
1244 llvm::errs() << "Total file size (from header): "
1246 llvm::errs() << "Decompression method: "
1247 << (CompressionFormat == llvm::compression::Format::Zlib
1248 ? "zlib"
1249 : "zstd")
1250 << "\n"
1251 << "Size before decompression: "
1253 << "Size after decompression: "
1255 << "Compression rate: "
1256 << llvm::format("%.2lf", CompressionRate) << "\n"
1257 << "Compression ratio: "
1258 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
1259 << "Decompression speed: "
1260 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
1261 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
1262 << "Recalculated hash: "
1263 << llvm::format_hex(RecalculatedHash, 16) << "\n"
1264 << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
1265 }
1266
1267 return llvm::MemoryBuffer::getMemBufferCopy(
1268 llvm::toStringRef(DecompressedData));
1269}
1270
1271
1274
1275 ErrorOr<std::unique_ptr> CodeOrErr =
1276 MemoryBuffer::getFileOrSTDIN(InputFileName, true);
1277 if (std::error_code EC = CodeOrErr.getError())
1278 return createFileError(InputFileName, EC);
1279
1280
1283 if (!DecompressedBufferOrErr)
1284 return createStringError(
1285 inconvertibleErrorCode(),
1286 "Failed to decompress input: " +
1287 llvm::toString(DecompressedBufferOrErr.takeError()));
1288
1289 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
1290
1291
1294 if (!FileHandlerOrErr)
1295 return FileHandlerOrErr.takeError();
1296
1297 std::unique_ptr &FH = *FileHandlerOrErr;
1298 assert(FH);
1299 return FH->listBundleIDs(DecompressedInput);
1300}
1301
1302
1303
1304
1307
1308
1310 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1311 dbgs() << "Compatible: Exact match: \t[CodeObject: "
1312 << CodeObjectInfo.str()
1313 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1314 return true;
1315 }
1316
1317
1319 !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) {
1320 DEBUG_WITH_TYPE(
1321 "CodeObjectCompatibility",
1322 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: "
1323 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1324 << "]\n");
1325 return false;
1326 }
1327
1328
1329 llvm::StringMap CodeObjectFeatureMap, TargetFeatureMap;
1331 CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap);
1334
1335
1336 if (!TargetProc || !CodeObjectProc ||
1337 CodeObjectProc.value() != TargetProc.value()) {
1338 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1339 dbgs() << "Incompatible: Processor mismatch \t[CodeObject: "
1340 << CodeObjectInfo.str()
1341 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1342 return false;
1343 }
1344
1345
1346
1347 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) {
1348 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1349 dbgs() << "Incompatible: CodeObject has more features "
1350 "than target \t[CodeObject: "
1351 << CodeObjectInfo.str()
1352 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1353 return false;
1354 }
1355
1356
1357
1358
1359
1360 for (const auto &CodeObjectFeature : CodeObjectFeatureMap) {
1361 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey());
1362 if (TargetFeature == TargetFeatureMap.end()) {
1363 DEBUG_WITH_TYPE(
1364 "CodeObjectCompatibility",
1365 dbgs()
1366 << "Incompatible: Value of CodeObject's non-ANY feature is "
1367 "not matching with Target feature's ANY value \t[CodeObject: "
1368 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1369 << "]\n");
1370 return false;
1371 } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) {
1372 DEBUG_WITH_TYPE(
1373 "CodeObjectCompatibility",
1374 dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is "
1375 "not matching with Target feature's non-ANY value "
1376 "\t[CodeObject: "
1377 << CodeObjectInfo.str()
1378 << "]\t:\t[Target: " << TargetInfo.str() << "]\n");
1379 return false;
1380 }
1381 }
1382
1383
1384
1385
1386
1387 DEBUG_WITH_TYPE(
1388 "CodeObjectCompatibility",
1389 dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: "
1390 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str()
1391 << "]\n");
1392 return true;
1393}
1394
1395
1397 std::error_code EC;
1398
1399
1401 llvm::raw_svector_ostream BufferStream(Buffer);
1402
1403
1407 ErrorOr<std::unique_ptr> CodeOrErr =
1408 MemoryBuffer::getFileOrSTDIN(I, true);
1409 if (std::error_code EC = CodeOrErr.getError())
1410 return createFileError(I, EC);
1411 InputBuffers.emplace_back(std::move(*CodeOrErr));
1412 }
1413
1414
1416 "Host input index undefined??");
1421 if (!FileHandlerOrErr)
1422 return FileHandlerOrErr.takeError();
1423
1424 std::unique_ptr &FH = *FileHandlerOrErr;
1425 assert(FH);
1426
1427
1428 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers))
1429 return Err;
1430
1431
1432
1433 auto Input = InputBuffers.begin();
1435 if (Error Err = FH->WriteBundleStart(BufferStream, Triple))
1436 return Err;
1437 if (Error Err = FH->WriteBundle(BufferStream, **Input))
1438 return Err;
1439 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple))
1440 return Err;
1441 ++Input;
1442 }
1443
1445 sys::fs::OF_None);
1446 if (EC)
1448
1451 std::unique_ptrllvm::MemoryBuffer BufferMemory =
1452 llvm::MemoryBuffer::getMemBufferCopy(
1453 llvm::StringRef(Buffer.data(), Buffer.size()));
1456 true},
1459 if (auto Error = CompressionResult.takeError())
1460 return Error;
1461
1462 auto CompressedMemBuffer = std::move(CompressionResult.get());
1463 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(),
1464 CompressedMemBuffer->getBufferEnd());
1465 } else
1466 CompressedBuffer = Buffer;
1467
1468 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size());
1469
1470 return FH->finalizeOutputFile();
1471}
1472
1473
1475
1476 ErrorOr<std::unique_ptr> CodeOrErr =
1478 true);
1479 if (std::error_code EC = CodeOrErr.getError())
1481
1482
1485 if (!DecompressedBufferOrErr)
1486 return createStringError(
1487 inconvertibleErrorCode(),
1488 "Failed to decompress input: " +
1489 llvm::toString(DecompressedBufferOrErr.takeError()));
1490
1491 MemoryBuffer &Input = **DecompressedBufferOrErr;
1492
1493
1496 if (!FileHandlerOrErr)
1497 return FileHandlerOrErr.takeError();
1498
1499 std::unique_ptr &FH = *FileHandlerOrErr;
1500 assert(FH);
1501
1502
1503 if (Error Err = FH->ReadHeader(Input))
1504 return Err;
1505
1506
1507 StringMap Worklist;
1510 Worklist[Triple] = *Output;
1511 ++Output;
1512 }
1513
1514
1515
1516 bool FoundHostBundle = false;
1517 while (!Worklist.empty()) {
1519 FH->ReadBundleStart(Input);
1520 if (!CurTripleOrErr)
1521 return CurTripleOrErr.takeError();
1522
1523
1524 if (!*CurTripleOrErr)
1525 break;
1526
1527 StringRef CurTriple = **CurTripleOrErr;
1528 assert(!CurTriple.empty());
1529
1530 auto Output = Worklist.begin();
1531 for (auto E = Worklist.end(); Output != E; Output++) {
1535 break;
1536 }
1537 }
1538
1539 if (Output == Worklist.end())
1540 continue;
1541
1542 std::error_code EC;
1543 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None);
1544 if (EC)
1545 return createFileError((*Output).second, EC);
1546 if (Error Err = FH->ReadBundle(OutputFile, Input))
1547 return Err;
1548 if (Error Err = FH->ReadBundleEnd(Input))
1549 return Err;
1550 Worklist.erase(Output);
1551
1552
1554 if (OffloadInfo.hasHostKind())
1555 FoundHostBundle = true;
1556 }
1557
1559 std::string ErrMsg = "Can't find bundles for";
1560 std::set Sorted;
1561 for (auto &E : Worklist)
1562 Sorted.insert(E.first());
1563 unsigned I = 0;
1564 unsigned Last = Sorted.size() - 1;
1565 for (auto &E : Sorted) {
1566 if (I != 0 && Last > 1)
1567 ErrMsg += ",";
1568 ErrMsg += " ";
1569 if (I == Last && I != 0)
1570 ErrMsg += "and ";
1571 ErrMsg += E.str();
1572 ++I;
1573 }
1574 return createStringError(inconvertibleErrorCode(), ErrMsg);
1575 }
1576
1577
1578
1580 for (auto &E : Worklist) {
1581 std::error_code EC;
1582 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1583 if (EC)
1584 return createFileError(E.second, EC);
1585
1586
1588 if (OffloadInfo.hasHostKind())
1589 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
1590 }
1591 return Error::success();
1592 }
1593
1594
1595
1598 return createStringError(inconvertibleErrorCode(),
1599 "Can't find bundle for the host target");
1600
1601
1602 for (auto &E : Worklist) {
1603 std::error_code EC;
1604 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1605 if (EC)
1606 return createFileError(E.second, EC);
1607 }
1608
1609 return Error::success();
1610}
1611
1613 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN
1614 : Archive::K_GNU;
1615}
1616
1617
1618
1619
1620
1621
1622
1623static bool
1627 if (!CompatibleTargets.empty()) {
1628 DEBUG_WITH_TYPE("CodeObjectCompatibility",
1629 dbgs() << "CompatibleTargets list should be empty\n");
1630 return false;
1631 }
1635 CompatibleTargets.push_back(Target);
1636 }
1637 return !CompatibleTargets.empty();
1638}
1639
1640
1641
1642
1643
1647 std::vector<std::unique_ptr> ArchiveBuffers;
1648 ErrorOr<std::unique_ptr> BufOrErr =
1649 MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false);
1650 if (std::error_code EC = BufOrErr.getError())
1651 return createFileError(ArchiveName, EC);
1652
1653 ArchiveBuffers.push_back(std::move(*BufOrErr));
1655 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1656 if (!LibOrErr)
1657 return LibOrErr.takeError();
1658
1659 auto Archive = std::move(*LibOrErr);
1660
1661 Error ArchiveErr = Error::success();
1662 auto ChildEnd = Archive->child_end();
1663
1664
1665 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1666 ArchiveIter != ChildEnd; ++ArchiveIter) {
1667 if (ArchiveErr)
1668 return ArchiveErr;
1669 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1670 if (!ArchiveChildNameOrErr)
1671 return ArchiveChildNameOrErr.takeError();
1672
1673 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1674 if (!CodeObjectBufferRefOrErr)
1675 return CodeObjectBufferRefOrErr.takeError();
1676
1677 auto CodeObjectBuffer =
1678 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1679
1682 if (!FileHandlerOrErr)
1683 return FileHandlerOrErr.takeError();
1684
1685 std::unique_ptr &FileHandler = *FileHandlerOrErr;
1686 assert(FileHandler);
1687
1688 std::set BundleIds;
1689 auto CodeObjectFileError =
1690 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds);
1691 if (CodeObjectFileError)
1692 return CodeObjectFileError;
1693
1695 if (ConflictingArchs) {
1696 std::string ErrMsg =
1697 Twine("conflicting TargetIDs [" + ConflictingArchs.value().first +
1698 ", " + ConflictingArchs.value().second + "] found in " +
1699 ArchiveChildNameOrErr.get() + " of " + ArchiveName)
1700 .str();
1701 return createStringError(inconvertibleErrorCode(), ErrMsg);
1702 }
1703 }
1704
1705 return ArchiveErr;
1706}
1707
1708
1709
1710
1711
1712
1713
1714
1716 std::vector<std::unique_ptr> ArchiveBuffers;
1717
1718
1719
1720 StringMap<std::vector> OutputArchivesMap;
1721
1722
1723 StringMap TargetOutputFileNameMap;
1724
1727 TargetOutputFileNameMap[Target] = *Output;
1728 ++Output;
1729 }
1730
1732
1734
1735
1736
1738 if (ArchiveError) {
1739 return ArchiveError;
1740 }
1741 }
1742
1743 ErrorOr<std::unique_ptr> BufOrErr =
1744 MemoryBuffer::getFileOrSTDIN(IFName, true, false);
1745 if (std::error_code EC = BufOrErr.getError())
1747
1748 ArchiveBuffers.push_back(std::move(*BufOrErr));
1750 Archive::create(ArchiveBuffers.back()->getMemBufferRef());
1751 if (!LibOrErr)
1752 return LibOrErr.takeError();
1753
1754 auto Archive = std::move(*LibOrErr);
1755
1756 Error ArchiveErr = Error::success();
1757 auto ChildEnd = Archive->child_end();
1758
1759
1760 for (auto ArchiveIter = Archive->child_begin(ArchiveErr);
1761 ArchiveIter != ChildEnd; ++ArchiveIter) {
1762 if (ArchiveErr)
1763 return ArchiveErr;
1764 auto ArchiveChildNameOrErr = (*ArchiveIter).getName();
1765 if (!ArchiveChildNameOrErr)
1766 return ArchiveChildNameOrErr.takeError();
1767
1768 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr);
1769
1770 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef();
1771 if (!CodeObjectBufferRefOrErr)
1772 return CodeObjectBufferRefOrErr.takeError();
1773
1774 auto TempCodeObjectBuffer =
1775 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false);
1776
1777
1781 if (!DecompressedBufferOrErr)
1782 return createStringError(
1783 inconvertibleErrorCode(),
1784 "Failed to decompress code object: " +
1785 llvm::toString(DecompressedBufferOrErr.takeError()));
1786
1787 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr;
1788
1791 if (!FileHandlerOrErr)
1792 return FileHandlerOrErr.takeError();
1793
1794 std::unique_ptr &FileHandler = *FileHandlerOrErr;
1795 assert(FileHandler &&
1796 "FileHandle creation failed for file in the archive!");
1797
1798 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
1799 return ReadErr;
1800
1802 FileHandler->ReadBundleStart(CodeObjectBuffer);
1803 if (!CurBundleIDOrErr)
1804 return CurBundleIDOrErr.takeError();
1805
1806 std::optional OptionalCurBundleID = *CurBundleIDOrErr;
1807
1808 if (!OptionalCurBundleID)
1809 continue;
1810 StringRef CodeObject = *OptionalCurBundleID;
1811
1812
1813
1814 while (!CodeObject.empty()) {
1819 std::string BundleData;
1820 raw_string_ostream DataStream(BundleData);
1821 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer))
1822 return Err;
1823
1824 for (auto &CompatibleTarget : CompatibleTargets) {
1826 BundledObjectFileName.assign(BundledObjectFile);
1827 auto OutputBundleName =
1828 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" +
1829 CodeObject +
1831 CodeObjectInfo.TargetID))
1832 .str();
1833
1834
1835 std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':',
1836 '_');
1837
1838 std::unique_ptr MemBuf = MemoryBuffer::getMemBufferCopy(
1839 DataStream.str(), OutputBundleName);
1840 ArchiveBuffers.push_back(std::move(MemBuf));
1841 llvm::MemoryBufferRef MemBufRef =
1842 MemoryBufferRef(*(ArchiveBuffers.back()));
1843
1844
1845
1846 OutputArchivesMap[CompatibleTarget].push_back(
1847 NewArchiveMember(MemBufRef));
1848 }
1849 }
1850
1851 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer))
1852 return Err;
1853
1855 FileHandler->ReadBundleStart(CodeObjectBuffer);
1856 if (!NextTripleOrErr)
1857 return NextTripleOrErr.takeError();
1858
1859 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : "";
1860 }
1861 }
1862
1863 assert(!ArchiveErr && "Error occurred while reading archive!");
1864
1865
1867 StringRef FileName = TargetOutputFileNameMap[Target];
1868 StringMapIterator<std::vectorllvm::NewArchiveMember> CurArchiveMembers =
1869 OutputArchivesMap.find(Target);
1870 if (CurArchiveMembers != OutputArchivesMap.end()) {
1871 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(),
1872 SymtabWritingMode::NormalSymtab,
1874 false, nullptr))
1875 return WriteErr;
1877 std::string ErrMsg =
1878 Twine("no compatible code object found for the target '" + Target +
1879 "' in heterogeneous archive library: " + IFName)
1880 .str();
1881 return createStringError(inconvertibleErrorCode(), ErrMsg);
1882 } else {
1883
1884
1885
1886 std::vectorllvm::NewArchiveMember EmptyArchive;
1887 EmptyArchive.clear();
1888 if (Error WriteErr = writeArchive(
1889 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab,
1891 return WriteErr;
1892 }
1893 }
1894
1895 return Error::success();
1896}
llvm::MachO::Target Target
static std::string getDeviceLibraryFileName(StringRef BundleFileName, StringRef Device)
static llvm::ManagedStatic< llvm::TimerGroup, CreateClangOffloadBundlerTimerGroup > ClangOffloadBundlerTimerGroup
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 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 > > decompress(const llvm::MemoryBuffer &Input, bool Verbose=false)
static llvm::Expected< std::unique_ptr< llvm::MemoryBuffer > > compress(llvm::compression::Params P, const llvm::MemoryBuffer &Input, uint16_t Version, bool Verbose=false)
llvm::compression::Format CompressionFormat
std::vector< std::string > OutputFileNames
std::vector< std::string > TargetNames
uint16_t CompressedBundleVersion
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