diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -73,6 +73,7 @@ OffloadBundlingJobClass, OffloadUnbundlingJobClass, OffloadWrapperJobClass, + LinkerWrapperJobClass, StaticLibJobClass, JobClassFirst = PreprocessJobClass, @@ -642,6 +643,17 @@ } }; +class LinkerWrapperJobAction : public JobAction { + void anchor() override; + +public: + LinkerWrapperJobAction(ActionList &Inputs, types::ID Type); + + static bool classof(const Action *A) { + return A->getKind() == LinkerWrapperJobClass; + } +}; + class StaticLibJobAction : public JobAction { void anchor() override; diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -208,6 +208,8 @@ Arguments = std::move(List); } + void replaceExecutable(const char *Exe) { Executable = Exe; } + const char *getExecutable() const { return Executable; } const llvm::opt::ArgStringList &getArguments() const { return Arguments; } diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -151,6 +151,7 @@ mutable std::unique_ptr IfsMerge; mutable std::unique_ptr OffloadBundler; mutable std::unique_ptr OffloadWrapper; + mutable std::unique_ptr LinkerWrapper; Tool *getClang() const; Tool *getFlang() const; @@ -161,6 +162,7 @@ Tool *getClangAs() const; Tool *getOffloadBundler() const; Tool *getOffloadWrapper() const; + Tool *getLinkerWrapper() const; mutable bool SanitizerArgsChecked = false; mutable std::unique_ptr XRayArguments; diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -43,6 +43,8 @@ return "clang-offload-unbundler"; case OffloadWrapperJobClass: return "clang-offload-wrapper"; + case LinkerWrapperJobClass: + return "clang-linker-wrapper"; case StaticLibJobClass: return "static-lib-linker"; } @@ -418,6 +420,12 @@ types::ID Type) : JobAction(OffloadWrapperJobClass, Inputs, Type) {} +void LinkerWrapperJobAction::anchor() {} + +LinkerWrapperJobAction::LinkerWrapperJobAction(ActionList &Inputs, + types::ID Type) + : JobAction(LinkerWrapperJobClass, Inputs, Type) {} + void StaticLibJobAction::anchor() {} StaticLibJobAction::StaticLibJobAction(ActionList &Inputs, types::ID Type) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -3913,14 +3913,16 @@ // Check if this Linker Job should emit a static library. if (ShouldEmitStaticLibrary(Args)) { LA = C.MakeAction(LinkerInputs, types::TY_Image); + } else if (Args.hasArg(options::OPT_fopenmp_new_driver) && + OffloadKinds != Action::OFK_None) { + LA = C.MakeAction(LinkerInputs, types::TY_Image); + LA->propagateHostOffloadInfo(OffloadKinds, + /*BoundArch=*/nullptr); } else { LA = C.MakeAction(LinkerInputs, types::TY_Image); } if (!Args.hasArg(options::OPT_fopenmp_new_driver)) LA = OffloadBuilder.processHostLinkAction(LA); - if (Args.hasArg(options::OPT_fopenmp_new_driver)) - LA->propagateHostOffloadInfo(OffloadKinds, - /*BoundArch=*/nullptr); Actions.push_back(LA); } diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -324,6 +324,12 @@ return OffloadWrapper.get(); } +Tool *ToolChain::getLinkerWrapper() const { + if (!LinkerWrapper) + LinkerWrapper.reset(new tools::LinkerWrapper(*this, getLink())); + return LinkerWrapper.get(); +} + Tool *ToolChain::getTool(Action::ActionClass AC) const { switch (AC) { case Action::AssembleJobClass: @@ -362,6 +368,8 @@ case Action::OffloadWrapperJobClass: return getOffloadWrapper(); + case Action::LinkerWrapperJobClass: + return getLinkerWrapper(); } llvm_unreachable("Invalid tool kind."); diff --git a/clang/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h --- a/clang/lib/Driver/ToolChains/Clang.h +++ b/clang/lib/Driver/ToolChains/Clang.h @@ -170,6 +170,21 @@ const char *LinkingOutput) const override; }; +/// Linker wrapper tool. +class LLVM_LIBRARY_VISIBILITY LinkerWrapper final : public Tool { + const Tool *Linker; + +public: + LinkerWrapper(const ToolChain &TC, const Tool *Linker) + : Tool("Offload::Linker", "linker", TC), Linker(Linker) {} + + bool hasIntegratedCPP() const override { return false; } + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; + } // end namespace tools } // end namespace driver diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -8122,3 +8122,28 @@ Args.MakeArgString(getToolChain().GetProgramPath(getShortName())), CmdArgs, Inputs, Output)); } + +void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + // Construct the link job so we can wrap around it. + Linker->ConstructJob(C, JA, Output, Inputs, Args, LinkingOutput); + const auto &LinkCommand = C.getJobs().getJobs().back(); + + CmdArgs.push_back("-linker-path"); + CmdArgs.push_back(LinkCommand->getExecutable()); + for (const char *LinkArg : LinkCommand->getArguments()) + CmdArgs.push_back(LinkArg); + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath("clang-linker-wrapper")); + + // Replace the executable and arguments associated with the link job to the + // wrapper. + LinkCommand->replaceExecutable(Exec); + LinkCommand->replaceArguments(CmdArgs); +} diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt --- a/clang/tools/CMakeLists.txt +++ b/clang/tools/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_subdirectory(clang-fuzzer) add_clang_subdirectory(clang-import-test) add_clang_subdirectory(clang-nvlink-wrapper) +add_clang_subdirectory(clang-linker-wrapper) add_clang_subdirectory(clang-offload-bundler) add_clang_subdirectory(clang-offload-wrapper) add_clang_subdirectory(clang-scan-deps) diff --git a/clang/tools/clang-linker-wrapper/CMakeLists.txt b/clang/tools/clang-linker-wrapper/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/clang/tools/clang-linker-wrapper/CMakeLists.txt @@ -0,0 +1,25 @@ +set(LLVM_LINK_COMPONENTS BitWriter Core Object Support) + +if(NOT CLANG_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_clang_executable(clang-linker-wrapper + ClangLinkerWrapper.cpp + + DEPENDS + ${tablegen_deps} + ) + +set(CLANG_LINKER_WRAPPER_LIB_DEPS + clangBasic + ) + +add_dependencies(clang clang-linker-wrapper) + +target_link_libraries(clang-linker-wrapper + PRIVATE + ${CLANG_LINKER_WRAPPER_LIB_DEPS} + ) + +install(TARGETS clang-linker-wrapper RUNTIME DESTINATION bin) diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp new file mode 100644 --- /dev/null +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -0,0 +1,91 @@ +//===-- clang-linker-wrapper/ClangLinkerWrapper.cpp - wrapper over linker-===// +// +// 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 +// +//===---------------------------------------------------------------------===// +/// +//===---------------------------------------------------------------------===// + +#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 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 + ClangLinkerWrapperCategory("clang-linker-wrapper options"); + +static cl::opt LinkerUserPath("linker-path", + cl::desc("Path of linker binary"), + cl::cat(ClangLinkerWrapperCategory)); + +// Do not parse linker options +static cl::list + LinkerArgs(cl::Sink, cl::desc("...")); + +static Error runLinker(std::string LinkerPath, + SmallVectorImpl &Args) { + std::vector LinkerArgs; + LinkerArgs.push_back(LinkerPath); + for (auto &Arg : Args) + LinkerArgs.push_back(Arg); + + if (sys::ExecuteAndWait(LinkerPath, LinkerArgs)) + return createStringError(inconvertibleErrorCode(), "'linker' failed"); + return Error::success(); +} + +static void PrintVersion(raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n'; +} + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + cl::SetVersionPrinter(PrintVersion); + cl::HideUnrelatedOptions(ClangLinkerWrapperCategory); + cl::ParseCommandLineOptions( + argc, argv, + "A wrapper utility over the host linker. It scans the input files for\n" + "sections that require additional processing prior to linking. The tool\n" + "will then transparently pass all arguments and input to the specified\n" + "host linker to create the final binary.\n"); + + if (Help) { + cl::PrintHelpMessage(); + return EXIT_SUCCESS; + } + + auto reportError = [argv](Error E) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); + exit(EXIT_FAILURE); + }; + + // TODO: Scan input object files for offloading sections and extract them. + // TODO: Perform appropriate device linking action. + // TODO: Wrap device image in a host binary and pass it to the linker. + WithColor::warning(errs(), argv[0]) << "Offload linking not yet supported.\n"; + + SmallVector Argv; + for (const std::string &Arg : LinkerArgs) + Argv.push_back(Arg); + + if (Error Err = runLinker(LinkerUserPath, Argv)) + reportError(std::move(Err)); + + return EXIT_SUCCESS; +}