Skip to content

Commit c4bfb84

Browse files
committed
[KernelInfo][LinkerWrapper] Extend with usual pass options
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). Fortuantely, 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. We will propose this patch upstream in the future.
1 parent 2476cd3 commit c4bfb84

File tree

9 files changed

+198
-2
lines changed

9 files changed

+198
-2
lines changed

clang/test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ llvm_canonicalize_cmake_booleans(
1111
CLANG_SPAWN_CC1
1212
CLANG_ENABLE_CIR
1313
ENABLE_BACKTRACES
14+
LLVM_BUILD_EXAMPLES
15+
LLVM_BYE_LINK_INTO_TOOLS
16+
LLVM_ENABLE_PLUGINS
1417
LLVM_ENABLE_ZLIB
1518
LLVM_ENABLE_ZSTD
1619
LLVM_ENABLE_PER_TARGET_RUNTIME_DIR
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Check that these simple command lines for listing LLVM options are supported,
2+
// as claimed by 'clang-linker-wrapper --help'.
3+
4+
// RUN: clang-linker-wrapper -mllvm --help 2>&1 | FileCheck %s
5+
// RUN: clang-linker-wrapper --offload-opt=--help 2>&1 | FileCheck %s
6+
7+
// Look for a few options supported only after -mllvm and --offload-opt.
8+
// CHECK: OPTIONS:
9+
// CHECK-DAG: --passes=<string>
10+
// CHECK-DAG: --load-pass-plugin=<string>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
; Check various clang-linker-wrapper pass options after -offload-opt.
2+
3+
; REQUIRES: llvm-plugins, llvm-examples
4+
; REQUIRES: x86-registered-target
5+
; REQUIRES: amdgpu-registered-target
6+
7+
; Setup.
8+
; RUN: split-file %s %t
9+
; RUN: opt -o %t/host-x86_64-unknown-linux-gnu.bc \
10+
; RUN: %t/host-x86_64-unknown-linux-gnu.ll
11+
; RUN: opt -o %t/openmp-amdgcn-amd-amdhsa.bc \
12+
; RUN: %t/openmp-amdgcn-amd-amdhsa.ll
13+
; RUN: clang-offload-packager -o %t/openmp-x86_64-unknown-linux-gnu.out \
14+
; RUN: --image=file=%t/openmp-amdgcn-amd-amdhsa.bc,triple=amdgcn-amd-amdhsa
15+
; RUN: %clang -cc1 -S -o %t/host-x86_64-unknown-linux-gnu.s \
16+
; RUN: -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa \
17+
; RUN: -fembed-offload-object=%t/openmp-x86_64-unknown-linux-gnu.out \
18+
; RUN: %t/host-x86_64-unknown-linux-gnu.bc
19+
; RUN: %clang -cc1as -o %t/host-x86_64-unknown-linux-gnu.o \
20+
; RUN: -triple x86_64-unknown-linux-gnu -filetype obj -target-cpu x86-64 \
21+
; RUN: %t/host-x86_64-unknown-linux-gnu.s
22+
23+
; Check plugin, -passes, and no remarks.
24+
; RUN: clang-linker-wrapper -o a.out --embed-bitcode \
25+
; RUN: --linker-path=/usr/bin/true %t/host-x86_64-unknown-linux-gnu.o \
26+
; RUN: %offload-opt-loadbye --offload-opt=-wave-goodbye \
27+
; RUN: --offload-opt=-passes="function(goodbye),module(inline)" 2>&1 | \
28+
; RUN: FileCheck -match-full-lines -check-prefixes=OUT %s
29+
30+
; Check plugin, -p, and remarks.
31+
; RUN: clang-linker-wrapper -o a.out --embed-bitcode \
32+
; RUN: --linker-path=/usr/bin/true %t/host-x86_64-unknown-linux-gnu.o \
33+
; RUN: %offload-opt-loadbye --offload-opt=-wave-goodbye \
34+
; RUN: --offload-opt=-p="function(goodbye),module(inline)" \
35+
; RUN: --offload-opt=-pass-remarks=inline \
36+
; RUN: --offload-opt=-pass-remarks-output=%t/remarks.yml \
37+
; RUN: --offload-opt=-pass-remarks-filter=inline \
38+
; RUN: --offload-opt=-pass-remarks-format=yaml 2>&1 | \
39+
; RUN: FileCheck -match-full-lines -check-prefixes=OUT,REM %s
40+
; RUN: FileCheck -input-file=%t/remarks.yml -match-full-lines \
41+
; RUN: -check-prefixes=YML %s
42+
43+
; Check handling of bad plugin.
44+
; RUN: not clang-linker-wrapper \
45+
; RUN: --offload-opt=-load-pass-plugin=%t/nonexistent.so 2>&1 | \
46+
; RUN: FileCheck -match-full-lines -check-prefixes=BAD-PLUGIN %s
47+
48+
; OUT-NOT: {{.}}
49+
; OUT: Bye: f
50+
; OUT-NEXT: Bye: test
51+
; REM-NEXT: remark: {{.*}} 'f' inlined into 'test' {{.*}}
52+
; OUT-NOT: {{.}}
53+
54+
; YML-NOT: {{.}}
55+
; YML: --- !Passed
56+
; YML-NEXT: Pass: inline
57+
; YML-NEXT: Name: Inlined
58+
; YML-NEXT: Function: test
59+
; YML-NEXT: Args:
60+
; YML: - Callee: f
61+
; YML: - Caller: test
62+
; YML: ...
63+
; YML-NOT: {{.}}
64+
65+
; BAD-PLUGIN-NOT: {{.}}
66+
; BAD-PLUGIN: {{.*}}Could not load library {{.*}}nonexistent.so{{.*}}
67+
; BAD-PLUGIN-NOT: {{.}}
68+
69+
;--- host-x86_64-unknown-linux-gnu.ll
70+
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"
71+
target triple = "x86_64-unknown-linux-gnu"
72+
73+
;--- openmp-amdgcn-amd-amdhsa.ll
74+
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"
75+
target triple = "amdgcn-amd-amdhsa"
76+
77+
define void @f() {
78+
entry:
79+
ret void
80+
}
81+
82+
define amdgpu_kernel void @test() {
83+
entry:
84+
call void @f()
85+
ret void
86+
}

clang/test/Driver/lit.local.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ config.suffixes = [
1919
".hip",
2020
".hipi",
2121
".hlsl",
22+
".ll",
2223
".yaml",
2324
".test",
2425
]

clang/test/lit.cfg.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@
109109
if config.clang_examples:
110110
config.available_features.add("examples")
111111

112+
if config.llvm_examples:
113+
config.available_features.add("llvm-examples")
114+
115+
if config.llvm_linked_bye_extension:
116+
config.substitutions.append(("%offload-opt-loadbye", ""))
117+
else:
118+
loadbye = f"-load-pass-plugin={config.llvm_shlib_dir}/Bye{config.llvm_shlib_ext}"
119+
config.substitutions.append(("%offload-opt-loadbye", f"--offload-opt={loadbye}"))
120+
112121

113122
def have_host_jit_feature_support(feature_name):
114123
clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
@@ -213,6 +222,9 @@ def have_host_clang_repl_cuda():
213222
if config.has_plugins and config.llvm_plugin_ext:
214223
config.available_features.add("plugins")
215224

225+
if config.llvm_has_plugins and config.llvm_plugin_ext:
226+
config.available_features.add("llvm-plugins")
227+
216228
if config.clang_default_pie_on_linux:
217229
config.available_features.add("default-pie-on-linux")
218230

clang/test/lit.site.cfg.py.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@")
77
config.llvm_tools_dir = lit_config.substitute(path(r"@LLVM_TOOLS_DIR@"))
88
config.llvm_libs_dir = lit_config.substitute(path(r"@LLVM_LIBS_DIR@"))
99
config.llvm_shlib_dir = lit_config.substitute(path(r"@SHLIBDIR@"))
10+
config.llvm_shlib_ext = "@SHLIBEXT@"
1011
config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@"
1112
config.lit_tools_dir = path(r"@LLVM_LIT_TOOLS_DIR@")
1213
config.errc_messages = "@LLVM_LIT_ERRC_MESSAGES@"
@@ -39,7 +40,10 @@ config.python_executable = "@Python3_EXECUTABLE@"
3940
config.use_z3_solver = lit_config.params.get('USE_Z3_SOLVER', "@USE_Z3_SOLVER@")
4041
config.has_plugins = @CLANG_PLUGIN_SUPPORT@
4142
config.clang_vendor_uti = "@CLANG_VENDOR_UTI@"
43+
config.llvm_examples = @LLVM_BUILD_EXAMPLES@
44+
config.llvm_linked_bye_extension = @LLVM_BYE_LINK_INTO_TOOLS@
4245
config.llvm_external_lit = path(r"@LLVM_EXTERNAL_LIT@")
46+
config.llvm_has_plugins = @LLVM_ENABLE_PLUGINS@
4347
config.standalone_build = @CLANG_BUILT_STANDALONE@
4448
config.ppc_linux_default_ieeelongdouble = @PPC_LINUX_DEFAULT_IEEELONGDOUBLE@
4549
config.have_llvm_driver = @LLVM_TOOL_LLVM_DRIVER_BUILD@

clang/tools/clang-linker-wrapper/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ target_link_libraries(clang-linker-wrapper
4141
PRIVATE
4242
${CLANG_LINKER_WRAPPER_LIB_DEPS}
4343
)
44+
45+
export_executable_symbols_for_plugins(clang-linker-wrapper)

clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#include "llvm/Option/ArgList.h"
3737
#include "llvm/Option/OptTable.h"
3838
#include "llvm/Option/Option.h"
39+
#include "llvm/Passes/PassPlugin.h"
40+
#include "llvm/Remarks/HotnessThresholdParser.h"
3941
#include "llvm/Support/CommandLine.h"
4042
#include "llvm/Support/Errc.h"
4143
#include "llvm/Support/FileOutputBuffer.h"
@@ -60,6 +62,54 @@ using namespace llvm;
6062
using namespace llvm::opt;
6163
using namespace llvm::object;
6264

65+
// Various tools (e.g., llc and opt) duplicate this series of declarations for
66+
// options related to passes and remarks.
67+
68+
static cl::opt<bool> RemarksWithHotness(
69+
"pass-remarks-with-hotness",
70+
cl::desc("With PGO, include profile count in optimization remarks"),
71+
cl::Hidden);
72+
73+
static cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser>
74+
RemarksHotnessThreshold(
75+
"pass-remarks-hotness-threshold",
76+
cl::desc("Minimum profile count required for "
77+
"an optimization remark to be output. "
78+
"Use 'auto' to apply the threshold from profile summary."),
79+
cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden);
80+
81+
static cl::opt<std::string>
82+
RemarksFilename("pass-remarks-output",
83+
cl::desc("Output filename for pass remarks"),
84+
cl::value_desc("filename"));
85+
86+
static cl::opt<std::string>
87+
RemarksPasses("pass-remarks-filter",
88+
cl::desc("Only record optimization remarks from passes whose "
89+
"names match the given regular expression"),
90+
cl::value_desc("regex"));
91+
92+
static cl::opt<std::string> RemarksFormat(
93+
"pass-remarks-format",
94+
cl::desc("The format used for serializing remarks (default: YAML)"),
95+
cl::value_desc("format"), cl::init("yaml"));
96+
97+
static cl::list<std::string>
98+
PassPlugins("load-pass-plugin",
99+
cl::desc("Load passes from plugin library"));
100+
101+
static cl::opt<std::string> PassPipeline(
102+
"passes",
103+
cl::desc(
104+
"A textual description of the pass pipeline. To have analysis passes "
105+
"available before a certain pass, add 'require<foo-analysis>'. "
106+
"'-passes' overrides the pass pipeline (but not all effects) from "
107+
"specifying '--opt-level=O?' (O2 is the default) to "
108+
"clang-linker-wrapper. Be sure to include the corresponding "
109+
"'default<O?>' in '-passes'."));
110+
static cl::alias PassPipeline2("p", cl::aliasopt(PassPipeline),
111+
cl::desc("Alias for -passes"));
112+
63113
/// Path of the current binary.
64114
static const char *LinkerExecutable;
65115

@@ -631,6 +681,12 @@ std::unique_ptr<lto::LTO> createLTO(
631681
Conf.CPU = Arch.str();
632682
Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
633683

684+
Conf.RemarksFilename = RemarksFilename;
685+
Conf.RemarksPasses = RemarksPasses;
686+
Conf.RemarksWithHotness = RemarksWithHotness;
687+
Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;
688+
Conf.RemarksFormat = RemarksFormat;
689+
634690
StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
635691
Conf.MAttrs = Features;
636692
std::optional<CodeGenOptLevel> CGOptLevelOrNone =
@@ -640,6 +696,17 @@ std::unique_ptr<lto::LTO> createLTO(
640696
Conf.OptLevel = OptLevel[1] - '0';
641697
Conf.DefaultTriple = Triple.getTriple();
642698

699+
// TODO: Should we complain about combining --opt-level and -passes, as opt
700+
// does? That might be too limiting in clang-linker-wrapper, so for now we
701+
// just warn in the help entry for -passes that the default<O?> corresponding
702+
// to --opt-level=O? should be included there. The problem is that
703+
// --opt-level produces effects in clang-linker-wrapper beyond what -passes
704+
// appears to be able to achieve, so rejecting the combination of --opt-level
705+
// and -passes would apparently make it impossible to combine those effects
706+
// with a custom pass pipeline.
707+
Conf.OptPipeline = PassPipeline;
708+
Conf.PassPlugins = PassPlugins;
709+
643710
LTOError = false;
644711
Conf.DiagHandler = diagnosticHandler;
645712

@@ -1669,6 +1736,13 @@ int main(int Argc, char **Argv) {
16691736
NewArgv.push_back(Arg->getValue());
16701737
for (const opt::Arg *Arg : Args.filtered(OPT_offload_opt_eq_minus))
16711738
NewArgv.push_back(Args.MakeArgString(StringRef("-") + Arg->getValue()));
1739+
SmallVector<PassPlugin, 1> PluginList;
1740+
PassPlugins.setCallback([&](const std::string &PluginPath) {
1741+
auto Plugin = PassPlugin::Load(PluginPath);
1742+
if (!Plugin)
1743+
report_fatal_error(Plugin.takeError(), /*gen_crash_diag=*/false);
1744+
PluginList.emplace_back(Plugin.get());
1745+
});
16721746
cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]);
16731747

16741748
Verbose = Args.hasArg(OPT_verbose);

clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,13 @@ def linker_arg_EQ : Joined<["--"], "linker-arg=">,
9494

9595
// Arguments for the LLVM backend.
9696
def mllvm : Separate<["-"], "mllvm">, Flags<[WrapperOnlyOption]>,
97-
MetaVarName<"<arg>">, HelpText<"Arguments passed to the LLVM invocation">;
97+
MetaVarName<"<arg>">,
98+
HelpText<"Arguments passed to LLVM, including Clang invocations, for which "
99+
"the '-mllvm' prefix is preserved. Use '-mllvm --help' for a list "
100+
"of options.">;
98101
def offload_opt_eq_minus : Joined<["--", "-"], "offload-opt=-">, Flags<[HelpHidden, WrapperOnlyOption]>,
99-
HelpText<"Options passed to LLVM">;
102+
HelpText<"Options passed to LLVM, not including the Clang invocation. Use "
103+
"'--offload-opt=--help' for a list of options.">;
100104

101105
// Standard linker flags also used by the linker wrapper.
102106
def sysroot_EQ : Joined<["--"], "sysroot=">, HelpText<"Set the system root">;

0 commit comments

Comments
 (0)