1386 lines · cpp
1//===-- clang-linker-wrapper/ClangLinkerWrapper.cpp - wrapper over linker-===//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-exception6//7//===---------------------------------------------------------------------===//8//9// This tool works as a wrapper over a linking job. This tool is used to create10// linked device images for offloading. It scans the linker's input for embedded11// device offloading data stored in sections `.llvm.offloading` and extracts it12// as a temporary file. The extracted device files will then be passed to a13// device linking job to create a final device image.14//15//===---------------------------------------------------------------------===//16 17#include "clang/Basic/TargetID.h"18#include "clang/Basic/Version.h"19#include "llvm/ADT/MapVector.h"20#include "llvm/BinaryFormat/Magic.h"21#include "llvm/Bitcode/BitcodeWriter.h"22#include "llvm/CodeGen/CommandFlags.h"23#include "llvm/Frontend/Offloading/OffloadWrapper.h"24#include "llvm/Frontend/Offloading/Utility.h"25#include "llvm/IR/Constants.h"26#include "llvm/IR/DiagnosticPrinter.h"27#include "llvm/IR/Module.h"28#include "llvm/IRReader/IRReader.h"29#include "llvm/LTO/LTO.h"30#include "llvm/MC/TargetRegistry.h"31#include "llvm/Object/Archive.h"32#include "llvm/Object/ArchiveWriter.h"33#include "llvm/Object/Binary.h"34#include "llvm/Object/ELFObjectFile.h"35#include "llvm/Object/IRObjectFile.h"36#include "llvm/Object/ObjectFile.h"37#include "llvm/Object/OffloadBinary.h"38#include "llvm/Option/ArgList.h"39#include "llvm/Option/OptTable.h"40#include "llvm/Option/Option.h"41#include "llvm/Passes/PassPlugin.h"42#include "llvm/Remarks/HotnessThresholdParser.h"43#include "llvm/Support/CommandLine.h"44#include "llvm/Support/Errc.h"45#include "llvm/Support/FileOutputBuffer.h"46#include "llvm/Support/FileSystem.h"47#include "llvm/Support/InitLLVM.h"48#include "llvm/Support/MemoryBuffer.h"49#include "llvm/Support/Parallel.h"50#include "llvm/Support/Path.h"51#include "llvm/Support/Program.h"52#include "llvm/Support/Signals.h"53#include "llvm/Support/SourceMgr.h"54#include "llvm/Support/StringSaver.h"55#include "llvm/Support/TargetSelect.h"56#include "llvm/Support/TimeProfiler.h"57#include "llvm/Support/WithColor.h"58#include "llvm/Support/raw_ostream.h"59#include "llvm/Target/TargetMachine.h"60#include "llvm/TargetParser/Host.h"61#include <atomic>62#include <optional>63 64using namespace llvm;65using namespace llvm::opt;66using namespace llvm::object;67 68// Various tools (e.g., llc and opt) duplicate this series of declarations for69// options related to passes and remarks.70 71static cl::opt<bool> RemarksWithHotness(72 "pass-remarks-with-hotness",73 cl::desc("With PGO, include profile count in optimization remarks"),74 cl::Hidden);75 76static cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser>77 RemarksHotnessThreshold(78 "pass-remarks-hotness-threshold",79 cl::desc("Minimum profile count required for "80 "an optimization remark to be output. "81 "Use 'auto' to apply the threshold from profile summary."),82 cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden);83 84static cl::opt<std::string>85 RemarksFilename("pass-remarks-output",86 cl::desc("Output filename for pass remarks"),87 cl::value_desc("filename"));88 89static cl::opt<std::string>90 RemarksPasses("pass-remarks-filter",91 cl::desc("Only record optimization remarks from passes whose "92 "names match the given regular expression"),93 cl::value_desc("regex"));94 95static cl::opt<std::string> RemarksFormat(96 "pass-remarks-format",97 cl::desc("The format used for serializing remarks (default: YAML)"),98 cl::value_desc("format"), cl::init("yaml"));99 100static cl::list<std::string>101 PassPlugins("load-pass-plugin",102 cl::desc("Load passes from plugin library"));103 104static cl::opt<std::string> PassPipeline(105 "passes",106 cl::desc(107 "A textual description of the pass pipeline. To have analysis passes "108 "available before a certain pass, add 'require<foo-analysis>'. "109 "'-passes' overrides the pass pipeline (but not all effects) from "110 "specifying '--opt-level=O?' (O2 is the default) to "111 "clang-linker-wrapper. Be sure to include the corresponding "112 "'default<O?>' in '-passes'."));113static cl::alias PassPipeline2("p", cl::aliasopt(PassPipeline),114 cl::desc("Alias for -passes"));115 116/// Path of the current binary.117static const char *LinkerExecutable;118 119/// Ssave intermediary results.120static bool SaveTemps = false;121 122/// Print arguments without executing.123static bool DryRun = false;124 125/// Print verbose output.126static bool Verbose = false;127 128/// Filename of the executable being created.129static StringRef ExecutableName;130 131/// Binary path for the CUDA installation.132static std::string CudaBinaryPath;133 134/// Mutex lock to protect writes to shared TempFiles in parallel.135static std::mutex TempFilesMutex;136 137/// Temporary files created by the linker wrapper.138static std::list<SmallString<128>> TempFiles;139 140/// Codegen flags for LTO backend.141static codegen::RegisterCodeGenFlags CodeGenFlags;142 143using OffloadingImage = OffloadBinary::OffloadingImage;144 145namespace llvm {146// Provide DenseMapInfo so that OffloadKind can be used in a DenseMap.147template <> struct DenseMapInfo<OffloadKind> {148 static inline OffloadKind getEmptyKey() { return OFK_LAST; }149 static inline OffloadKind getTombstoneKey() {150 return static_cast<OffloadKind>(OFK_LAST + 1);151 }152 static unsigned getHashValue(const OffloadKind &Val) { return Val; }153 154 static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) {155 return LHS == RHS;156 }157};158} // namespace llvm159 160namespace {161using std::error_code;162 163/// Must not overlap with llvm::opt::DriverFlag.164enum WrapperFlags {165 WrapperOnlyOption = (1 << 4), // Options only used by the linker wrapper.166 DeviceOnlyOption = (1 << 5), // Options only used for device linking.167};168 169enum ID {170 OPT_INVALID = 0, // This is not an option ID.171#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),172#include "LinkerWrapperOpts.inc"173 LastOption174#undef OPTION175};176 177#define OPTTABLE_STR_TABLE_CODE178#include "LinkerWrapperOpts.inc"179#undef OPTTABLE_STR_TABLE_CODE180 181#define OPTTABLE_PREFIXES_TABLE_CODE182#include "LinkerWrapperOpts.inc"183#undef OPTTABLE_PREFIXES_TABLE_CODE184 185static constexpr OptTable::Info InfoTable[] = {186#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),187#include "LinkerWrapperOpts.inc"188#undef OPTION189};190 191class WrapperOptTable : public opt::GenericOptTable {192public:193 WrapperOptTable()194 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}195};196 197const OptTable &getOptTable() {198 static const WrapperOptTable *Table = []() {199 auto Result = std::make_unique<WrapperOptTable>();200 return Result.release();201 }();202 return *Table;203}204 205void printCommands(ArrayRef<StringRef> CmdArgs) {206 if (CmdArgs.empty())207 return;208 209 llvm::errs() << " \"" << CmdArgs.front() << "\" ";210 for (auto IC = std::next(CmdArgs.begin()), IE = CmdArgs.end(); IC != IE; ++IC)211 llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n");212}213 214[[noreturn]] void reportError(Error E) {215 outs().flush();216 logAllUnhandledErrors(std::move(E),217 WithColor::error(errs(), LinkerExecutable));218 exit(EXIT_FAILURE);219}220 221std::string getMainExecutable(const char *Name) {222 void *Ptr = (void *)(intptr_t)&getMainExecutable;223 auto COWPath = sys::fs::getMainExecutable(Name, Ptr);224 return sys::path::parent_path(COWPath).str();225}226 227/// Get a temporary filename suitable for output.228Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) {229 std::scoped_lock<decltype(TempFilesMutex)> Lock(TempFilesMutex);230 SmallString<128> OutputFile;231 std::string PrefixStr = clang::sanitizeTargetIDInFileName(Prefix.str());232 233 if (SaveTemps) {234 (PrefixStr + "." + Extension).toNullTerminatedStringRef(OutputFile);235 } else {236 if (std::error_code EC =237 sys::fs::createTemporaryFile(PrefixStr, Extension, OutputFile))238 return createFileError(OutputFile, EC);239 }240 241 TempFiles.emplace_back(std::move(OutputFile));242 return TempFiles.back();243}244 245/// Execute the command \p ExecutablePath with the arguments \p Args.246Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {247 if (Verbose || DryRun)248 printCommands(Args);249 250 if (!DryRun)251 if (sys::ExecuteAndWait(ExecutablePath, Args))252 return createStringError(253 "'%s' failed", sys::path::filename(ExecutablePath).str().c_str());254 return Error::success();255}256 257Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {258 259 ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);260 if (!Path)261 Path = sys::findProgramByName(Name);262 if (!Path && DryRun)263 return Name.str();264 if (!Path)265 return createStringError(Path.getError(),266 "Unable to find '" + Name + "' in path");267 return *Path;268}269 270bool linkerSupportsLTO(const ArgList &Args) {271 llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));272 return Triple.isNVPTX() || Triple.isAMDGPU() ||273 (!Triple.isGPU() &&274 Args.getLastArgValue(OPT_linker_path_EQ).ends_with("lld"));275}276 277/// Returns the hashed value for a constant string.278std::string getHash(StringRef Str) {279 llvm::MD5 Hasher;280 llvm::MD5::MD5Result Hash;281 Hasher.update(Str);282 Hasher.final(Hash);283 return llvm::utohexstr(Hash.low(), /*LowerCase=*/true);284}285 286/// Renames offloading entry sections in a relocatable link so they do not287/// conflict with a later link job.288Error relocateOffloadSection(const ArgList &Args, StringRef Output) {289 llvm::Triple Triple(290 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));291 if (Triple.isOSWindows())292 return createStringError(293 "Relocatable linking is not supported on COFF targets");294 295 Expected<std::string> ObjcopyPath =296 findProgram("llvm-objcopy", {getMainExecutable("llvm-objcopy")});297 if (!ObjcopyPath)298 return ObjcopyPath.takeError();299 300 // Use the linker output file to get a unique hash. This creates a unique301 // identifier to rename the sections to that is deterministic to the contents.302 auto BufferOrErr = DryRun ? MemoryBuffer::getMemBuffer("")303 : MemoryBuffer::getFileOrSTDIN(Output);304 if (!BufferOrErr)305 return createStringError("Failed to open %s", Output.str().c_str());306 std::string Suffix = "_" + getHash((*BufferOrErr)->getBuffer());307 308 SmallVector<StringRef> ObjcopyArgs = {309 *ObjcopyPath,310 Output,311 };312 313 // Remove the old .llvm.offloading section to prevent further linking.314 ObjcopyArgs.emplace_back("--remove-section");315 ObjcopyArgs.emplace_back(".llvm.offloading");316 StringRef Prefix = "llvm";317 auto Section = (Prefix + "_offload_entries").str();318 // Rename the offloading entires to make them private to this link unit.319 ObjcopyArgs.emplace_back("--rename-section");320 ObjcopyArgs.emplace_back(321 Args.MakeArgString(Section + "=" + Section + Suffix));322 323 // Rename the __start_ / __stop_ symbols appropriately to iterate over the324 // newly renamed section containing the offloading entries.325 ObjcopyArgs.emplace_back("--redefine-sym");326 ObjcopyArgs.emplace_back(Args.MakeArgString("__start_" + Section + "=" +327 "__start_" + Section + Suffix));328 ObjcopyArgs.emplace_back("--redefine-sym");329 ObjcopyArgs.emplace_back(Args.MakeArgString("__stop_" + Section + "=" +330 "__stop_" + Section + Suffix));331 332 if (Error Err = executeCommands(*ObjcopyPath, ObjcopyArgs))333 return Err;334 335 return Error::success();336}337 338/// Runs the wrapped linker job with the newly created input.339Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) {340 llvm::TimeTraceScope TimeScope("Execute host linker");341 342 // Render the linker arguments and add the newly created image. We add it343 // after the output file to ensure it is linked with the correct libraries.344 StringRef LinkerPath = Args.getLastArgValue(OPT_linker_path_EQ);345 if (LinkerPath.empty())346 return createStringError("linker path missing, must pass 'linker-path'");347 ArgStringList NewLinkerArgs;348 for (const opt::Arg *Arg : Args) {349 // Do not forward arguments only intended for the linker wrapper.350 if (Arg->getOption().hasFlag(WrapperOnlyOption))351 continue;352 353 Arg->render(Args, NewLinkerArgs);354 if (Arg->getOption().matches(OPT_o) || Arg->getOption().matches(OPT_out))355 llvm::transform(Files, std::back_inserter(NewLinkerArgs),356 [&](StringRef Arg) { return Args.MakeArgString(Arg); });357 }358 359 SmallVector<StringRef> LinkerArgs({LinkerPath});360 for (StringRef Arg : NewLinkerArgs)361 LinkerArgs.push_back(Arg);362 if (Error Err = executeCommands(LinkerPath, LinkerArgs))363 return Err;364 365 if (Args.hasArg(OPT_relocatable))366 return relocateOffloadSection(Args, ExecutableName);367 368 return Error::success();369}370 371void printVersion(raw_ostream &OS) {372 OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n';373}374 375namespace nvptx {376Expected<StringRef>377fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,378 const ArgList &Args) {379 llvm::TimeTraceScope TimeScope("NVPTX fatbinary");380 // NVPTX uses the fatbinary program to bundle the linked images.381 Expected<std::string> FatBinaryPath =382 findProgram("fatbinary", {CudaBinaryPath + "/bin"});383 if (!FatBinaryPath)384 return FatBinaryPath.takeError();385 386 llvm::Triple Triple(387 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));388 389 // Create a new file to write the linked device image to.390 auto TempFileOrErr =391 createOutputFile(sys::path::filename(ExecutableName), "fatbin");392 if (!TempFileOrErr)393 return TempFileOrErr.takeError();394 395 SmallVector<StringRef, 16> CmdArgs;396 CmdArgs.push_back(*FatBinaryPath);397 CmdArgs.push_back(Triple.isArch64Bit() ? "-64" : "-32");398 CmdArgs.push_back("--create");399 CmdArgs.push_back(*TempFileOrErr);400 for (const auto &[File, Arch] : InputFiles)401 CmdArgs.push_back(Args.MakeArgString(402 "--image3=kind=elf,sm=" + Arch.drop_front(3) + ",file=" + File));403 404 if (Error Err = executeCommands(*FatBinaryPath, CmdArgs))405 return std::move(Err);406 407 return *TempFileOrErr;408}409} // namespace nvptx410 411namespace amdgcn {412Expected<StringRef>413fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,414 const ArgList &Args) {415 llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary");416 417 // AMDGPU uses the clang-offload-bundler to bundle the linked images.418 Expected<std::string> OffloadBundlerPath = findProgram(419 "clang-offload-bundler", {getMainExecutable("clang-offload-bundler")});420 if (!OffloadBundlerPath)421 return OffloadBundlerPath.takeError();422 423 // Create a new file to write the linked device image to.424 auto TempFileOrErr =425 createOutputFile(sys::path::filename(ExecutableName), "hipfb");426 if (!TempFileOrErr)427 return TempFileOrErr.takeError();428 429 BumpPtrAllocator Alloc;430 StringSaver Saver(Alloc);431 432 SmallVector<StringRef, 16> CmdArgs;433 CmdArgs.push_back(*OffloadBundlerPath);434 CmdArgs.push_back("-type=o");435 CmdArgs.push_back("-bundle-align=4096");436 437 if (Args.hasArg(OPT_compress))438 CmdArgs.push_back("-compress");439 if (auto *Arg = Args.getLastArg(OPT_compression_level_eq))440 CmdArgs.push_back(441 Args.MakeArgString(Twine("-compression-level=") + Arg->getValue()));442 443 SmallVector<StringRef> Targets = {"-targets=host-x86_64-unknown-linux-gnu"};444 for (const auto &[File, Arch] : InputFiles) {445 Targets.push_back(Saver.save(Arch == "amdgcnspirv"446 ? "hip-spirv64-amd-amdhsa--" + Arch447 : "hip-amdgcn-amd-amdhsa--" + Arch));448 }449 CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));450 451#ifdef _WIN32452 CmdArgs.push_back("-input=NUL");453#else454 CmdArgs.push_back("-input=/dev/null");455#endif456 for (const auto &[File, Arch] : InputFiles)457 CmdArgs.push_back(Saver.save("-input=" + File));458 459 CmdArgs.push_back(Saver.save("-output=" + *TempFileOrErr));460 461 if (Error Err = executeCommands(*OffloadBundlerPath, CmdArgs))462 return std::move(Err);463 464 return *TempFileOrErr;465}466} // namespace amdgcn467 468namespace generic {469Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,470 uint16_t ActiveOffloadKindMask) {471 llvm::TimeTraceScope TimeScope("Clang");472 // Use `clang` to invoke the appropriate device tools.473 Expected<std::string> ClangPath =474 findProgram("clang", {getMainExecutable("clang")});475 if (!ClangPath)476 return ClangPath.takeError();477 478 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));479 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);480 // Create a new file to write the linked device image to. Assume that the481 // input filename already has the device and architecture.482 std::string OutputFileBase =483 "." + Triple.getArchName().str() + "." + Arch.str();484 auto TempFileOrErr = createOutputFile(485 sys::path::filename(ExecutableName) + OutputFileBase, "img");486 if (!TempFileOrErr)487 return TempFileOrErr.takeError();488 489 SmallVector<StringRef, 16> CmdArgs{490 *ClangPath,491 "--no-default-config",492 "-o",493 *TempFileOrErr,494 // Without -dumpdir, Clang will place auxiliary output files in the495 // temporary directory of TempFileOrErr, where they will not easily be496 // found by the user and might eventually be automatically removed. Tell497 // Clang to instead place them alongside the final executable.498 "-dumpdir",499 Args.MakeArgString(ExecutableName + OutputFileBase + ".img."),500 Args.MakeArgString("--target=" + Triple.getTriple()),501 };502 503 if (!Arch.empty())504 Triple.isAMDGPU() ? CmdArgs.push_back(Args.MakeArgString("-mcpu=" + Arch))505 : CmdArgs.push_back(Args.MakeArgString("-march=" + Arch));506 507 // AMDGPU is always in LTO mode currently.508 if (Triple.isAMDGPU())509 CmdArgs.push_back("-flto");510 511 // Forward all of the `--offload-opt` and similar options to the device.512 for (auto &Arg : Args.filtered(OPT_offload_opt_eq_minus, OPT_mllvm))513 CmdArgs.append(514 {"-Xlinker",515 Args.MakeArgString("--plugin-opt=" + StringRef(Arg->getValue()))});516 517 if (!Triple.isNVPTX() && !Triple.isSPIRV())518 CmdArgs.push_back("-Wl,--no-undefined");519 520 for (StringRef InputFile : InputFiles)521 CmdArgs.push_back(InputFile);522 523 // If this is CPU offloading we copy the input libraries.524 if (!Triple.isGPU()) {525 CmdArgs.push_back("-Wl,-Bsymbolic");526 CmdArgs.push_back("-shared");527 ArgStringList LinkerArgs;528 for (const opt::Arg *Arg :529 Args.filtered(OPT_INPUT, OPT_library, OPT_library_path, OPT_rpath,530 OPT_whole_archive, OPT_no_whole_archive)) {531 // Sometimes needed libraries are passed by name, such as when using532 // sanitizers. We need to check the file magic for any libraries.533 if (Arg->getOption().matches(OPT_INPUT)) {534 if (!sys::fs::exists(Arg->getValue()) ||535 sys::fs::is_directory(Arg->getValue()))536 continue;537 538 file_magic Magic;539 if (auto EC = identify_magic(Arg->getValue(), Magic))540 return createStringError("Failed to open %s", Arg->getValue());541 if (Magic != file_magic::archive &&542 Magic != file_magic::elf_shared_object)543 continue;544 }545 if (Arg->getOption().matches(OPT_whole_archive))546 LinkerArgs.push_back(Args.MakeArgString("-Wl,--whole-archive"));547 else if (Arg->getOption().matches(OPT_no_whole_archive))548 LinkerArgs.push_back(Args.MakeArgString("-Wl,--no-whole-archive"));549 else550 Arg->render(Args, LinkerArgs);551 }552 llvm::append_range(CmdArgs, LinkerArgs);553 }554 555 // Pass on -mllvm options to the linker invocation.556 for (const opt::Arg *Arg : Args.filtered(OPT_mllvm))557 CmdArgs.append({"-Xlinker", Args.MakeArgString(558 "-mllvm=" + StringRef(Arg->getValue()))});559 560 if (SaveTemps && linkerSupportsLTO(Args))561 CmdArgs.push_back("-Wl,--save-temps");562 563 if (Args.hasArg(OPT_embed_bitcode))564 CmdArgs.push_back("-Wl,--lto-emit-llvm");565 566 // For linking device code with the SYCL offload kind, special handling is567 // required. Passing --sycl-link to clang results in a call to568 // clang-sycl-linker. Additional linker flags required by clang-sycl-linker569 // will be communicated via the -Xlinker option.570 if (ActiveOffloadKindMask & OFK_SYCL) {571 CmdArgs.push_back("--sycl-link");572 CmdArgs.append(573 {"-Xlinker", Args.MakeArgString("-triple=" + Triple.getTriple())});574 CmdArgs.append({"-Xlinker", Args.MakeArgString("-arch=" + Arch)});575 }576 577 for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))578 CmdArgs.append({"-Xlinker", Args.MakeArgString(Arg)});579 for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ))580 CmdArgs.push_back(Args.MakeArgString(Arg));581 582 if (Error Err = executeCommands(*ClangPath, CmdArgs))583 return std::move(Err);584 585 return *TempFileOrErr;586}587} // namespace generic588 589Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,590 const ArgList &Args,591 uint16_t ActiveOffloadKindMask) {592 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));593 switch (Triple.getArch()) {594 case Triple::nvptx:595 case Triple::nvptx64:596 case Triple::amdgcn:597 case Triple::x86:598 case Triple::x86_64:599 case Triple::aarch64:600 case Triple::aarch64_be:601 case Triple::ppc64:602 case Triple::ppc64le:603 case Triple::spirv64:604 case Triple::systemz:605 case Triple::loongarch64:606 return generic::clang(InputFiles, Args, ActiveOffloadKindMask);607 default:608 return createStringError(Triple.getArchName() +609 " linking is not supported");610 }611}612 613Error containerizeRawImage(std::unique_ptr<MemoryBuffer> &Img, OffloadKind Kind,614 const ArgList &Args) {615 llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));616 if (Kind == OFK_OpenMP && Triple.isSPIRV() &&617 Triple.getVendor() == llvm::Triple::Intel)618 return offloading::intel::containerizeOpenMPSPIRVImage(Img);619 return Error::success();620}621 622Expected<StringRef> writeOffloadFile(const OffloadFile &File) {623 const OffloadBinary &Binary = *File.getBinary();624 625 StringRef Prefix =626 sys::path::stem(Binary.getMemoryBufferRef().getBufferIdentifier());627 SmallString<128> Filename;628 (Prefix + "-" + Binary.getTriple() + "-" + Binary.getArch())629 .toVector(Filename);630 auto TempFileOrErr = createOutputFile(Filename, "o");631 if (!TempFileOrErr)632 return TempFileOrErr.takeError();633 634 Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =635 FileOutputBuffer::create(*TempFileOrErr, Binary.getImage().size());636 if (!OutputOrErr)637 return OutputOrErr.takeError();638 std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);639 llvm::copy(Binary.getImage(), Output->getBufferStart());640 if (Error E = Output->commit())641 return std::move(E);642 643 return *TempFileOrErr;644}645 646// Compile the module to an object file using the appropriate target machine for647// the host triple.648Expected<StringRef> compileModule(Module &M, OffloadKind Kind) {649 llvm::TimeTraceScope TimeScope("Compile module");650 std::string Msg;651 const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg);652 if (!T)653 return createStringError(Msg);654 655 auto Options =656 codegen::InitTargetOptionsFromCodeGenFlags(M.getTargetTriple());657 StringRef CPU = "";658 StringRef Features = "";659 std::unique_ptr<TargetMachine> TM(660 T->createTargetMachine(M.getTargetTriple(), CPU, Features, Options,661 Reloc::PIC_, M.getCodeModel()));662 663 if (M.getDataLayout().isDefault())664 M.setDataLayout(TM->createDataLayout());665 666 int FD = -1;667 auto TempFileOrErr =668 createOutputFile(sys::path::filename(ExecutableName) + "." +669 getOffloadKindName(Kind) + ".image.wrapper",670 "o");671 if (!TempFileOrErr)672 return TempFileOrErr.takeError();673 if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, FD))674 return errorCodeToError(EC);675 676 auto OS = std::make_unique<llvm::raw_fd_ostream>(FD, true);677 678 legacy::PassManager CodeGenPasses;679 TargetLibraryInfoImpl TLII(M.getTargetTriple());680 CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));681 if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr,682 CodeGenFileType::ObjectFile))683 return createStringError("Failed to execute host backend");684 CodeGenPasses.run(M);685 686 return *TempFileOrErr;687}688 689/// Creates the object file containing the device image and runtime690/// registration code from the device images stored in \p Images.691Expected<StringRef>692wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,693 const ArgList &Args, OffloadKind Kind) {694 llvm::TimeTraceScope TimeScope("Wrap bundled images");695 696 SmallVector<ArrayRef<char>, 4> BuffersToWrap;697 for (const auto &Buffer : Buffers)698 BuffersToWrap.emplace_back(699 ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize()));700 701 LLVMContext Context;702 Module M("offload.wrapper.module", Context);703 M.setTargetTriple(Triple(704 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple())));705 706 switch (Kind) {707 case OFK_OpenMP:708 if (Error Err = offloading::wrapOpenMPBinaries(709 M, BuffersToWrap, offloading::getOffloadEntryArray(M),710 /*Suffix=*/"", /*Relocatable=*/Args.hasArg(OPT_relocatable)))711 return std::move(Err);712 break;713 case OFK_Cuda:714 if (Error Err = offloading::wrapCudaBinary(715 M, BuffersToWrap.front(), offloading::getOffloadEntryArray(M),716 /*Suffix=*/"", /*EmitSurfacesAndTextures=*/false))717 return std::move(Err);718 break;719 case OFK_HIP:720 if (Error Err = offloading::wrapHIPBinary(721 M, BuffersToWrap.front(), offloading::getOffloadEntryArray(M)))722 return std::move(Err);723 break;724 case OFK_SYCL: {725 // TODO: fill these options once the Driver supports them.726 offloading::SYCLJITOptions Options;727 if (Error Err =728 offloading::wrapSYCLBinaries(M, BuffersToWrap.front(), Options))729 return std::move(Err);730 break;731 }732 default:733 return createStringError(getOffloadKindName(Kind) +734 " wrapping is not supported");735 }736 737 if (Args.hasArg(OPT_print_wrapped_module))738 errs() << M;739 if (Args.hasArg(OPT_save_temps)) {740 int FD = -1;741 auto TempFileOrErr =742 createOutputFile(sys::path::filename(ExecutableName) + "." +743 getOffloadKindName(Kind) + ".image.wrapper",744 "bc");745 if (!TempFileOrErr)746 return TempFileOrErr.takeError();747 if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, FD))748 return errorCodeToError(EC);749 llvm::raw_fd_ostream OS(FD, true);750 WriteBitcodeToFile(M, OS);751 }752 753 auto FileOrErr = compileModule(M, Kind);754 if (!FileOrErr)755 return FileOrErr.takeError();756 return *FileOrErr;757}758 759Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>760bundleOpenMP(ArrayRef<OffloadingImage> Images) {761 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;762 for (const OffloadingImage &Image : Images)763 Buffers.emplace_back(764 MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Image)));765 766 return std::move(Buffers);767}768 769Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>770bundleSYCL(ArrayRef<OffloadingImage> Images) {771 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;772 if (DryRun) {773 // In dry-run mode there is an empty input which is insufficient for the774 // testing. Therefore, we return here a stub image.775 OffloadingImage Image;776 Image.TheImageKind = IMG_None;777 Image.TheOffloadKind = OffloadKind::OFK_SYCL;778 Image.StringData["symbols"] = "stub";779 Image.Image = MemoryBuffer::getMemBufferCopy("");780 SmallString<0> SerializedImage = OffloadBinary::write(Image);781 Buffers.emplace_back(MemoryBuffer::getMemBufferCopy(SerializedImage));782 return std::move(Buffers);783 }784 785 for (const OffloadingImage &Image : Images) {786 // clang-sycl-linker packs outputs into one binary blob. Therefore, it is787 // passed to Offload Wrapper as is.788 StringRef S(Image.Image->getBufferStart(), Image.Image->getBufferSize());789 Buffers.emplace_back(MemoryBuffer::getMemBufferCopy(S));790 }791 792 return std::move(Buffers);793}794 795Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>796bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {797 SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;798 for (const OffloadingImage &Image : Images)799 InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(),800 Image.StringData.lookup("arch")));801 802 auto FileOrErr = nvptx::fatbinary(InputFiles, Args);803 if (!FileOrErr)804 return FileOrErr.takeError();805 806 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =807 llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);808 809 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;810 if (std::error_code EC = ImageOrError.getError())811 return createFileError(*FileOrErr, EC);812 Buffers.emplace_back(std::move(*ImageOrError));813 814 return std::move(Buffers);815}816 817Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>818bundleHIP(ArrayRef<OffloadingImage> Images, const ArgList &Args) {819 SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;820 for (const OffloadingImage &Image : Images)821 InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(),822 Image.StringData.lookup("arch")));823 824 auto FileOrErr = amdgcn::fatbinary(InputFiles, Args);825 if (!FileOrErr)826 return FileOrErr.takeError();827 828 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =829 llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);830 831 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;832 if (std::error_code EC = ImageOrError.getError())833 return createFileError(*FileOrErr, EC);834 Buffers.emplace_back(std::move(*ImageOrError));835 836 return std::move(Buffers);837}838 839/// Transforms the input \p Images into the binary format the runtime expects840/// for the given \p Kind.841Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>842bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,843 OffloadKind Kind) {844 llvm::TimeTraceScope TimeScope("Bundle linked output");845 switch (Kind) {846 case OFK_OpenMP:847 return bundleOpenMP(Images);848 case OFK_SYCL:849 return bundleSYCL(Images);850 case OFK_Cuda:851 return bundleCuda(Images, Args);852 case OFK_HIP:853 return bundleHIP(Images, Args);854 default:855 return createStringError(getOffloadKindName(Kind) +856 " bundling is not supported");857 }858}859 860/// Returns a new ArgList containg arguments used for the device linking phase.861DerivedArgList getLinkerArgs(ArrayRef<OffloadFile> Input,862 const InputArgList &Args) {863 DerivedArgList DAL = DerivedArgList(DerivedArgList(Args));864 for (Arg *A : Args)865 DAL.append(A);866 867 // Set the subarchitecture and target triple for this compilation.868 const OptTable &Tbl = getOptTable();869 StringRef Arch = Args.MakeArgString(Input.front().getBinary()->getArch());870 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch_EQ),871 Arch == "generic" ? "" : Arch);872 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple_EQ),873 Args.MakeArgString(Input.front().getBinary()->getTriple()));874 875 // If every input file is bitcode we have whole program visibility as we876 // do only support static linking with bitcode.877 auto ContainsBitcode = [](const OffloadFile &F) {878 return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode;879 };880 if (llvm::all_of(Input, ContainsBitcode))881 DAL.AddFlagArg(nullptr, Tbl.getOption(OPT_whole_program));882 883 // Forward '-Xoffload-linker' options to the appropriate backend.884 for (StringRef Arg : Args.getAllArgValues(OPT_device_linker_args_EQ)) {885 auto [Triple, Value] = Arg.split('=');886 llvm::Triple TT(Triple);887 // If this isn't a recognized triple then it's an `arg=value` option.888 if (TT.getArch() == Triple::ArchType::UnknownArch)889 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),890 Args.MakeArgString(Arg));891 else if (Value.empty())892 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),893 Args.MakeArgString(Triple));894 else if (Triple == DAL.getLastArgValue(OPT_triple_EQ))895 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),896 Args.MakeArgString(Value));897 }898 899 // Forward '-Xoffload-compiler' options to the appropriate backend.900 for (StringRef Arg : Args.getAllArgValues(OPT_device_compiler_args_EQ)) {901 auto [Triple, Value] = Arg.split('=');902 llvm::Triple TT(Triple);903 // If this isn't a recognized triple then it's an `arg=value` option.904 if (TT.getArch() == Triple::ArchType::UnknownArch)905 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_compiler_arg_EQ),906 Args.MakeArgString(Arg));907 else if (Value.empty())908 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_compiler_arg_EQ),909 Args.MakeArgString(Triple));910 else if (Triple == DAL.getLastArgValue(OPT_triple_EQ))911 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_compiler_arg_EQ),912 Args.MakeArgString(Value));913 }914 915 return DAL;916}917 918Error handleOverrideImages(919 const InputArgList &Args,920 MapVector<OffloadKind, SmallVector<OffloadingImage, 0>> &Images) {921 for (StringRef Arg : Args.getAllArgValues(OPT_override_image)) {922 OffloadKind Kind = getOffloadKind(Arg.split("=").first);923 StringRef Filename = Arg.split("=").second;924 925 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =926 MemoryBuffer::getFileOrSTDIN(Filename);927 if (std::error_code EC = BufferOrErr.getError())928 return createFileError(Filename, EC);929 930 Expected<std::unique_ptr<ObjectFile>> ElfOrErr =931 ObjectFile::createELFObjectFile(**BufferOrErr,932 /*InitContent=*/false);933 if (!ElfOrErr)934 return ElfOrErr.takeError();935 ObjectFile &Elf = **ElfOrErr;936 937 OffloadingImage TheImage{};938 TheImage.TheImageKind = IMG_Object;939 TheImage.TheOffloadKind = Kind;940 TheImage.StringData["triple"] =941 Args.MakeArgString(Elf.makeTriple().getTriple());942 if (std::optional<StringRef> CPU = Elf.tryGetCPUName())943 TheImage.StringData["arch"] = Args.MakeArgString(*CPU);944 TheImage.Image = std::move(*BufferOrErr);945 946 Images[Kind].emplace_back(std::move(TheImage));947 }948 return Error::success();949}950 951/// Transforms all the extracted offloading input files into an image that can952/// be registered by the runtime. If NeedsWrapping is false, writes bundled953/// output directly without wrapping or host linking.954Expected<SmallVector<StringRef>>955linkAndWrapDeviceFiles(ArrayRef<SmallVector<OffloadFile>> LinkerInputFiles,956 const InputArgList &Args, char **Argv, int Argc,957 bool NeedsWrapping) {958 llvm::TimeTraceScope TimeScope("Handle all device input");959 960 std::mutex ImageMtx;961 MapVector<OffloadKind, SmallVector<OffloadingImage, 0>> Images;962 963 // Initialize the images with any overriding inputs.964 if (Args.hasArg(OPT_override_image))965 if (Error Err = handleOverrideImages(Args, Images))966 return std::move(Err);967 968 auto Err = parallelForEachError(LinkerInputFiles, [&](auto &Input) -> Error {969 llvm::TimeTraceScope TimeScope("Link device input");970 971 // Each thread needs its own copy of the base arguments to maintain972 // per-device argument storage of synthetic strings.973 const OptTable &Tbl = getOptTable();974 BumpPtrAllocator Alloc;975 StringSaver Saver(Alloc);976 auto BaseArgs =977 Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [](StringRef Err) {978 reportError(createStringError(Err));979 });980 auto LinkerArgs = getLinkerArgs(Input, BaseArgs);981 982 uint16_t ActiveOffloadKindMask = 0u;983 for (const auto &File : Input)984 ActiveOffloadKindMask |= File.getBinary()->getOffloadKind();985 986 // Linking images of SYCL offload kind with images of other kind is not987 // supported.988 // TODO: Remove the above limitation.989 if ((ActiveOffloadKindMask & OFK_SYCL) &&990 ((ActiveOffloadKindMask ^ OFK_SYCL) != 0))991 return createStringError("Linking images of SYCL offload kind with "992 "images of any other kind is not supported");993 994 // Write any remaining device inputs to an output file.995 SmallVector<StringRef> InputFiles;996 for (const OffloadFile &File : Input) {997 auto FileNameOrErr = writeOffloadFile(File);998 if (!FileNameOrErr)999 return FileNameOrErr.takeError();1000 InputFiles.emplace_back(*FileNameOrErr);1001 }1002 1003 // Link the remaining device files using the device linker.1004 auto OutputOrErr =1005 linkDevice(InputFiles, LinkerArgs, ActiveOffloadKindMask);1006 if (!OutputOrErr)1007 return OutputOrErr.takeError();1008 1009 // Store the offloading image for each linked output file.1010 for (OffloadKind Kind = OFK_OpenMP; Kind != OFK_LAST;1011 Kind = static_cast<OffloadKind>((uint16_t)(Kind) << 1)) {1012 if ((ActiveOffloadKindMask & Kind) == 0)1013 continue;1014 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =1015 llvm::MemoryBuffer::getFileOrSTDIN(*OutputOrErr);1016 if (std::error_code EC = FileOrErr.getError()) {1017 if (DryRun)1018 FileOrErr = MemoryBuffer::getMemBuffer("");1019 else1020 return createFileError(*OutputOrErr, EC);1021 }1022 1023 // Manually containerize offloading images not in ELF format.1024 if (Error E = containerizeRawImage(*FileOrErr, Kind, LinkerArgs))1025 return E;1026 1027 std::scoped_lock<decltype(ImageMtx)> Guard(ImageMtx);1028 OffloadingImage TheImage{};1029 TheImage.TheImageKind =1030 Args.hasArg(OPT_embed_bitcode) ? IMG_Bitcode : IMG_Object;1031 TheImage.TheOffloadKind = Kind;1032 TheImage.StringData["triple"] =1033 Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_triple_EQ));1034 TheImage.StringData["arch"] =1035 Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_arch_EQ));1036 TheImage.Image = std::move(*FileOrErr);1037 1038 Images[Kind].emplace_back(std::move(TheImage));1039 }1040 return Error::success();1041 });1042 if (Err)1043 return std::move(Err);1044 1045 // Create a binary image of each offloading image and either embed it into a1046 // new object file, or if all inputs were direct offload binaries, emit the1047 // fat binary directly (e.g. .hipfb / .fatbin).1048 SmallVector<StringRef> WrappedOutput;1049 for (auto &[Kind, Input] : Images) {1050 // We sort the entries before bundling so they appear in a deterministic1051 // order in the final binary.1052 llvm::sort(Input, [](OffloadingImage &A, OffloadingImage &B) {1053 return A.StringData["triple"] > B.StringData["triple"] ||1054 A.StringData["arch"] > B.StringData["arch"] ||1055 A.TheOffloadKind < B.TheOffloadKind;1056 });1057 auto BundledImagesOrErr = bundleLinkedOutput(Input, Args, Kind);1058 if (!BundledImagesOrErr)1059 return BundledImagesOrErr.takeError();1060 1061 if (!NeedsWrapping) {1062 if (BundledImagesOrErr->size() != 1)1063 return createStringError(1064 "Expected a single bundled image for direct fat binary output");1065 1066 Expected<std::unique_ptr<FileOutputBuffer>> FOBOrErr =1067 FileOutputBuffer::create(1068 ExecutableName, BundledImagesOrErr->front()->getBufferSize());1069 if (!FOBOrErr)1070 return FOBOrErr.takeError();1071 std::unique_ptr<FileOutputBuffer> FOB = std::move(*FOBOrErr);1072 llvm::copy(BundledImagesOrErr->front()->getBuffer(),1073 FOB->getBufferStart());1074 if (Error E = FOB->commit())1075 return std::move(E);1076 1077 continue;1078 }1079 1080 auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Args, Kind);1081 if (!OutputOrErr)1082 return OutputOrErr.takeError();1083 WrappedOutput.push_back(*OutputOrErr);1084 }1085 1086 return WrappedOutput;1087}1088 1089std::optional<std::string> findFile(StringRef Dir, StringRef Root,1090 const Twine &Name) {1091 SmallString<128> Path;1092 if (Dir.starts_with("="))1093 sys::path::append(Path, Root, Dir.substr(1), Name);1094 else1095 sys::path::append(Path, Dir, Name);1096 1097 if (sys::fs::exists(Path))1098 return static_cast<std::string>(Path);1099 return std::nullopt;1100}1101 1102std::optional<std::string>1103findFromSearchPaths(StringRef Name, StringRef Root,1104 ArrayRef<StringRef> SearchPaths) {1105 for (StringRef Dir : SearchPaths)1106 if (std::optional<std::string> File = findFile(Dir, Root, Name))1107 return File;1108 return std::nullopt;1109}1110 1111std::optional<std::string>1112searchLibraryBaseName(StringRef Name, StringRef Root,1113 ArrayRef<StringRef> SearchPaths) {1114 for (StringRef Dir : SearchPaths) {1115 if (std::optional<std::string> File =1116 findFile(Dir, Root, "lib" + Name + ".so"))1117 return File;1118 if (std::optional<std::string> File =1119 findFile(Dir, Root, "lib" + Name + ".a"))1120 return File;1121 }1122 return std::nullopt;1123}1124 1125/// Search for static libraries in the linker's library path given input like1126/// `-lfoo` or `-l:libfoo.a`.1127std::optional<std::string> searchLibrary(StringRef Input, StringRef Root,1128 ArrayRef<StringRef> SearchPaths) {1129 if (Input.starts_with(":") || Input.ends_with(".lib"))1130 return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);1131 return searchLibraryBaseName(Input, Root, SearchPaths);1132}1133 1134/// Search the input files and libraries for embedded device offloading code1135/// and add it to the list of files to be linked. Files coming from static1136/// libraries are only added to the input if they are used by an existing1137/// input file. Returns a list of input files intended for a single linking job.1138Expected<SmallVector<SmallVector<OffloadFile>>>1139getDeviceInput(const ArgList &Args) {1140 llvm::TimeTraceScope TimeScope("ExtractDeviceCode");1141 1142 // Skip all the input if the user is overriding the output.1143 if (Args.hasArg(OPT_override_image))1144 return SmallVector<SmallVector<OffloadFile>>();1145 1146 StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ);1147 SmallVector<StringRef> LibraryPaths;1148 for (const opt::Arg *Arg : Args.filtered(OPT_library_path, OPT_libpath))1149 LibraryPaths.push_back(Arg->getValue());1150 1151 BumpPtrAllocator Alloc;1152 StringSaver Saver(Alloc);1153 1154 // Try to extract device code from the linker input files.1155 bool WholeArchive = Args.hasArg(OPT_wholearchive_flag) ? true : false;1156 SmallVector<OffloadFile> ObjectFilesToExtract;1157 SmallVector<OffloadFile> ArchiveFilesToExtract;1158 for (const opt::Arg *Arg : Args.filtered(1159 OPT_INPUT, OPT_library, OPT_whole_archive, OPT_no_whole_archive)) {1160 if (Arg->getOption().matches(OPT_whole_archive) ||1161 Arg->getOption().matches(OPT_no_whole_archive)) {1162 WholeArchive = Arg->getOption().matches(OPT_whole_archive);1163 continue;1164 }1165 1166 std::optional<std::string> Filename =1167 Arg->getOption().matches(OPT_library)1168 ? searchLibrary(Arg->getValue(), Root, LibraryPaths)1169 : std::string(Arg->getValue());1170 1171 if (!Filename && Arg->getOption().matches(OPT_library))1172 reportError(1173 createStringError("unable to find library -l%s", Arg->getValue()));1174 1175 if (!Filename || !sys::fs::exists(*Filename) ||1176 sys::fs::is_directory(*Filename))1177 continue;1178 1179 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =1180 MemoryBuffer::getFileOrSTDIN(*Filename);1181 if (std::error_code EC = BufferOrErr.getError())1182 return createFileError(*Filename, EC);1183 1184 MemoryBufferRef Buffer = **BufferOrErr;1185 if (identify_magic(Buffer.getBuffer()) == file_magic::elf_shared_object)1186 continue;1187 1188 SmallVector<OffloadFile> Binaries;1189 if (Error Err = extractOffloadBinaries(Buffer, Binaries))1190 return std::move(Err);1191 1192 for (auto &OffloadFile : Binaries) {1193 if (identify_magic(Buffer.getBuffer()) == file_magic::archive &&1194 !WholeArchive)1195 ArchiveFilesToExtract.emplace_back(std::move(OffloadFile));1196 else1197 ObjectFilesToExtract.emplace_back(std::move(OffloadFile));1198 }1199 }1200 1201 // Link all standard input files and update the list of symbols.1202 MapVector<OffloadFile::TargetID, SmallVector<OffloadFile, 0>> InputFiles;1203 for (OffloadFile &Binary : ObjectFilesToExtract) {1204 if (!Binary.getBinary())1205 continue;1206 1207 SmallVector<OffloadFile::TargetID> CompatibleTargets = {Binary};1208 for (const auto &[ID, Input] : InputFiles)1209 if (object::areTargetsCompatible(Binary, ID))1210 CompatibleTargets.emplace_back(ID);1211 1212 for (const auto &[Index, ID] : llvm::enumerate(CompatibleTargets)) {1213 // If another target needs this binary it must be copied instead.1214 if (Index == CompatibleTargets.size() - 1)1215 InputFiles[ID].emplace_back(std::move(Binary));1216 else1217 InputFiles[ID].emplace_back(Binary.copy());1218 }1219 }1220 1221 llvm::DenseSet<StringRef> ShouldExtract;1222 for (auto &Arg : Args.getAllArgValues(OPT_should_extract))1223 ShouldExtract.insert(Arg);1224 1225 // We only extract archive members from the fat binary if we find a used or1226 // requested target. Unlike normal static archive handling, we just extract1227 // every object file contained in the archive.1228 for (OffloadFile &Binary : ArchiveFilesToExtract) {1229 if (!Binary.getBinary())1230 continue;1231 1232 SmallVector<OffloadFile::TargetID> CompatibleTargets = {Binary};1233 for (const auto &[ID, Input] : InputFiles)1234 if (object::areTargetsCompatible(Binary, ID))1235 CompatibleTargets.emplace_back(ID);1236 1237 for (const auto &[Index, ID] : llvm::enumerate(CompatibleTargets)) {1238 // Only extract an if we have an an object matching this target or it1239 // was specifically requested.1240 if (!InputFiles.count(ID) && !ShouldExtract.contains(ID.second))1241 continue;1242 1243 // If another target needs this binary it must be copied instead.1244 if (Index == CompatibleTargets.size() - 1)1245 InputFiles[ID].emplace_back(std::move(Binary));1246 else1247 InputFiles[ID].emplace_back(Binary.copy());1248 }1249 }1250 1251 SmallVector<SmallVector<OffloadFile>> InputsForTarget;1252 for (auto &[ID, Input] : InputFiles)1253 InputsForTarget.emplace_back(std::move(Input));1254 1255 return std::move(InputsForTarget);1256}1257 1258} // namespace1259 1260int main(int Argc, char **Argv) {1261 InitLLVM X(Argc, Argv);1262 InitializeAllTargetInfos();1263 InitializeAllTargets();1264 InitializeAllTargetMCs();1265 InitializeAllAsmParsers();1266 InitializeAllAsmPrinters();1267 1268 LinkerExecutable = Argv[0];1269 sys::PrintStackTraceOnErrorSignal(Argv[0]);1270 1271 const OptTable &Tbl = getOptTable();1272 BumpPtrAllocator Alloc;1273 StringSaver Saver(Alloc);1274 auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) {1275 reportError(createStringError(Err));1276 });1277 1278 if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) {1279 Tbl.printHelp(1280 outs(),1281 "clang-linker-wrapper [options] -- <options to passed to the linker>",1282 "\nA wrapper utility over the host linker. It scans the input files\n"1283 "for sections that require additional processing prior to linking.\n"1284 "The will then transparently pass all arguments and input to the\n"1285 "specified host linker to create the final binary.\n",1286 Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden));1287 return EXIT_SUCCESS;1288 }1289 if (Args.hasArg(OPT_v)) {1290 printVersion(outs());1291 return EXIT_SUCCESS;1292 }1293 1294 // This forwards '-mllvm' arguments to LLVM if present.1295 SmallVector<const char *> NewArgv = {Argv[0]};1296 for (const opt::Arg *Arg : Args.filtered(OPT_mllvm))1297 NewArgv.push_back(Arg->getValue());1298 for (const opt::Arg *Arg : Args.filtered(OPT_offload_opt_eq_minus))1299 NewArgv.push_back(Arg->getValue());1300 SmallVector<PassPlugin, 1> PluginList;1301 PassPlugins.setCallback([&](const std::string &PluginPath) {1302 auto Plugin = PassPlugin::Load(PluginPath);1303 if (!Plugin)1304 reportFatalUsageError(Plugin.takeError());1305 PluginList.emplace_back(Plugin.get());1306 });1307 cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]);1308 1309 Verbose = Args.hasArg(OPT_verbose);1310 DryRun = Args.hasArg(OPT_dry_run);1311 SaveTemps = Args.hasArg(OPT_save_temps);1312 CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str();1313 1314 llvm::Triple Triple(1315 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));1316 if (Args.hasArg(OPT_o))1317 ExecutableName = Args.getLastArgValue(OPT_o, "a.out");1318 else if (Args.hasArg(OPT_out))1319 ExecutableName = Args.getLastArgValue(OPT_out, "a.exe");1320 else1321 ExecutableName = Triple.isOSWindows() ? "a.exe" : "a.out";1322 1323 parallel::strategy = hardware_concurrency(1);1324 if (auto *Arg = Args.getLastArg(OPT_wrapper_jobs)) {1325 StringRef Val = Arg->getValue();1326 if (Val.equals_insensitive("jobserver"))1327 parallel::strategy = jobserver_concurrency();1328 else {1329 unsigned Threads = 0;1330 if (!llvm::to_integer(Val, Threads) || Threads == 0)1331 reportError(createStringError(1332 "%s: expected a positive integer or 'jobserver', got '%s'",1333 Arg->getSpelling().data(), Val.data()));1334 else1335 parallel::strategy = hardware_concurrency(Threads);1336 }1337 }1338 1339 if (Args.hasArg(OPT_wrapper_time_trace_eq)) {1340 unsigned Granularity;1341 Args.getLastArgValue(OPT_wrapper_time_trace_granularity, "500")1342 .getAsInteger(10, Granularity);1343 timeTraceProfilerInitialize(Granularity, Argv[0]);1344 }1345 1346 {1347 llvm::TimeTraceScope TimeScope("Execute linker wrapper");1348 1349 // Extract the device input files stored in the host fat binary.1350 auto DeviceInputFiles = getDeviceInput(Args);1351 if (!DeviceInputFiles)1352 reportError(DeviceInputFiles.takeError());1353 1354 // Check if we should emit fat binary directly without wrapping or host1355 // linking.1356 bool EmitFatbinOnly = Args.hasArg(OPT_emit_fatbin_only);1357 1358 // Link and process the device images. The function may emit a direct fat1359 // binary if --emit-fatbin-only is specified.1360 auto FilesOrErr = linkAndWrapDeviceFiles(*DeviceInputFiles, Args, Argv,1361 Argc, !EmitFatbinOnly);1362 if (!FilesOrErr)1363 reportError(FilesOrErr.takeError());1364 1365 // Run the host linking job with the rendered arguments.1366 if (!EmitFatbinOnly) {1367 if (Error Err = runLinker(*FilesOrErr, Args))1368 reportError(std::move(Err));1369 }1370 }1371 1372 if (const opt::Arg *Arg = Args.getLastArg(OPT_wrapper_time_trace_eq)) {1373 if (Error Err = timeTraceProfilerWrite(Arg->getValue(), ExecutableName))1374 reportError(std::move(Err));1375 timeTraceProfilerCleanup();1376 }1377 1378 // Remove the temporary files created.1379 if (!SaveTemps)1380 for (const auto &TempFile : TempFiles)1381 if (std::error_code EC = sys::fs::remove(TempFile))1382 reportError(createFileError(TempFile, EC));1383 1384 return EXIT_SUCCESS;1385}1386