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 <OBackend = **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