[clang] 95c8f74 - [Clang] Introduce Clang Linker Wrapper Tool

Joseph Huber via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 31 12:56:20 PST 2022


Author: Joseph Huber
Date: 2022-01-31T15:56:04-05:00
New Revision: 95c8f7464092e2ccd45c3ae6dc42da6bd9a037b5

URL: https://github.com/llvm/llvm-project/commit/95c8f7464092e2ccd45c3ae6dc42da6bd9a037b5
DIFF: https://github.com/llvm/llvm-project/commit/95c8f7464092e2ccd45c3ae6dc42da6bd9a037b5.diff

LOG: [Clang] Introduce Clang Linker Wrapper Tool

This patch introduces a linker wrapper tool that allows us to preprocess
files before they are sent to the linker. This adds a dummy action and
job to the driver stage that builds the linker command as usual and then
replaces the command line with the wrapper tool.

Depends on D116543

Reviewed By: JonChesterfield

Differential Revision: https://reviews.llvm.org/D116544

Added: 
    clang/docs/ClangLinkerWrapper.rst
    clang/tools/clang-linker-wrapper/CMakeLists.txt
    clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Driver/Action.h
    clang/include/clang/Driver/Job.h
    clang/include/clang/Driver/ToolChain.h
    clang/lib/Driver/Action.cpp
    clang/lib/Driver/Driver.cpp
    clang/lib/Driver/ToolChain.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Driver/ToolChains/Clang.h
    clang/tools/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangLinkerWrapper.rst b/clang/docs/ClangLinkerWrapper.rst
new file mode 100644
index 0000000000000..3bb5a67789a23
--- /dev/null
+++ b/clang/docs/ClangLinkerWrapper.rst
@@ -0,0 +1,61 @@
+====================
+Clang Linker Wrapper
+====================
+
+.. contents::
+   :local:
+
+.. _clang-linker-wrapper:
+
+Introduction
+============
+
+This tool works as a wrapper over a linking job. The tool is used to create
+linked device images for offloading. It scans the linker's input for embedded
+device offloading data stored in sections ``.llvm.offloading.<triple>.<arch>``
+and extracts it as a temporary file. The extracted device files will then be
+passed to a device linking job to create a final device image.
+
+Usage
+=====
+
+This tool can be used with the following options. Arguments to the host linker
+being wrapper around are passed as positional arguments using the ``--`` flag to
+override parsing.
+
+.. code-block:: console
+
+  USAGE: clang-linker-wrapper [options] <options to be passed to linker>...
+  
+  OPTIONS:
+  
+  Generic Options:
+  
+    --help                    - Display available options (--help-hidden for more)
+    --help-list               - Display list of available options (--help-list-hidden for more)
+    --version                 - Display the version of this program
+  
+  clang-linker-wrapper options:
+  
+    --host-triple=<string>    - Triple to use for the host compilation
+    --linker-path=<string>    - Path of linker binary
+    --opt-level=<string>      - Optimization level for LTO
+    --ptxas-option=<string>   - Argument to pass to the ptxas invocation
+    --save-temps              - Save intermediary results.
+    --strip-sections          - Strip offloading sections from the host object file.
+    --target-embed-bc         - Embed linked bitcode instead of an executable device image
+    --target-feature=<string> - Target features for triple
+    --target-library=<string> - Path for the target bitcode library
+    -v                        - Verbose output from tools
+
+Example
+=======
+
+This tool links object files with offloading images embedded within it using the
+``-fembed-offload-object`` flag in Clang. Given an input file containing the
+magic section we can pass it to this tool to extract the data contained at that
+section and run a device linking job on it.
+
+.. code-block:: console
+
+  clang-linker-wrapper -host-triple x86_64-unknown-linux-gnu -linker-path /usr/bin/ld -- <Args>

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3a42b4252ed73..0c9968597b737 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -257,7 +257,7 @@ OpenMP Support in Clang
 -----------------------
 
 - ``clang-nvlink-wrapper`` tool introduced to support linking of cubin files archived in an archive. See :doc:`ClangNvlinkWrapper`.
-
+- ``clang-linker-wrapper`` tool introduced to support linking using a new OpenMP target offloading method. See :doc:`ClangLinkerWrapper`.
 
 CUDA Support in Clang
 ---------------------

