1 //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file implements an offload bundling API that bundles different files 11 /// that relate with the same source code but different targets into a single 12 /// one. Also the implements the opposite functionality, i.e. unbundle files 13 /// previous created by this API. 14 /// 15 //===----------------------------------------------------------------------===// 16 17 #include "clang/Driver/OffloadBundler.h" 18 #include "clang/Basic/Cuda.h" 19 #include "clang/Basic/TargetID.h" 20 #include "clang/Basic/Version.h" 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" 51 #include <algorithm> 52 #include <cassert> 53 #include <cstddef> 54 #include <cstdint> 55 #include <forward_list> 56 #include <llvm/Support/Process.h> 57 #include <memory> 58 #include <set> 59 #include <string> 60 #include <system_error> 61 #include <utility> 62 63 using namespace llvm; 64 using namespace llvm::object; 65 using namespace clang; 66 67 static llvm::TimerGroup 68 ClangOffloadBundlerTimerGroup("Clang Offload Bundler Timer Group", 69 "Timer group for clang offload bundler"); 70 71 /// Magic string that marks the existence of offloading data. 72 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" 73 74 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target, 75 const OffloadBundlerConfig &BC) 76 : BundlerConfig(BC) { 77 78 // TODO: Add error checking from ClangOffloadBundler.cpp 79 auto TargetFeatures = Target.split(':'); 80 auto TripleOrGPU = TargetFeatures.first.rsplit('-'); 81 82 if (clang::StringToOffloadArch(TripleOrGPU.second) != 83 clang::OffloadArch::UNKNOWN) { 84 auto KindTriple = TripleOrGPU.first.split('-'); 85 this->OffloadKind = KindTriple.first; 86 87 // Enforce optional env field to standardize bundles 88 llvm::Triple t = llvm::Triple(KindTriple.second); 89 this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(), 90 t.getOSName(), t.getEnvironmentName()); 91 92 this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); 93 } else { 94 auto KindTriple = TargetFeatures.first.split('-'); 95 this->OffloadKind = KindTriple.first; 96 97 // Enforce optional env field to standardize bundles 98 llvm::Triple t = llvm::Triple(KindTriple.second); 99 this->Triple = llvm::Triple(t.getArchName(), t.getVendorName(), 100 t.getOSName(), t.getEnvironmentName()); 101 102 this->TargetID = ""; 103 } 104 } 105 106 bool OffloadTargetInfo::hasHostKind() const { 107 return this->OffloadKind == "host"; 108 } 109 110 bool OffloadTargetInfo::isOffloadKindValid() const { 111 return OffloadKind == "host" || OffloadKind == "openmp" || 112 OffloadKind == "hip" || OffloadKind == "hipv4"; 113 } 114 115 bool OffloadTargetInfo::isOffloadKindCompatible( 116 const StringRef TargetOffloadKind) const { 117 if ((OffloadKind == TargetOffloadKind) || 118 (OffloadKind == "hip" && TargetOffloadKind == "hipv4") || 119 (OffloadKind == "hipv4" && TargetOffloadKind == "hip")) 120 return true; 121 122 if (BundlerConfig.HipOpenmpCompatible) { 123 bool HIPCompatibleWithOpenMP = OffloadKind.starts_with_insensitive("hip") && 124 TargetOffloadKind == "openmp"; 125 bool OpenMPCompatibleWithHIP = 126 OffloadKind == "openmp" && 127 TargetOffloadKind.starts_with_insensitive("hip"); 128 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP; 129 } 130 return false; 131 } 132 133 bool OffloadTargetInfo::isTripleValid() const { 134 return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; 135 } 136 137 bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { 138 return OffloadKind == Target.OffloadKind && 139 Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; 140 } 141 142 std::string OffloadTargetInfo::str() const { 143 return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); 144 } 145 146 static StringRef getDeviceFileExtension(StringRef Device, 147 StringRef BundleFileName) { 148 if (Device.contains("gfx")) 149 return ".bc"; 150 if (Device.contains("sm_")) 151 return ".cubin"; 152 return sys::path::extension(BundleFileName); 153 } 154 155 static std::string getDeviceLibraryFileName(StringRef BundleFileName, 156 StringRef Device) { 157 StringRef LibName = sys::path::stem(BundleFileName); 158 StringRef Extension = getDeviceFileExtension(Device, BundleFileName); 159 160 std::string Result; 161 Result += LibName; 162 Result += Extension; 163 return Result; 164 } 165 166 namespace { 167 /// Generic file handler interface. 168 class FileHandler { 169 public: 170 struct BundleInfo { 171 StringRef BundleID; 172 }; 173 174 FileHandler() {} 175 176 virtual ~FileHandler() {} 177 178 /// Update the file handler with information from the header of the bundled 179 /// file. 180 virtual Error ReadHeader(MemoryBuffer &Input) = 0; 181 182 /// Read the marker of the next bundled to be read in the file. The bundle 183 /// name is returned if there is one in the file, or `std::nullopt` if there 184 /// are no more bundles to be read. 185 virtual Expected<std::optional<StringRef>> 186 ReadBundleStart(MemoryBuffer &Input) = 0; 187 188 /// Read the marker that closes the current bundle. 189 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; 190 191 /// Read the current bundle and write the result into the stream \a OS. 192 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 193 194 /// Write the header of the bundled file to \a OS based on the information 195 /// gathered from \a Inputs. 196 virtual Error WriteHeader(raw_ostream &OS, 197 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; 198 199 /// Write the marker that initiates a bundle for the triple \a TargetTriple to 200 /// \a OS. 201 virtual Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) = 0; 202 203 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a 204 /// OS. 205 virtual Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) = 0; 206 207 /// Write the bundle from \a Input into \a OS. 208 virtual Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 209 210 /// Finalize output file. 211 virtual Error finalizeOutputFile() { return Error::success(); } 212 213 /// List bundle IDs in \a Input. 214 virtual Error listBundleIDs(MemoryBuffer &Input) { 215 if (Error Err = ReadHeader(Input)) 216 return Err; 217 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 218 llvm::outs() << Info.BundleID << '\n'; 219 Error Err = listBundleIDsCallback(Input, Info); 220 if (Err) 221 return Err; 222 return Error::success(); 223 }); 224 } 225 226 /// Get bundle IDs in \a Input in \a BundleIds. 227 virtual Error getBundleIDs(MemoryBuffer &Input, 228 std::set<StringRef> &BundleIds) { 229 if (Error Err = ReadHeader(Input)) 230 return Err; 231 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 232 BundleIds.insert(Info.BundleID); 233 Error Err = listBundleIDsCallback(Input, Info); 234 if (Err) 235 return Err; 236 return Error::success(); 237 }); 238 } 239 240 /// For each bundle in \a Input, do \a Func. 241 Error forEachBundle(MemoryBuffer &Input, 242 std::function<Error(const BundleInfo &)> Func) { 243 while (true) { 244 Expected<std::optional<StringRef>> CurTripleOrErr = 245 ReadBundleStart(Input); 246 if (!CurTripleOrErr) 247 return CurTripleOrErr.takeError(); 248 249 // No more bundles. 250 if (!*CurTripleOrErr) 251 break; 252 253 StringRef CurTriple = **CurTripleOrErr; 254 assert(!CurTriple.empty()); 255 256 BundleInfo Info{CurTriple}; 257 if (Error Err = Func(Info)) 258 return Err; 259 } 260 return Error::success(); 261 } 262 263 protected: 264 virtual Error listBundleIDsCallback(MemoryBuffer &Input, 265 const BundleInfo &Info) { 266 return Error::success(); 267 } 268 }; 269 270 /// Handler for binary files. The bundled file will have the following format 271 /// (all integers are stored in little-endian format): 272 /// 273 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) 274 /// 275 /// NumberOfOffloadBundles (8-byte integer) 276 /// 277 /// OffsetOfBundle1 (8-byte integer) 278 /// SizeOfBundle1 (8-byte integer) 279 /// NumberOfBytesInTripleOfBundle1 (8-byte integer) 280 /// TripleOfBundle1 (byte length defined before) 281 /// 282 /// ... 283 /// 284 /// OffsetOfBundleN (8-byte integer) 285 /// SizeOfBundleN (8-byte integer) 286 /// NumberOfBytesInTripleOfBundleN (8-byte integer) 287 /// TripleOfBundleN (byte length defined before) 288 /// 289 /// Bundle1 290 /// ... 291 /// BundleN 292 293 /// Read 8-byte integers from a buffer in little-endian format. 294 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { 295 return llvm::support::endian::read64le(Buffer.data() + pos); 296 } 297 298 /// Write 8-byte integers to a buffer in little-endian format. 299 static void Write8byteIntegerToBuffer(raw_ostream &OS, uint64_t Val) { 300 llvm::support::endian::write(OS, Val, llvm::endianness::little); 301 } 302 303 class BinaryFileHandler final : public FileHandler { 304 /// Information about the bundles extracted from the header. 305 struct BinaryBundleInfo final : public BundleInfo { 306 /// Size of the bundle. 307 uint64_t Size = 0u; 308 /// Offset at which the bundle starts in the bundled file. 309 uint64_t Offset = 0u; 310 311 BinaryBundleInfo() {} 312 BinaryBundleInfo(uint64_t Size, uint64_t Offset) 313 : Size(Size), Offset(Offset) {} 314 }; 315 316 /// Map between a triple and the corresponding bundle information. 317 StringMap<BinaryBundleInfo> BundlesInfo; 318 319 /// Iterator for the bundle information that is being read. 320 StringMap<BinaryBundleInfo>::iterator CurBundleInfo; 321 StringMap<BinaryBundleInfo>::iterator NextBundleInfo; 322 323 /// Current bundle target to be written. 324 std::string CurWriteBundleTarget; 325 326 /// Configuration options and arrays for this bundler job 327 const OffloadBundlerConfig &BundlerConfig; 328 329 public: 330 // TODO: Add error checking from ClangOffloadBundler.cpp 331 BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {} 332 333 ~BinaryFileHandler() final {} 334 335 Error ReadHeader(MemoryBuffer &Input) final { 336 StringRef FC = Input.getBuffer(); 337 338 // Initialize the current bundle with the end of the container. 339 CurBundleInfo = BundlesInfo.end(); 340 341 // Check if buffer is smaller than magic string. 342 size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 343 if (ReadChars > FC.size()) 344 return Error::success(); 345 346 // Check if no magic was found. 347 if (llvm::identify_magic(FC) != llvm::file_magic::offload_bundle) 348 return Error::success(); 349 350 // Read number of bundles. 351 if (ReadChars + 8 > FC.size()) 352 return Error::success(); 353 354 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); 355 ReadChars += 8; 356 357 // Read bundle offsets, sizes and triples. 358 for (uint64_t i = 0; i < NumberOfBundles; ++i) { 359 360 // Read offset. 361 if (ReadChars + 8 > FC.size()) 362 return Error::success(); 363 364 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); 365 ReadChars += 8; 366 367 // Read size. 368 if (ReadChars + 8 > FC.size()) 369 return Error::success(); 370 371 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); 372 ReadChars += 8; 373 374 // Read triple size. 375 if (ReadChars + 8 > FC.size()) 376 return Error::success(); 377 378 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); 379 ReadChars += 8; 380 381 // Read triple. 382 if (ReadChars + TripleSize > FC.size()) 383 return Error::success(); 384 385 StringRef Triple(&FC.data()[ReadChars], TripleSize); 386 ReadChars += TripleSize; 387 388 // Check if the offset and size make sense. 389 if (!Offset || Offset + Size > FC.size()) 390 return Error::success(); 391 392 assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??"); 393 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); 394 } 395 // Set the iterator to where we will start to read. 396 CurBundleInfo = BundlesInfo.end(); 397 NextBundleInfo = BundlesInfo.begin(); 398 return Error::success(); 399 } 400 401 Expected<std::optional<StringRef>> 402 ReadBundleStart(MemoryBuffer &Input) final { 403 if (NextBundleInfo == BundlesInfo.end()) 404 return std::nullopt; 405 CurBundleInfo = NextBundleInfo++; 406 return CurBundleInfo->first(); 407 } 408 409 Error ReadBundleEnd(MemoryBuffer &Input) final { 410 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 411 return Error::success(); 412 } 413 414 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 415 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 416 StringRef FC = Input.getBuffer(); 417 OS.write(FC.data() + CurBundleInfo->second.Offset, 418 CurBundleInfo->second.Size); 419 return Error::success(); 420 } 421 422 Error WriteHeader(raw_ostream &OS, 423 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 424 425 // Compute size of the header. 426 uint64_t HeaderSize = 0; 427 428 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 429 HeaderSize += 8; // Number of Bundles 430 431 for (auto &T : BundlerConfig.TargetNames) { 432 HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. 433 HeaderSize += T.size(); // The triple. 434 } 435 436 // Write to the buffer the header. 437 OS << OFFLOAD_BUNDLER_MAGIC_STR; 438 439 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size()); 440 441 unsigned Idx = 0; 442 for (auto &T : BundlerConfig.TargetNames) { 443 MemoryBuffer &MB = *Inputs[Idx++]; 444 HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment); 445 // Bundle offset. 446 Write8byteIntegerToBuffer(OS, HeaderSize); 447 // Size of the bundle (adds to the next bundle's offset) 448 Write8byteIntegerToBuffer(OS, MB.getBufferSize()); 449 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); 450 HeaderSize += MB.getBufferSize(); 451 // Size of the triple 452 Write8byteIntegerToBuffer(OS, T.size()); 453 // Triple 454 OS << T; 455 } 456 return Error::success(); 457 } 458 459 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 460 CurWriteBundleTarget = TargetTriple.str(); 461 return Error::success(); 462 } 463 464 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 465 return Error::success(); 466 } 467 468 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 469 auto BI = BundlesInfo[CurWriteBundleTarget]; 470 471 // Pad with 0 to reach specified offset. 472 size_t CurrentPos = OS.tell(); 473 size_t PaddingSize = BI.Offset > CurrentPos ? BI.Offset - CurrentPos : 0; 474 for (size_t I = 0; I < PaddingSize; ++I) 475 OS.write('\0'); 476 assert(OS.tell() == BI.Offset); 477 478 OS.write(Input.getBufferStart(), Input.getBufferSize()); 479 480 return Error::success(); 481 } 482 }; 483 484 // This class implements a list of temporary files that are removed upon 485 // object destruction. 486 class TempFileHandlerRAII { 487 public: 488 ~TempFileHandlerRAII() { 489 for (const auto &File : Files) 490 sys::fs::remove(File); 491 } 492 493 // Creates temporary file with given contents. 494 Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) { 495 SmallString<128u> File; 496 if (std::error_code EC = 497 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) 498 return createFileError(File, EC); 499 Files.push_front(File); 500 501 if (Contents) { 502 std::error_code EC; 503 raw_fd_ostream OS(File, EC); 504 if (EC) 505 return createFileError(File, EC); 506 OS.write(Contents->data(), Contents->size()); 507 } 508 return Files.front().str(); 509 } 510 511 private: 512 std::forward_list<SmallString<128u>> Files; 513 }; 514 515 /// Handler for object files. The bundles are organized by sections with a 516 /// designated name. 517 /// 518 /// To unbundle, we just copy the contents of the designated section. 519 class ObjectFileHandler final : public FileHandler { 520 521 /// The object file we are currently dealing with. 522 std::unique_ptr<ObjectFile> Obj; 523 524 /// Return the input file contents. 525 StringRef getInputFileContents() const { return Obj->getData(); } 526 527 /// Return bundle name (<kind>-<triple>) if the provided section is an offload 528 /// section. 529 static Expected<std::optional<StringRef>> 530 IsOffloadSection(SectionRef CurSection) { 531 Expected<StringRef> NameOrErr = CurSection.getName(); 532 if (!NameOrErr) 533 return NameOrErr.takeError(); 534 535 // If it does not start with the reserved suffix, just skip this section. 536 if (llvm::identify_magic(*NameOrErr) != llvm::file_magic::offload_bundle) 537 return std::nullopt; 538 539 // Return the triple that is right after the reserved prefix. 540 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 541 } 542 543 /// Total number of inputs. 544 unsigned NumberOfInputs = 0; 545 546 /// Total number of processed inputs, i.e, inputs that were already 547 /// read from the buffers. 548 unsigned NumberOfProcessedInputs = 0; 549 550 /// Iterator of the current and next section. 551 section_iterator CurrentSection; 552 section_iterator NextSection; 553 554 /// Configuration options and arrays for this bundler job 555 const OffloadBundlerConfig &BundlerConfig; 556 557 public: 558 // TODO: Add error checking from ClangOffloadBundler.cpp 559 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn, 560 const OffloadBundlerConfig &BC) 561 : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()), 562 NextSection(Obj->section_begin()), BundlerConfig(BC) {} 563 564 ~ObjectFileHandler() final {} 565 566 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 567 568 Expected<std::optional<StringRef>> 569 ReadBundleStart(MemoryBuffer &Input) final { 570 while (NextSection != Obj->section_end()) { 571 CurrentSection = NextSection; 572 ++NextSection; 573 574 // Check if the current section name starts with the reserved prefix. If 575 // so, return the triple. 576 Expected<std::optional<StringRef>> TripleOrErr = 577 IsOffloadSection(*CurrentSection); 578 if (!TripleOrErr) 579 return TripleOrErr.takeError(); 580 if (*TripleOrErr) 581 return **TripleOrErr; 582 } 583 return std::nullopt; 584 } 585 586 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 587 588 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 589 Expected<StringRef> ContentOrErr = CurrentSection->getContents(); 590 if (!ContentOrErr) 591 return ContentOrErr.takeError(); 592 StringRef Content = *ContentOrErr; 593 594 // Copy fat object contents to the output when extracting host bundle. 595 std::string ModifiedContent; 596 if (Content.size() == 1u && Content.front() == 0) { 597 auto HostBundleOrErr = getHostBundle( 598 StringRef(Input.getBufferStart(), Input.getBufferSize())); 599 if (!HostBundleOrErr) 600 return HostBundleOrErr.takeError(); 601 602 ModifiedContent = std::move(*HostBundleOrErr); 603 Content = ModifiedContent; 604 } 605 606 OS.write(Content.data(), Content.size()); 607 return Error::success(); 608 } 609 610 Error WriteHeader(raw_ostream &OS, 611 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 612 assert(BundlerConfig.HostInputIndex != ~0u && 613 "Host input index not defined."); 614 615 // Record number of inputs. 616 NumberOfInputs = Inputs.size(); 617 return Error::success(); 618 } 619 620 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 621 ++NumberOfProcessedInputs; 622 return Error::success(); 623 } 624 625 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 626 return Error::success(); 627 } 628 629 Error finalizeOutputFile() final { 630 assert(NumberOfProcessedInputs <= NumberOfInputs && 631 "Processing more inputs that actually exist!"); 632 assert(BundlerConfig.HostInputIndex != ~0u && 633 "Host input index not defined."); 634 635 // If this is not the last output, we don't have to do anything. 636 if (NumberOfProcessedInputs != NumberOfInputs) 637 return Error::success(); 638 639 // We will use llvm-objcopy to add target objects sections to the output 640 // fat object. These sections should have 'exclude' flag set which tells 641 // link editor to remove them from linker inputs when linking executable or 642 // shared library. 643 644 assert(BundlerConfig.ObjcopyPath != "" && 645 "llvm-objcopy path not specified"); 646 647 // Temporary files that need to be removed. 648 TempFileHandlerRAII TempFiles; 649 650 // Compose llvm-objcopy command line for add target objects' sections with 651 // appropriate flags. 652 BumpPtrAllocator Alloc; 653 StringSaver SS{Alloc}; 654 SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; 655 656 for (unsigned I = 0; I < NumberOfInputs; ++I) { 657 StringRef InputFile = BundlerConfig.InputFileNames[I]; 658 if (I == BundlerConfig.HostInputIndex) { 659 // Special handling for the host bundle. We do not need to add a 660 // standard bundle for the host object since we are going to use fat 661 // object as a host object. Therefore use dummy contents (one zero byte) 662 // when creating section for the host bundle. 663 Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); 664 if (!TempFileOrErr) 665 return TempFileOrErr.takeError(); 666 InputFile = *TempFileOrErr; 667 } 668 669 ObjcopyArgs.push_back( 670 SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR + 671 BundlerConfig.TargetNames[I] + "=" + InputFile)); 672 ObjcopyArgs.push_back( 673 SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR + 674 BundlerConfig.TargetNames[I] + "=readonly,exclude")); 675 } 676 ObjcopyArgs.push_back("--"); 677 ObjcopyArgs.push_back( 678 BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]); 679 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front()); 680 681 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 682 return Err; 683 684 return Error::success(); 685 } 686 687 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 688 return Error::success(); 689 } 690 691 private: 692 Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { 693 // If the user asked for the commands to be printed out, we do that 694 // instead of executing it. 695 if (BundlerConfig.PrintExternalCommands) { 696 errs() << "\"" << Objcopy << "\""; 697 for (StringRef Arg : drop_begin(Args, 1)) 698 errs() << " \"" << Arg << "\""; 699 errs() << "\n"; 700 } else { 701 if (sys::ExecuteAndWait(Objcopy, Args)) 702 return createStringError(inconvertibleErrorCode(), 703 "'llvm-objcopy' tool failed"); 704 } 705 return Error::success(); 706 } 707 708 Expected<std::string> getHostBundle(StringRef Input) { 709 TempFileHandlerRAII TempFiles; 710 711 auto ModifiedObjPathOrErr = TempFiles.Create(std::nullopt); 712 if (!ModifiedObjPathOrErr) 713 return ModifiedObjPathOrErr.takeError(); 714 StringRef ModifiedObjPath = *ModifiedObjPathOrErr; 715 716 BumpPtrAllocator Alloc; 717 StringSaver SS{Alloc}; 718 SmallVector<StringRef, 16> ObjcopyArgs{"llvm-objcopy"}; 719 720 ObjcopyArgs.push_back("--regex"); 721 ObjcopyArgs.push_back("--remove-section=__CLANG_OFFLOAD_BUNDLE__.*"); 722 ObjcopyArgs.push_back("--"); 723 724 StringRef ObjcopyInputFileName; 725 // When unbundling an archive, the content of each object file in the 726 // archive is passed to this function by parameter Input, which is different 727 // from the content of the original input archive file, therefore it needs 728 // to be saved to a temporary file before passed to llvm-objcopy. Otherwise, 729 // Input is the same as the content of the original input file, therefore 730 // temporary file is not needed. 731 if (StringRef(BundlerConfig.FilesType).starts_with("a")) { 732 auto InputFileOrErr = 733 TempFiles.Create(ArrayRef<char>(Input.data(), Input.size())); 734 if (!InputFileOrErr) 735 return InputFileOrErr.takeError(); 736 ObjcopyInputFileName = *InputFileOrErr; 737 } else 738 ObjcopyInputFileName = BundlerConfig.InputFileNames.front(); 739 740 ObjcopyArgs.push_back(ObjcopyInputFileName); 741 ObjcopyArgs.push_back(ModifiedObjPath); 742 743 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 744 return std::move(Err); 745 746 auto BufOrErr = MemoryBuffer::getFile(ModifiedObjPath); 747 if (!BufOrErr) 748 return createStringError(BufOrErr.getError(), 749 "Failed to read back the modified object file"); 750 751 return BufOrErr->get()->getBuffer().str(); 752 } 753 }; 754 755 /// Handler for text files. The bundled file will have the following format. 756 /// 757 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 758 /// Bundle 1 759 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 760 /// ... 761 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 762 /// Bundle N 763 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 764 class TextFileHandler final : public FileHandler { 765 /// String that begins a line comment. 766 StringRef Comment; 767 768 /// String that initiates a bundle. 769 std::string BundleStartString; 770 771 /// String that closes a bundle. 772 std::string BundleEndString; 773 774 /// Number of chars read from input. 775 size_t ReadChars = 0u; 776 777 protected: 778 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 779 780 Expected<std::optional<StringRef>> 781 ReadBundleStart(MemoryBuffer &Input) final { 782 StringRef FC = Input.getBuffer(); 783 784 // Find start of the bundle. 785 ReadChars = FC.find(BundleStartString, ReadChars); 786 if (ReadChars == FC.npos) 787 return std::nullopt; 788 789 // Get position of the triple. 790 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); 791 792 // Get position that closes the triple. 793 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); 794 if (TripleEnd == FC.npos) 795 return std::nullopt; 796 797 // Next time we read after the new line. 798 ++ReadChars; 799 800 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); 801 } 802 803 Error ReadBundleEnd(MemoryBuffer &Input) final { 804 StringRef FC = Input.getBuffer(); 805 806 // Read up to the next new line. 807 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); 808 809 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); 810 if (TripleEnd != FC.npos) 811 // Next time we read after the new line. 812 ++ReadChars; 813 814 return Error::success(); 815 } 816 817 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 818 StringRef FC = Input.getBuffer(); 819 size_t BundleStart = ReadChars; 820 821 // Find end of the bundle. 822 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); 823 824 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); 825 OS << Bundle; 826 827 return Error::success(); 828 } 829 830 Error WriteHeader(raw_ostream &OS, 831 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 832 return Error::success(); 833 } 834 835 Error WriteBundleStart(raw_ostream &OS, StringRef TargetTriple) final { 836 OS << BundleStartString << TargetTriple << "\n"; 837 return Error::success(); 838 } 839 840 Error WriteBundleEnd(raw_ostream &OS, StringRef TargetTriple) final { 841 OS << BundleEndString << TargetTriple << "\n"; 842 return Error::success(); 843 } 844 845 Error WriteBundle(raw_ostream &OS, MemoryBuffer &Input) final { 846 OS << Input.getBuffer(); 847 return Error::success(); 848 } 849 850 public: 851 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) { 852 BundleStartString = 853 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; 854 BundleEndString = 855 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; 856 } 857 858 Error listBundleIDsCallback(MemoryBuffer &Input, 859 const BundleInfo &Info) final { 860 // TODO: To list bundle IDs in a bundled text file we need to go through 861 // all bundles. The format of bundled text file may need to include a 862 // header if the performance of listing bundle IDs of bundled text file is 863 // important. 864 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); 865 if (Error Err = ReadBundleEnd(Input)) 866 return Err; 867 return Error::success(); 868 } 869 }; 870 } // namespace 871 872 /// Return an appropriate object file handler. We use the specific object 873 /// handler if we know how to deal with that format, otherwise we use a default 874 /// binary file handler. 875 static std::unique_ptr<FileHandler> 876 CreateObjectFileHandler(MemoryBuffer &FirstInput, 877 const OffloadBundlerConfig &BundlerConfig) { 878 // Check if the input file format is one that we know how to deal with. 879 Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); 880 881 // We only support regular object files. If failed to open the input as a 882 // known binary or this is not an object file use the default binary handler. 883 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) 884 return std::make_unique<BinaryFileHandler>(BundlerConfig); 885 886 // Otherwise create an object file handler. The handler will be owned by the 887 // client of this function. 888 return std::make_unique<ObjectFileHandler>( 889 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())), 890 BundlerConfig); 891 } 892 893 /// Return an appropriate handler given the input files and options. 894 static Expected<std::unique_ptr<FileHandler>> 895 CreateFileHandler(MemoryBuffer &FirstInput, 896 const OffloadBundlerConfig &BundlerConfig) { 897 std::string FilesType = BundlerConfig.FilesType; 898 899 if (FilesType == "i") 900 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 901 if (FilesType == "ii") 902 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 903 if (FilesType == "cui") 904 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 905 if (FilesType == "hipi") 906 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 907 // TODO: `.d` should be eventually removed once `-M` and its variants are 908 // handled properly in offload compilation. 909 if (FilesType == "d") 910 return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 911 if (FilesType == "ll") 912 return std::make_unique<TextFileHandler>(/*Comment=*/";"); 913 if (FilesType == "bc") 914 return std::make_unique<BinaryFileHandler>(BundlerConfig); 915 if (FilesType == "s") 916 return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 917 if (FilesType == "o") 918 return CreateObjectFileHandler(FirstInput, BundlerConfig); 919 if (FilesType == "a") 920 return CreateObjectFileHandler(FirstInput, BundlerConfig); 921 if (FilesType == "gch") 922 return std::make_unique<BinaryFileHandler>(BundlerConfig); 923 if (FilesType == "ast") 924 return std::make_unique<BinaryFileHandler>(BundlerConfig); 925 926 return createStringError(errc::invalid_argument, 927 "'" + FilesType + "': invalid file type specified"); 928 } 929 930 OffloadBundlerConfig::OffloadBundlerConfig() { 931 if (llvm::compression::zstd::isAvailable()) { 932 CompressionFormat = llvm::compression::Format::Zstd; 933 // Compression level 3 is usually sufficient for zstd since long distance 934 // matching is enabled. 935 CompressionLevel = 3; 936 } else if (llvm::compression::zlib::isAvailable()) { 937 CompressionFormat = llvm::compression::Format::Zlib; 938 // Use default level for zlib since higher level does not have significant 939 // improvement. 940 CompressionLevel = llvm::compression::zlib::DefaultCompression; 941 } 942 auto IgnoreEnvVarOpt = 943 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_IGNORE_ENV_VAR"); 944 if (IgnoreEnvVarOpt.has_value() && IgnoreEnvVarOpt.value() == "1") 945 return; 946 947 auto VerboseEnvVarOpt = llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_VERBOSE"); 948 if (VerboseEnvVarOpt.has_value()) 949 Verbose = VerboseEnvVarOpt.value() == "1"; 950 951 auto CompressEnvVarOpt = 952 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESS"); 953 if (CompressEnvVarOpt.has_value()) 954 Compress = CompressEnvVarOpt.value() == "1"; 955 956 auto CompressionLevelEnvVarOpt = 957 llvm::sys::Process::GetEnv("OFFLOAD_BUNDLER_COMPRESSION_LEVEL"); 958 if (CompressionLevelEnvVarOpt.has_value()) { 959 llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value(); 960 int Level; 961 if (!CompressionLevelStr.getAsInteger(10, Level)) 962 CompressionLevel = Level; 963 else 964 llvm::errs() 965 << "Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: " 966 << CompressionLevelStr.str() << ". Ignoring it.\n"; 967 } 968 } 969 970 // Utility function to format numbers with commas 971 static std::string formatWithCommas(unsigned long long Value) { 972 std::string Num = std::to_string(Value); 973 int InsertPosition = Num.length() - 3; 974 while (InsertPosition > 0) { 975 Num.insert(InsertPosition, ","); 976 InsertPosition -= 3; 977 } 978 return Num; 979 } 980 981 llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> 982 CompressedOffloadBundle::compress(llvm::compression::Params P, 983 const llvm::MemoryBuffer &Input, 984 bool Verbose) { 985 if (!llvm::compression::zstd::isAvailable() && 986 !llvm::compression::zlib::isAvailable()) 987 return createStringError(llvm::inconvertibleErrorCode(), 988 "Compression not supported"); 989 990 llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time", 991 ClangOffloadBundlerTimerGroup); 992 if (Verbose) 993 HashTimer.startTimer(); 994 llvm::MD5 Hash; 995 llvm::MD5::MD5Result Result; 996 Hash.update(Input.getBuffer()); 997 Hash.final(Result); 998 uint64_t TruncatedHash = Result.low(); 999 if (Verbose) 1000 HashTimer.stopTimer(); 1001 1002 SmallVector<uint8_t, 0> CompressedBuffer; 1003 auto BufferUint8 = llvm::ArrayRef<uint8_t>( 1004 reinterpret_cast<const uint8_t *>(Input.getBuffer().data()), 1005 Input.getBuffer().size()); 1006 1007 llvm::Timer CompressTimer("Compression Timer", "Compression time", 1008 ClangOffloadBundlerTimerGroup); 1009 if (Verbose) 1010 CompressTimer.startTimer(); 1011 llvm::compression::compress(P, BufferUint8, CompressedBuffer); 1012 if (Verbose) 1013 CompressTimer.stopTimer(); 1014 1015 uint16_t CompressionMethod = static_cast<uint16_t>(P.format); 1016 uint32_t UncompressedSize = Input.getBuffer().size(); 1017 uint32_t TotalFileSize = MagicNumber.size() + sizeof(TotalFileSize) + 1018 sizeof(Version) + sizeof(CompressionMethod) + 1019 sizeof(UncompressedSize) + sizeof(TruncatedHash) + 1020 CompressedBuffer.size(); 1021 1022 SmallVector<char, 0> FinalBuffer; 1023 llvm::raw_svector_ostream OS(FinalBuffer); 1024 OS << MagicNumber; 1025 OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version)); 1026 OS.write(reinterpret_cast<const char *>(&CompressionMethod), 1027 sizeof(CompressionMethod)); 1028 OS.write(reinterpret_cast<const char *>(&TotalFileSize), 1029 sizeof(TotalFileSize)); 1030 OS.write(reinterpret_cast<const char *>(&UncompressedSize), 1031 sizeof(UncompressedSize)); 1032 OS.write(reinterpret_cast<const char *>(&TruncatedHash), 1033 sizeof(TruncatedHash)); 1034 OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()), 1035 CompressedBuffer.size()); 1036 1037 if (Verbose) { 1038 auto MethodUsed = 1039 P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib"; 1040 double CompressionRate = 1041 static_cast<double>(UncompressedSize) / CompressedBuffer.size(); 1042 double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime(); 1043 double CompressionSpeedMBs = 1044 (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds; 1045 1046 llvm::errs() << "Compressed bundle format version: " << Version << "\n" 1047 << "Total file size (including headers): " 1048 << formatWithCommas(TotalFileSize) << " bytes\n" 1049 << "Compression method used: " << MethodUsed << "\n" 1050 << "Compression level: " << P.level << "\n" 1051 << "Binary size before compression: " 1052 << formatWithCommas(UncompressedSize) << " bytes\n" 1053 << "Binary size after compression: " 1054 << formatWithCommas(CompressedBuffer.size()) << " bytes\n" 1055 << "Compression rate: " 1056 << llvm::format("%.2lf", CompressionRate) << "\n" 1057 << "Compression ratio: " 1058 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n" 1059 << "Compression speed: " 1060 << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n" 1061 << "Truncated MD5 hash: " 1062 << llvm::format_hex(TruncatedHash, 16) << "\n"; 1063 } 1064 return llvm::MemoryBuffer::getMemBufferCopy( 1065 llvm::StringRef(FinalBuffer.data(), FinalBuffer.size())); 1066 } 1067 1068 llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> 1069 CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input, 1070 bool Verbose) { 1071 1072 StringRef Blob = Input.getBuffer(); 1073 1074 if (Blob.size() < V1HeaderSize) 1075 return llvm::MemoryBuffer::getMemBufferCopy(Blob); 1076 1077 if (llvm::identify_magic(Blob) != 1078 llvm::file_magic::offload_bundle_compressed) { 1079 if (Verbose) 1080 llvm::errs() << "Uncompressed bundle.\n"; 1081 return llvm::MemoryBuffer::getMemBufferCopy(Blob); 1082 } 1083 1084 size_t CurrentOffset = MagicSize; 1085 1086 uint16_t ThisVersion; 1087 memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t)); 1088 CurrentOffset += VersionFieldSize; 1089 1090 uint16_t CompressionMethod; 1091 memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t)); 1092 CurrentOffset += MethodFieldSize; 1093 1094 uint32_t TotalFileSize; 1095 if (ThisVersion >= 2) { 1096 if (Blob.size() < V2HeaderSize) 1097 return createStringError(inconvertibleErrorCode(), 1098 "Compressed bundle header size too small"); 1099 memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint32_t)); 1100 CurrentOffset += FileSizeFieldSize; 1101 } 1102 1103 uint32_t UncompressedSize; 1104 memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t)); 1105 CurrentOffset += UncompressedSizeFieldSize; 1106 1107 uint64_t StoredHash; 1108 memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t)); 1109 CurrentOffset += HashFieldSize; 1110 1111 llvm::compression::Format CompressionFormat; 1112 if (CompressionMethod == 1113 static_cast<uint16_t>(llvm::compression::Format::Zlib)) 1114 CompressionFormat = llvm::compression::Format::Zlib; 1115 else if (CompressionMethod == 1116 static_cast<uint16_t>(llvm::compression::Format::Zstd)) 1117 CompressionFormat = llvm::compression::Format::Zstd; 1118 else 1119 return createStringError(inconvertibleErrorCode(), 1120 "Unknown compressing method"); 1121 1122 llvm::Timer DecompressTimer("Decompression Timer", "Decompression time", 1123 ClangOffloadBundlerTimerGroup); 1124 if (Verbose) 1125 DecompressTimer.startTimer(); 1126 1127 SmallVector<uint8_t, 0> DecompressedData; 1128 StringRef CompressedData = Blob.substr(CurrentOffset); 1129 if (llvm::Error DecompressionError = llvm::compression::decompress( 1130 CompressionFormat, llvm::arrayRefFromStringRef(CompressedData), 1131 DecompressedData, UncompressedSize)) 1132 return createStringError(inconvertibleErrorCode(), 1133 "Could not decompress embedded file contents: " + 1134 llvm::toString(std::move(DecompressionError))); 1135 1136 if (Verbose) { 1137 DecompressTimer.stopTimer(); 1138 1139 double DecompressionTimeSeconds = 1140 DecompressTimer.getTotalTime().getWallTime(); 1141 1142 // Recalculate MD5 hash for integrity check 1143 llvm::Timer HashRecalcTimer("Hash Recalculation Timer", 1144 "Hash recalculation time", 1145 ClangOffloadBundlerTimerGroup); 1146 HashRecalcTimer.startTimer(); 1147 llvm::MD5 Hash; 1148 llvm::MD5::MD5Result Result; 1149 Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData.data(), 1150 DecompressedData.size())); 1151 Hash.final(Result); 1152 uint64_t RecalculatedHash = Result.low(); 1153 HashRecalcTimer.stopTimer(); 1154 bool HashMatch = (StoredHash == RecalculatedHash); 1155 1156 double CompressionRate = 1157 static_cast<double>(UncompressedSize) / CompressedData.size(); 1158 double DecompressionSpeedMBs = 1159 (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds; 1160 1161 llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n"; 1162 if (ThisVersion >= 2) 1163 llvm::errs() << "Total file size (from header): " 1164 << formatWithCommas(TotalFileSize) << " bytes\n"; 1165 llvm::errs() << "Decompression method: " 1166 << (CompressionFormat == llvm::compression::Format::Zlib 1167 ? "zlib" 1168 : "zstd") 1169 << "\n" 1170 << "Size before decompression: " 1171 << formatWithCommas(CompressedData.size()) << " bytes\n" 1172 << "Size after decompression: " 1173 << formatWithCommas(UncompressedSize) << " bytes\n" 1174 << "Compression rate: " 1175 << llvm::format("%.2lf", CompressionRate) << "\n" 1176 << "Compression ratio: " 1177 << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n" 1178 << "Decompression speed: " 1179 << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n" 1180 << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n" 1181 << "Recalculated hash: " 1182 << llvm::format_hex(RecalculatedHash, 16) << "\n" 1183 << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n"; 1184 } 1185 1186 return llvm::MemoryBuffer::getMemBufferCopy( 1187 llvm::toStringRef(DecompressedData)); 1188 } 1189 1190 // List bundle IDs. Return true if an error was found. 1191 Error OffloadBundler::ListBundleIDsInFile( 1192 StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) { 1193 // Open Input file. 1194 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1195 MemoryBuffer::getFileOrSTDIN(InputFileName); 1196 if (std::error_code EC = CodeOrErr.getError()) 1197 return createFileError(InputFileName, EC); 1198 1199 // Decompress the input if necessary. 1200 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 1201 CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose); 1202 if (!DecompressedBufferOrErr) 1203 return createStringError( 1204 inconvertibleErrorCode(), 1205 "Failed to decompress input: " + 1206 llvm::toString(DecompressedBufferOrErr.takeError())); 1207 1208 MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr; 1209 1210 // Select the right files handler. 1211 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1212 CreateFileHandler(DecompressedInput, BundlerConfig); 1213 if (!FileHandlerOrErr) 1214 return FileHandlerOrErr.takeError(); 1215 1216 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1217 assert(FH); 1218 return FH->listBundleIDs(DecompressedInput); 1219 } 1220 1221 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given 1222 /// target \p TargetInfo. 1223 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id 1224 bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, 1225 const OffloadTargetInfo &TargetInfo) { 1226 1227 // Compatible in case of exact match. 1228 if (CodeObjectInfo == TargetInfo) { 1229 DEBUG_WITH_TYPE("CodeObjectCompatibility", 1230 dbgs() << "Compatible: Exact match: \t[CodeObject: " 1231 << CodeObjectInfo.str() 1232 << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1233 return true; 1234 } 1235 1236 // Incompatible if Kinds or Triples mismatch. 1237 if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || 1238 !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { 1239 DEBUG_WITH_TYPE( 1240 "CodeObjectCompatibility", 1241 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " 1242 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 1243 << "]\n"); 1244 return false; 1245 } 1246 1247 // Incompatible if Processors mismatch. 1248 llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap; 1249 std::optional<StringRef> CodeObjectProc = clang::parseTargetID( 1250 CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap); 1251 std::optional<StringRef> TargetProc = clang::parseTargetID( 1252 TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap); 1253 1254 // Both TargetProc and CodeObjectProc can't be empty here. 1255 if (!TargetProc || !CodeObjectProc || 1256 CodeObjectProc.value() != TargetProc.value()) { 1257 DEBUG_WITH_TYPE("CodeObjectCompatibility", 1258 dbgs() << "Incompatible: Processor mismatch \t[CodeObject: " 1259 << CodeObjectInfo.str() 1260 << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1261 return false; 1262 } 1263 1264 // Incompatible if CodeObject has more features than Target, irrespective of 1265 // type or sign of features. 1266 if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) { 1267 DEBUG_WITH_TYPE("CodeObjectCompatibility", 1268 dbgs() << "Incompatible: CodeObject has more features " 1269 "than target \t[CodeObject: " 1270 << CodeObjectInfo.str() 1271 << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1272 return false; 1273 } 1274 1275 // Compatible if each target feature specified by target is compatible with 1276 // target feature of code object. The target feature is compatible if the 1277 // code object does not specify it (meaning Any), or if it specifies it 1278 // with the same value (meaning On or Off). 1279 for (const auto &CodeObjectFeature : CodeObjectFeatureMap) { 1280 auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey()); 1281 if (TargetFeature == TargetFeatureMap.end()) { 1282 DEBUG_WITH_TYPE( 1283 "CodeObjectCompatibility", 1284 dbgs() 1285 << "Incompatible: Value of CodeObject's non-ANY feature is " 1286 "not matching with Target feature's ANY value \t[CodeObject: " 1287 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 1288 << "]\n"); 1289 return false; 1290 } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) { 1291 DEBUG_WITH_TYPE( 1292 "CodeObjectCompatibility", 1293 dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is " 1294 "not matching with Target feature's non-ANY value " 1295 "\t[CodeObject: " 1296 << CodeObjectInfo.str() 1297 << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 1298 return false; 1299 } 1300 } 1301 1302 // CodeObject is compatible if all features of Target are: 1303 // - either, present in the Code Object's features map with the same sign, 1304 // - or, the feature is missing from CodeObjects's features map i.e. it is 1305 // set to ANY 1306 DEBUG_WITH_TYPE( 1307 "CodeObjectCompatibility", 1308 dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: " 1309 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 1310 << "]\n"); 1311 return true; 1312 } 1313 1314 /// Bundle the files. Return true if an error was found. 1315 Error OffloadBundler::BundleFiles() { 1316 std::error_code EC; 1317 1318 // Create a buffer to hold the content before compressing. 1319 SmallVector<char, 0> Buffer; 1320 llvm::raw_svector_ostream BufferStream(Buffer); 1321 1322 // Open input files. 1323 SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; 1324 InputBuffers.reserve(BundlerConfig.InputFileNames.size()); 1325 for (auto &I : BundlerConfig.InputFileNames) { 1326 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1327 MemoryBuffer::getFileOrSTDIN(I); 1328 if (std::error_code EC = CodeOrErr.getError()) 1329 return createFileError(I, EC); 1330 InputBuffers.emplace_back(std::move(*CodeOrErr)); 1331 } 1332 1333 // Get the file handler. We use the host buffer as reference. 1334 assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) && 1335 "Host input index undefined??"); 1336 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler( 1337 *InputBuffers[BundlerConfig.AllowNoHost ? 0 1338 : BundlerConfig.HostInputIndex], 1339 BundlerConfig); 1340 if (!FileHandlerOrErr) 1341 return FileHandlerOrErr.takeError(); 1342 1343 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1344 assert(FH); 1345 1346 // Write header. 1347 if (Error Err = FH->WriteHeader(BufferStream, InputBuffers)) 1348 return Err; 1349 1350 // Write all bundles along with the start/end markers. If an error was found 1351 // writing the end of the bundle component, abort the bundle writing. 1352 auto Input = InputBuffers.begin(); 1353 for (auto &Triple : BundlerConfig.TargetNames) { 1354 if (Error Err = FH->WriteBundleStart(BufferStream, Triple)) 1355 return Err; 1356 if (Error Err = FH->WriteBundle(BufferStream, **Input)) 1357 return Err; 1358 if (Error Err = FH->WriteBundleEnd(BufferStream, Triple)) 1359 return Err; 1360 ++Input; 1361 } 1362 1363 raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC, 1364 sys::fs::OF_None); 1365 if (EC) 1366 return createFileError(BundlerConfig.OutputFileNames.front(), EC); 1367 1368 SmallVector<char, 0> CompressedBuffer; 1369 if (BundlerConfig.Compress) { 1370 std::unique_ptr<llvm::MemoryBuffer> BufferMemory = 1371 llvm::MemoryBuffer::getMemBufferCopy( 1372 llvm::StringRef(Buffer.data(), Buffer.size())); 1373 auto CompressionResult = CompressedOffloadBundle::compress( 1374 {BundlerConfig.CompressionFormat, BundlerConfig.CompressionLevel, 1375 /*zstdEnableLdm=*/true}, 1376 *BufferMemory, BundlerConfig.Verbose); 1377 if (auto Error = CompressionResult.takeError()) 1378 return Error; 1379 1380 auto CompressedMemBuffer = std::move(CompressionResult.get()); 1381 CompressedBuffer.assign(CompressedMemBuffer->getBufferStart(), 1382 CompressedMemBuffer->getBufferEnd()); 1383 } else 1384 CompressedBuffer = Buffer; 1385 1386 OutputFile.write(CompressedBuffer.data(), CompressedBuffer.size()); 1387 1388 return FH->finalizeOutputFile(); 1389 } 1390 1391 // Unbundle the files. Return true if an error was found. 1392 Error OffloadBundler::UnbundleFiles() { 1393 // Open Input file. 1394 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 1395 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front()); 1396 if (std::error_code EC = CodeOrErr.getError()) 1397 return createFileError(BundlerConfig.InputFileNames.front(), EC); 1398 1399 // Decompress the input if necessary. 1400 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 1401 CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose); 1402 if (!DecompressedBufferOrErr) 1403 return createStringError( 1404 inconvertibleErrorCode(), 1405 "Failed to decompress input: " + 1406 llvm::toString(DecompressedBufferOrErr.takeError())); 1407 1408 MemoryBuffer &Input = **DecompressedBufferOrErr; 1409 1410 // Select the right files handler. 1411 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1412 CreateFileHandler(Input, BundlerConfig); 1413 if (!FileHandlerOrErr) 1414 return FileHandlerOrErr.takeError(); 1415 1416 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 1417 assert(FH); 1418 1419 // Read the header of the bundled file. 1420 if (Error Err = FH->ReadHeader(Input)) 1421 return Err; 1422 1423 // Create a work list that consist of the map triple/output file. 1424 StringMap<StringRef> Worklist; 1425 auto Output = BundlerConfig.OutputFileNames.begin(); 1426 for (auto &Triple : BundlerConfig.TargetNames) { 1427 Worklist[Triple] = *Output; 1428 ++Output; 1429 } 1430 1431 // Read all the bundles that are in the work list. If we find no bundles we 1432 // assume the file is meant for the host target. 1433 bool FoundHostBundle = false; 1434 while (!Worklist.empty()) { 1435 Expected<std::optional<StringRef>> CurTripleOrErr = 1436 FH->ReadBundleStart(Input); 1437 if (!CurTripleOrErr) 1438 return CurTripleOrErr.takeError(); 1439 1440 // We don't have more bundles. 1441 if (!*CurTripleOrErr) 1442 break; 1443 1444 StringRef CurTriple = **CurTripleOrErr; 1445 assert(!CurTriple.empty()); 1446 1447 auto Output = Worklist.begin(); 1448 for (auto E = Worklist.end(); Output != E; Output++) { 1449 if (isCodeObjectCompatible( 1450 OffloadTargetInfo(CurTriple, BundlerConfig), 1451 OffloadTargetInfo((*Output).first(), BundlerConfig))) { 1452 break; 1453 } 1454 } 1455 1456 if (Output == Worklist.end()) 1457 continue; 1458 // Check if the output file can be opened and copy the bundle to it. 1459 std::error_code EC; 1460 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); 1461 if (EC) 1462 return createFileError((*Output).second, EC); 1463 if (Error Err = FH->ReadBundle(OutputFile, Input)) 1464 return Err; 1465 if (Error Err = FH->ReadBundleEnd(Input)) 1466 return Err; 1467 Worklist.erase(Output); 1468 1469 // Record if we found the host bundle. 1470 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig); 1471 if (OffloadInfo.hasHostKind()) 1472 FoundHostBundle = true; 1473 } 1474 1475 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) { 1476 std::string ErrMsg = "Can't find bundles for"; 1477 std::set<StringRef> Sorted; 1478 for (auto &E : Worklist) 1479 Sorted.insert(E.first()); 1480 unsigned I = 0; 1481 unsigned Last = Sorted.size() - 1; 1482 for (auto &E : Sorted) { 1483 if (I != 0 && Last > 1) 1484 ErrMsg += ","; 1485 ErrMsg += " "; 1486 if (I == Last && I != 0) 1487 ErrMsg += "and "; 1488 ErrMsg += E.str(); 1489 ++I; 1490 } 1491 return createStringError(inconvertibleErrorCode(), ErrMsg); 1492 } 1493 1494 // If no bundles were found, assume the input file is the host bundle and 1495 // create empty files for the remaining targets. 1496 if (Worklist.size() == BundlerConfig.TargetNames.size()) { 1497 for (auto &E : Worklist) { 1498 std::error_code EC; 1499 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1500 if (EC) 1501 return createFileError(E.second, EC); 1502 1503 // If this entry has a host kind, copy the input file to the output file. 1504 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig); 1505 if (OffloadInfo.hasHostKind()) 1506 OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); 1507 } 1508 return Error::success(); 1509 } 1510 1511 // If we found elements, we emit an error if none of those were for the host 1512 // in case host bundle name was provided in command line. 1513 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u || 1514 BundlerConfig.AllowMissingBundles)) 1515 return createStringError(inconvertibleErrorCode(), 1516 "Can't find bundle for the host target"); 1517 1518 // If we still have any elements in the worklist, create empty files for them. 1519 for (auto &E : Worklist) { 1520 std::error_code EC; 1521 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1522 if (EC) 1523 return createFileError(E.second, EC); 1524 } 1525 1526 return Error::success(); 1527 } 1528 1529 static Archive::Kind getDefaultArchiveKindForHost() { 1530 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN 1531 : Archive::K_GNU; 1532 } 1533 1534 /// @brief Computes a list of targets among all given targets which are 1535 /// compatible with this code object 1536 /// @param [in] CodeObjectInfo Code Object 1537 /// @param [out] CompatibleTargets List of all compatible targets among all 1538 /// given targets 1539 /// @return false, if no compatible target is found. 1540 static bool 1541 getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, 1542 SmallVectorImpl<StringRef> &CompatibleTargets, 1543 const OffloadBundlerConfig &BundlerConfig) { 1544 if (!CompatibleTargets.empty()) { 1545 DEBUG_WITH_TYPE("CodeObjectCompatibility", 1546 dbgs() << "CompatibleTargets list should be empty\n"); 1547 return false; 1548 } 1549 for (auto &Target : BundlerConfig.TargetNames) { 1550 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig); 1551 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) 1552 CompatibleTargets.push_back(Target); 1553 } 1554 return !CompatibleTargets.empty(); 1555 } 1556 1557 // Check that each code object file in the input archive conforms to following 1558 // rule: for a specific processor, a feature either shows up in all target IDs, 1559 // or does not show up in any target IDs. Otherwise the target ID combination is 1560 // invalid. 1561 static Error 1562 CheckHeterogeneousArchive(StringRef ArchiveName, 1563 const OffloadBundlerConfig &BundlerConfig) { 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); 1569 1570 ArchiveBuffers.push_back(std::move(*BufOrErr)); 1571 Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1572 Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1573 if (!LibOrErr) 1574 return LibOrErr.takeError(); 1575 1576 auto Archive = std::move(*LibOrErr); 1577 1578 Error ArchiveErr = Error::success(); 1579 auto ChildEnd = Archive->child_end(); 1580 1581 /// Iterate over all bundled code object files in the input archive. 1582 for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1583 ArchiveIter != ChildEnd; ++ArchiveIter) { 1584 if (ArchiveErr) 1585 return ArchiveErr; 1586 auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1587 if (!ArchiveChildNameOrErr) 1588 return ArchiveChildNameOrErr.takeError(); 1589 1590 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1591 if (!CodeObjectBufferRefOrErr) 1592 return CodeObjectBufferRefOrErr.takeError(); 1593 1594 auto CodeObjectBuffer = 1595 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1596 1597 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1598 CreateFileHandler(*CodeObjectBuffer, BundlerConfig); 1599 if (!FileHandlerOrErr) 1600 return FileHandlerOrErr.takeError(); 1601 1602 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1603 assert(FileHandler); 1604 1605 std::set<StringRef> BundleIds; 1606 auto CodeObjectFileError = 1607 FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds); 1608 if (CodeObjectFileError) 1609 return CodeObjectFileError; 1610 1611 auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds); 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) 1617 .str(); 1618 return createStringError(inconvertibleErrorCode(), ErrMsg); 1619 } 1620 } 1621 1622 return ArchiveErr; 1623 } 1624 1625 /// UnbundleArchive takes an archive file (".a") as input containing bundled 1626 /// code object files, and a list of offload targets (not host), and extracts 1627 /// the code objects into a new archive file for each offload target. Each 1628 /// resulting archive file contains all code object files corresponding to that 1629 /// particular offload target. The created archive file does not 1630 /// contain an index of the symbols and code object files are named as 1631 /// <<Parent Bundle Name>-<CodeObject's TargetID>>, with ':' replaced with '_'. 1632 Error OffloadBundler::UnbundleArchive() { 1633 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 1634 1635 /// Map of target names with list of object files that will form the device 1636 /// specific archive for that target 1637 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; 1638 1639 // Map of target names and output archive filenames 1640 StringMap<StringRef> TargetOutputFileNameMap; 1641 1642 auto Output = BundlerConfig.OutputFileNames.begin(); 1643 for (auto &Target : BundlerConfig.TargetNames) { 1644 TargetOutputFileNameMap[Target] = *Output; 1645 ++Output; 1646 } 1647 1648 StringRef IFName = BundlerConfig.InputFileNames.front(); 1649 1650 if (BundlerConfig.CheckInputArchive) { 1651 // For a specific processor, a feature either shows up in all target IDs, or 1652 // does not show up in any target IDs. Otherwise the target ID combination 1653 // is invalid. 1654 auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig); 1655 if (ArchiveError) { 1656 return ArchiveError; 1657 } 1658 } 1659 1660 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 1661 MemoryBuffer::getFileOrSTDIN(IFName, true, false); 1662 if (std::error_code EC = BufOrErr.getError()) 1663 return createFileError(BundlerConfig.InputFileNames.front(), EC); 1664 1665 ArchiveBuffers.push_back(std::move(*BufOrErr)); 1666 Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1667 Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1668 if (!LibOrErr) 1669 return LibOrErr.takeError(); 1670 1671 auto Archive = std::move(*LibOrErr); 1672 1673 Error ArchiveErr = Error::success(); 1674 auto ChildEnd = Archive->child_end(); 1675 1676 /// Iterate over all bundled code object files in the input archive. 1677 for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1678 ArchiveIter != ChildEnd; ++ArchiveIter) { 1679 if (ArchiveErr) 1680 return ArchiveErr; 1681 auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1682 if (!ArchiveChildNameOrErr) 1683 return ArchiveChildNameOrErr.takeError(); 1684 1685 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); 1686 1687 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1688 if (!CodeObjectBufferRefOrErr) 1689 return CodeObjectBufferRefOrErr.takeError(); 1690 1691 auto TempCodeObjectBuffer = 1692 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1693 1694 // Decompress the buffer if necessary. 1695 Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr = 1696 CompressedOffloadBundle::decompress(*TempCodeObjectBuffer, 1697 BundlerConfig.Verbose); 1698 if (!DecompressedBufferOrErr) 1699 return createStringError( 1700 inconvertibleErrorCode(), 1701 "Failed to decompress code object: " + 1702 llvm::toString(DecompressedBufferOrErr.takeError())); 1703 1704 MemoryBuffer &CodeObjectBuffer = **DecompressedBufferOrErr; 1705 1706 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1707 CreateFileHandler(CodeObjectBuffer, BundlerConfig); 1708 if (!FileHandlerOrErr) 1709 return FileHandlerOrErr.takeError(); 1710 1711 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1712 assert(FileHandler && 1713 "FileHandle creation failed for file in the archive!"); 1714 1715 if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer)) 1716 return ReadErr; 1717 1718 Expected<std::optional<StringRef>> CurBundleIDOrErr = 1719 FileHandler->ReadBundleStart(CodeObjectBuffer); 1720 if (!CurBundleIDOrErr) 1721 return CurBundleIDOrErr.takeError(); 1722 1723 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; 1724 // No device code in this child, skip. 1725 if (!OptionalCurBundleID) 1726 continue; 1727 StringRef CodeObject = *OptionalCurBundleID; 1728 1729 // Process all bundle entries (CodeObjects) found in this child of input 1730 // archive. 1731 while (!CodeObject.empty()) { 1732 SmallVector<StringRef> CompatibleTargets; 1733 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig); 1734 if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets, 1735 BundlerConfig)) { 1736 std::string BundleData; 1737 raw_string_ostream DataStream(BundleData); 1738 if (Error Err = FileHandler->ReadBundle(DataStream, CodeObjectBuffer)) 1739 return Err; 1740 1741 for (auto &CompatibleTarget : CompatibleTargets) { 1742 SmallString<128> BundledObjectFileName; 1743 BundledObjectFileName.assign(BundledObjectFile); 1744 auto OutputBundleName = 1745 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + 1746 CodeObject + 1747 getDeviceLibraryFileName(BundledObjectFileName, 1748 CodeObjectInfo.TargetID)) 1749 .str(); 1750 // Replace ':' in optional target feature list with '_' to ensure 1751 // cross-platform validity. 1752 std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', 1753 '_'); 1754 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())); 1760 1761 // For inserting <CompatibleTarget, list<CodeObject>> entry in 1762 // OutputArchivesMap. 1763 if (!OutputArchivesMap.contains(CompatibleTarget)) { 1764 1765 std::vector<NewArchiveMember> ArchiveMembers; 1766 ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); 1767 OutputArchivesMap.insert_or_assign(CompatibleTarget, 1768 std::move(ArchiveMembers)); 1769 } else { 1770 OutputArchivesMap[CompatibleTarget].push_back( 1771 NewArchiveMember(MemBufRef)); 1772 } 1773 } 1774 } 1775 1776 if (Error Err = FileHandler->ReadBundleEnd(CodeObjectBuffer)) 1777 return Err; 1778 1779 Expected<std::optional<StringRef>> NextTripleOrErr = 1780 FileHandler->ReadBundleStart(CodeObjectBuffer); 1781 if (!NextTripleOrErr) 1782 return NextTripleOrErr.takeError(); 1783 1784 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : ""; 1785 } // End of processing of all bundle entries of this child of input archive. 1786 } // End of while over children of input archive. 1787 1788 assert(!ArchiveErr && "Error occurred while reading archive!"); 1789 1790 /// Write out an archive for each target 1791 for (auto &Target : BundlerConfig.TargetNames) { 1792 StringRef FileName = TargetOutputFileNameMap[Target]; 1793 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = 1794 OutputArchivesMap.find(Target); 1795 if (CurArchiveMembers != OutputArchivesMap.end()) { 1796 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), 1797 SymtabWritingMode::NormalSymtab, 1798 getDefaultArchiveKindForHost(), true, 1799 false, nullptr)) 1800 return WriteErr; 1801 } else if (!BundlerConfig.AllowMissingBundles) { 1802 std::string ErrMsg = 1803 Twine("no compatible code object found for the target '" + Target + 1804 "' in heterogeneous archive library: " + IFName) 1805 .str(); 1806 return createStringError(inconvertibleErrorCode(), ErrMsg); 1807 } else { // Create an empty archive file if no compatible code object is 1808 // found and "allow-missing-bundles" is enabled. It ensures that 1809 // the linker using output of this step doesn't complain about 1810 // the missing input file. 1811 std::vector<llvm::NewArchiveMember> EmptyArchive; 1812 EmptyArchive.clear(); 1813 if (Error WriteErr = writeArchive( 1814 FileName, EmptyArchive, SymtabWritingMode::NormalSymtab, 1815 getDefaultArchiveKindForHost(), true, false, nullptr)) 1816 return WriteErr; 1817 } 1818 } 1819 1820 return Error::success(); 1821 } 1822