[clang] [LinkerWrapper] Extend with usual pass options (PR #96704)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 25 14:34:56 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-driver
Author: Joel E. Denny (jdenny-ornl)
<details>
<summary>Changes</summary>
The goal of this patch is to enable utilizing LLVM plugin passes and remarks for GPU offload code at link time. Specifically, this patch extends clang-linker-wrapper's `--offload-opt` (and consequently `-mllvm`) to accept the various LLVM pass options that tools like opt usually accept. Those options include `--passes`, `--load-pass-plugin`, and various remarks options.
Unlike many other LLVM options that are inherited from linked code by clang-linker-wrapper (e.g., `-pass-remarks` is already implemented in `llvm/lib/IR/DiagnosticHandler.cpp`), these options are implemented separately as needed by each tool (e.g., opt, llc). Fortunately, this patch is able to handle most of the implementation by passing the option values to `lto::Config`.
For testing plugin support, this patch uses the simple `Bye` plugin from LLVM core, but that requires several small Clang test suite config extensions.
---
Full diff: https://github.com/llvm/llvm-project/pull/96704.diff
9 Files Affected:
- (modified) clang/test/CMakeLists.txt (+3)
- (added) clang/test/Driver/linker-wrapper-llvm-help.c (+10)
- (added) clang/test/Driver/linker-wrapper-passes.ll (+86)
- (modified) clang/test/Driver/lit.local.cfg (+1)
- (modified) clang/test/lit.cfg.py (+12)
- (modified) clang/test/lit.site.cfg.py.in (+4)
- (modified) clang/tools/clang-linker-wrapper/CMakeLists.txt (+2)
- (modified) clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp (+74)
- (modified) clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td (+6-2)
``````````diff
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index 5fceb1d710334..8303269a9ad07 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -11,6 +11,9 @@ llvm_canonicalize_cmake_booleans(
CLANG_SPAWN_CC1
CLANG_ENABLE_CIR
ENABLE_BACKTRACES
+ LLVM_BUILD_EXAMPLES
+ LLVM_BYE_LINK_INTO_TOOLS
+ LLVM_ENABLE_PLUGINS
LLVM_ENABLE_ZLIB
LLVM_ENABLE_ZSTD
LLVM_ENABLE_PER_TARGET_RUNTIME_DIR
diff --git a/clang/test/Driver/linker-wrapper-llvm-help.c b/clang/test/Driver/linker-wrapper-llvm-help.c
new file mode 100644
index 0000000000000..ffd1cf78bcd9a
--- /dev/null
+++ b/clang/test/Driver/linker-wrapper-llvm-help.c
@@ -0,0 +1,10 @@
+// Check that these simple command lines for listing LLVM options are supported,
+// as claimed by 'clang-linker-wrapper --help'.
+
+// RUN: clang-linker-wrapper -mllvm --help 2>&1 | FileCheck %s
+// RUN: clang-linker-wrapper --offload-opt=--help 2>&1 | FileCheck %s
+
+// Look for a few options supported only after -mllvm and --offload-opt.
+// CHECK: OPTIONS:
+// CHECK-DAG: --passes=<string>
+// CHECK-DAG: --load-pass-plugin=<string>
diff --git a/clang/test/Driver/linker-wrapper-passes.ll b/clang/test/Driver/linker-wrapper-passes.ll
new file mode 100644
index 0000000000000..28493b9a88eb1
--- /dev/null
+++ b/clang/test/Driver/linker-wrapper-passes.ll
@@ -0,0 +1,86 @@
+; Check various clang-linker-wrapper pass options after -offload-opt.
+
+; REQUIRES: llvm-plugins, llvm-examples
+; REQUIRES: x86-registered-target
+; REQUIRES: amdgpu-registered-target
+
+; Setup.
+; RUN: split-file %s %t
+; RUN: opt -o %t/host-x86_64-unknown-linux-gnu.bc \
+; RUN: %t/host-x86_64-unknown-linux-gnu.ll
+; RUN: opt -o %t/openmp-amdgcn-amd-amdhsa.bc \
+; RUN: %t/openmp-amdgcn-amd-amdhsa.ll
+; RUN: clang-offload-packager -o %t/openmp-x86_64-unknown-linux-gnu.out \
+; RUN: --image=file=%t/openmp-amdgcn-amd-amdhsa.bc,triple=amdgcn-amd-amdhsa
+; RUN: %clang -cc1 -S -o %t/host-x86_64-unknown-linux-gnu.s \
+; RUN: -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa \
+; RUN: -fembed-offload-object=%t/openmp-x86_64-unknown-linux-gnu.out \
+; RUN: %t/host-x86_64-unknown-linux-gnu.bc
+; RUN: %clang -cc1as -o %t/host-x86_64-unknown-linux-gnu.o \
+; RUN: -triple x86_64-unknown-linux-gnu -filetype obj -target-cpu x86-64 \
+; RUN: %t/host-x86_64-unknown-linux-gnu.s
+
+; Check plugin, -passes, and no remarks.
+; RUN: clang-linker-wrapper -o a.out --embed-bitcode \
+; RUN: --linker-path=/usr/bin/true %t/host-x86_64-unknown-linux-gnu.o \
+; RUN: %offload-opt-loadbye --offload-opt=-wave-goodbye \
+; RUN: --offload-opt=-passes="function(goodbye),module(inline)" 2>&1 | \
+; RUN: FileCheck -match-full-lines -check-prefixes=OUT %s
+
+; Check plugin, -p, and remarks.
+; RUN: clang-linker-wrapper -o a.out --embed-bitcode \
+; RUN: --linker-path=/usr/bin/true %t/host-x86_64-unknown-linux-gnu.o \
+; RUN: %offload-opt-loadbye --offload-opt=-wave-goodbye \
+; RUN: --offload-opt=-p="function(goodbye),module(inline)" \
+; RUN: --offload-opt=-pass-remarks=inline \
+; RUN: --offload-opt=-pass-remarks-output=%t/remarks.yml \
+; RUN: --offload-opt=-pass-remarks-filter=inline \
+; RUN: --offload-opt=-pass-remarks-format=yaml 2>&1 | \
+; RUN: FileCheck -match-full-lines -check-prefixes=OUT,REM %s
+; RUN: FileCheck -input-file=%t/remarks.yml -match-full-lines \
+; RUN: -check-prefixes=YML %s
+
+; Check handling of bad plugin.
+; RUN: not clang-linker-wrapper \
+; RUN: --offload-opt=-load-pass-plugin=%t/nonexistent.so 2>&1 | \
+; RUN: FileCheck -match-full-lines -check-prefixes=BAD-PLUGIN %s
+
+; OUT-NOT: {{.}}
+; OUT: Bye: f
+; OUT-NEXT: Bye: test
+; REM-NEXT: remark: {{.*}} 'f' inlined into 'test' {{.*}}
+; OUT-NOT: {{.}}
+
+; YML-NOT: {{.}}
+; YML: --- !Passed
+; YML-NEXT: Pass: inline
+; YML-NEXT: Name: Inlined
+; YML-NEXT: Function: test
+; YML-NEXT: Args:
+; YML: - Callee: f
+; YML: - Caller: test
+; YML: ...
+; YML-NOT: {{.}}
+
+; BAD-PLUGIN-NOT: {{.}}
+; BAD-PLUGIN: {{.*}}Could not load library {{.*}}nonexistent.so{{.*}}
+; BAD-PLUGIN-NOT: {{.}}
+
+;--- host-x86_64-unknown-linux-gnu.ll
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+;--- openmp-amdgcn-amd-amdhsa.ll
+target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
+target triple = "amdgcn-amd-amdhsa"
+
+define void @f() {
+entry:
+ ret void
+}
+
+define amdgpu_kernel void @test() {
+entry:
+ call void @f()
+ ret void
+}
diff --git a/clang/test/Driver/lit.local.cfg b/clang/test/Driver/lit.local.cfg
index 6370e9f92d89b..9bde2333a2e0d 100644
--- a/clang/test/Driver/lit.local.cfg
+++ b/clang/test/Driver/lit.local.cfg
@@ -19,6 +19,7 @@ config.suffixes = [
".hip",
".hipi",
".hlsl",
+ ".ll",
".yaml",
".test",
]
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index e5630a07424c7..2e0fbc2c9e1dd 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -109,6 +109,15 @@
if config.clang_examples:
config.available_features.add("examples")
+if config.llvm_examples:
+ config.available_features.add("llvm-examples")
+
+if config.llvm_linked_bye_extension:
+ config.substitutions.append(("%offload-opt-loadbye", ""))
+else:
+ loadbye = f"-load-pass-plugin={config.llvm_shlib_dir}/Bye{config.llvm_shlib_ext}"
+ config.substitutions.append(("%offload-opt-loadbye", f"--offload-opt={loadbye}"))
+
def have_host_jit_feature_support(feature_name):
clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
@@ -213,6 +222,9 @@ def have_host_clang_repl_cuda():
if config.has_plugins and config.llvm_plugin_ext:
config.available_features.add("plugins")
+if config.llvm_has_plugins and config.llvm_plugin_ext:
+ config.available_features.add("llvm-plugins")
+
if config.clang_default_pie_on_linux:
config.available_features.add("default-pie-on-linux")
diff --git a/clang/test/lit.site.cfg.py.in b/clang/test/lit.site.cfg.py.in
index 1cbd876ac5bb9..2cc70e52f1aa1 100644
--- a/clang/test/lit.site.cfg.py.in
+++ b/clang/test/lit.site.cfg.py.in
@@ -7,6 +7,7 @@ config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@")
config.llvm_tools_dir = lit_config.substitute(path(r"@LLVM_TOOLS_DIR@"))
config.llvm_libs_dir = lit_config.substitute(path(r"@LLVM_LIBS_DIR@"))
config.llvm_shlib_dir = lit_config.substitute(path(r"@SHLIBDIR@"))
+config.llvm_shlib_ext = "@SHLIBEXT@"
config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@"
config.lit_tools_dir = path(r"@LLVM_LIT_TOOLS_DIR@")
config.errc_messages = "@LLVM_LIT_ERRC_MESSAGES@"
@@ -39,7 +40,10 @@ config.python_executable = "@Python3_EXECUTABLE@"
config.use_z3_solver = lit_config.params.get('USE_Z3_SOLVER', "@USE_Z3_SOLVER@")
config.has_plugins = @CLANG_PLUGIN_SUPPORT@
config.clang_vendor_uti = "@CLANG_VENDOR_UTI@"
+config.llvm_examples = @LLVM_BUILD_EXAMPLES@
+config.llvm_linked_bye_extension = @LLVM_BYE_LINK_INTO_TOOLS@
config.llvm_external_lit = path(r"@LLVM_EXTERNAL_LIT@")
+config.llvm_has_plugins = @LLVM_ENABLE_PLUGINS@
config.standalone_build = @CLANG_BUILT_STANDALONE@
config.ppc_linux_default_ieeelongdouble = @PPC_LINUX_DEFAULT_IEEELONGDOUBLE@
config.have_llvm_driver = @LLVM_TOOL_LLVM_DRIVER_BUILD@
diff --git a/clang/tools/clang-linker-wrapper/CMakeLists.txt b/clang/tools/clang-linker-wrapper/CMakeLists.txt
index 5556869affaa6..bf37d8031025e 100644
--- a/clang/tools/clang-linker-wrapper/CMakeLists.txt
+++ b/clang/tools/clang-linker-wrapper/CMakeLists.txt
@@ -41,3 +41,5 @@ target_link_libraries(clang-linker-wrapper
PRIVATE
${CLANG_LINKER_WRAPPER_LIB_DEPS}
)
+
+export_executable_symbols_for_plugins(clang-linker-wrapper)
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index 9027076119cf9..cb4cc5debae87 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -37,6 +37,8 @@
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileOutputBuffer.h"
@@ -62,6 +64,54 @@ using namespace llvm;
using namespace llvm::opt;
using namespace llvm::object;
+// Various tools (e.g., llc and opt) duplicate this series of declarations for
+// options related to passes and remarks.
+
+static cl::opt<bool> RemarksWithHotness(
+ "pass-remarks-with-hotness",
+ cl::desc("With PGO, include profile count in optimization remarks"),
+ cl::Hidden);
+
+static cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser>
+ RemarksHotnessThreshold(
+ "pass-remarks-hotness-threshold",
+ cl::desc("Minimum profile count required for "
+ "an optimization remark to be output. "
+ "Use 'auto' to apply the threshold from profile summary."),
+ cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden);
+
+static cl::opt<std::string>
+ RemarksFilename("pass-remarks-output",
+ cl::desc("Output filename for pass remarks"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ RemarksPasses("pass-remarks-filter",
+ cl::desc("Only record optimization remarks from passes whose "
+ "names match the given regular expression"),
+ cl::value_desc("regex"));
+
+static cl::opt<std::string> RemarksFormat(
+ "pass-remarks-format",
+ cl::desc("The format used for serializing remarks (default: YAML)"),
+ cl::value_desc("format"), cl::init("yaml"));
+
+static cl::list<std::string>
+ PassPlugins("load-pass-plugin",
+ cl::desc("Load passes from plugin library"));
+
+static cl::opt<std::string> PassPipeline(
+ "passes",
+ cl::desc(
+ "A textual description of the pass pipeline. To have analysis passes "
+ "available before a certain pass, add 'require<foo-analysis>'. "
+ "'-passes' overrides the pass pipeline (but not all effects) from "
+ "specifying '--opt-level=O?' (O2 is the default) to "
+ "clang-linker-wrapper. Be sure to include the corresponding "
+ "'default<O?>' in '-passes'."));
+static cl::alias PassPipeline2("p", cl::aliasopt(PassPipeline),
+ cl::desc("Alias for -passes"));
+
/// Path of the current binary.
static const char *LinkerExecutable;
@@ -628,6 +678,12 @@ std::unique_ptr<lto::LTO> createLTO(
Conf.CPU = Arch.str();
Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
+ Conf.RemarksFilename = RemarksFilename;
+ Conf.RemarksPasses = RemarksPasses;
+ Conf.RemarksWithHotness = RemarksWithHotness;
+ Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;
+ Conf.RemarksFormat = RemarksFormat;
+
StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
Conf.MAttrs = Features;
std::optional<CodeGenOptLevel> CGOptLevelOrNone =
@@ -637,6 +693,17 @@ std::unique_ptr<lto::LTO> createLTO(
Conf.OptLevel = OptLevel[1] - '0';
Conf.DefaultTriple = Triple.getTriple();
+ // TODO: Should we complain about combining --opt-level and -passes, as opt
+ // does? That might be too limiting in clang-linker-wrapper, so for now we
+ // just warn in the help entry for -passes that the default<O?> corresponding
+ // to --opt-level=O? should be included there. The problem is that
+ // --opt-level produces effects in clang-linker-wrapper beyond what -passes
+ // appears to be able to achieve, so rejecting the combination of --opt-level
+ // and -passes would apparently make it impossible to combine those effects
+ // with a custom pass pipeline.
+ Conf.OptPipeline = PassPipeline;
+ Conf.PassPlugins = PassPlugins;
+
LTOError = false;
Conf.DiagHandler = diagnosticHandler;
@@ -1660,6 +1727,13 @@ int main(int Argc, char **Argv) {
NewArgv.push_back(Arg->getValue());
for (const opt::Arg *Arg : Args.filtered(OPT_offload_opt_eq_minus))
NewArgv.push_back(Args.MakeArgString(StringRef("-") + Arg->getValue()));
+ SmallVector<PassPlugin, 1> PluginList;
+ PassPlugins.setCallback([&](const std::string &PluginPath) {
+ auto Plugin = PassPlugin::Load(PluginPath);
+ if (!Plugin)
+ report_fatal_error(Plugin.takeError(), /*gen_crash_diag=*/false);
+ PluginList.emplace_back(Plugin.get());
+ });
cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]);
Verbose = Args.hasArg(OPT_verbose);
diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
index eb31b98a3f545..9c27e588fc4f5 100644
--- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
+++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
@@ -94,9 +94,13 @@ def linker_arg_EQ : Joined<["--"], "linker-arg=">,
// Arguments for the LLVM backend.
def mllvm : Separate<["-"], "mllvm">, Flags<[WrapperOnlyOption]>,
- MetaVarName<"<arg>">, HelpText<"Arguments passed to the LLVM invocation">;
+ MetaVarName<"<arg>">,
+ HelpText<"Arguments passed to LLVM, including Clang invocations, for which "
+ "the '-mllvm' prefix is preserved. Use '-mllvm --help' for a list "
+ "of options.">;
def offload_opt_eq_minus : Joined<["--", "-"], "offload-opt=-">, Flags<[HelpHidden, WrapperOnlyOption]>,
- HelpText<"Options passed to LLVM">;
+ HelpText<"Options passed to LLVM, not including the Clang invocation. Use "
+ "'--offload-opt=--help' for a list of options.">;
// Standard linker flags also used by the linker wrapper.
def sysroot_EQ : Joined<["--"], "sysroot=">, HelpText<"Set the system root">;
``````````
</details>
https://github.com/llvm/llvm-project/pull/96704
More information about the cfe-commits
mailing list