diff  --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h
index ba84d886a6cf0..3b6c9e31faa3e 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -73,6 +73,7 @@ class Action {
     OffloadBundlingJobClass,
     OffloadUnbundlingJobClass,
     OffloadWrapperJobClass,
+    LinkerWrapperJobClass,
     StaticLibJobClass,
 
     JobClassFirst = PreprocessJobClass,
@@ -642,6 +643,17 @@ class OffloadWrapperJobAction : public JobAction {
   }
 };
 
+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
index 6e3b51f2a7995..ae9337f3c2d0a 100644
--- a/clang/include/clang/Driver/Job.h
+++ b/clang/include/clang/Driver/Job.h
@@ -208,6 +208,8 @@ class Command {
     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
index 7cd6a5fd37773..bfc46af002657 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -151,6 +151,7 @@ class ToolChain {
   mutable std::unique_ptr<Tool> IfsMerge;
   mutable std::unique_ptr<Tool> OffloadBundler;
   mutable std::unique_ptr<Tool> OffloadWrapper;
+  mutable std::unique_ptr<Tool> LinkerWrapper;
 
   Tool *getClang() const;
   Tool *getFlang() const;
@@ -161,6 +162,7 @@ class ToolChain {
   Tool *getClangAs() const;
   Tool *getOffloadBundler() const;
   Tool *getOffloadWrapper() const;
+  Tool *getLinkerWrapper() const;
 
   mutable bool SanitizerArgsChecked = false;
   mutable std::unique_ptr<XRayArgs> XRayArguments;

diff  --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index e2d2f6c22de06..eb08bfe9cde56 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -43,6 +43,8 @@ const char *Action::getClassName(ActionClass AC) {
     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 @@ OffloadWrapperJobAction::OffloadWrapperJobAction(ActionList &Inputs,
                                                  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
index 0dba3e33eb6eb..b8ef960f30459 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3955,14 +3955,16 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
     // Check if this Linker Job should emit a static library.
     if (ShouldEmitStaticLibrary(Args)) {
       LA = C.MakeAction<StaticLibJobAction>(LinkerInputs, types::TY_Image);
+    } else if (Args.hasArg(options::OPT_fopenmp_new_driver) &&
+               OffloadKinds != Action::OFK_None) {
+      LA = C.MakeAction<LinkerWrapperJobAction>(LinkerInputs, types::TY_Image);
+      LA->propagateHostOffloadInfo(OffloadKinds,
+                                   /*BoundArch=*/nullptr);
     } else {
       LA = C.MakeAction<LinkJobAction>(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
index 3c13f1b1cacba..d657d21bfcdb0 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -327,6 +327,12 @@ Tool *ToolChain::getOffloadWrapper() const {
   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:
@@ -365,6 +371,8 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
 
   case Action::OffloadWrapperJobClass:
     return getOffloadWrapper();
+  case Action::LinkerWrapperJobClass:
+    return getLinkerWrapper();
   }
 
   llvm_unreachable("Invalid tool kind.");

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 66943302388de..59139eca54ee9 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8142,3 +8142,28 @@ void OffloadWrapper::ConstructJob(Compilation &C, const JobAction &JA,
       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/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h
index 013cd2341e17c..79407c9884d51 100644
--- a/clang/lib/Driver/ToolChains/Clang.h
+++ b/clang/lib/Driver/ToolChains/Clang.h
@@ -170,6 +170,21 @@ class LLVM_LIBRARY_VISIBILITY OffloadWrapper final : public Tool {
                     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/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index 38b7496b97f72..b071a776b32ae 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_subdirectory(clang-format-vs)
 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
index 0000000000000..17c50b6d80cad
--- /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
index 0000000000000..5cadb3dbb1e98
--- /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<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
+    ClangLinkerWrapperCategory("clang-linker-wrapper options");
+
+static cl::opt<std::string> LinkerUserPath("linker-path",
+                                           cl::desc("Path of linker binary"),
+                                           cl::cat(ClangLinkerWrapperCategory));
+
+// Do not parse linker options
+static cl::list<std::string>
+    LinkerArgs(cl::Sink, cl::desc("<options to be passed to linker>..."));
+
+static Error runLinker(std::string LinkerPath,
+                       SmallVectorImpl<std::string> &Args) {
+  std::vector<StringRef> 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<std::string, 0> Argv;
+  for (const std::string &Arg : LinkerArgs)
+    Argv.push_back(Arg);
+
+  if (Error Err = runLinker(LinkerUserPath, Argv))
+    reportError(std::move(Err));
+
+  return EXIT_SUCCESS;
+}


        


More information about the cfe-commits mailing list