123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- //===-- clang-nvlink-wrapper/ClangNvlinkWrapper.cpp - wrapper over nvlink-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===---------------------------------------------------------------------===//
- ///
- /// \file
- /// This tool works as a wrapper over nvlink program. It transparently passes
- /// every input option and objects to nvlink except archive files. It reads
- /// each input archive file to extract archived cubin files as temporary files.
- /// These temp (*.cubin) files are passed to nvlink, because nvlink does not
- /// support linking of archive files implicitly.
- ///
- /// During linking of heterogeneous device archive libraries, the
- /// clang-offload-bundler creates a device specific archive of cubin files.
- /// Such an archive is then passed to this tool to extract cubin files before
- /// passing to nvlink.
- ///
- /// Example:
- /// clang-nvlink-wrapper -o a.out-openmp-nvptx64 /tmp/libTest-nvptx-sm_50.a
- ///
- /// 1. Extract (libTest-nvptx-sm_50.a) => /tmp/a.cubin /tmp/b.cubin
- /// 2. nvlink -o a.out-openmp-nvptx64 /tmp/a.cubin /tmp/b.cubin
- //===---------------------------------------------------------------------===//
- #include "clang/Basic/Version.h"
- #include "llvm/Object/Archive.h"
- #include "llvm/Support/CommandLine.h"
- #include "llvm/Support/Errc.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/Program.h"
- #include "llvm/Support/Signals.h"
- #include "llvm/Support/StringSaver.h"
- #include "llvm/Support/WithColor.h"
- #include "llvm/Support/raw_ostream.h"
- using namespace llvm;
- static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
- // Mark all our options with this category, everything else (except for -help)
- // will be hidden.
- static cl::OptionCategory
- ClangNvlinkWrapperCategory("clang-nvlink-wrapper options");
- static cl::opt<std::string> NvlinkUserPath("nvlink-path",
- cl::desc("Path of nvlink binary"),
- cl::cat(ClangNvlinkWrapperCategory));
- // Do not parse nvlink options
- static cl::list<std::string>
- NVArgs(cl::Sink, cl::desc("<options to be passed to nvlink>..."));
- static bool isEmptyFile(StringRef Filename) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
- MemoryBuffer::getFileOrSTDIN(Filename, false, false);
- if (std::error_code EC = BufOrErr.getError())
- return false;
- return (*BufOrErr)->getBuffer().empty();
- }
- static Error runNVLink(std::string NVLinkPath,
- SmallVectorImpl<std::string> &Args) {
- std::vector<StringRef> NVLArgs;
- NVLArgs.push_back(NVLinkPath);
- StringRef Output = *(llvm::find(Args, "-o") + 1);
- for (auto &Arg : Args) {
- if (!(sys::fs::exists(Arg) && Arg != Output && isEmptyFile(Arg)))
- NVLArgs.push_back(Arg);
- }
- if (sys::ExecuteAndWait(NVLinkPath, NVLArgs))
- return createStringError(inconvertibleErrorCode(), "'nvlink' failed");
- return Error::success();
- }
- static Error extractArchiveFiles(StringRef Filename,
- SmallVectorImpl<std::string> &Args,
- SmallVectorImpl<std::string> &TmpFiles) {
- std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
- MemoryBuffer::getFileOrSTDIN(Filename, false, false);
- if (std::error_code EC = BufOrErr.getError())
- return createFileError(Filename, EC);
- ArchiveBuffers.push_back(std::move(*BufOrErr));
- Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr =
- object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());
- if (!LibOrErr)
- return LibOrErr.takeError();
- auto Archive = std::move(*LibOrErr);
- Error Err = Error::success();
- auto ChildEnd = Archive->child_end();
- for (auto ChildIter = Archive->child_begin(Err); ChildIter != ChildEnd;
- ++ChildIter) {
- if (Err)
- return Err;
- auto ChildNameOrErr = (*ChildIter).getName();
- if (!ChildNameOrErr)
- return ChildNameOrErr.takeError();
- StringRef ChildName = sys::path::filename(ChildNameOrErr.get());
- auto ChildBufferRefOrErr = (*ChildIter).getMemoryBufferRef();
- if (!ChildBufferRefOrErr)
- return ChildBufferRefOrErr.takeError();
- auto ChildBuffer =
- MemoryBuffer::getMemBuffer(ChildBufferRefOrErr.get(), false);
- auto ChildNameSplit = ChildName.split('.');
- SmallString<16> Path;
- int FileDesc;
- if (std::error_code EC = sys::fs::createTemporaryFile(
- (ChildNameSplit.first), (ChildNameSplit.second), FileDesc, Path))
- return createFileError(ChildName, EC);
- std::string TmpFileName(Path.str());
- Args.push_back(TmpFileName);
- TmpFiles.push_back(TmpFileName);
- std::error_code EC;
- raw_fd_ostream OS(Path.c_str(), EC, sys::fs::OF_None);
- if (EC)
- return createFileError(TmpFileName, errc::io_error);
- OS << ChildBuffer->getBuffer();
- OS.close();
- }
- return Err;
- }
- static Error cleanupTmpFiles(SmallVectorImpl<std::string> &TmpFiles) {
- for (auto &TmpFile : TmpFiles) {
- if (std::error_code EC = sys::fs::remove(TmpFile))
- return createFileError(TmpFile, errc::no_such_file_or_directory);
- }
- return Error::success();
- }
- static void PrintVersion(raw_ostream &OS) {
- OS << clang::getClangToolFullVersion("clang-nvlink-wrapper") << '\n';
- }
- int main(int argc, const char **argv) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- cl::SetVersionPrinter(PrintVersion);
- cl::HideUnrelatedOptions(ClangNvlinkWrapperCategory);
- cl::ParseCommandLineOptions(
- argc, argv,
- "A wrapper tool over nvlink program. It transparently passes every \n"
- "input option and objects to nvlink except archive files and path of \n"
- "nvlink binary. It reads each input archive file to extract archived \n"
- "cubin files as temporary files.\n");
- if (Help) {
- cl::PrintHelpMessage();
- return 0;
- }
- auto reportError = [argv](Error E) {
- logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
- exit(1);
- };
- std::string NvlinkPath;
- SmallVector<const char *, 0> Argv(argv, argv + argc);
- SmallVector<std::string, 0> ArgvSubst;
- SmallVector<std::string, 0> TmpFiles;
- BumpPtrAllocator Alloc;
- StringSaver Saver(Alloc);
- cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);
- for (const std::string &Arg : NVArgs) {
- if (sys::path::extension(Arg) == ".a") {
- if (Error Err = extractArchiveFiles(Arg, ArgvSubst, TmpFiles))
- reportError(std::move(Err));
- } else {
- ArgvSubst.push_back(Arg);
- }
- }
- NvlinkPath = NvlinkUserPath;
- // If user hasn't specified nvlink binary then search it in PATH
- if (NvlinkPath.empty()) {
- ErrorOr<std::string> NvlinkPathErr = sys::findProgramByName("nvlink");
- if (!NvlinkPathErr) {
- reportError(createStringError(NvlinkPathErr.getError(),
- "unable to find 'nvlink' in path"));
- }
- NvlinkPath = NvlinkPathErr.get();
- }
- if (Error Err = runNVLink(NvlinkPath, ArgvSubst))
- reportError(std::move(Err));
- if (Error Err = cleanupTmpFiles(TmpFiles))
- reportError(std::move(Err));
- return 0;
- }
|