137d0568aSJoseph Huber //===-- clang-nvlink-wrapper/ClangNVLinkWrapper.cpp - NVIDIA linker util --===// 237d0568aSJoseph Huber // 337d0568aSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 437d0568aSJoseph Huber // See https://llvm.org/LICENSE.txt for license information. 537d0568aSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 637d0568aSJoseph Huber // 737d0568aSJoseph Huber //===---------------------------------------------------------------------===// 837d0568aSJoseph Huber // 937d0568aSJoseph Huber // This tool wraps around the NVIDIA linker called 'nvlink'. The NVIDIA linker 1037d0568aSJoseph Huber // is required to create NVPTX applications, but does not support common 1137d0568aSJoseph Huber // features like LTO or archives. This utility wraps around the tool to cover 1237d0568aSJoseph Huber // its deficiencies. This tool can be removed once NVIDIA improves their linker 1337d0568aSJoseph Huber // or ports it to `ld.lld`. 1437d0568aSJoseph Huber // 1537d0568aSJoseph Huber //===---------------------------------------------------------------------===// 1637d0568aSJoseph Huber 1737d0568aSJoseph Huber #include "clang/Basic/Version.h" 1837d0568aSJoseph Huber 1937d0568aSJoseph Huber #include "llvm/ADT/StringExtras.h" 2037d0568aSJoseph Huber #include "llvm/BinaryFormat/Magic.h" 2137d0568aSJoseph Huber #include "llvm/Bitcode/BitcodeWriter.h" 2237d0568aSJoseph Huber #include "llvm/CodeGen/CommandFlags.h" 2337d0568aSJoseph Huber #include "llvm/IR/DiagnosticPrinter.h" 2437d0568aSJoseph Huber #include "llvm/LTO/LTO.h" 2537d0568aSJoseph Huber #include "llvm/Object/Archive.h" 2637d0568aSJoseph Huber #include "llvm/Object/ArchiveWriter.h" 2737d0568aSJoseph Huber #include "llvm/Object/Binary.h" 2837d0568aSJoseph Huber #include "llvm/Object/ELFObjectFile.h" 2937d0568aSJoseph Huber #include "llvm/Object/IRObjectFile.h" 3037d0568aSJoseph Huber #include "llvm/Object/ObjectFile.h" 3137d0568aSJoseph Huber #include "llvm/Object/OffloadBinary.h" 3237d0568aSJoseph Huber #include "llvm/Option/ArgList.h" 3337d0568aSJoseph Huber #include "llvm/Option/OptTable.h" 3437d0568aSJoseph Huber #include "llvm/Option/Option.h" 3537d0568aSJoseph Huber #include "llvm/Remarks/HotnessThresholdParser.h" 3637d0568aSJoseph Huber #include "llvm/Support/CommandLine.h" 3737d0568aSJoseph Huber #include "llvm/Support/FileOutputBuffer.h" 3837d0568aSJoseph Huber #include "llvm/Support/FileSystem.h" 3937d0568aSJoseph Huber #include "llvm/Support/InitLLVM.h" 4037d0568aSJoseph Huber #include "llvm/Support/MemoryBuffer.h" 4137d0568aSJoseph Huber #include "llvm/Support/Path.h" 4237d0568aSJoseph Huber #include "llvm/Support/Program.h" 4337d0568aSJoseph Huber #include "llvm/Support/Signals.h" 4437d0568aSJoseph Huber #include "llvm/Support/StringSaver.h" 4537d0568aSJoseph Huber #include "llvm/Support/TargetSelect.h" 4637d0568aSJoseph Huber #include "llvm/Support/WithColor.h" 4737d0568aSJoseph Huber 4837d0568aSJoseph Huber using namespace llvm; 4937d0568aSJoseph Huber using namespace llvm::opt; 5037d0568aSJoseph Huber using namespace llvm::object; 5137d0568aSJoseph Huber 5237d0568aSJoseph Huber // Various tools (e.g., llc and opt) duplicate this series of declarations for 5337d0568aSJoseph Huber // options related to passes and remarks. 5437d0568aSJoseph Huber static cl::opt<bool> RemarksWithHotness( 5537d0568aSJoseph Huber "pass-remarks-with-hotness", 5637d0568aSJoseph Huber cl::desc("With PGO, include profile count in optimization remarks"), 5737d0568aSJoseph Huber cl::Hidden); 5837d0568aSJoseph Huber 5937d0568aSJoseph Huber static cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser> 6037d0568aSJoseph Huber RemarksHotnessThreshold( 6137d0568aSJoseph Huber "pass-remarks-hotness-threshold", 6237d0568aSJoseph Huber cl::desc("Minimum profile count required for " 6337d0568aSJoseph Huber "an optimization remark to be output. " 6437d0568aSJoseph Huber "Use 'auto' to apply the threshold from profile summary."), 6537d0568aSJoseph Huber cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden); 6637d0568aSJoseph Huber 6737d0568aSJoseph Huber static cl::opt<std::string> 6837d0568aSJoseph Huber RemarksFilename("pass-remarks-output", 6937d0568aSJoseph Huber cl::desc("Output filename for pass remarks"), 7037d0568aSJoseph Huber cl::value_desc("filename")); 7137d0568aSJoseph Huber 7237d0568aSJoseph Huber static cl::opt<std::string> 7337d0568aSJoseph Huber RemarksPasses("pass-remarks-filter", 7437d0568aSJoseph Huber cl::desc("Only record optimization remarks from passes whose " 7537d0568aSJoseph Huber "names match the given regular expression"), 7637d0568aSJoseph Huber cl::value_desc("regex")); 7737d0568aSJoseph Huber 7837d0568aSJoseph Huber static cl::opt<std::string> RemarksFormat( 7937d0568aSJoseph Huber "pass-remarks-format", 8037d0568aSJoseph Huber cl::desc("The format used for serializing remarks (default: YAML)"), 8137d0568aSJoseph Huber cl::value_desc("format"), cl::init("yaml")); 8237d0568aSJoseph Huber 8337d0568aSJoseph Huber static cl::list<std::string> 8437d0568aSJoseph Huber PassPlugins("load-pass-plugin", 8537d0568aSJoseph Huber cl::desc("Load passes from plugin library")); 8637d0568aSJoseph Huber 8737d0568aSJoseph Huber static void printVersion(raw_ostream &OS) { 8837d0568aSJoseph Huber OS << clang::getClangToolFullVersion("clang-nvlink-wrapper") << '\n'; 8937d0568aSJoseph Huber } 9037d0568aSJoseph Huber 9137d0568aSJoseph Huber /// The value of `argv[0]` when run. 9237d0568aSJoseph Huber static const char *Executable; 9337d0568aSJoseph Huber 9437d0568aSJoseph Huber /// Temporary files to be cleaned up. 9537d0568aSJoseph Huber static SmallVector<SmallString<128>> TempFiles; 9637d0568aSJoseph Huber 9737d0568aSJoseph Huber /// Codegen flags for LTO backend. 9837d0568aSJoseph Huber static codegen::RegisterCodeGenFlags CodeGenFlags; 9937d0568aSJoseph Huber 10037d0568aSJoseph Huber namespace { 10137d0568aSJoseph Huber // Must not overlap with llvm::opt::DriverFlag. 10237d0568aSJoseph Huber enum WrapperFlags { WrapperOnlyOption = (1 << 4) }; 10337d0568aSJoseph Huber 10437d0568aSJoseph Huber enum ID { 10537d0568aSJoseph Huber OPT_INVALID = 0, // This is not an option ID. 10637d0568aSJoseph Huber #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 10737d0568aSJoseph Huber #include "NVLinkOpts.inc" 10837d0568aSJoseph Huber LastOption 10937d0568aSJoseph Huber #undef OPTION 11037d0568aSJoseph Huber }; 11137d0568aSJoseph Huber 112*dd647e3eSChandler Carruth #define OPTTABLE_STR_TABLE_CODE 11337d0568aSJoseph Huber #include "NVLinkOpts.inc" 114*dd647e3eSChandler Carruth #undef OPTTABLE_STR_TABLE_CODE 115*dd647e3eSChandler Carruth 116*dd647e3eSChandler Carruth #define OPTTABLE_PREFIXES_TABLE_CODE 117*dd647e3eSChandler Carruth #include "NVLinkOpts.inc" 118*dd647e3eSChandler Carruth #undef OPTTABLE_PREFIXES_TABLE_CODE 11937d0568aSJoseph Huber 12037d0568aSJoseph Huber static constexpr OptTable::Info InfoTable[] = { 12137d0568aSJoseph Huber #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 12237d0568aSJoseph Huber #include "NVLinkOpts.inc" 12337d0568aSJoseph Huber #undef OPTION 12437d0568aSJoseph Huber }; 12537d0568aSJoseph Huber 12637d0568aSJoseph Huber class WrapperOptTable : public opt::GenericOptTable { 12737d0568aSJoseph Huber public: 128*dd647e3eSChandler Carruth WrapperOptTable() 129*dd647e3eSChandler Carruth : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {} 13037d0568aSJoseph Huber }; 13137d0568aSJoseph Huber 13237d0568aSJoseph Huber const OptTable &getOptTable() { 13337d0568aSJoseph Huber static const WrapperOptTable *Table = []() { 13437d0568aSJoseph Huber auto Result = std::make_unique<WrapperOptTable>(); 13537d0568aSJoseph Huber return Result.release(); 13637d0568aSJoseph Huber }(); 13737d0568aSJoseph Huber return *Table; 13837d0568aSJoseph Huber } 13937d0568aSJoseph Huber 14037d0568aSJoseph Huber [[noreturn]] void reportError(Error E) { 14137d0568aSJoseph Huber outs().flush(); 14237d0568aSJoseph Huber logAllUnhandledErrors(std::move(E), WithColor::error(errs(), Executable)); 14337d0568aSJoseph Huber exit(EXIT_FAILURE); 14437d0568aSJoseph Huber } 14537d0568aSJoseph Huber 14637d0568aSJoseph Huber void diagnosticHandler(const DiagnosticInfo &DI) { 14737d0568aSJoseph Huber std::string ErrStorage; 14837d0568aSJoseph Huber raw_string_ostream OS(ErrStorage); 14937d0568aSJoseph Huber DiagnosticPrinterRawOStream DP(OS); 15037d0568aSJoseph Huber DI.print(DP); 15137d0568aSJoseph Huber 15237d0568aSJoseph Huber switch (DI.getSeverity()) { 15337d0568aSJoseph Huber case DS_Error: 15437d0568aSJoseph Huber WithColor::error(errs(), Executable) << ErrStorage << "\n"; 15537d0568aSJoseph Huber break; 15637d0568aSJoseph Huber case DS_Warning: 15737d0568aSJoseph Huber WithColor::warning(errs(), Executable) << ErrStorage << "\n"; 15837d0568aSJoseph Huber break; 15937d0568aSJoseph Huber case DS_Note: 16037d0568aSJoseph Huber WithColor::note(errs(), Executable) << ErrStorage << "\n"; 16137d0568aSJoseph Huber break; 16237d0568aSJoseph Huber case DS_Remark: 16337d0568aSJoseph Huber WithColor::remark(errs()) << ErrStorage << "\n"; 16437d0568aSJoseph Huber break; 16537d0568aSJoseph Huber } 16637d0568aSJoseph Huber } 16737d0568aSJoseph Huber 16837d0568aSJoseph Huber Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix, 16937d0568aSJoseph Huber StringRef Extension) { 17037d0568aSJoseph Huber SmallString<128> OutputFile; 17137d0568aSJoseph Huber if (Args.hasArg(OPT_save_temps)) { 17237d0568aSJoseph Huber (Prefix + "." + Extension).toNullTerminatedStringRef(OutputFile); 17337d0568aSJoseph Huber } else { 17437d0568aSJoseph Huber if (std::error_code EC = 17537d0568aSJoseph Huber sys::fs::createTemporaryFile(Prefix, Extension, OutputFile)) 17637d0568aSJoseph Huber return createFileError(OutputFile, EC); 17737d0568aSJoseph Huber } 17837d0568aSJoseph Huber 17937d0568aSJoseph Huber TempFiles.emplace_back(std::move(OutputFile)); 18037d0568aSJoseph Huber return TempFiles.back(); 18137d0568aSJoseph Huber } 18237d0568aSJoseph Huber 183624d3221SJoseph Huber Expected<std::string> findProgram(const ArgList &Args, StringRef Name, 184624d3221SJoseph Huber ArrayRef<StringRef> Paths) { 185624d3221SJoseph Huber if (Args.hasArg(OPT_dry_run)) 186624d3221SJoseph Huber return Name.str(); 18737d0568aSJoseph Huber ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths); 18837d0568aSJoseph Huber if (!Path) 18937d0568aSJoseph Huber Path = sys::findProgramByName(Name); 19037d0568aSJoseph Huber if (!Path) 19137d0568aSJoseph Huber return createStringError(Path.getError(), 19237d0568aSJoseph Huber "Unable to find '" + Name + "' in path"); 19337d0568aSJoseph Huber return *Path; 19437d0568aSJoseph Huber } 19537d0568aSJoseph Huber 19637d0568aSJoseph Huber std::optional<std::string> findFile(StringRef Dir, StringRef Root, 19737d0568aSJoseph Huber const Twine &Name) { 19837d0568aSJoseph Huber SmallString<128> Path; 19937d0568aSJoseph Huber if (Dir.starts_with("=")) 20037d0568aSJoseph Huber sys::path::append(Path, Root, Dir.substr(1), Name); 20137d0568aSJoseph Huber else 20237d0568aSJoseph Huber sys::path::append(Path, Dir, Name); 20337d0568aSJoseph Huber 20437d0568aSJoseph Huber if (sys::fs::exists(Path)) 20537d0568aSJoseph Huber return static_cast<std::string>(Path); 20637d0568aSJoseph Huber return std::nullopt; 20737d0568aSJoseph Huber } 20837d0568aSJoseph Huber 20937d0568aSJoseph Huber std::optional<std::string> 21037d0568aSJoseph Huber findFromSearchPaths(StringRef Name, StringRef Root, 21137d0568aSJoseph Huber ArrayRef<StringRef> SearchPaths) { 21237d0568aSJoseph Huber for (StringRef Dir : SearchPaths) 21337d0568aSJoseph Huber if (std::optional<std::string> File = findFile(Dir, Root, Name)) 21437d0568aSJoseph Huber return File; 21537d0568aSJoseph Huber return std::nullopt; 21637d0568aSJoseph Huber } 21737d0568aSJoseph Huber 21837d0568aSJoseph Huber std::optional<std::string> 21937d0568aSJoseph Huber searchLibraryBaseName(StringRef Name, StringRef Root, 22037d0568aSJoseph Huber ArrayRef<StringRef> SearchPaths) { 22137d0568aSJoseph Huber for (StringRef Dir : SearchPaths) 22237d0568aSJoseph Huber if (std::optional<std::string> File = 22337d0568aSJoseph Huber findFile(Dir, Root, "lib" + Name + ".a")) 22437d0568aSJoseph Huber return File; 22537d0568aSJoseph Huber return std::nullopt; 22637d0568aSJoseph Huber } 22737d0568aSJoseph Huber 22837d0568aSJoseph Huber /// Search for static libraries in the linker's library path given input like 22937d0568aSJoseph Huber /// `-lfoo` or `-l:libfoo.a`. 23037d0568aSJoseph Huber std::optional<std::string> searchLibrary(StringRef Input, StringRef Root, 23137d0568aSJoseph Huber ArrayRef<StringRef> SearchPaths) { 23237d0568aSJoseph Huber if (Input.starts_with(":")) 23337d0568aSJoseph Huber return findFromSearchPaths(Input.drop_front(), Root, SearchPaths); 23437d0568aSJoseph Huber return searchLibraryBaseName(Input, Root, SearchPaths); 23537d0568aSJoseph Huber } 23637d0568aSJoseph Huber 23737d0568aSJoseph Huber void printCommands(ArrayRef<StringRef> CmdArgs) { 23837d0568aSJoseph Huber if (CmdArgs.empty()) 23937d0568aSJoseph Huber return; 24037d0568aSJoseph Huber 241787a6d57SJoseph Huber errs() << " \"" << CmdArgs.front() << "\" "; 242787a6d57SJoseph Huber errs() << join(std::next(CmdArgs.begin()), CmdArgs.end(), " ") << "\n"; 24337d0568aSJoseph Huber } 24437d0568aSJoseph Huber 24537d0568aSJoseph Huber /// A minimum symbol interface that provides the necessary information to 24637d0568aSJoseph Huber /// extract archive members and resolve LTO symbols. 24737d0568aSJoseph Huber struct Symbol { 24837d0568aSJoseph Huber enum Flags { 24937d0568aSJoseph Huber None = 0, 25037d0568aSJoseph Huber Undefined = 1 << 0, 25137d0568aSJoseph Huber Weak = 1 << 1, 25237d0568aSJoseph Huber }; 25337d0568aSJoseph Huber 25437d0568aSJoseph Huber Symbol() : File(), Flags(None), UsedInRegularObj(false) {} 255a78861fcSJoseph Huber Symbol(Symbol::Flags Flags) : File(), Flags(Flags), UsedInRegularObj(true) {} 25637d0568aSJoseph Huber 25737d0568aSJoseph Huber Symbol(MemoryBufferRef File, const irsymtab::Reader::SymbolRef Sym) 25837d0568aSJoseph Huber : File(File), Flags(0), UsedInRegularObj(false) { 25937d0568aSJoseph Huber if (Sym.isUndefined()) 26037d0568aSJoseph Huber Flags |= Undefined; 26137d0568aSJoseph Huber if (Sym.isWeak()) 26237d0568aSJoseph Huber Flags |= Weak; 26337d0568aSJoseph Huber } 26437d0568aSJoseph Huber 26537d0568aSJoseph Huber Symbol(MemoryBufferRef File, const SymbolRef Sym) 26637d0568aSJoseph Huber : File(File), Flags(0), UsedInRegularObj(false) { 26737d0568aSJoseph Huber auto FlagsOrErr = Sym.getFlags(); 26837d0568aSJoseph Huber if (!FlagsOrErr) 26937d0568aSJoseph Huber reportError(FlagsOrErr.takeError()); 27037d0568aSJoseph Huber if (*FlagsOrErr & SymbolRef::SF_Undefined) 27137d0568aSJoseph Huber Flags |= Undefined; 27237d0568aSJoseph Huber if (*FlagsOrErr & SymbolRef::SF_Weak) 27337d0568aSJoseph Huber Flags |= Weak; 27437d0568aSJoseph Huber 27537d0568aSJoseph Huber auto NameOrErr = Sym.getName(); 27637d0568aSJoseph Huber if (!NameOrErr) 27737d0568aSJoseph Huber reportError(NameOrErr.takeError()); 27837d0568aSJoseph Huber } 27937d0568aSJoseph Huber 28037d0568aSJoseph Huber bool isWeak() const { return Flags & Weak; } 28137d0568aSJoseph Huber bool isUndefined() const { return Flags & Undefined; } 28237d0568aSJoseph Huber 28337d0568aSJoseph Huber MemoryBufferRef File; 28437d0568aSJoseph Huber uint32_t Flags; 28537d0568aSJoseph Huber bool UsedInRegularObj; 28637d0568aSJoseph Huber }; 28737d0568aSJoseph Huber 28837d0568aSJoseph Huber Expected<StringRef> runPTXAs(StringRef File, const ArgList &Args) { 28937d0568aSJoseph Huber std::string CudaPath = Args.getLastArgValue(OPT_cuda_path_EQ).str(); 290624d3221SJoseph Huber std::string GivenPath = Args.getLastArgValue(OPT_ptxas_path_EQ).str(); 291e391ba07SJoseph Huber Expected<std::string> PTXAsPath = 292624d3221SJoseph Huber findProgram(Args, "ptxas", {CudaPath + "/bin", GivenPath}); 29337d0568aSJoseph Huber if (!PTXAsPath) 29437d0568aSJoseph Huber return PTXAsPath.takeError(); 2952bf58f5dSJoseph Huber if (!Args.hasArg(OPT_arch)) 2962bf58f5dSJoseph Huber return createStringError( 2972bf58f5dSJoseph Huber "must pass in an explicit nvptx64 gpu architecture to 'ptxas'"); 29837d0568aSJoseph Huber 29937d0568aSJoseph Huber auto TempFileOrErr = createTempFile( 30037d0568aSJoseph Huber Args, sys::path::stem(Args.getLastArgValue(OPT_o, "a.out")), "cubin"); 30137d0568aSJoseph Huber if (!TempFileOrErr) 30237d0568aSJoseph Huber return TempFileOrErr.takeError(); 30337d0568aSJoseph Huber 30437d0568aSJoseph Huber SmallVector<StringRef> AssemblerArgs({*PTXAsPath, "-m64", "-c", File}); 30537d0568aSJoseph Huber if (Args.hasArg(OPT_verbose)) 30637d0568aSJoseph Huber AssemblerArgs.push_back("-v"); 30737d0568aSJoseph Huber if (Args.hasArg(OPT_g)) { 30837d0568aSJoseph Huber if (Args.hasArg(OPT_O)) 30937d0568aSJoseph Huber WithColor::warning(errs(), Executable) 31037d0568aSJoseph Huber << "Optimized debugging not supported, overriding to '-O0'\n"; 31137d0568aSJoseph Huber AssemblerArgs.push_back("-O0"); 31237d0568aSJoseph Huber } else 31337d0568aSJoseph Huber AssemblerArgs.push_back( 31437d0568aSJoseph Huber Args.MakeArgString("-O" + Args.getLastArgValue(OPT_O, "3"))); 31537d0568aSJoseph Huber AssemblerArgs.append({"-arch", Args.getLastArgValue(OPT_arch)}); 31637d0568aSJoseph Huber AssemblerArgs.append({"-o", *TempFileOrErr}); 31737d0568aSJoseph Huber 31837d0568aSJoseph Huber if (Args.hasArg(OPT_dry_run) || Args.hasArg(OPT_verbose)) 31937d0568aSJoseph Huber printCommands(AssemblerArgs); 32037d0568aSJoseph Huber if (Args.hasArg(OPT_dry_run)) 32137d0568aSJoseph Huber return Args.MakeArgString(*TempFileOrErr); 32237d0568aSJoseph Huber if (sys::ExecuteAndWait(*PTXAsPath, AssemblerArgs)) 32337d0568aSJoseph Huber return createStringError("'" + sys::path::filename(*PTXAsPath) + "'" + 32437d0568aSJoseph Huber " failed"); 32537d0568aSJoseph Huber return Args.MakeArgString(*TempFileOrErr); 32637d0568aSJoseph Huber } 32737d0568aSJoseph Huber 32837d0568aSJoseph Huber Expected<std::unique_ptr<lto::LTO>> createLTO(const ArgList &Args) { 32937d0568aSJoseph Huber const llvm::Triple Triple("nvptx64-nvidia-cuda"); 33037d0568aSJoseph Huber lto::Config Conf; 33137d0568aSJoseph Huber lto::ThinBackend Backend; 33237d0568aSJoseph Huber unsigned Jobs = 0; 33337d0568aSJoseph Huber if (auto *Arg = Args.getLastArg(OPT_jobs)) 334787a6d57SJoseph Huber if (!to_integer(Arg->getValue(), Jobs) || Jobs == 0) 33537d0568aSJoseph Huber reportError(createStringError("%s: expected a positive integer, got '%s'", 33637d0568aSJoseph Huber Arg->getSpelling().data(), 33737d0568aSJoseph Huber Arg->getValue())); 338787a6d57SJoseph Huber Backend = 339787a6d57SJoseph Huber lto::createInProcessThinBackend(heavyweight_hardware_concurrency(Jobs)); 34037d0568aSJoseph Huber 34137d0568aSJoseph Huber Conf.CPU = Args.getLastArgValue(OPT_arch); 34237d0568aSJoseph Huber Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple); 34337d0568aSJoseph Huber 34437d0568aSJoseph Huber Conf.RemarksFilename = RemarksFilename; 34537d0568aSJoseph Huber Conf.RemarksPasses = RemarksPasses; 34637d0568aSJoseph Huber Conf.RemarksWithHotness = RemarksWithHotness; 34737d0568aSJoseph Huber Conf.RemarksHotnessThreshold = RemarksHotnessThreshold; 34837d0568aSJoseph Huber Conf.RemarksFormat = RemarksFormat; 34937d0568aSJoseph Huber 350416731bfSJoseph Huber Conf.MAttrs = llvm::codegen::getMAttrs(); 35137d0568aSJoseph Huber std::optional<CodeGenOptLevel> CGOptLevelOrNone = 35237d0568aSJoseph Huber CodeGenOpt::parseLevel(Args.getLastArgValue(OPT_O, "2")[0]); 35337d0568aSJoseph Huber assert(CGOptLevelOrNone && "Invalid optimization level"); 35437d0568aSJoseph Huber Conf.CGOptLevel = *CGOptLevelOrNone; 35537d0568aSJoseph Huber Conf.OptLevel = Args.getLastArgValue(OPT_O, "2")[0] - '0'; 35637d0568aSJoseph Huber Conf.DefaultTriple = Triple.getTriple(); 35737d0568aSJoseph Huber 3583c639b83SJoel E. Denny Conf.OptPipeline = Args.getLastArgValue(OPT_lto_newpm_passes, ""); 35937d0568aSJoseph Huber Conf.PassPlugins = PassPlugins; 3603c639b83SJoel E. Denny Conf.DebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager); 36137d0568aSJoseph Huber 36237d0568aSJoseph Huber Conf.DiagHandler = diagnosticHandler; 36337d0568aSJoseph Huber Conf.CGFileType = CodeGenFileType::AssemblyFile; 36437d0568aSJoseph Huber 36537d0568aSJoseph Huber if (Args.hasArg(OPT_lto_emit_llvm)) { 36637d0568aSJoseph Huber Conf.PreCodeGenModuleHook = [&](size_t, const Module &M) { 36737d0568aSJoseph Huber std::error_code EC; 36837d0568aSJoseph Huber raw_fd_ostream LinkedBitcode(Args.getLastArgValue(OPT_o, "a.out"), EC); 36937d0568aSJoseph Huber if (EC) 37037d0568aSJoseph Huber reportError(errorCodeToError(EC)); 37137d0568aSJoseph Huber WriteBitcodeToFile(M, LinkedBitcode); 37237d0568aSJoseph Huber return false; 37337d0568aSJoseph Huber }; 37437d0568aSJoseph Huber } 37537d0568aSJoseph Huber 37637d0568aSJoseph Huber if (Args.hasArg(OPT_save_temps)) 37737d0568aSJoseph Huber if (Error Err = Conf.addSaveTemps( 37837d0568aSJoseph Huber (Args.getLastArgValue(OPT_o, "a.out") + ".").str())) 37937d0568aSJoseph Huber return Err; 38037d0568aSJoseph Huber 38137d0568aSJoseph Huber unsigned Partitions = 1; 38237d0568aSJoseph Huber if (auto *Arg = Args.getLastArg(OPT_lto_partitions)) 383787a6d57SJoseph Huber if (!to_integer(Arg->getValue(), Partitions) || Partitions == 0) 38437d0568aSJoseph Huber reportError(createStringError("%s: expected a positive integer, got '%s'", 38537d0568aSJoseph Huber Arg->getSpelling().data(), 38637d0568aSJoseph Huber Arg->getValue())); 38737d0568aSJoseph Huber lto::LTO::LTOKind Kind = Args.hasArg(OPT_thinlto) ? lto::LTO::LTOK_UnifiedThin 38837d0568aSJoseph Huber : lto::LTO::LTOK_Default; 38937d0568aSJoseph Huber return std::make_unique<lto::LTO>(std::move(Conf), Backend, Partitions, Kind); 39037d0568aSJoseph Huber } 39137d0568aSJoseph Huber 39237d0568aSJoseph Huber Expected<bool> getSymbolsFromBitcode(MemoryBufferRef Buffer, 39337d0568aSJoseph Huber StringMap<Symbol> &SymTab, bool IsLazy) { 39437d0568aSJoseph Huber Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer); 39537d0568aSJoseph Huber if (!IRSymtabOrErr) 39637d0568aSJoseph Huber return IRSymtabOrErr.takeError(); 39737d0568aSJoseph Huber bool Extracted = !IsLazy; 39837d0568aSJoseph Huber StringMap<Symbol> PendingSymbols; 39937d0568aSJoseph Huber for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) { 40037d0568aSJoseph Huber for (const auto &IRSym : IRSymtabOrErr->TheReader.module_symbols(I)) { 40137d0568aSJoseph Huber if (IRSym.isFormatSpecific() || !IRSym.isGlobal()) 40237d0568aSJoseph Huber continue; 40337d0568aSJoseph Huber 40437d0568aSJoseph Huber Symbol &OldSym = !SymTab.count(IRSym.getName()) && IsLazy 40537d0568aSJoseph Huber ? PendingSymbols[IRSym.getName()] 40637d0568aSJoseph Huber : SymTab[IRSym.getName()]; 40737d0568aSJoseph Huber Symbol Sym = Symbol(Buffer, IRSym); 40837d0568aSJoseph Huber if (OldSym.File.getBuffer().empty()) 40937d0568aSJoseph Huber OldSym = Sym; 41037d0568aSJoseph Huber 41137d0568aSJoseph Huber bool ResolvesReference = 41237d0568aSJoseph Huber !Sym.isUndefined() && 41337d0568aSJoseph Huber (OldSym.isUndefined() || (OldSym.isWeak() && !Sym.isWeak())) && 41437d0568aSJoseph Huber !(OldSym.isWeak() && OldSym.isUndefined() && IsLazy); 41537d0568aSJoseph Huber Extracted |= ResolvesReference; 41637d0568aSJoseph Huber 41737d0568aSJoseph Huber Sym.UsedInRegularObj = OldSym.UsedInRegularObj; 41837d0568aSJoseph Huber if (ResolvesReference) 41937d0568aSJoseph Huber OldSym = Sym; 42037d0568aSJoseph Huber } 42137d0568aSJoseph Huber } 42237d0568aSJoseph Huber if (Extracted) 423d6905ea9SJoseph Huber for (const auto &[Name, Symbol] : PendingSymbols) 42437d0568aSJoseph Huber SymTab[Name] = Symbol; 42537d0568aSJoseph Huber return Extracted; 42637d0568aSJoseph Huber } 42737d0568aSJoseph Huber 42837d0568aSJoseph Huber Expected<bool> getSymbolsFromObject(ObjectFile &ObjFile, 42937d0568aSJoseph Huber StringMap<Symbol> &SymTab, bool IsLazy) { 43037d0568aSJoseph Huber bool Extracted = !IsLazy; 43137d0568aSJoseph Huber StringMap<Symbol> PendingSymbols; 43237d0568aSJoseph Huber for (SymbolRef ObjSym : ObjFile.symbols()) { 43337d0568aSJoseph Huber auto NameOrErr = ObjSym.getName(); 43437d0568aSJoseph Huber if (!NameOrErr) 43537d0568aSJoseph Huber return NameOrErr.takeError(); 43637d0568aSJoseph Huber 43737d0568aSJoseph Huber Symbol &OldSym = !SymTab.count(*NameOrErr) && IsLazy 43837d0568aSJoseph Huber ? PendingSymbols[*NameOrErr] 43937d0568aSJoseph Huber : SymTab[*NameOrErr]; 44037d0568aSJoseph Huber Symbol Sym = Symbol(ObjFile.getMemoryBufferRef(), ObjSym); 44137d0568aSJoseph Huber if (OldSym.File.getBuffer().empty()) 44237d0568aSJoseph Huber OldSym = Sym; 44337d0568aSJoseph Huber 44437d0568aSJoseph Huber bool ResolvesReference = OldSym.isUndefined() && !Sym.isUndefined() && 44537d0568aSJoseph Huber (!OldSym.isWeak() || !IsLazy); 44637d0568aSJoseph Huber Extracted |= ResolvesReference; 44737d0568aSJoseph Huber 44837d0568aSJoseph Huber if (ResolvesReference) 44937d0568aSJoseph Huber OldSym = Sym; 45037d0568aSJoseph Huber OldSym.UsedInRegularObj = true; 45137d0568aSJoseph Huber } 45237d0568aSJoseph Huber if (Extracted) 453d6905ea9SJoseph Huber for (const auto &[Name, Symbol] : PendingSymbols) 45437d0568aSJoseph Huber SymTab[Name] = Symbol; 45537d0568aSJoseph Huber return Extracted; 45637d0568aSJoseph Huber } 45737d0568aSJoseph Huber 45837d0568aSJoseph Huber Expected<bool> getSymbols(MemoryBufferRef Buffer, StringMap<Symbol> &SymTab, 45937d0568aSJoseph Huber bool IsLazy) { 46037d0568aSJoseph Huber switch (identify_magic(Buffer.getBuffer())) { 46137d0568aSJoseph Huber case file_magic::bitcode: { 46237d0568aSJoseph Huber return getSymbolsFromBitcode(Buffer, SymTab, IsLazy); 46337d0568aSJoseph Huber } 46437d0568aSJoseph Huber case file_magic::elf_relocatable: { 46537d0568aSJoseph Huber Expected<std::unique_ptr<ObjectFile>> ObjFile = 46637d0568aSJoseph Huber ObjectFile::createObjectFile(Buffer); 46737d0568aSJoseph Huber if (!ObjFile) 46837d0568aSJoseph Huber return ObjFile.takeError(); 46937d0568aSJoseph Huber return getSymbolsFromObject(**ObjFile, SymTab, IsLazy); 47037d0568aSJoseph Huber } 47137d0568aSJoseph Huber default: 47237d0568aSJoseph Huber return createStringError("Unsupported file type"); 47337d0568aSJoseph Huber } 47437d0568aSJoseph Huber } 47537d0568aSJoseph Huber 47637d0568aSJoseph Huber Expected<SmallVector<StringRef>> getInput(const ArgList &Args) { 47737d0568aSJoseph Huber SmallVector<StringRef> LibraryPaths; 47837d0568aSJoseph Huber for (const opt::Arg *Arg : Args.filtered(OPT_library_path)) 47937d0568aSJoseph Huber LibraryPaths.push_back(Arg->getValue()); 48037d0568aSJoseph Huber 48137d0568aSJoseph Huber bool WholeArchive = false; 48237d0568aSJoseph Huber SmallVector<std::pair<std::unique_ptr<MemoryBuffer>, bool>> InputFiles; 48337d0568aSJoseph Huber for (const opt::Arg *Arg : Args.filtered( 48437d0568aSJoseph Huber OPT_INPUT, OPT_library, OPT_whole_archive, OPT_no_whole_archive)) { 48537d0568aSJoseph Huber if (Arg->getOption().matches(OPT_whole_archive) || 48637d0568aSJoseph Huber Arg->getOption().matches(OPT_no_whole_archive)) { 48737d0568aSJoseph Huber WholeArchive = Arg->getOption().matches(OPT_whole_archive); 48837d0568aSJoseph Huber continue; 48937d0568aSJoseph Huber } 49037d0568aSJoseph Huber 49137d0568aSJoseph Huber std::optional<std::string> Filename = 49237d0568aSJoseph Huber Arg->getOption().matches(OPT_library) 49337d0568aSJoseph Huber ? searchLibrary(Arg->getValue(), /*Root=*/"", LibraryPaths) 49437d0568aSJoseph Huber : std::string(Arg->getValue()); 49537d0568aSJoseph Huber 49637d0568aSJoseph Huber if (!Filename && Arg->getOption().matches(OPT_library)) 49737d0568aSJoseph Huber return createStringError("unable to find library -l%s", Arg->getValue()); 49837d0568aSJoseph Huber 49937d0568aSJoseph Huber if (!Filename || !sys::fs::exists(*Filename) || 50037d0568aSJoseph Huber sys::fs::is_directory(*Filename)) 50137d0568aSJoseph Huber continue; 50237d0568aSJoseph Huber 50337d0568aSJoseph Huber ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = 50437d0568aSJoseph Huber MemoryBuffer::getFileOrSTDIN(*Filename); 50537d0568aSJoseph Huber if (std::error_code EC = BufferOrErr.getError()) 50637d0568aSJoseph Huber return createFileError(*Filename, EC); 50737d0568aSJoseph Huber 50837d0568aSJoseph Huber MemoryBufferRef Buffer = **BufferOrErr; 50937d0568aSJoseph Huber switch (identify_magic(Buffer.getBuffer())) { 51037d0568aSJoseph Huber case file_magic::bitcode: 51137d0568aSJoseph Huber case file_magic::elf_relocatable: 51237d0568aSJoseph Huber InputFiles.emplace_back(std::move(*BufferOrErr), /*IsLazy=*/false); 51337d0568aSJoseph Huber break; 51437d0568aSJoseph Huber case file_magic::archive: { 515787a6d57SJoseph Huber Expected<std::unique_ptr<object::Archive>> LibFile = 51637d0568aSJoseph Huber object::Archive::create(Buffer); 51737d0568aSJoseph Huber if (!LibFile) 51837d0568aSJoseph Huber return LibFile.takeError(); 51937d0568aSJoseph Huber Error Err = Error::success(); 52037d0568aSJoseph Huber for (auto Child : (*LibFile)->children(Err)) { 52137d0568aSJoseph Huber auto ChildBufferOrErr = Child.getMemoryBufferRef(); 52237d0568aSJoseph Huber if (!ChildBufferOrErr) 52337d0568aSJoseph Huber return ChildBufferOrErr.takeError(); 52437d0568aSJoseph Huber std::unique_ptr<MemoryBuffer> ChildBuffer = 52537d0568aSJoseph Huber MemoryBuffer::getMemBufferCopy( 52637d0568aSJoseph Huber ChildBufferOrErr->getBuffer(), 52737d0568aSJoseph Huber ChildBufferOrErr->getBufferIdentifier()); 52837d0568aSJoseph Huber InputFiles.emplace_back(std::move(ChildBuffer), !WholeArchive); 52937d0568aSJoseph Huber } 53037d0568aSJoseph Huber if (Err) 53137d0568aSJoseph Huber return Err; 53237d0568aSJoseph Huber break; 53337d0568aSJoseph Huber } 53437d0568aSJoseph Huber default: 53537d0568aSJoseph Huber return createStringError("Unsupported file type"); 53637d0568aSJoseph Huber } 53737d0568aSJoseph Huber } 53837d0568aSJoseph Huber 53937d0568aSJoseph Huber bool Extracted = true; 54037d0568aSJoseph Huber StringMap<Symbol> SymTab; 541a78861fcSJoseph Huber for (auto &Sym : Args.getAllArgValues(OPT_u)) 542a78861fcSJoseph Huber SymTab[Sym] = Symbol(Symbol::Undefined); 54337d0568aSJoseph Huber SmallVector<std::unique_ptr<MemoryBuffer>> LinkerInput; 54437d0568aSJoseph Huber while (Extracted) { 54537d0568aSJoseph Huber Extracted = false; 54637d0568aSJoseph Huber for (auto &[Input, IsLazy] : InputFiles) { 54737d0568aSJoseph Huber if (!Input) 54837d0568aSJoseph Huber continue; 54937d0568aSJoseph Huber 55037d0568aSJoseph Huber // Archive members only extract if they define needed symbols. We will 55137d0568aSJoseph Huber // re-scan all the inputs if any files were extracted for the link job. 55237d0568aSJoseph Huber Expected<bool> ExtractOrErr = getSymbols(*Input, SymTab, IsLazy); 55337d0568aSJoseph Huber if (!ExtractOrErr) 55437d0568aSJoseph Huber return ExtractOrErr.takeError(); 55537d0568aSJoseph Huber 55637d0568aSJoseph Huber Extracted |= *ExtractOrErr; 55737d0568aSJoseph Huber if (!*ExtractOrErr) 55837d0568aSJoseph Huber continue; 55937d0568aSJoseph Huber 56037d0568aSJoseph Huber LinkerInput.emplace_back(std::move(Input)); 56137d0568aSJoseph Huber } 56237d0568aSJoseph Huber } 56337d0568aSJoseph Huber InputFiles.clear(); 56437d0568aSJoseph Huber 56537d0568aSJoseph Huber // Extract any bitcode files to be passed to the LTO pipeline. 56637d0568aSJoseph Huber SmallVector<std::unique_ptr<MemoryBuffer>> BitcodeFiles; 56737d0568aSJoseph Huber for (auto &Input : LinkerInput) 56837d0568aSJoseph Huber if (identify_magic(Input->getBuffer()) == file_magic::bitcode) 56937d0568aSJoseph Huber BitcodeFiles.emplace_back(std::move(Input)); 570787a6d57SJoseph Huber erase_if(LinkerInput, [](const auto &F) { return !F; }); 57137d0568aSJoseph Huber 57237d0568aSJoseph Huber // Run the LTO pipeline on the extracted inputs. 57337d0568aSJoseph Huber SmallVector<StringRef> Files; 57437d0568aSJoseph Huber if (!BitcodeFiles.empty()) { 57537d0568aSJoseph Huber auto LTOBackendOrErr = createLTO(Args); 57637d0568aSJoseph Huber if (!LTOBackendOrErr) 57737d0568aSJoseph Huber return LTOBackendOrErr.takeError(); 57837d0568aSJoseph Huber lto::LTO <OBackend = **LTOBackendOrErr; 57937d0568aSJoseph Huber for (auto &BitcodeFile : BitcodeFiles) { 58037d0568aSJoseph Huber Expected<std::unique_ptr<lto::InputFile>> BitcodeFileOrErr = 581787a6d57SJoseph Huber lto::InputFile::create(*BitcodeFile); 58237d0568aSJoseph Huber if (!BitcodeFileOrErr) 58337d0568aSJoseph Huber return BitcodeFileOrErr.takeError(); 58437d0568aSJoseph Huber 58537d0568aSJoseph Huber const auto Symbols = (*BitcodeFileOrErr)->symbols(); 58637d0568aSJoseph Huber SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size()); 58737d0568aSJoseph Huber size_t Idx = 0; 58837d0568aSJoseph Huber for (auto &Sym : Symbols) { 58937d0568aSJoseph Huber lto::SymbolResolution &Res = Resolutions[Idx++]; 59037d0568aSJoseph Huber Symbol ObjSym = SymTab[Sym.getName()]; 59137d0568aSJoseph Huber // We will use this as the prevailing symbol in LTO if it is not 59237d0568aSJoseph Huber // undefined and it is from the file that contained the canonical 59337d0568aSJoseph Huber // definition. 59437d0568aSJoseph Huber Res.Prevailing = !Sym.isUndefined() && ObjSym.File == *BitcodeFile; 59537d0568aSJoseph Huber 59637d0568aSJoseph Huber // We need LTO to preseve the following global symbols: 597e603df08SJoseph Huber // 1) All symbols during a relocatable link. 598e603df08SJoseph Huber // 2) Symbols used in regular objects. 599e603df08SJoseph Huber // 3) Prevailing symbols that are needed visible to the gpu runtime. 60037d0568aSJoseph Huber Res.VisibleToRegularObj = 601e603df08SJoseph Huber Args.hasArg(OPT_relocatable) || ObjSym.UsedInRegularObj || 60237d0568aSJoseph Huber (Res.Prevailing && 60337d0568aSJoseph Huber (Sym.getVisibility() != GlobalValue::HiddenVisibility && 60437d0568aSJoseph Huber !Sym.canBeOmittedFromSymbolTable())); 60537d0568aSJoseph Huber 60637d0568aSJoseph Huber // Identify symbols that must be exported dynamically and can be 60737d0568aSJoseph Huber // referenced by other files, (i.e. the runtime). 60837d0568aSJoseph Huber Res.ExportDynamic = 60937d0568aSJoseph Huber Sym.getVisibility() != GlobalValue::HiddenVisibility && 61037d0568aSJoseph Huber !Sym.canBeOmittedFromSymbolTable(); 61137d0568aSJoseph Huber 61237d0568aSJoseph Huber // The NVIDIA platform does not support any symbol preemption. 61337d0568aSJoseph Huber Res.FinalDefinitionInLinkageUnit = true; 61437d0568aSJoseph Huber 61537d0568aSJoseph Huber // We do not support linker redefined symbols (e.g. --wrap) for device 61637d0568aSJoseph Huber // image linking, so the symbols will not be changed after LTO. 61737d0568aSJoseph Huber Res.LinkerRedefined = false; 61837d0568aSJoseph Huber } 61937d0568aSJoseph Huber 62037d0568aSJoseph Huber // Add the bitcode file with its resolved symbols to the LTO job. 62137d0568aSJoseph Huber if (Error Err = LTOBackend.add(std::move(*BitcodeFileOrErr), Resolutions)) 62237d0568aSJoseph Huber return Err; 62337d0568aSJoseph Huber } 62437d0568aSJoseph Huber 62537d0568aSJoseph Huber // Run the LTO job to compile the bitcode. 62637d0568aSJoseph Huber size_t MaxTasks = LTOBackend.getMaxTasks(); 62737d0568aSJoseph Huber SmallVector<StringRef> LTOFiles(MaxTasks); 62837d0568aSJoseph Huber auto AddStream = 62937d0568aSJoseph Huber [&](size_t Task, 63037d0568aSJoseph Huber const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { 63137d0568aSJoseph Huber int FD = -1; 63237d0568aSJoseph Huber auto &TempFile = LTOFiles[Task]; 63337d0568aSJoseph Huber if (Args.hasArg(OPT_lto_emit_asm)) 63437d0568aSJoseph Huber TempFile = Args.getLastArgValue(OPT_o, "a.out"); 63537d0568aSJoseph Huber else { 63637d0568aSJoseph Huber auto TempFileOrErr = createTempFile( 63737d0568aSJoseph Huber Args, sys::path::stem(Args.getLastArgValue(OPT_o, "a.out")), "s"); 63837d0568aSJoseph Huber if (!TempFileOrErr) 63937d0568aSJoseph Huber reportError(TempFileOrErr.takeError()); 64037d0568aSJoseph Huber TempFile = Args.MakeArgString(*TempFileOrErr); 64137d0568aSJoseph Huber } 64237d0568aSJoseph Huber if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD)) 64337d0568aSJoseph Huber reportError(errorCodeToError(EC)); 64437d0568aSJoseph Huber return std::make_unique<CachedFileStream>( 645787a6d57SJoseph Huber std::make_unique<raw_fd_ostream>(FD, true)); 64637d0568aSJoseph Huber }; 64737d0568aSJoseph Huber 64837d0568aSJoseph Huber if (Error Err = LTOBackend.run(AddStream)) 64937d0568aSJoseph Huber return Err; 65037d0568aSJoseph Huber 65137d0568aSJoseph Huber if (Args.hasArg(OPT_lto_emit_llvm) || Args.hasArg(OPT_lto_emit_asm)) 65237d0568aSJoseph Huber return Files; 65337d0568aSJoseph Huber 65437d0568aSJoseph Huber for (StringRef LTOFile : LTOFiles) { 65537d0568aSJoseph Huber auto FileOrErr = runPTXAs(LTOFile, Args); 65637d0568aSJoseph Huber if (!FileOrErr) 65737d0568aSJoseph Huber return FileOrErr.takeError(); 65837d0568aSJoseph Huber Files.emplace_back(*FileOrErr); 65937d0568aSJoseph Huber } 66037d0568aSJoseph Huber } 66137d0568aSJoseph Huber 662787a6d57SJoseph Huber // Create a copy for each file to a new file ending in `.cubin`. The 'nvlink' 66337d0568aSJoseph Huber // linker requires all NVPTX inputs to have this extension for some reason. 664787a6d57SJoseph Huber // We don't use a symbolic link because it's not supported on Windows and some 665787a6d57SJoseph Huber // of this input files could be extracted from an archive. 66637d0568aSJoseph Huber for (auto &Input : LinkerInput) { 66737d0568aSJoseph Huber auto TempFileOrErr = createTempFile( 66837d0568aSJoseph Huber Args, sys::path::stem(Input->getBufferIdentifier()), "cubin"); 66937d0568aSJoseph Huber if (!TempFileOrErr) 67037d0568aSJoseph Huber return TempFileOrErr.takeError(); 67137d0568aSJoseph Huber Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr = 67237d0568aSJoseph Huber FileOutputBuffer::create(*TempFileOrErr, Input->getBuffer().size()); 67337d0568aSJoseph Huber if (!OutputOrErr) 67437d0568aSJoseph Huber return OutputOrErr.takeError(); 67537d0568aSJoseph Huber std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr); 676787a6d57SJoseph Huber copy(Input->getBuffer(), Output->getBufferStart()); 67737d0568aSJoseph Huber if (Error E = Output->commit()) 67837d0568aSJoseph Huber return E; 67937d0568aSJoseph Huber Files.emplace_back(Args.MakeArgString(*TempFileOrErr)); 68037d0568aSJoseph Huber } 68137d0568aSJoseph Huber 68237d0568aSJoseph Huber return Files; 68337d0568aSJoseph Huber } 68437d0568aSJoseph Huber 68537d0568aSJoseph Huber Error runNVLink(ArrayRef<StringRef> Files, const ArgList &Args) { 68637d0568aSJoseph Huber if (Args.hasArg(OPT_lto_emit_asm) || Args.hasArg(OPT_lto_emit_llvm)) 68737d0568aSJoseph Huber return Error::success(); 68837d0568aSJoseph Huber 68937d0568aSJoseph Huber std::string CudaPath = Args.getLastArgValue(OPT_cuda_path_EQ).str(); 690624d3221SJoseph Huber Expected<std::string> NVLinkPath = 691624d3221SJoseph Huber findProgram(Args, "nvlink", {CudaPath + "/bin"}); 69237d0568aSJoseph Huber if (!NVLinkPath) 69337d0568aSJoseph Huber return NVLinkPath.takeError(); 69437d0568aSJoseph Huber 6952bf58f5dSJoseph Huber if (!Args.hasArg(OPT_arch)) 6962bf58f5dSJoseph Huber return createStringError( 6972bf58f5dSJoseph Huber "must pass in an explicit nvptx64 gpu architecture to 'nvlink'"); 6982bf58f5dSJoseph Huber 69937d0568aSJoseph Huber ArgStringList NewLinkerArgs; 70037d0568aSJoseph Huber for (const opt::Arg *Arg : Args) { 70137d0568aSJoseph Huber // Do not forward arguments only intended for the linker wrapper. 70237d0568aSJoseph Huber if (Arg->getOption().hasFlag(WrapperOnlyOption)) 70337d0568aSJoseph Huber continue; 70437d0568aSJoseph Huber 70537d0568aSJoseph Huber // Do not forward any inputs that we have processed. 70637d0568aSJoseph Huber if (Arg->getOption().matches(OPT_INPUT) || 70737d0568aSJoseph Huber Arg->getOption().matches(OPT_library)) 70837d0568aSJoseph Huber continue; 70937d0568aSJoseph Huber 71037d0568aSJoseph Huber Arg->render(Args, NewLinkerArgs); 71137d0568aSJoseph Huber } 71237d0568aSJoseph Huber 713787a6d57SJoseph Huber transform(Files, std::back_inserter(NewLinkerArgs), 71437d0568aSJoseph Huber [&](StringRef Arg) { return Args.MakeArgString(Arg); }); 71537d0568aSJoseph Huber 71637d0568aSJoseph Huber SmallVector<StringRef> LinkerArgs({*NVLinkPath}); 71737d0568aSJoseph Huber if (!Args.hasArg(OPT_o)) 71837d0568aSJoseph Huber LinkerArgs.append({"-o", "a.out"}); 71937d0568aSJoseph Huber for (StringRef Arg : NewLinkerArgs) 72037d0568aSJoseph Huber LinkerArgs.push_back(Arg); 72137d0568aSJoseph Huber 72237d0568aSJoseph Huber if (Args.hasArg(OPT_dry_run) || Args.hasArg(OPT_verbose)) 72337d0568aSJoseph Huber printCommands(LinkerArgs); 72437d0568aSJoseph Huber if (Args.hasArg(OPT_dry_run)) 72537d0568aSJoseph Huber return Error::success(); 72637d0568aSJoseph Huber if (sys::ExecuteAndWait(*NVLinkPath, LinkerArgs)) 72737d0568aSJoseph Huber return createStringError("'" + sys::path::filename(*NVLinkPath) + "'" + 72837d0568aSJoseph Huber " failed"); 72937d0568aSJoseph Huber return Error::success(); 73037d0568aSJoseph Huber } 73137d0568aSJoseph Huber 73237d0568aSJoseph Huber } // namespace 73337d0568aSJoseph Huber 73437d0568aSJoseph Huber int main(int argc, char **argv) { 73537d0568aSJoseph Huber InitLLVM X(argc, argv); 73637d0568aSJoseph Huber InitializeAllTargetInfos(); 73737d0568aSJoseph Huber InitializeAllTargets(); 73837d0568aSJoseph Huber InitializeAllTargetMCs(); 73937d0568aSJoseph Huber InitializeAllAsmParsers(); 74037d0568aSJoseph Huber InitializeAllAsmPrinters(); 74137d0568aSJoseph Huber 74237d0568aSJoseph Huber Executable = argv[0]; 74337d0568aSJoseph Huber sys::PrintStackTraceOnErrorSignal(argv[0]); 74437d0568aSJoseph Huber 74537d0568aSJoseph Huber const OptTable &Tbl = getOptTable(); 74637d0568aSJoseph Huber BumpPtrAllocator Alloc; 74737d0568aSJoseph Huber StringSaver Saver(Alloc); 74837d0568aSJoseph Huber auto Args = Tbl.parseArgs(argc, argv, OPT_INVALID, Saver, [&](StringRef Err) { 74937d0568aSJoseph Huber reportError(createStringError(inconvertibleErrorCode(), Err)); 75037d0568aSJoseph Huber }); 75137d0568aSJoseph Huber 75237d0568aSJoseph Huber if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) { 75337d0568aSJoseph Huber Tbl.printHelp( 75437d0568aSJoseph Huber outs(), "clang-nvlink-wrapper [options] <options to passed to nvlink>", 75537d0568aSJoseph Huber "A utility that wraps around the NVIDIA 'nvlink' linker.\n" 75637d0568aSJoseph Huber "This enables static linking and LTO handling for NVPTX targets.", 75737d0568aSJoseph Huber Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden)); 75837d0568aSJoseph Huber return EXIT_SUCCESS; 75937d0568aSJoseph Huber } 76037d0568aSJoseph Huber 76137d0568aSJoseph Huber if (Args.hasArg(OPT_version)) 76237d0568aSJoseph Huber printVersion(outs()); 76337d0568aSJoseph Huber 76437d0568aSJoseph Huber // This forwards '-mllvm' arguments to LLVM if present. 76537d0568aSJoseph Huber SmallVector<const char *> NewArgv = {argv[0]}; 76637d0568aSJoseph Huber for (const opt::Arg *Arg : Args.filtered(OPT_mllvm)) 76737d0568aSJoseph Huber NewArgv.push_back(Arg->getValue()); 76837d0568aSJoseph Huber for (const opt::Arg *Arg : Args.filtered(OPT_plugin_opt)) 76937d0568aSJoseph Huber NewArgv.push_back(Arg->getValue()); 77037d0568aSJoseph Huber cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]); 77137d0568aSJoseph Huber 77237d0568aSJoseph Huber // Get the input files to pass to 'nvlink'. 77337d0568aSJoseph Huber auto FilesOrErr = getInput(Args); 77437d0568aSJoseph Huber if (!FilesOrErr) 77537d0568aSJoseph Huber reportError(FilesOrErr.takeError()); 77637d0568aSJoseph Huber 77737d0568aSJoseph Huber // Run 'nvlink' on the generated inputs. 77837d0568aSJoseph Huber if (Error Err = runNVLink(*FilesOrErr, Args)) 77937d0568aSJoseph Huber reportError(std::move(Err)); 78037d0568aSJoseph Huber 78137d0568aSJoseph Huber // Remove the temporary files created. 78237d0568aSJoseph Huber if (!Args.hasArg(OPT_save_temps)) 78337d0568aSJoseph Huber for (const auto &TempFile : TempFiles) 78437d0568aSJoseph Huber if (std::error_code EC = sys::fs::remove(TempFile)) 78537d0568aSJoseph Huber reportError(createFileError(TempFile, EC)); 78637d0568aSJoseph Huber 78737d0568aSJoseph Huber return EXIT_SUCCESS; 78837d0568aSJoseph Huber } 789