xref: /llvm-project/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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 &LTOBackend = **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