brintos

brintos / llvm-project-archived public Read only

0
0
Text · 28.5 KiB · a6c7a3a Raw
819 lines · cpp
1//===-- clang-nvlink-wrapper/ClangNVLinkWrapper.cpp - NVIDIA linker util --===//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 wraps around the NVIDIA linker called 'nvlink'. The NVIDIA linker10// is required to create NVPTX applications, but does not support common11// features like LTO or archives. This utility wraps around the tool to cover12// its deficiencies. This tool can be removed once NVIDIA improves their linker13// or ports it to `ld.lld`.14//15//===---------------------------------------------------------------------===//16 17#include "clang/Basic/Version.h"18 19#include "llvm/ADT/StringExtras.h"20#include "llvm/BinaryFormat/Magic.h"21#include "llvm/Bitcode/BitcodeWriter.h"22#include "llvm/CodeGen/CommandFlags.h"23#include "llvm/IR/DiagnosticPrinter.h"24#include "llvm/LTO/LTO.h"25#include "llvm/Object/Archive.h"26#include "llvm/Object/ArchiveWriter.h"27#include "llvm/Object/Binary.h"28#include "llvm/Object/ELFObjectFile.h"29#include "llvm/Object/IRObjectFile.h"30#include "llvm/Object/ObjectFile.h"31#include "llvm/Object/OffloadBinary.h"32#include "llvm/Option/ArgList.h"33#include "llvm/Option/OptTable.h"34#include "llvm/Option/Option.h"35#include "llvm/Remarks/HotnessThresholdParser.h"36#include "llvm/Support/CommandLine.h"37#include "llvm/Support/FileOutputBuffer.h"38#include "llvm/Support/FileSystem.h"39#include "llvm/Support/InitLLVM.h"40#include "llvm/Support/MemoryBuffer.h"41#include "llvm/Support/Path.h"42#include "llvm/Support/Program.h"43#include "llvm/Support/Signals.h"44#include "llvm/Support/StringSaver.h"45#include "llvm/Support/TargetSelect.h"46#include "llvm/Support/WithColor.h"47 48using namespace llvm;49using namespace llvm::opt;50using namespace llvm::object;51 52// Various tools (e.g., llc and opt) duplicate this series of declarations for53// options related to passes and remarks.54static cl::opt<bool> RemarksWithHotness(55    "pass-remarks-with-hotness",56    cl::desc("With PGO, include profile count in optimization remarks"),57    cl::Hidden);58 59static cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser>60    RemarksHotnessThreshold(61        "pass-remarks-hotness-threshold",62        cl::desc("Minimum profile count required for "63                 "an optimization remark to be output. "64                 "Use 'auto' to apply the threshold from profile summary."),65        cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden);66 67static cl::opt<std::string>68    RemarksFilename("pass-remarks-output",69                    cl::desc("Output filename for pass remarks"),70                    cl::value_desc("filename"));71 72static cl::opt<std::string>73    RemarksPasses("pass-remarks-filter",74                  cl::desc("Only record optimization remarks from passes whose "75                           "names match the given regular expression"),76                  cl::value_desc("regex"));77 78static cl::opt<std::string> RemarksFormat(79    "pass-remarks-format",80    cl::desc("The format used for serializing remarks (default: YAML)"),81    cl::value_desc("format"), cl::init("yaml"));82 83static cl::list<std::string>84    PassPlugins("load-pass-plugin",85                cl::desc("Load passes from plugin library"));86 87static void printVersion(raw_ostream &OS) {88  OS << clang::getClangToolFullVersion("clang-nvlink-wrapper") << '\n';89}90 91/// The value of `argv[0]` when run.92static const char *Executable;93 94/// Temporary files to be cleaned up.95static SmallVector<SmallString<128>> TempFiles;96 97/// Codegen flags for LTO backend.98static codegen::RegisterCodeGenFlags CodeGenFlags;99 100namespace {101// Must not overlap with llvm::opt::DriverFlag.102enum WrapperFlags { WrapperOnlyOption = (1 << 4) };103 104enum ID {105  OPT_INVALID = 0, // This is not an option ID.106#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),107#include "NVLinkOpts.inc"108  LastOption109#undef OPTION110};111 112#define OPTTABLE_STR_TABLE_CODE113#include "NVLinkOpts.inc"114#undef OPTTABLE_STR_TABLE_CODE115 116#define OPTTABLE_PREFIXES_TABLE_CODE117#include "NVLinkOpts.inc"118#undef OPTTABLE_PREFIXES_TABLE_CODE119 120static constexpr OptTable::Info InfoTable[] = {121#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),122#include "NVLinkOpts.inc"123#undef OPTION124};125 126class WrapperOptTable : public opt::GenericOptTable {127public:128  WrapperOptTable()129      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}130};131 132const OptTable &getOptTable() {133  static const WrapperOptTable *Table = []() {134    auto Result = std::make_unique<WrapperOptTable>();135    return Result.release();136  }();137  return *Table;138}139 140[[noreturn]] void reportError(Error E) {141  outs().flush();142  logAllUnhandledErrors(std::move(E), WithColor::error(errs(), Executable));143  exit(EXIT_FAILURE);144}145 146void diagnosticHandler(const DiagnosticInfo &DI) {147  std::string ErrStorage;148  raw_string_ostream OS(ErrStorage);149  DiagnosticPrinterRawOStream DP(OS);150  DI.print(DP);151 152  switch (DI.getSeverity()) {153  case DS_Error:154    WithColor::error(errs(), Executable) << ErrStorage << "\n";155    break;156  case DS_Warning:157    WithColor::warning(errs(), Executable) << ErrStorage << "\n";158    break;159  case DS_Note:160    WithColor::note(errs(), Executable) << ErrStorage << "\n";161    break;162  case DS_Remark:163    WithColor::remark(errs()) << ErrStorage << "\n";164    break;165  }166}167 168bool hasFatBinary(const ArgList &Args, MemoryBufferRef Buffer) {169  if (Args.hasArg(OPT_dry_run) && Args.hasArg(OPT_assume_device_object))170    return false;171  if (identify_magic(Buffer.getBuffer()) != file_magic::elf_relocatable)172    return false;173  Expected<std::unique_ptr<ObjectFile>> ObjFile =174      ObjectFile::createObjectFile(Buffer);175  if (!ObjFile) // Assume fatbin if the object creation fails.176    return !errorToBool(ObjFile.takeError());177  return (*ObjFile)->getArch() != Triple::nvptx &&178         (*ObjFile)->getArch() != Triple::nvptx64;179}180 181Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix,182                                   StringRef Extension) {183  SmallString<128> OutputFile;184  if (Args.hasArg(OPT_save_temps)) {185    (Prefix + "." + Extension).toNullTerminatedStringRef(OutputFile);186  } else {187    if (std::error_code EC =188            sys::fs::createTemporaryFile(Prefix, Extension, OutputFile))189      return createFileError(OutputFile, EC);190  }191 192  TempFiles.emplace_back(std::move(OutputFile));193  return TempFiles.back();194}195 196Expected<std::string> findProgram(const ArgList &Args, StringRef Name,197                                  ArrayRef<StringRef> Paths) {198  if (Args.hasArg(OPT_dry_run))199    return Name.str();200  ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);201  if (!Path)202    Path = sys::findProgramByName(Name);203  if (!Path)204    return createStringError(Path.getError(),205                             "Unable to find '" + Name + "' in path");206  return *Path;207}208 209std::optional<std::string> findFile(StringRef Dir, StringRef Root,210                                    const Twine &Name) {211  SmallString<128> Path;212  if (Dir.starts_with("="))213    sys::path::append(Path, Root, Dir.substr(1), Name);214  else215    sys::path::append(Path, Dir, Name);216 217  if (sys::fs::exists(Path))218    return static_cast<std::string>(Path);219  return std::nullopt;220}221 222std::optional<std::string>223findFromSearchPaths(StringRef Name, StringRef Root,224                    ArrayRef<StringRef> SearchPaths) {225  for (StringRef Dir : SearchPaths)226    if (std::optional<std::string> File = findFile(Dir, Root, Name))227      return File;228  return std::nullopt;229}230 231std::optional<std::string>232searchLibraryBaseName(StringRef Name, StringRef Root,233                      ArrayRef<StringRef> SearchPaths) {234  for (StringRef Dir : SearchPaths)235    if (std::optional<std::string> File =236            findFile(Dir, Root, "lib" + Name + ".a"))237      return File;238  return std::nullopt;239}240 241/// Search for static libraries in the linker's library path given input like242/// `-lfoo` or `-l:libfoo.a`.243std::optional<std::string> searchLibrary(StringRef Input, StringRef Root,244                                         ArrayRef<StringRef> SearchPaths) {245  if (Input.starts_with(":"))246    return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);247  return searchLibraryBaseName(Input, Root, SearchPaths);248}249 250void printCommands(ArrayRef<StringRef> CmdArgs) {251  if (CmdArgs.empty())252    return;253 254  errs() << " \"" << CmdArgs.front() << "\" ";255  errs() << join(std::next(CmdArgs.begin()), CmdArgs.end(), " ") << "\n";256}257 258/// A minimum symbol interface that provides the necessary information to259/// extract archive members and resolve LTO symbols.260struct Symbol {261  enum Flags {262    None = 0,263    Undefined = 1 << 0,264    Weak = 1 << 1,265  };266 267  Symbol() : File(), Flags(None), UsedInRegularObj(false) {}268  Symbol(Symbol::Flags Flags) : File(), Flags(Flags), UsedInRegularObj(true) {}269 270  Symbol(MemoryBufferRef File, const irsymtab::Reader::SymbolRef Sym)271      : File(File), Flags(0), UsedInRegularObj(false) {272    if (Sym.isUndefined())273      Flags |= Undefined;274    if (Sym.isWeak())275      Flags |= Weak;276  }277 278  Symbol(MemoryBufferRef File, const SymbolRef Sym)279      : File(File), Flags(0), UsedInRegularObj(false) {280    auto FlagsOrErr = Sym.getFlags();281    if (!FlagsOrErr)282      reportError(FlagsOrErr.takeError());283    if (*FlagsOrErr & SymbolRef::SF_Undefined)284      Flags |= Undefined;285    if (*FlagsOrErr & SymbolRef::SF_Weak)286      Flags |= Weak;287 288    auto NameOrErr = Sym.getName();289    if (!NameOrErr)290      reportError(NameOrErr.takeError());291  }292 293  bool isWeak() const { return Flags & Weak; }294  bool isUndefined() const { return Flags & Undefined; }295 296  MemoryBufferRef File;297  uint32_t Flags;298  bool UsedInRegularObj;299};300 301Expected<StringRef> runPTXAs(StringRef File, const ArgList &Args) {302  SmallVector<StringRef, 1> SearchPaths;303  if (Arg *A = Args.getLastArg(OPT_cuda_path_EQ))304    SearchPaths.push_back(Args.MakeArgString(A->getValue() + Twine("/bin")));305  if (Arg *A = Args.getLastArg(OPT_ptxas_path_EQ))306    SearchPaths.push_back(Args.MakeArgString(A->getValue()));307 308  Expected<std::string> PTXAsPath = findProgram(Args, "ptxas", SearchPaths);309  if (!PTXAsPath)310    return PTXAsPath.takeError();311 312  if (!Args.hasArg(OPT_arch))313    return createStringError(314        "must pass in an explicit nvptx64 gpu architecture to 'ptxas'");315 316  auto TempFileOrErr = createTempFile(317      Args, sys::path::stem(Args.getLastArgValue(OPT_o, "a.out")), "cubin");318  if (!TempFileOrErr)319    return TempFileOrErr.takeError();320 321  SmallVector<StringRef> AssemblerArgs({*PTXAsPath, "-m64", "-c", File});322  if (Args.hasArg(OPT_verbose))323    AssemblerArgs.push_back("-v");324  if (Args.hasArg(OPT_g)) {325    if (Args.hasArg(OPT_O))326      WithColor::warning(errs(), Executable)327          << "Optimized debugging not supported, overriding to '-O0'\n";328    AssemblerArgs.push_back("-O0");329  } else330    AssemblerArgs.push_back(331        Args.MakeArgString("-O" + Args.getLastArgValue(OPT_O, "3")));332  AssemblerArgs.append({"-arch", Args.getLastArgValue(OPT_arch)});333  AssemblerArgs.append({"-o", *TempFileOrErr});334 335  if (Args.hasArg(OPT_dry_run) || Args.hasArg(OPT_verbose))336    printCommands(AssemblerArgs);337  if (Args.hasArg(OPT_dry_run))338    return Args.MakeArgString(*TempFileOrErr);339  if (sys::ExecuteAndWait(*PTXAsPath, AssemblerArgs))340    return createStringError("'" + sys::path::filename(*PTXAsPath) + "'" +341                             " failed");342  return Args.MakeArgString(*TempFileOrErr);343}344 345Expected<std::unique_ptr<lto::LTO>> createLTO(const ArgList &Args) {346  const llvm::Triple Triple("nvptx64-nvidia-cuda");347  lto::Config Conf;348  lto::ThinBackend Backend;349  unsigned Jobs = 0;350  if (auto *Arg = Args.getLastArg(OPT_jobs))351    if (!to_integer(Arg->getValue(), Jobs) || Jobs == 0)352      reportError(createStringError("%s: expected a positive integer, got '%s'",353                                    Arg->getSpelling().data(),354                                    Arg->getValue()));355  Backend =356      lto::createInProcessThinBackend(heavyweight_hardware_concurrency(Jobs));357 358  Conf.CPU = Args.getLastArgValue(OPT_arch);359  Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);360 361  Conf.RemarksFilename =362      Args.getLastArgValue(OPT_opt_remarks_filename, RemarksFilename);363  Conf.RemarksPasses =364      Args.getLastArgValue(OPT_opt_remarks_filter, RemarksPasses);365  Conf.RemarksFormat =366      Args.getLastArgValue(OPT_opt_remarks_format, RemarksFormat);367 368  Conf.RemarksWithHotness =369      Args.hasArg(OPT_opt_remarks_with_hotness) || RemarksWithHotness;370  Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;371 372  Conf.MAttrs = llvm::codegen::getMAttrs();373  std::optional<CodeGenOptLevel> CGOptLevelOrNone =374      CodeGenOpt::parseLevel(Args.getLastArgValue(OPT_O, "2")[0]);375  assert(CGOptLevelOrNone && "Invalid optimization level");376  Conf.CGOptLevel = *CGOptLevelOrNone;377  Conf.OptLevel = Args.getLastArgValue(OPT_O, "2")[0] - '0';378  Conf.DefaultTriple = Triple.getTriple();379 380  Conf.OptPipeline = Args.getLastArgValue(OPT_lto_newpm_passes, "");381  Conf.PassPlugins = PassPlugins;382  Conf.DebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager);383 384  Conf.DiagHandler = diagnosticHandler;385  Conf.CGFileType = CodeGenFileType::AssemblyFile;386 387  if (Args.hasArg(OPT_lto_emit_llvm)) {388    Conf.PreCodeGenModuleHook = [&](size_t, const Module &M) {389      std::error_code EC;390      raw_fd_ostream LinkedBitcode(Args.getLastArgValue(OPT_o, "a.out"), EC);391      if (EC)392        reportError(errorCodeToError(EC));393      WriteBitcodeToFile(M, LinkedBitcode);394      return false;395    };396  }397 398  if (Args.hasArg(OPT_save_temps))399    if (Error Err = Conf.addSaveTemps(400            (Args.getLastArgValue(OPT_o, "a.out") + ".").str()))401      return Err;402 403  unsigned Partitions = 1;404  if (auto *Arg = Args.getLastArg(OPT_lto_partitions))405    if (!to_integer(Arg->getValue(), Partitions) || Partitions == 0)406      reportError(createStringError("%s: expected a positive integer, got '%s'",407                                    Arg->getSpelling().data(),408                                    Arg->getValue()));409  lto::LTO::LTOKind Kind = Args.hasArg(OPT_thinlto) ? lto::LTO::LTOK_UnifiedThin410                                                    : lto::LTO::LTOK_Default;411  return std::make_unique<lto::LTO>(std::move(Conf), Backend, Partitions, Kind);412}413 414Expected<bool> getSymbolsFromBitcode(MemoryBufferRef Buffer,415                                     StringMap<Symbol> &SymTab, bool IsLazy) {416  Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer);417  if (!IRSymtabOrErr)418    return IRSymtabOrErr.takeError();419  bool Extracted = !IsLazy;420  StringMap<Symbol> PendingSymbols;421  for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) {422    for (const auto &IRSym : IRSymtabOrErr->TheReader.module_symbols(I)) {423      if (IRSym.isFormatSpecific() || !IRSym.isGlobal())424        continue;425 426      Symbol &OldSym = !SymTab.count(IRSym.getName()) && IsLazy427                           ? PendingSymbols[IRSym.getName()]428                           : SymTab[IRSym.getName()];429      Symbol Sym = Symbol(Buffer, IRSym);430      if (OldSym.File.getBuffer().empty())431        OldSym = Sym;432 433      bool ResolvesReference =434          !Sym.isUndefined() &&435          (OldSym.isUndefined() || (OldSym.isWeak() && !Sym.isWeak())) &&436          !(OldSym.isWeak() && OldSym.isUndefined() && IsLazy);437      Extracted |= ResolvesReference;438 439      Sym.UsedInRegularObj = OldSym.UsedInRegularObj;440      if (ResolvesReference)441        OldSym = Sym;442    }443  }444  if (Extracted)445    for (const auto &[Name, Symbol] : PendingSymbols)446      SymTab[Name] = Symbol;447  return Extracted;448}449 450Expected<bool> getSymbolsFromObject(ObjectFile &ObjFile,451                                    StringMap<Symbol> &SymTab, bool IsLazy) {452  bool Extracted = !IsLazy;453  StringMap<Symbol> PendingSymbols;454  for (SymbolRef ObjSym : ObjFile.symbols()) {455    auto NameOrErr = ObjSym.getName();456    if (!NameOrErr)457      return NameOrErr.takeError();458 459    Symbol &OldSym = !SymTab.count(*NameOrErr) && IsLazy460                         ? PendingSymbols[*NameOrErr]461                         : SymTab[*NameOrErr];462    Symbol Sym = Symbol(ObjFile.getMemoryBufferRef(), ObjSym);463    if (OldSym.File.getBuffer().empty())464      OldSym = Sym;465 466    bool ResolvesReference = OldSym.isUndefined() && !Sym.isUndefined() &&467                             (!OldSym.isWeak() || !IsLazy);468    Extracted |= ResolvesReference;469 470    if (ResolvesReference)471      OldSym = Sym;472    OldSym.UsedInRegularObj = true;473  }474  if (Extracted)475    for (const auto &[Name, Symbol] : PendingSymbols)476      SymTab[Name] = Symbol;477  return Extracted;478}479 480Expected<bool> getSymbols(MemoryBufferRef Buffer, StringMap<Symbol> &SymTab,481                          bool IsLazy) {482  switch (identify_magic(Buffer.getBuffer())) {483  case file_magic::bitcode: {484    return getSymbolsFromBitcode(Buffer, SymTab, IsLazy);485  }486  case file_magic::elf_relocatable: {487    Expected<std::unique_ptr<ObjectFile>> ObjFile =488        ObjectFile::createObjectFile(Buffer);489    if (!ObjFile)490      return ObjFile.takeError();491    return getSymbolsFromObject(**ObjFile, SymTab, IsLazy);492  }493  default:494    return createStringError("Unsupported file type");495  }496}497 498Expected<SmallVector<StringRef>> getInput(const ArgList &Args) {499  SmallVector<StringRef> LibraryPaths;500  for (const opt::Arg *Arg : Args.filtered(OPT_library_path))501    LibraryPaths.push_back(Arg->getValue());502 503  bool WholeArchive = false;504  SmallVector<std::pair<std::unique_ptr<MemoryBuffer>, bool>> InputFiles;505  for (const opt::Arg *Arg : Args.filtered(506           OPT_INPUT, OPT_library, OPT_whole_archive, OPT_no_whole_archive)) {507    if (Arg->getOption().matches(OPT_whole_archive) ||508        Arg->getOption().matches(OPT_no_whole_archive)) {509      WholeArchive = Arg->getOption().matches(OPT_whole_archive);510      continue;511    }512 513    std::optional<std::string> Filename =514        Arg->getOption().matches(OPT_library)515            ? searchLibrary(Arg->getValue(), /*Root=*/"", LibraryPaths)516            : std::string(Arg->getValue());517 518    if (!Filename && Arg->getOption().matches(OPT_library))519      return createStringError("unable to find library -l%s", Arg->getValue());520 521    if (!Filename || !sys::fs::exists(*Filename) ||522        sys::fs::is_directory(*Filename))523      continue;524 525    ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =526        MemoryBuffer::getFileOrSTDIN(*Filename);527    if (std::error_code EC = BufferOrErr.getError())528      return createFileError(*Filename, EC);529 530    MemoryBufferRef Buffer = **BufferOrErr;531    switch (identify_magic(Buffer.getBuffer())) {532    case file_magic::bitcode:533    case file_magic::elf_relocatable:534      InputFiles.emplace_back(std::move(*BufferOrErr), /*IsLazy=*/false);535      break;536    case file_magic::archive: {537      Expected<std::unique_ptr<object::Archive>> LibFile =538          object::Archive::create(Buffer);539      if (!LibFile)540        return LibFile.takeError();541      Error Err = Error::success();542      for (auto Child : (*LibFile)->children(Err)) {543        auto ChildBufferOrErr = Child.getMemoryBufferRef();544        if (!ChildBufferOrErr)545          return ChildBufferOrErr.takeError();546        std::unique_ptr<MemoryBuffer> ChildBuffer =547            MemoryBuffer::getMemBufferCopy(548                ChildBufferOrErr->getBuffer(),549                ChildBufferOrErr->getBufferIdentifier());550        InputFiles.emplace_back(std::move(ChildBuffer), !WholeArchive);551      }552      if (Err)553        return Err;554      break;555    }556    default:557      return createStringError("Unsupported file type");558    }559  }560 561  bool Extracted = true;562  StringMap<Symbol> SymTab;563  for (auto &Sym : Args.getAllArgValues(OPT_u))564    SymTab[Sym] = Symbol(Symbol::Undefined);565  SmallVector<std::unique_ptr<MemoryBuffer>> LinkerInput;566  while (Extracted) {567    Extracted = false;568    for (auto &[Input, IsLazy] : InputFiles) {569      if (!Input)570        continue;571 572      if (hasFatBinary(Args, *Input)) {573        LinkerInput.emplace_back(std::move(Input));574        continue;575      }576 577      // Archive members only extract if they define needed symbols. We will578      // re-scan all the inputs if any files were extracted for the link job.579      Expected<bool> ExtractOrErr = getSymbols(*Input, SymTab, IsLazy);580      if (!ExtractOrErr)581        return ExtractOrErr.takeError();582 583      Extracted |= *ExtractOrErr;584      if (!*ExtractOrErr)585        continue;586 587      LinkerInput.emplace_back(std::move(Input));588    }589  }590  InputFiles.clear();591 592  // Extract any bitcode files to be passed to the LTO pipeline.593  SmallVector<std::unique_ptr<MemoryBuffer>> BitcodeFiles;594  for (auto &Input : LinkerInput)595    if (identify_magic(Input->getBuffer()) == file_magic::bitcode)596      BitcodeFiles.emplace_back(std::move(Input));597  erase_if(LinkerInput, [](const auto &F) { return !F; });598 599  // Run the LTO pipeline on the extracted inputs.600  SmallVector<StringRef> Files;601  if (!BitcodeFiles.empty()) {602    auto LTOBackendOrErr = createLTO(Args);603    if (!LTOBackendOrErr)604      return LTOBackendOrErr.takeError();605    lto::LTO &LTOBackend = **LTOBackendOrErr;606    for (auto &BitcodeFile : BitcodeFiles) {607      Expected<std::unique_ptr<lto::InputFile>> BitcodeFileOrErr =608          lto::InputFile::create(*BitcodeFile);609      if (!BitcodeFileOrErr)610        return BitcodeFileOrErr.takeError();611 612      const auto Symbols = (*BitcodeFileOrErr)->symbols();613      SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size());614      size_t Idx = 0;615      for (auto &Sym : Symbols) {616        lto::SymbolResolution &Res = Resolutions[Idx++];617        Symbol ObjSym = SymTab[Sym.getName()];618        // We will use this as the prevailing symbol in LTO if it is not619        // undefined and it is from the file that contained the canonical620        // definition.621        Res.Prevailing = !Sym.isUndefined() && ObjSym.File == *BitcodeFile;622 623        // We need LTO to preseve the following global symbols:624        // 1) All symbols during a relocatable link.625        // 2) Symbols used in regular objects.626        // 3) Prevailing symbols that are needed visible to the gpu runtime.627        Res.VisibleToRegularObj =628            Args.hasArg(OPT_relocatable) || ObjSym.UsedInRegularObj ||629            (Res.Prevailing &&630             (Sym.getVisibility() != GlobalValue::HiddenVisibility &&631              !Sym.canBeOmittedFromSymbolTable()));632 633        // Identify symbols that must be exported dynamically and can be634        // referenced by other files, (i.e. the runtime).635        Res.ExportDynamic =636            Sym.getVisibility() != GlobalValue::HiddenVisibility &&637            !Sym.canBeOmittedFromSymbolTable();638 639        // The NVIDIA platform does not support any symbol preemption.640        Res.FinalDefinitionInLinkageUnit = true;641 642        // We do not support linker redefined symbols (e.g. --wrap) for device643        // image linking, so the symbols will not be changed after LTO.644        Res.LinkerRedefined = false;645      }646 647      // Add the bitcode file with its resolved symbols to the LTO job.648      if (Error Err = LTOBackend.add(std::move(*BitcodeFileOrErr), Resolutions))649        return Err;650    }651 652    // Run the LTO job to compile the bitcode.653    size_t MaxTasks = LTOBackend.getMaxTasks();654    SmallVector<StringRef> LTOFiles(MaxTasks);655    auto AddStream =656        [&](size_t Task,657            const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {658      int FD = -1;659      auto &TempFile = LTOFiles[Task];660      if (Args.hasArg(OPT_lto_emit_asm))661        TempFile = Args.getLastArgValue(OPT_o, "a.out");662      else {663        auto TempFileOrErr = createTempFile(664            Args, sys::path::stem(Args.getLastArgValue(OPT_o, "a.out")), "s");665        if (!TempFileOrErr)666          reportError(TempFileOrErr.takeError());667        TempFile = Args.MakeArgString(*TempFileOrErr);668      }669      if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD))670        reportError(errorCodeToError(EC));671      return std::make_unique<CachedFileStream>(672          std::make_unique<raw_fd_ostream>(FD, true));673    };674 675    if (Error Err = LTOBackend.run(AddStream))676      return Err;677 678    if (Args.hasArg(OPT_lto_emit_llvm) || Args.hasArg(OPT_lto_emit_asm))679      return Files;680 681    for (StringRef LTOFile : LTOFiles) {682      auto FileOrErr = runPTXAs(LTOFile, Args);683      if (!FileOrErr)684        return FileOrErr.takeError();685      Files.emplace_back(*FileOrErr);686    }687  }688 689  // Create a copy for each file to a new file ending in `.cubin`. The 'nvlink'690  // linker requires all NVPTX inputs to have this extension for some reason.691  // We don't use a symbolic link because it's not supported on Windows and some692  // of this input files could be extracted from an archive.693  for (auto &Input : LinkerInput) {694    auto TempFileOrErr = createTempFile(695        Args, sys::path::stem(Input->getBufferIdentifier()),696        hasFatBinary(Args, Input->getMemBufferRef()) ? "o" : "cubin");697    if (!TempFileOrErr)698      return TempFileOrErr.takeError();699    Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =700        FileOutputBuffer::create(*TempFileOrErr, Input->getBuffer().size());701    if (!OutputOrErr)702      return OutputOrErr.takeError();703    std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);704    copy(Input->getBuffer(), Output->getBufferStart());705    if (Error E = Output->commit())706      return E;707    Files.emplace_back(Args.MakeArgString(*TempFileOrErr));708  }709 710  return Files;711}712 713Error runNVLink(ArrayRef<StringRef> Files, const ArgList &Args) {714  if (Args.hasArg(OPT_lto_emit_asm) || Args.hasArg(OPT_lto_emit_llvm))715    return Error::success();716 717  SmallVector<StringRef, 1> SearchPaths;718  if (Arg *A = Args.getLastArg(OPT_cuda_path_EQ))719    SearchPaths.push_back(Args.MakeArgString(A->getValue() + Twine("/bin")));720 721  Expected<std::string> NVLinkPath = findProgram(Args, "nvlink", SearchPaths);722  if (!NVLinkPath)723    return NVLinkPath.takeError();724 725  if (!Args.hasArg(OPT_arch))726    return createStringError(727        "must pass in an explicit nvptx64 gpu architecture to 'nvlink'");728 729  ArgStringList NewLinkerArgs;730  for (const opt::Arg *Arg : Args) {731    // Do not forward arguments only intended for the linker wrapper.732    if (Arg->getOption().hasFlag(WrapperOnlyOption))733      continue;734 735    // Do not forward any inputs that we have processed.736    if (Arg->getOption().matches(OPT_INPUT) ||737        Arg->getOption().matches(OPT_library))738      continue;739 740    Arg->render(Args, NewLinkerArgs);741  }742 743  transform(Files, std::back_inserter(NewLinkerArgs),744            [&](StringRef Arg) { return Args.MakeArgString(Arg); });745 746  SmallVector<StringRef> LinkerArgs({*NVLinkPath});747  if (!Args.hasArg(OPT_o))748    LinkerArgs.append({"-o", "a.out"});749  for (StringRef Arg : NewLinkerArgs)750    LinkerArgs.push_back(Arg);751 752  if (Args.hasArg(OPT_dry_run) || Args.hasArg(OPT_verbose))753    printCommands(LinkerArgs);754  if (Args.hasArg(OPT_dry_run))755    return Error::success();756  if (sys::ExecuteAndWait(*NVLinkPath, LinkerArgs))757    return createStringError("'" + sys::path::filename(*NVLinkPath) + "'" +758                             " failed");759  return Error::success();760}761 762} // namespace763 764int main(int argc, char **argv) {765  InitLLVM X(argc, argv);766  InitializeAllTargetInfos();767  InitializeAllTargets();768  InitializeAllTargetMCs();769  InitializeAllAsmParsers();770  InitializeAllAsmPrinters();771 772  Executable = argv[0];773  sys::PrintStackTraceOnErrorSignal(argv[0]);774 775  const OptTable &Tbl = getOptTable();776  BumpPtrAllocator Alloc;777  StringSaver Saver(Alloc);778  auto Args = Tbl.parseArgs(argc, argv, OPT_INVALID, Saver, [&](StringRef Err) {779    reportError(createStringError(inconvertibleErrorCode(), Err));780  });781 782  if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) {783    Tbl.printHelp(784        outs(), "clang-nvlink-wrapper [options] <options to passed to nvlink>",785        "A utility that wraps around the NVIDIA 'nvlink' linker.\n"786        "This enables static linking and LTO handling for NVPTX targets.",787        Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden));788    return EXIT_SUCCESS;789  }790 791  if (Args.hasArg(OPT_version))792    printVersion(outs());793 794  // This forwards '-mllvm' arguments to LLVM if present.795  SmallVector<const char *> NewArgv = {argv[0]};796  for (const opt::Arg *Arg : Args.filtered(OPT_mllvm))797    NewArgv.push_back(Arg->getValue());798  for (const opt::Arg *Arg : Args.filtered(OPT_plugin_opt))799    NewArgv.push_back(Arg->getValue());800  cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]);801 802  // Get the input files to pass to 'nvlink'.803  auto FilesOrErr = getInput(Args);804  if (!FilesOrErr)805    reportError(FilesOrErr.takeError());806 807  // Run 'nvlink' on the generated inputs.808  if (Error Err = runNVLink(*FilesOrErr, Args))809    reportError(std::move(Err));810 811  // Remove the temporary files created.812  if (!Args.hasArg(OPT_save_temps))813    for (const auto &TempFile : TempFiles)814      if (std::error_code EC = sys::fs::remove(TempFile))815        reportError(createFileError(TempFile, EC));816 817  return EXIT_SUCCESS;818}819