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/StringMap.h" 25 #include "llvm/ADT/StringRef.h" 26 #include "llvm/ADT/Triple.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/Debug.h" 33 #include "llvm/Support/EndianStream.h" 34 #include "llvm/Support/Errc.h" 35 #include "llvm/Support/Error.h" 36 #include "llvm/Support/ErrorOr.h" 37 #include "llvm/Support/FileSystem.h" 38 #include "llvm/Support/Host.h" 39 #include "llvm/Support/MemoryBuffer.h" 40 #include "llvm/Support/Path.h" 41 #include "llvm/Support/Program.h" 42 #include "llvm/Support/Signals.h" 43 #include "llvm/Support/StringSaver.h" 44 #include "llvm/Support/WithColor.h" 45 #include "llvm/Support/raw_ostream.h" 46 #include <algorithm> 47 #include <cassert> 48 #include <cstddef> 49 #include <cstdint> 50 #include <forward_list> 51 #include <memory> 52 #include <set> 53 #include <string> 54 #include <system_error> 55 #include <utility> 56 57 using namespace llvm; 58 using namespace llvm::object; 59 using namespace clang; 60 61 /// Magic string that marks the existence of offloading data. 62 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" 63 64 OffloadTargetInfo::OffloadTargetInfo(const StringRef Target, 65 const OffloadBundlerConfig &BC) 66 : BundlerConfig(BC) { 67 68 // TODO: Add error checking from ClangOffloadBundler.cpp 69 auto TargetFeatures = Target.split(':'); 70 auto TripleOrGPU = TargetFeatures.first.rsplit('-'); 71 72 if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) { 73 auto KindTriple = TripleOrGPU.first.split('-'); 74 this->OffloadKind = KindTriple.first; 75 this->Triple = llvm::Triple(KindTriple.second); 76 this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); 77 } else { 78 auto KindTriple = TargetFeatures.first.split('-'); 79 this->OffloadKind = KindTriple.first; 80 this->Triple = llvm::Triple(KindTriple.second); 81 this->TargetID = ""; 82 } 83 } 84 85 bool OffloadTargetInfo::hasHostKind() const { 86 return this->OffloadKind == "host"; 87 } 88 89 bool OffloadTargetInfo::isOffloadKindValid() const { 90 return OffloadKind == "host" || OffloadKind == "openmp" || 91 OffloadKind == "hip" || OffloadKind == "hipv4"; 92 } 93 94 bool OffloadTargetInfo::isOffloadKindCompatible( 95 const StringRef TargetOffloadKind) const { 96 if (OffloadKind == TargetOffloadKind) 97 return true; 98 if (BundlerConfig.HipOpenmpCompatible) { 99 bool HIPCompatibleWithOpenMP = OffloadKind.startswith_insensitive("hip") && 100 TargetOffloadKind == "openmp"; 101 bool OpenMPCompatibleWithHIP = 102 OffloadKind == "openmp" && 103 TargetOffloadKind.startswith_insensitive("hip"); 104 return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP; 105 } 106 return false; 107 } 108 109 bool OffloadTargetInfo::isTripleValid() const { 110 return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; 111 } 112 113 bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { 114 return OffloadKind == Target.OffloadKind && 115 Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; 116 } 117 118 std::string OffloadTargetInfo::str() const { 119 return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); 120 } 121 122 static StringRef getDeviceFileExtension(StringRef Device, 123 StringRef BundleFileName) { 124 if (Device.contains("gfx")) 125 return ".bc"; 126 if (Device.contains("sm_")) 127 return ".cubin"; 128 return sys::path::extension(BundleFileName); 129 } 130 131 static std::string getDeviceLibraryFileName(StringRef BundleFileName, 132 StringRef Device) { 133 StringRef LibName = sys::path::stem(BundleFileName); 134 StringRef Extension = getDeviceFileExtension(Device, BundleFileName); 135 136 std::string Result; 137 Result += LibName; 138 Result += Extension; 139 return Result; 140 } 141 142 /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given 143 /// target \p TargetInfo. 144 /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id 145 bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, 146 const OffloadTargetInfo &TargetInfo) { 147 148 // Compatible in case of exact match. 149 if (CodeObjectInfo == TargetInfo) { 150 DEBUG_WITH_TYPE("CodeObjectCompatibility", 151 dbgs() << "Compatible: Exact match: \t[CodeObject: " 152 << CodeObjectInfo.str() 153 << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 154 return true; 155 } 156 157 // Incompatible if Kinds or Triples mismatch. 158 if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || 159 !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { 160 DEBUG_WITH_TYPE( 161 "CodeObjectCompatibility", 162 dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " 163 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 164 << "]\n"); 165 return false; 166 } 167 168 // Incompatible if target IDs are incompatible. 169 if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID, 170 TargetInfo.TargetID)) { 171 DEBUG_WITH_TYPE( 172 "CodeObjectCompatibility", 173 dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: " 174 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 175 << "]\n"); 176 return false; 177 } 178 179 DEBUG_WITH_TYPE( 180 "CodeObjectCompatibility", 181 dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " 182 << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 183 << "]\n"); 184 return true; 185 } 186 187 namespace { 188 /// Generic file handler interface. 189 class FileHandler { 190 public: 191 struct BundleInfo { 192 StringRef BundleID; 193 }; 194 195 FileHandler() {} 196 197 virtual ~FileHandler() {} 198 199 /// Update the file handler with information from the header of the bundled 200 /// file. 201 virtual Error ReadHeader(MemoryBuffer &Input) = 0; 202 203 /// Read the marker of the next bundled to be read in the file. The bundle 204 /// name is returned if there is one in the file, or `std::nullopt` if there 205 /// are no more bundles to be read. 206 virtual Expected<std::optional<StringRef>> 207 ReadBundleStart(MemoryBuffer &Input) = 0; 208 209 /// Read the marker that closes the current bundle. 210 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; 211 212 /// Read the current bundle and write the result into the stream \a OS. 213 virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 214 215 /// Write the header of the bundled file to \a OS based on the information 216 /// gathered from \a Inputs. 217 virtual Error WriteHeader(raw_fd_ostream &OS, 218 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; 219 220 /// Write the marker that initiates a bundle for the triple \a TargetTriple to 221 /// \a OS. 222 virtual Error WriteBundleStart(raw_fd_ostream &OS, 223 StringRef TargetTriple) = 0; 224 225 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a 226 /// OS. 227 virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0; 228 229 /// Write the bundle from \a Input into \a OS. 230 virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0; 231 232 /// List bundle IDs in \a Input. 233 virtual Error listBundleIDs(MemoryBuffer &Input) { 234 if (Error Err = ReadHeader(Input)) 235 return Err; 236 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 237 llvm::outs() << Info.BundleID << '\n'; 238 Error Err = listBundleIDsCallback(Input, Info); 239 if (Err) 240 return Err; 241 return Error::success(); 242 }); 243 } 244 245 /// For each bundle in \a Input, do \a Func. 246 Error forEachBundle(MemoryBuffer &Input, 247 std::function<Error(const BundleInfo &)> Func) { 248 while (true) { 249 Expected<std::optional<StringRef>> CurTripleOrErr = 250 ReadBundleStart(Input); 251 if (!CurTripleOrErr) 252 return CurTripleOrErr.takeError(); 253 254 // No more bundles. 255 if (!*CurTripleOrErr) 256 break; 257 258 StringRef CurTriple = **CurTripleOrErr; 259 assert(!CurTriple.empty()); 260 261 BundleInfo Info{CurTriple}; 262 if (Error Err = Func(Info)) 263 return Err; 264 } 265 return Error::success(); 266 } 267 268 protected: 269 virtual Error listBundleIDsCallback(MemoryBuffer &Input, 270 const BundleInfo &Info) { 271 return Error::success(); 272 } 273 }; 274 275 /// Handler for binary files. The bundled file will have the following format 276 /// (all integers are stored in little-endian format): 277 /// 278 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) 279 /// 280 /// NumberOfOffloadBundles (8-byte integer) 281 /// 282 /// OffsetOfBundle1 (8-byte integer) 283 /// SizeOfBundle1 (8-byte integer) 284 /// NumberOfBytesInTripleOfBundle1 (8-byte integer) 285 /// TripleOfBundle1 (byte length defined before) 286 /// 287 /// ... 288 /// 289 /// OffsetOfBundleN (8-byte integer) 290 /// SizeOfBundleN (8-byte integer) 291 /// NumberOfBytesInTripleOfBundleN (8-byte integer) 292 /// TripleOfBundleN (byte length defined before) 293 /// 294 /// Bundle1 295 /// ... 296 /// BundleN 297 298 /// Read 8-byte integers from a buffer in little-endian format. 299 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { 300 return llvm::support::endian::read64le(Buffer.data() + pos); 301 } 302 303 /// Write 8-byte integers to a buffer in little-endian format. 304 static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) { 305 llvm::support::endian::write(OS, Val, llvm::support::little); 306 } 307 308 class BinaryFileHandler final : public FileHandler { 309 /// Information about the bundles extracted from the header. 310 struct BinaryBundleInfo final : public BundleInfo { 311 /// Size of the bundle. 312 uint64_t Size = 0u; 313 /// Offset at which the bundle starts in the bundled file. 314 uint64_t Offset = 0u; 315 316 BinaryBundleInfo() {} 317 BinaryBundleInfo(uint64_t Size, uint64_t Offset) 318 : Size(Size), Offset(Offset) {} 319 }; 320 321 /// Map between a triple and the corresponding bundle information. 322 StringMap<BinaryBundleInfo> BundlesInfo; 323 324 /// Iterator for the bundle information that is being read. 325 StringMap<BinaryBundleInfo>::iterator CurBundleInfo; 326 StringMap<BinaryBundleInfo>::iterator NextBundleInfo; 327 328 /// Current bundle target to be written. 329 std::string CurWriteBundleTarget; 330 331 /// Configuration options and arrays for this bundler job 332 const OffloadBundlerConfig &BundlerConfig; 333 334 public: 335 // TODO: Add error checking from ClangOffloadBundler.cpp 336 BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {} 337 338 ~BinaryFileHandler() final {} 339 340 Error ReadHeader(MemoryBuffer &Input) final { 341 StringRef FC = Input.getBuffer(); 342 343 // Initialize the current bundle with the end of the container. 344 CurBundleInfo = BundlesInfo.end(); 345 346 // Check if buffer is smaller than magic string. 347 size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 348 if (ReadChars > FC.size()) 349 return Error::success(); 350 351 // Check if no magic was found. 352 StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 353 if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR)) 354 return Error::success(); 355 356 // Read number of bundles. 357 if (ReadChars + 8 > FC.size()) 358 return Error::success(); 359 360 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); 361 ReadChars += 8; 362 363 // Read bundle offsets, sizes and triples. 364 for (uint64_t i = 0; i < NumberOfBundles; ++i) { 365 366 // Read offset. 367 if (ReadChars + 8 > FC.size()) 368 return Error::success(); 369 370 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); 371 ReadChars += 8; 372 373 // Read size. 374 if (ReadChars + 8 > FC.size()) 375 return Error::success(); 376 377 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); 378 ReadChars += 8; 379 380 // Read triple size. 381 if (ReadChars + 8 > FC.size()) 382 return Error::success(); 383 384 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); 385 ReadChars += 8; 386 387 // Read triple. 388 if (ReadChars + TripleSize > FC.size()) 389 return Error::success(); 390 391 StringRef Triple(&FC.data()[ReadChars], TripleSize); 392 ReadChars += TripleSize; 393 394 // Check if the offset and size make sense. 395 if (!Offset || Offset + Size > FC.size()) 396 return Error::success(); 397 398 assert(BundlesInfo.find(Triple) == BundlesInfo.end() && 399 "Triple is duplicated??"); 400 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); 401 } 402 // Set the iterator to where we will start to read. 403 CurBundleInfo = BundlesInfo.end(); 404 NextBundleInfo = BundlesInfo.begin(); 405 return Error::success(); 406 } 407 408 Expected<std::optional<StringRef>> 409 ReadBundleStart(MemoryBuffer &Input) final { 410 if (NextBundleInfo == BundlesInfo.end()) 411 return std::nullopt; 412 CurBundleInfo = NextBundleInfo++; 413 return CurBundleInfo->first(); 414 } 415 416 Error ReadBundleEnd(MemoryBuffer &Input) final { 417 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 418 return Error::success(); 419 } 420 421 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 422 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 423 StringRef FC = Input.getBuffer(); 424 OS.write(FC.data() + CurBundleInfo->second.Offset, 425 CurBundleInfo->second.Size); 426 return Error::success(); 427 } 428 429 Error WriteHeader(raw_fd_ostream &OS, 430 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 431 432 // Compute size of the header. 433 uint64_t HeaderSize = 0; 434 435 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 436 HeaderSize += 8; // Number of Bundles 437 438 for (auto &T : BundlerConfig.TargetNames) { 439 HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. 440 HeaderSize += T.size(); // The triple. 441 } 442 443 // Write to the buffer the header. 444 OS << OFFLOAD_BUNDLER_MAGIC_STR; 445 446 Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size()); 447 448 unsigned Idx = 0; 449 for (auto &T : BundlerConfig.TargetNames) { 450 MemoryBuffer &MB = *Inputs[Idx++]; 451 HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment); 452 // Bundle offset. 453 Write8byteIntegerToBuffer(OS, HeaderSize); 454 // Size of the bundle (adds to the next bundle's offset) 455 Write8byteIntegerToBuffer(OS, MB.getBufferSize()); 456 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); 457 HeaderSize += MB.getBufferSize(); 458 // Size of the triple 459 Write8byteIntegerToBuffer(OS, T.size()); 460 // Triple 461 OS << T; 462 } 463 return Error::success(); 464 } 465 466 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 467 CurWriteBundleTarget = TargetTriple.str(); 468 return Error::success(); 469 } 470 471 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 472 return Error::success(); 473 } 474 475 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 476 auto BI = BundlesInfo[CurWriteBundleTarget]; 477 OS.seek(BI.Offset); 478 OS.write(Input.getBufferStart(), Input.getBufferSize()); 479 return Error::success(); 480 } 481 }; 482 483 // This class implements a list of temporary files that are removed upon 484 // object destruction. 485 class TempFileHandlerRAII { 486 public: 487 ~TempFileHandlerRAII() { 488 for (const auto &File : Files) 489 sys::fs::remove(File); 490 } 491 492 // Creates temporary file with given contents. 493 Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) { 494 SmallString<128u> File; 495 if (std::error_code EC = 496 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) 497 return createFileError(File, EC); 498 Files.push_front(File); 499 500 if (Contents) { 501 std::error_code EC; 502 raw_fd_ostream OS(File, EC); 503 if (EC) 504 return createFileError(File, EC); 505 OS.write(Contents->data(), Contents->size()); 506 } 507 return Files.front().str(); 508 } 509 510 private: 511 std::forward_list<SmallString<128u>> Files; 512 }; 513 514 /// Handler for object files. The bundles are organized by sections with a 515 /// designated name. 516 /// 517 /// To unbundle, we just copy the contents of the designated section. 518 class ObjectFileHandler final : public FileHandler { 519 520 /// The object file we are currently dealing with. 521 std::unique_ptr<ObjectFile> Obj; 522 523 /// Return the input file contents. 524 StringRef getInputFileContents() const { return Obj->getData(); } 525 526 /// Return bundle name (<kind>-<triple>) if the provided section is an offload 527 /// section. 528 static Expected<std::optional<StringRef>> 529 IsOffloadSection(SectionRef CurSection) { 530 Expected<StringRef> NameOrErr = CurSection.getName(); 531 if (!NameOrErr) 532 return NameOrErr.takeError(); 533 534 // If it does not start with the reserved suffix, just skip this section. 535 if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR)) 536 return std::nullopt; 537 538 // Return the triple that is right after the reserved prefix. 539 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 540 } 541 542 /// Total number of inputs. 543 unsigned NumberOfInputs = 0; 544 545 /// Total number of processed inputs, i.e, inputs that were already 546 /// read from the buffers. 547 unsigned NumberOfProcessedInputs = 0; 548 549 /// Iterator of the current and next section. 550 section_iterator CurrentSection; 551 section_iterator NextSection; 552 553 /// Configuration options and arrays for this bundler job 554 const OffloadBundlerConfig &BundlerConfig; 555 556 public: 557 // TODO: Add error checking from ClangOffloadBundler.cpp 558 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn, 559 const OffloadBundlerConfig &BC) 560 : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()), 561 NextSection(Obj->section_begin()), BundlerConfig(BC) {} 562 563 ~ObjectFileHandler() final {} 564 565 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 566 567 Expected<std::optional<StringRef>> 568 ReadBundleStart(MemoryBuffer &Input) final { 569 while (NextSection != Obj->section_end()) { 570 CurrentSection = NextSection; 571 ++NextSection; 572 573 // Check if the current section name starts with the reserved prefix. If 574 // so, return the triple. 575 Expected<std::optional<StringRef>> TripleOrErr = 576 IsOffloadSection(*CurrentSection); 577 if (!TripleOrErr) 578 return TripleOrErr.takeError(); 579 if (*TripleOrErr) 580 return **TripleOrErr; 581 } 582 return std::nullopt; 583 } 584 585 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 586 587 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 588 Expected<StringRef> ContentOrErr = CurrentSection->getContents(); 589 if (!ContentOrErr) 590 return ContentOrErr.takeError(); 591 StringRef Content = *ContentOrErr; 592 593 // Copy fat object contents to the output when extracting host bundle. 594 if (Content.size() == 1u && Content.front() == 0) 595 Content = StringRef(Input.getBufferStart(), Input.getBufferSize()); 596 597 OS.write(Content.data(), Content.size()); 598 return Error::success(); 599 } 600 601 Error WriteHeader(raw_fd_ostream &OS, 602 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 603 assert(BundlerConfig.HostInputIndex != ~0u && 604 "Host input index not defined."); 605 606 // Record number of inputs. 607 NumberOfInputs = Inputs.size(); 608 return Error::success(); 609 } 610 611 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 612 ++NumberOfProcessedInputs; 613 return Error::success(); 614 } 615 616 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 617 assert(NumberOfProcessedInputs <= NumberOfInputs && 618 "Processing more inputs that actually exist!"); 619 assert(BundlerConfig.HostInputIndex != ~0u && 620 "Host input index not defined."); 621 622 // If this is not the last output, we don't have to do anything. 623 if (NumberOfProcessedInputs != NumberOfInputs) 624 return Error::success(); 625 626 // We will use llvm-objcopy to add target objects sections to the output 627 // fat object. These sections should have 'exclude' flag set which tells 628 // link editor to remove them from linker inputs when linking executable or 629 // shared library. 630 631 assert(BundlerConfig.ObjcopyPath != "" && 632 "llvm-objcopy path not specified"); 633 634 // We write to the output file directly. So, we close it and use the name 635 // to pass down to llvm-objcopy. 636 OS.close(); 637 638 // Temporary files that need to be removed. 639 TempFileHandlerRAII TempFiles; 640 641 // Compose llvm-objcopy command line for add target objects' sections with 642 // appropriate flags. 643 BumpPtrAllocator Alloc; 644 StringSaver SS{Alloc}; 645 SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; 646 647 for (unsigned I = 0; I < NumberOfInputs; ++I) { 648 StringRef InputFile = BundlerConfig.InputFileNames[I]; 649 if (I == BundlerConfig.HostInputIndex) { 650 // Special handling for the host bundle. We do not need to add a 651 // standard bundle for the host object since we are going to use fat 652 // object as a host object. Therefore use dummy contents (one zero byte) 653 // when creating section for the host bundle. 654 Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); 655 if (!TempFileOrErr) 656 return TempFileOrErr.takeError(); 657 InputFile = *TempFileOrErr; 658 } 659 660 ObjcopyArgs.push_back( 661 SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR + 662 BundlerConfig.TargetNames[I] + "=" + InputFile)); 663 ObjcopyArgs.push_back( 664 SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR + 665 BundlerConfig.TargetNames[I] + "=readonly,exclude")); 666 } 667 ObjcopyArgs.push_back("--"); 668 ObjcopyArgs.push_back( 669 BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]); 670 ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front()); 671 672 if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 673 return Err; 674 675 return Error::success(); 676 } 677 678 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 679 return Error::success(); 680 } 681 682 private: 683 Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { 684 // If the user asked for the commands to be printed out, we do that 685 // instead of executing it. 686 if (BundlerConfig.PrintExternalCommands) { 687 errs() << "\"" << Objcopy << "\""; 688 for (StringRef Arg : drop_begin(Args, 1)) 689 errs() << " \"" << Arg << "\""; 690 errs() << "\n"; 691 } else { 692 if (sys::ExecuteAndWait(Objcopy, Args)) 693 return createStringError(inconvertibleErrorCode(), 694 "'llvm-objcopy' tool failed"); 695 } 696 return Error::success(); 697 } 698 }; 699 700 /// Handler for text files. The bundled file will have the following format. 701 /// 702 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 703 /// Bundle 1 704 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 705 /// ... 706 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 707 /// Bundle N 708 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 709 class TextFileHandler final : public FileHandler { 710 /// String that begins a line comment. 711 StringRef Comment; 712 713 /// String that initiates a bundle. 714 std::string BundleStartString; 715 716 /// String that closes a bundle. 717 std::string BundleEndString; 718 719 /// Number of chars read from input. 720 size_t ReadChars = 0u; 721 722 protected: 723 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 724 725 Expected<std::optional<StringRef>> 726 ReadBundleStart(MemoryBuffer &Input) final { 727 StringRef FC = Input.getBuffer(); 728 729 // Find start of the bundle. 730 ReadChars = FC.find(BundleStartString, ReadChars); 731 if (ReadChars == FC.npos) 732 return std::nullopt; 733 734 // Get position of the triple. 735 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); 736 737 // Get position that closes the triple. 738 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); 739 if (TripleEnd == FC.npos) 740 return std::nullopt; 741 742 // Next time we read after the new line. 743 ++ReadChars; 744 745 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); 746 } 747 748 Error ReadBundleEnd(MemoryBuffer &Input) final { 749 StringRef FC = Input.getBuffer(); 750 751 // Read up to the next new line. 752 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); 753 754 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); 755 if (TripleEnd != FC.npos) 756 // Next time we read after the new line. 757 ++ReadChars; 758 759 return Error::success(); 760 } 761 762 Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 763 StringRef FC = Input.getBuffer(); 764 size_t BundleStart = ReadChars; 765 766 // Find end of the bundle. 767 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); 768 769 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); 770 OS << Bundle; 771 772 return Error::success(); 773 } 774 775 Error WriteHeader(raw_fd_ostream &OS, 776 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 777 return Error::success(); 778 } 779 780 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 781 OS << BundleStartString << TargetTriple << "\n"; 782 return Error::success(); 783 } 784 785 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 786 OS << BundleEndString << TargetTriple << "\n"; 787 return Error::success(); 788 } 789 790 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 791 OS << Input.getBuffer(); 792 return Error::success(); 793 } 794 795 public: 796 TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) { 797 BundleStartString = 798 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; 799 BundleEndString = 800 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; 801 } 802 803 Error listBundleIDsCallback(MemoryBuffer &Input, 804 const BundleInfo &Info) final { 805 // TODO: To list bundle IDs in a bundled text file we need to go through 806 // all bundles. The format of bundled text file may need to include a 807 // header if the performance of listing bundle IDs of bundled text file is 808 // important. 809 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); 810 if (Error Err = ReadBundleEnd(Input)) 811 return Err; 812 return Error::success(); 813 } 814 }; 815 } // namespace 816 817 /// Return an appropriate object file handler. We use the specific object 818 /// handler if we know how to deal with that format, otherwise we use a default 819 /// binary file handler. 820 static std::unique_ptr<FileHandler> 821 CreateObjectFileHandler(MemoryBuffer &FirstInput, 822 const OffloadBundlerConfig &BundlerConfig) { 823 // Check if the input file format is one that we know how to deal with. 824 Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); 825 826 // We only support regular object files. If failed to open the input as a 827 // known binary or this is not an object file use the default binary handler. 828 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) 829 return std::make_unique<BinaryFileHandler>(BundlerConfig); 830 831 // Otherwise create an object file handler. The handler will be owned by the 832 // client of this function. 833 return std::make_unique<ObjectFileHandler>( 834 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())), 835 BundlerConfig); 836 } 837 838 /// Return an appropriate handler given the input files and options. 839 static Expected<std::unique_ptr<FileHandler>> 840 CreateFileHandler(MemoryBuffer &FirstInput, 841 const OffloadBundlerConfig &BundlerConfig) { 842 std::string FilesType = BundlerConfig.FilesType; 843 844 if (FilesType == "i") 845 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 846 if (FilesType == "ii") 847 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 848 if (FilesType == "cui") 849 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 850 if (FilesType == "hipi") 851 return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 852 // TODO: `.d` should be eventually removed once `-M` and its variants are 853 // handled properly in offload compilation. 854 if (FilesType == "d") 855 return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 856 if (FilesType == "ll") 857 return std::make_unique<TextFileHandler>(/*Comment=*/";"); 858 if (FilesType == "bc") 859 return std::make_unique<BinaryFileHandler>(BundlerConfig); 860 if (FilesType == "s") 861 return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 862 if (FilesType == "o") 863 return CreateObjectFileHandler(FirstInput, BundlerConfig); 864 if (FilesType == "a") 865 return CreateObjectFileHandler(FirstInput, BundlerConfig); 866 if (FilesType == "gch") 867 return std::make_unique<BinaryFileHandler>(BundlerConfig); 868 if (FilesType == "ast") 869 return std::make_unique<BinaryFileHandler>(BundlerConfig); 870 871 return createStringError(errc::invalid_argument, 872 "'" + FilesType + "': invalid file type specified"); 873 } 874 875 // List bundle IDs. Return true if an error was found. 876 Error OffloadBundler::ListBundleIDsInFile( 877 StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) { 878 // Open Input file. 879 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 880 MemoryBuffer::getFileOrSTDIN(InputFileName); 881 if (std::error_code EC = CodeOrErr.getError()) 882 return createFileError(InputFileName, EC); 883 884 MemoryBuffer &Input = **CodeOrErr; 885 886 // Select the right files handler. 887 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 888 CreateFileHandler(Input, BundlerConfig); 889 if (!FileHandlerOrErr) 890 return FileHandlerOrErr.takeError(); 891 892 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 893 assert(FH); 894 return FH->listBundleIDs(Input); 895 } 896 897 /// Bundle the files. Return true if an error was found. 898 Error OffloadBundler::BundleFiles() { 899 std::error_code EC; 900 901 // Create output file. 902 raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC, 903 sys::fs::OF_None); 904 if (EC) 905 return createFileError(BundlerConfig.OutputFileNames.front(), EC); 906 907 // Open input files. 908 SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; 909 InputBuffers.reserve(BundlerConfig.InputFileNames.size()); 910 for (auto &I : BundlerConfig.InputFileNames) { 911 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 912 MemoryBuffer::getFileOrSTDIN(I); 913 if (std::error_code EC = CodeOrErr.getError()) 914 return createFileError(I, EC); 915 InputBuffers.emplace_back(std::move(*CodeOrErr)); 916 } 917 918 // Get the file handler. We use the host buffer as reference. 919 assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) && 920 "Host input index undefined??"); 921 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler( 922 *InputBuffers[BundlerConfig.AllowNoHost ? 0 923 : BundlerConfig.HostInputIndex], 924 BundlerConfig); 925 if (!FileHandlerOrErr) 926 return FileHandlerOrErr.takeError(); 927 928 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 929 assert(FH); 930 931 // Write header. 932 if (Error Err = FH->WriteHeader(OutputFile, InputBuffers)) 933 return Err; 934 935 // Write all bundles along with the start/end markers. If an error was found 936 // writing the end of the bundle component, abort the bundle writing. 937 auto Input = InputBuffers.begin(); 938 for (auto &Triple : BundlerConfig.TargetNames) { 939 if (Error Err = FH->WriteBundleStart(OutputFile, Triple)) 940 return Err; 941 if (Error Err = FH->WriteBundle(OutputFile, **Input)) 942 return Err; 943 if (Error Err = FH->WriteBundleEnd(OutputFile, Triple)) 944 return Err; 945 ++Input; 946 } 947 return Error::success(); 948 } 949 950 // Unbundle the files. Return true if an error was found. 951 Error OffloadBundler::UnbundleFiles() { 952 // Open Input file. 953 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 954 MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front()); 955 if (std::error_code EC = CodeOrErr.getError()) 956 return createFileError(BundlerConfig.InputFileNames.front(), EC); 957 958 MemoryBuffer &Input = **CodeOrErr; 959 960 // Select the right files handler. 961 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 962 CreateFileHandler(Input, BundlerConfig); 963 if (!FileHandlerOrErr) 964 return FileHandlerOrErr.takeError(); 965 966 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 967 assert(FH); 968 969 // Read the header of the bundled file. 970 if (Error Err = FH->ReadHeader(Input)) 971 return Err; 972 973 // Create a work list that consist of the map triple/output file. 974 StringMap<StringRef> Worklist; 975 auto Output = BundlerConfig.OutputFileNames.begin(); 976 for (auto &Triple : BundlerConfig.TargetNames) { 977 Worklist[Triple] = *Output; 978 ++Output; 979 } 980 981 // Read all the bundles that are in the work list. If we find no bundles we 982 // assume the file is meant for the host target. 983 bool FoundHostBundle = false; 984 while (!Worklist.empty()) { 985 Expected<std::optional<StringRef>> CurTripleOrErr = 986 FH->ReadBundleStart(Input); 987 if (!CurTripleOrErr) 988 return CurTripleOrErr.takeError(); 989 990 // We don't have more bundles. 991 if (!*CurTripleOrErr) 992 break; 993 994 StringRef CurTriple = **CurTripleOrErr; 995 assert(!CurTriple.empty()); 996 997 auto Output = Worklist.begin(); 998 for (auto E = Worklist.end(); Output != E; Output++) { 999 if (isCodeObjectCompatible( 1000 OffloadTargetInfo(CurTriple, BundlerConfig), 1001 OffloadTargetInfo((*Output).first(), BundlerConfig))) { 1002 break; 1003 } 1004 } 1005 1006 if (Output == Worklist.end()) 1007 continue; 1008 // Check if the output file can be opened and copy the bundle to it. 1009 std::error_code EC; 1010 raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); 1011 if (EC) 1012 return createFileError((*Output).second, EC); 1013 if (Error Err = FH->ReadBundle(OutputFile, Input)) 1014 return Err; 1015 if (Error Err = FH->ReadBundleEnd(Input)) 1016 return Err; 1017 Worklist.erase(Output); 1018 1019 // Record if we found the host bundle. 1020 auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig); 1021 if (OffloadInfo.hasHostKind()) 1022 FoundHostBundle = true; 1023 } 1024 1025 if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) { 1026 std::string ErrMsg = "Can't find bundles for"; 1027 std::set<StringRef> Sorted; 1028 for (auto &E : Worklist) 1029 Sorted.insert(E.first()); 1030 unsigned I = 0; 1031 unsigned Last = Sorted.size() - 1; 1032 for (auto &E : Sorted) { 1033 if (I != 0 && Last > 1) 1034 ErrMsg += ","; 1035 ErrMsg += " "; 1036 if (I == Last && I != 0) 1037 ErrMsg += "and "; 1038 ErrMsg += E.str(); 1039 ++I; 1040 } 1041 return createStringError(inconvertibleErrorCode(), ErrMsg); 1042 } 1043 1044 // If no bundles were found, assume the input file is the host bundle and 1045 // create empty files for the remaining targets. 1046 if (Worklist.size() == BundlerConfig.TargetNames.size()) { 1047 for (auto &E : Worklist) { 1048 std::error_code EC; 1049 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1050 if (EC) 1051 return createFileError(E.second, EC); 1052 1053 // If this entry has a host kind, copy the input file to the output file. 1054 auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig); 1055 if (OffloadInfo.hasHostKind()) 1056 OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); 1057 } 1058 return Error::success(); 1059 } 1060 1061 // If we found elements, we emit an error if none of those were for the host 1062 // in case host bundle name was provided in command line. 1063 if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u || 1064 BundlerConfig.AllowMissingBundles)) 1065 return createStringError(inconvertibleErrorCode(), 1066 "Can't find bundle for the host target"); 1067 1068 // If we still have any elements in the worklist, create empty files for them. 1069 for (auto &E : Worklist) { 1070 std::error_code EC; 1071 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1072 if (EC) 1073 return createFileError(E.second, EC); 1074 } 1075 1076 return Error::success(); 1077 } 1078 1079 static Archive::Kind getDefaultArchiveKindForHost() { 1080 return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN 1081 : Archive::K_GNU; 1082 } 1083 1084 /// @brief Computes a list of targets among all given targets which are 1085 /// compatible with this code object 1086 /// @param [in] CodeObjectInfo Code Object 1087 /// @param [out] CompatibleTargets List of all compatible targets among all 1088 /// given targets 1089 /// @return false, if no compatible target is found. 1090 static bool 1091 getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, 1092 SmallVectorImpl<StringRef> &CompatibleTargets, 1093 const OffloadBundlerConfig &BundlerConfig) { 1094 if (!CompatibleTargets.empty()) { 1095 DEBUG_WITH_TYPE("CodeObjectCompatibility", 1096 dbgs() << "CompatibleTargets list should be empty\n"); 1097 return false; 1098 } 1099 for (auto &Target : BundlerConfig.TargetNames) { 1100 auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig); 1101 if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) 1102 CompatibleTargets.push_back(Target); 1103 } 1104 return !CompatibleTargets.empty(); 1105 } 1106 1107 /// UnbundleArchive takes an archive file (".a") as input containing bundled 1108 /// code object files, and a list of offload targets (not host), and extracts 1109 /// the code objects into a new archive file for each offload target. Each 1110 /// resulting archive file contains all code object files corresponding to that 1111 /// particular offload target. The created archive file does not 1112 /// contain an index of the symbols and code object files are named as 1113 /// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'. 1114 Error OffloadBundler::UnbundleArchive() { 1115 std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 1116 1117 /// Map of target names with list of object files that will form the device 1118 /// specific archive for that target 1119 StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; 1120 1121 // Map of target names and output archive filenames 1122 StringMap<StringRef> TargetOutputFileNameMap; 1123 1124 auto Output = BundlerConfig.OutputFileNames.begin(); 1125 for (auto &Target : BundlerConfig.TargetNames) { 1126 TargetOutputFileNameMap[Target] = *Output; 1127 ++Output; 1128 } 1129 1130 StringRef IFName = BundlerConfig.InputFileNames.front(); 1131 1132 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 1133 MemoryBuffer::getFileOrSTDIN(IFName, true, false); 1134 if (std::error_code EC = BufOrErr.getError()) 1135 return createFileError(BundlerConfig.InputFileNames.front(), EC); 1136 1137 ArchiveBuffers.push_back(std::move(*BufOrErr)); 1138 Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1139 Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1140 if (!LibOrErr) 1141 return LibOrErr.takeError(); 1142 1143 auto Archive = std::move(*LibOrErr); 1144 1145 Error ArchiveErr = Error::success(); 1146 auto ChildEnd = Archive->child_end(); 1147 1148 /// Iterate over all bundled code object files in the input archive. 1149 for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1150 ArchiveIter != ChildEnd; ++ArchiveIter) { 1151 if (ArchiveErr) 1152 return ArchiveErr; 1153 auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1154 if (!ArchiveChildNameOrErr) 1155 return ArchiveChildNameOrErr.takeError(); 1156 1157 StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); 1158 1159 auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1160 if (!CodeObjectBufferRefOrErr) 1161 return CodeObjectBufferRefOrErr.takeError(); 1162 1163 auto CodeObjectBuffer = 1164 MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1165 1166 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1167 CreateFileHandler(*CodeObjectBuffer, BundlerConfig); 1168 if (!FileHandlerOrErr) 1169 return FileHandlerOrErr.takeError(); 1170 1171 std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1172 assert(FileHandler && 1173 "FileHandle creation failed for file in the archive!"); 1174 1175 if (Error ReadErr = FileHandler->ReadHeader(*CodeObjectBuffer)) 1176 return ReadErr; 1177 1178 Expected<std::optional<StringRef>> CurBundleIDOrErr = 1179 FileHandler->ReadBundleStart(*CodeObjectBuffer); 1180 if (!CurBundleIDOrErr) 1181 return CurBundleIDOrErr.takeError(); 1182 1183 std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; 1184 // No device code in this child, skip. 1185 if (!OptionalCurBundleID) 1186 continue; 1187 StringRef CodeObject = *OptionalCurBundleID; 1188 1189 // Process all bundle entries (CodeObjects) found in this child of input 1190 // archive. 1191 while (!CodeObject.empty()) { 1192 SmallVector<StringRef> CompatibleTargets; 1193 auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig); 1194 if (CodeObjectInfo.hasHostKind()) { 1195 // Do nothing, we don't extract host code yet. 1196 } else if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets, 1197 BundlerConfig)) { 1198 std::string BundleData; 1199 raw_string_ostream DataStream(BundleData); 1200 if (Error Err = FileHandler->ReadBundle(DataStream, *CodeObjectBuffer)) 1201 return Err; 1202 1203 for (auto &CompatibleTarget : CompatibleTargets) { 1204 SmallString<128> BundledObjectFileName; 1205 BundledObjectFileName.assign(BundledObjectFile); 1206 auto OutputBundleName = 1207 Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + 1208 CodeObject + 1209 getDeviceLibraryFileName(BundledObjectFileName, 1210 CodeObjectInfo.TargetID)) 1211 .str(); 1212 // Replace ':' in optional target feature list with '_' to ensure 1213 // cross-platform validity. 1214 std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', 1215 '_'); 1216 1217 std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy( 1218 DataStream.str(), OutputBundleName); 1219 ArchiveBuffers.push_back(std::move(MemBuf)); 1220 llvm::MemoryBufferRef MemBufRef = 1221 MemoryBufferRef(*(ArchiveBuffers.back())); 1222 1223 // For inserting <CompatibleTarget, list<CodeObject>> entry in 1224 // OutputArchivesMap. 1225 if (OutputArchivesMap.find(CompatibleTarget) == 1226 OutputArchivesMap.end()) { 1227 1228 std::vector<NewArchiveMember> ArchiveMembers; 1229 ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); 1230 OutputArchivesMap.insert_or_assign(CompatibleTarget, 1231 std::move(ArchiveMembers)); 1232 } else { 1233 OutputArchivesMap[CompatibleTarget].push_back( 1234 NewArchiveMember(MemBufRef)); 1235 } 1236 } 1237 } 1238 1239 if (Error Err = FileHandler->ReadBundleEnd(*CodeObjectBuffer)) 1240 return Err; 1241 1242 Expected<std::optional<StringRef>> NextTripleOrErr = 1243 FileHandler->ReadBundleStart(*CodeObjectBuffer); 1244 if (!NextTripleOrErr) 1245 return NextTripleOrErr.takeError(); 1246 1247 CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : ""; 1248 } // End of processing of all bundle entries of this child of input archive. 1249 } // End of while over children of input archive. 1250 1251 assert(!ArchiveErr && "Error occurred while reading archive!"); 1252 1253 /// Write out an archive for each target 1254 for (auto &Target : BundlerConfig.TargetNames) { 1255 StringRef FileName = TargetOutputFileNameMap[Target]; 1256 StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = 1257 OutputArchivesMap.find(Target); 1258 if (CurArchiveMembers != OutputArchivesMap.end()) { 1259 if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), 1260 true, getDefaultArchiveKindForHost(), 1261 true, false, nullptr)) 1262 return WriteErr; 1263 } else if (!BundlerConfig.AllowMissingBundles) { 1264 std::string ErrMsg = 1265 Twine("no compatible code object found for the target '" + Target + 1266 "' in heterogeneous archive library: " + IFName) 1267 .str(); 1268 return createStringError(inconvertibleErrorCode(), ErrMsg); 1269 } else { // Create an empty archive file if no compatible code object is 1270 // found and "allow-missing-bundles" is enabled. It ensures that 1271 // the linker using output of this step doesn't complain about 1272 // the missing input file. 1273 std::vector<llvm::NewArchiveMember> EmptyArchive; 1274 EmptyArchive.clear(); 1275 if (Error WriteErr = writeArchive(FileName, EmptyArchive, true, 1276 getDefaultArchiveKindForHost(), true, 1277 false, nullptr)) 1278 return WriteErr; 1279 } 1280 } 1281 1282 return Error::success(); 1283 } 1284