[clang] [llvm] [Instrumentor] A configurable instrumentation pass plugin (PR #151551)
Stefan Gränitz via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 31 09:19:42 PDT 2025
https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/151551
>From ce746d89f4c9474d510c0fdcccd61ad599ef8e49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 18 Jun 2025 21:57:45 +0200
Subject: [PATCH 1/5] Draft reference plugin and end-to-end tests in clang
---
clang/test/Driver/pass-plugin-entrypoints.c | 66 ++++++++
clang/test/Driver/pass-plugin-params.c | 23 +++
clang/test/lit.cfg.py | 7 +
clang/test/lit.site.cfg.py.in | 1 +
llvm/tools/plugins-shlib/CMakeLists.txt | 6 +
llvm/tools/plugins-shlib/ReferencePlugin.cpp | 149 +++++++++++++++++++
6 files changed, 252 insertions(+)
create mode 100644 clang/test/Driver/pass-plugin-entrypoints.c
create mode 100644 clang/test/Driver/pass-plugin-params.c
create mode 100644 llvm/tools/plugins-shlib/CMakeLists.txt
create mode 100644 llvm/tools/plugins-shlib/ReferencePlugin.cpp
diff --git a/clang/test/Driver/pass-plugin-entrypoints.c b/clang/test/Driver/pass-plugin-entrypoints.c
new file mode 100644
index 0000000000000..1af0f0375d628
--- /dev/null
+++ b/clang/test/Driver/pass-plugin-entrypoints.c
@@ -0,0 +1,66 @@
+// REQUIRES: pass-plugins
+// UNSUPPORTED: system-windows
+
+// Test default entry-point
+// RUN: %clang -O0 -fpass-plugin=%pass_plugin_reference \
+// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
+// RUN: FileCheck --check-prefix=EP-EARLY-SIMPL %s
+//
+// RUN: %clang -O2 -fpass-plugin=%pass_plugin_reference \
+// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
+// RUN: FileCheck --check-prefixes=EP-EARLY-SIMPL,LTO-NONE %s
+//
+// RUN: %clang -c -flto=full -O2 -fpass-plugin=%pass_plugin_reference \
+// RUN: -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
+// RUN: FileCheck --check-prefixes=EP-EARLY-SIMPL,LTO-FULL %s
+//
+// RUN: %clang -c -flto=thin -O2 -fpass-plugin=%pass_plugin_reference \
+// RUN: -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
+// RUN: FileCheck --check-prefixes=EP-EARLY-SIMPL,LTO-THIN %s
+//
+// EP-EARLY-SIMPL: Running pass: TestModulePass
+// EP-EARLY-SIMPL: Entry-point: registerPipelineEarlySimplificationEPCallback
+//
+// LTO-NONE: LTO-phase: None
+// LTO-FULL: LTO-phase: FullLTOPreLink
+// LTO-THIN: LTO-phase: ThinLTOPreLink
+
+// Pass doesn't run if default entry-point is disabled
+// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck %s
+//
+// CHECK-NOT: Running pass: TestModulePass
+
+// Test pipeline-start entry-point
+// RUN: env registerPipelineStartEPCallback=On \
+// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=EP-START %s
+//
+// EP-START: Running pass: TestModulePass
+// EP-START: Entry-point: registerPipelineStartEPCallback
+
+// Test optimizer entry-points
+// RUN: env registerOptimizerEarlyEPCallback=On \
+// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -O2 -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=OPT-EARLY %s
+//
+// OPT-EARLY: Running pass: TestModulePass
+// OPT-EARLY: Entry-point: registerOptimizerEarlyEPCallback
+// OPT-EARLY: Running pass: LowerConstantIntrinsicsPass
+
+#if LLVM_VERSION_MAJOR > 20
+// RUN: env registerOptimizerLastEPCallback=On \
+// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -O2 -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=OPT-LAST %s
+//
+// OPT-LAST: Running pass: LowerConstantIntrinsicsPass
+// OPT-LAST: Running pass: TestModulePass on [module]
+// OPT-LAST: Entry-point: registerOptimizerLastEPCallback
+#endif
+
+// TODO: Why is late optimizer entry-point reached in LTO-mode??
+// RUN: env registerOptimizerLastEPCallback=On \
+// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -O2 -flto -c -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1
+
+int main() { return 0; }
diff --git a/clang/test/Driver/pass-plugin-params.c b/clang/test/Driver/pass-plugin-params.c
new file mode 100644
index 0000000000000..328f315e8cec7
--- /dev/null
+++ b/clang/test/Driver/pass-plugin-params.c
@@ -0,0 +1,23 @@
+// REQUIRES: pass-plugins
+// UNSUPPORTED: system-windows
+
+// RUN: %clang -fpass-plugin=%pass_plugin_reference \
+// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-FALSE %s
+//
+// TODO: Can we make this work? (i.e. without the extra -mllvm from below)
+// RUN: not %clang -fpass-plugin=%pass_plugin_reference -wave-goodbye \
+// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-ERR-CLANG %s
+//
+// FIXME: This is supposed to work (i.e. without the extra -load from below)
+// RUN: not %clang -fpass-plugin=%pass_plugin_reference -mllvm -wave-goodbye \
+// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-ERR-LLVM %s
+//
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -Xclang -load -Xclang %pass_plugin_reference -mllvm -wave-goodbye \
+// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-TRUE %s
+//
+// PARAM-ERR-CLANG: error: unknown argument
+// PARAM-ERR-LLVM: Unknown command line argument
+// PARAM-TRUE: Plugin parameter value -wave-goodbye=true
+// PARAM-FALSE: Plugin parameter value -wave-goodbye=false
+
+int main() { return 0; }
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index 1957bb1715eb6..da360e52c7241 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -264,6 +264,13 @@ def have_host_clang_repl_cuda():
if config.clang_default_pie_on_linux:
config.available_features.add("default-pie-on-linux")
+# Run end-to-end tests if the reference pass-plugin exists in LLVM
+plugin_name = f"{config.llvm_plugin_prefix}ReferencePlugin{config.llvm_plugin_ext}"
+plugin_shlib = os.path.join(config.llvm_shlib_dir, plugin_name)
+if os.path.exists(plugin_shlib):
+ config.available_features.add("pass-plugins")
+ config.substitutions.append(("%pass_plugin_reference", plugin_shlib))
+
# Set available features we allow tests to conditionalize on.
#
if config.clang_default_cxx_stdlib != "":
diff --git a/clang/test/lit.site.cfg.py.in b/clang/test/lit.site.cfg.py.in
index 176cf644badcc..7cef6ba341e0c 100644
--- a/clang/test/lit.site.cfg.py.in
+++ b/clang/test/lit.site.cfg.py.in
@@ -47,6 +47,7 @@ config.have_llvm_driver = @LLVM_TOOL_LLVM_DRIVER_BUILD@
config.spirv_tools_tests = @LLVM_INCLUDE_SPIRV_TOOLS_TESTS@
config.substitutions.append(("%llvm-version-major", "@LLVM_VERSION_MAJOR@"))
config.has_key_instructions = @LLVM_EXPERIMENTAL_KEY_INSTRUCTIONS@
+config.llvm_plugin_prefix = "@CMAKE_SHARED_LIBRARY_PREFIX@"
import lit.llvm
lit.llvm.initialize(lit_config, config)
diff --git a/llvm/tools/plugins-shlib/CMakeLists.txt b/llvm/tools/plugins-shlib/CMakeLists.txt
new file mode 100644
index 0000000000000..f0d5a86383772
--- /dev/null
+++ b/llvm/tools/plugins-shlib/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_llvm_library(ReferencePlugin SHARED INSTALL_WITH_TOOLCHAIN
+ ReferencePlugin.cpp
+
+ DEPENDS
+ intrinsics_gen
+)
diff --git a/llvm/tools/plugins-shlib/ReferencePlugin.cpp b/llvm/tools/plugins-shlib/ReferencePlugin.cpp
new file mode 100644
index 0000000000000..aa9164449375f
--- /dev/null
+++ b/llvm/tools/plugins-shlib/ReferencePlugin.cpp
@@ -0,0 +1,149 @@
+//===- lib/plugins-shlib/ReferencePlugin.cpp ------------------------------===//
+//
+// 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 "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <optional>
+#include <string>
+
+using namespace llvm;
+
+static cl::opt<bool> Wave("wave-goodbye", cl::init(false),
+ cl::desc("wave good bye"));
+
+static std::optional<std::string> getEnv(const std::string &Var) {
+ const char *Val = std::getenv(Var.c_str());
+ if (!Val)
+ return std::nullopt;
+ return std::string(Val);
+}
+
+static bool getEnvBool(const std::string &VarName, bool Default = false) {
+ if (auto ValOpt = getEnv(VarName)) {
+ std::string V = *ValOpt;
+ std::transform(V.begin(), V.end(), V.begin(), ::tolower);
+
+ if (V == "1" || V == "true" || V == "yes" || V == "on")
+ return true;
+ if (V == "0" || V == "false" || V == "no" || V == "off")
+ return false;
+ }
+ return Default;
+}
+
+static std::string getLTOPhaseStr(ThinOrFullLTOPhase P) {
+ switch (P) {
+ case ThinOrFullLTOPhase::None: return "None";
+ case ThinOrFullLTOPhase::ThinLTOPreLink: return "ThinLTOPreLink";
+ case ThinOrFullLTOPhase::ThinLTOPostLink: return "ThinLTOPostLink";
+ case ThinOrFullLTOPhase::FullLTOPreLink: return "FullLTOPreLink";
+ case ThinOrFullLTOPhase::FullLTOPostLink: return "FullLTOPostLink";
+ }
+}
+
+struct TestModulePass : public PassInfoMixin<TestModulePass> {
+ std::string EP;
+ std::string LTOPhase;
+ TestModulePass(StringRef EntryPoint, ThinOrFullLTOPhase Phase = ThinOrFullLTOPhase::None) : EP(EntryPoint.str()), LTOPhase(getLTOPhaseStr(Phase)) {}
+ PreservedAnalyses run(Module &, ModuleAnalysisManager &) {
+ fprintf(stderr, "Entry-point: %s\n", EP.c_str());
+ fprintf(stderr, "LTO-phase: %s\n", LTOPhase.c_str());
+ return PreservedAnalyses::all();
+ }
+};
+
+struct TestFunctionPass : public PassInfoMixin<TestFunctionPass> {
+ std::string EP;
+ TestFunctionPass(StringRef EntryPoint) : EP(EntryPoint.str()) {}
+ PreservedAnalyses run(Function &, FunctionAnalysisManager &) {
+ fprintf(stderr, "Entry-point: %s\n", EP.c_str());
+ return PreservedAnalyses::all();
+ }
+};
+
+static void registerCallbacks(PassBuilder &PB) {
+ printf("Plugin parameter value -wave-goodbye=%s\n", Wave ? "true" : "false");
+
+ // Entry-points for module passes
+ if (getEnvBool("registerPipelineStartEPCallback"))
+ PB.registerPipelineStartEPCallback(
+ [](ModulePassManager &MPM, OptimizationLevel Opt) {
+ MPM.addPass(TestModulePass("registerPipelineStartEPCallback"));
+ return true;
+ });
+ if (getEnvBool("registerPipelineEarlySimplificationEPCallback", true))
+ PB.registerPipelineEarlySimplificationEPCallback(
+ [](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
+ MPM.addPass(TestModulePass("registerPipelineEarlySimplificationEPCallback", Phase));
+ return true;
+ });
+ if (getEnvBool("registerOptimizerEarlyEPCallback"))
+ PB.registerOptimizerEarlyEPCallback(
+ [](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
+ MPM.addPass(TestModulePass("registerOptimizerEarlyEPCallback", Phase));
+ return true;
+ });
+ if (getEnvBool("registerOptimizerLastEPCallback"))
+ PB.registerOptimizerLastEPCallback(
+ [](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
+ MPM.addPass(TestModulePass("registerOptimizerLastEPCallback", Phase));
+ return true;
+ });
+ if (getEnvBool("registerFullLinkTimeOptimizationEarlyEPCallback"))
+ PB.registerFullLinkTimeOptimizationEarlyEPCallback(
+ [](ModulePassManager &MPM, OptimizationLevel Opt) {
+ MPM.addPass(TestModulePass("registerFullLinkTimeOptimizationEarlyEPCallback"));
+ return true;
+ });
+ if (getEnvBool("registerFullLinkTimeOptimizationLastEPCallback"))
+ PB.registerFullLinkTimeOptimizationLastEPCallback(
+ [](ModulePassManager &MPM, OptimizationLevel Opt) {
+ MPM.addPass(TestModulePass("registerFullLinkTimeOptimizationLastEPCallback"));
+ return true;
+ });
+
+ // Entry-points for function passes
+ if (getEnvBool("registerPeepholeEPCallback"))
+ PB.registerPeepholeEPCallback(
+ [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+ FPM.addPass(TestFunctionPass("registerPeepholeEPCallback"));
+ return true;
+ });
+ if (getEnvBool("registerScalarOptimizerLateEPCallback"))
+ PB.registerScalarOptimizerLateEPCallback(
+ [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+ FPM.addPass(TestFunctionPass("registerScalarOptimizerLateEPCallback"));
+ return true;
+ });
+ if (getEnvBool("registerVectorizerStartEPCallback"))
+ PB.registerVectorizerStartEPCallback(
+ [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+ FPM.addPass(TestFunctionPass("registerVectorizerStartEPCallback"));
+ return true;
+ });
+
+#if LLVM_VERSION_MAJOR > 20
+ if (getEnvBool("registerVectorizerEndEPCallback"))
+ PB.registerVectorizerEndEPCallback(
+ [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+ FPM.addPass(TestFunctionPass("registerVectorizerEndEPCallback"));
+ return true;
+ });
+#endif
+
+ // TODO: registerLateLoopOptimizationsEPCallback, registerCGSCCOptimizerLateEPCallback
+}
+
+extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
+llvmGetPassPluginInfo() {
+ return {LLVM_PLUGIN_API_VERSION, "ReferencePlugin", LLVM_VERSION_STRING,
+ registerCallbacks};
+}
>From b854b49703cd3b5ba30d4f20febec30ff1a77196 Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <johannes at jdoerfert.de>
Date: Wed, 4 Dec 2024 17:00:37 -0800
Subject: [PATCH 2/5] [Instrumentor] A configurable instrumentation pass
Both internally and externally we instrument code all the time. Usually,
those are hand written traversals that look for some specific
instructions or program points and then provide information to a
runtime. Since the real difference is in the runtime, and the
instrumentation is basically always the same, people can use this
Instrumentor pass and configure it to their needs.
Initial implementation only instruments alloca instructions but shows
the setup, and configurability via a JSON file.
---
.../Transforms/Instrumentation/Instrumentor.h | 56 +++
.../Instrumentation/InstrumentorConfig.def | 56 +++
llvm/lib/Passes/PassBuilder.cpp | 1 +
llvm/lib/Passes/PassRegistry.def | 1 +
.../Transforms/Instrumentation/CMakeLists.txt | 1 +
.../Instrumentation/Instrumentor.cpp | 341 ++++++++++++++++++
.../Instrumentation/Instrumentor/alloca.ll | 62 ++++
.../Instrumentor/default_config.json | 13 +
.../Instrumentor/just_calls_config.json | 14 +
.../Instrumentor/read_config.ll | 22 ++
.../Instrumentor/write_config.ll | 3 +
11 files changed, 570 insertions(+)
create mode 100644 llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
create mode 100644 llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
create mode 100644 llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
create mode 100644 llvm/test/Instrumentation/Instrumentor/alloca.ll
create mode 100644 llvm/test/Instrumentation/Instrumentor/default_config.json
create mode 100644 llvm/test/Instrumentation/Instrumentor/just_calls_config.json
create mode 100644 llvm/test/Instrumentation/Instrumentor/read_config.ll
create mode 100644 llvm/test/Instrumentation/Instrumentor/write_config.ll
diff --git a/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h b/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
new file mode 100644
index 0000000000000..0da4562be6a7a
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
@@ -0,0 +1,56 @@
+//===- Transforms/Instrumentation/Instrumentor.h --------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A highly configurable instrumentation pass.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
+#define LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
+
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/PassManager.h"
+
+#include <functional>
+
+namespace llvm {
+
+/// Configuration for the Instrumentor. First generic configuration, followed by
+/// the selection of what instruction classes and instructions should be
+/// instrumented and how.
+struct InstrumentorConfig {
+
+ /// An optional callback that takes the instruction that is about to be
+ /// instrumented and can return false if it should be skipped.
+ using CallbackTy = std::function<bool(Instruction &)>;
+
+#define SECTION_START(SECTION, CLASS) struct {
+
+#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE) \
+ TYPE NAME = DEFAULT_VALUE;
+
+#define CONFIG(SECTION, TYPE, NAME, DEFAULT_VALUE) TYPE NAME = DEFAULT_VALUE;
+
+#define SECTION_END(SECTION) \
+ } \
+ SECTION;
+
+#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+};
+
+class InstrumentorPass : public PassInfoMixin<InstrumentorPass> {
+ InstrumentorConfig IC;
+
+public:
+ InstrumentorPass(InstrumentorConfig IC = InstrumentorConfig{}) : IC(IC) {}
+
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+};
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
diff --git a/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def b/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
new file mode 100644
index 0000000000000..1d45919608b32
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
@@ -0,0 +1,56 @@
+//===- Transforms/Instrumentation/InstrumentorConfig.def ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+// No include guards
+
+/// Base configuration
+///{
+SECTION_START(Base, )
+
+/// The base name which defines the runtime call names, i.a.,
+/// <BaseName><instruction/location>(...)
+CONFIG(Base, std::string, RuntimeName, "__instrumentor_")
+
+/// Print the signatures of all used runtime functions.
+CONFIG(Base, bool, PrintRuntimeSignatures, true)
+
+SECTION_END(Base)
+///}
+
+/// AllocaInst
+///{
+SECTION_START(Alloca, AllocaInst)
+
+/// Should allocas be instrumented.
+CONFIG(Alloca, bool, Instrument, true)
+
+/// Selection of information passed to the runtime.
+///{
+/// The actual allocated pointer.
+CONFIG(Alloca, bool, Value, /* PtrTy */ true)
+/// The size of the entire allocation.
+CONFIG(Alloca, bool, AllocationSize, /* I64 */ true)
+/// The minimal alignment requested statically.
+CONFIG(Alloca, bool, Alignment, /* I64 */ true)
+///}
+
+/// Should the value be replaced by the runtime call result.
+CONFIG(Alloca, bool, ReplaceValue, true)
+
+/// Optional callback, see CallbackTy.
+CONFIG_INTERNAL(Alloca, CallbackTy, CB, nullptr)
+
+SECTION_END(Alloca)
+///}
+
+#undef SECTION_START
+#undef CONFIG
+#undef CONFIG_INTERNAL
+#undef SECTION_END
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index f810368a84940..0276798f3b662 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -244,6 +244,7 @@
#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
+#include "llvm/Transforms/Instrumentation/Instrumentor.h"
#include "llvm/Transforms/Instrumentation/KCFI.h"
#include "llvm/Transforms/Instrumentation/LowerAllowCheckPass.h"
#include "llvm/Transforms/Instrumentation/MemProfInstrumentation.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 1b111dc20d35c..b14a347c66241 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -100,6 +100,7 @@ MODULE_PASS("instrprof", InstrProfilingLoweringPass())
MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass())
MODULE_PASS("print<ctx-prof-analysis>", CtxProfAnalysisPrinterPass(errs()))
MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass())
+MODULE_PASS("instrumentor", InstrumentorPass())
MODULE_PASS("iroutliner", IROutlinerPass())
MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass())
MODULE_PASS("lower-emutls", LowerEmuTLSPass())
diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
index 15fd421a41b0f..6f8dad9799489 100644
--- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -12,6 +12,7 @@ add_llvm_component_library(LLVMInstrumentation
NumericalStabilitySanitizer.cpp
IndirectCallPromotion.cpp
InstrProfiling.cpp
+ Instrumentor.cpp
KCFI.cpp
LowerAllowCheckPass.cpp
PGOCtxProfFlattening.cpp
diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp b/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
new file mode 100644
index 0000000000000..6c9242126bff7
--- /dev/null
+++ b/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
@@ -0,0 +1,341 @@
+//===-- Instrumentor.cpp - Highly configurable instrumentation pass -------===//
+//
+// 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 "llvm/Transforms/Instrumentation/Instrumentor.h"
+
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/ConstantFolder.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <type_traits>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "instrumentor"
+
+cl::opt<std::string> WriteJSONConfig(
+ "instrumentor-write-config-file",
+ cl::desc(
+ "Write the instrumentor configuration into the specified JSON file"),
+ cl::init(""));
+cl::opt<std::string> ReadJSONConfig(
+ "instrumentor-read-config-file",
+ cl::desc(
+ "Read the instrumentor configuration from the specified JSON file"),
+ cl::init(""));
+
+namespace {
+
+template <typename... Targs>
+void dumpObject(json::OStream &J, Targs... Fargs) {}
+
+void writeInstrumentorConfig(InstrumentorConfig &IC) {
+ if (WriteJSONConfig.empty())
+ return;
+
+ std::error_code EC;
+ raw_fd_stream OS(WriteJSONConfig, EC);
+ if (EC) {
+ errs() << "WARNING: Failed to open instrumentor configuration file for "
+ "writing: "
+ << EC.message() << "\n";
+ return;
+ }
+
+ json::OStream J(OS, 2);
+ J.objectBegin();
+
+#define SECTION_START(SECTION, CLASS) \
+ J.attributeBegin(#SECTION); \
+ J.objectBegin();
+#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE)
+#define CONFIG(SECTION, TYPE, NAME, DEFAULT_VALUE) \
+ J.attribute(#NAME, IC.SECTION.NAME);
+#define SECTION_END(SECTION) \
+ J.objectEnd(); \
+ J.attributeEnd();
+
+#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+
+ J.objectEnd();
+}
+
+bool readInstrumentorConfigFromJSON(InstrumentorConfig &IC) {
+ if (ReadJSONConfig.empty())
+ return true;
+
+ std::error_code EC;
+ auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(ReadJSONConfig);
+ if (std::error_code EC = BufferOrErr.getError()) {
+ errs() << "WARNING: Failed to open instrumentor configuration file for "
+ "reading: "
+ << EC.message() << "\n";
+ return false;
+ }
+ auto Buffer = std::move(BufferOrErr.get());
+ json::Path::Root NullRoot;
+ auto Parsed = json::parse(Buffer->getBuffer());
+ if (!Parsed) {
+ errs() << "WARNING: Failed to parse the instrumentor configuration file: "
+ << Parsed.takeError() << "\n";
+ return false;
+ }
+ auto *Config = Parsed->getAsObject();
+ if (!Config) {
+ errs() << "WARNING: Failed to parse the instrumentor configuration file: "
+ "Expected "
+ "an object '{ ... }'\n";
+ return false;
+ }
+
+ auto End = Config->end(), It = Config->begin();
+
+#define CONFIG(SECTION, TYPE, NAME, DEFAULT_VALUE) \
+ It = Config->find(#SECTION); \
+ if (It != End) { \
+ if (auto *InstObj = It->second.getAsObject()) { \
+ if (auto *Val = InstObj->get(#NAME)) { \
+ if (!json::fromJSON(*Val, IC.SECTION.NAME, NullRoot)) \
+ errs() << "WARNING: Failed to read " #SECTION "." #NAME " as " #TYPE \
+ << "\n"; \
+ } \
+ } \
+ }
+
+#define SECTION_START(SECTION, CLASS)
+#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE)
+#define SECTION_END(SECTION)
+
+#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+
+ return true;
+}
+
+raw_ostream &printAsCType(raw_ostream &OS, Type *T) {
+ if (T->isPointerTy())
+ return OS << "void* ";
+ if (T->isIntegerTy())
+ return OS << "int" << T->getIntegerBitWidth() << "_t ";
+ return OS << *T << " ";
+}
+
+class InstrumentorImpl final {
+public:
+ InstrumentorImpl(const InstrumentorConfig &IC, Module &M)
+ : IC(IC), M(M), Ctx(M.getContext()),
+ IRB(Ctx, ConstantFolder(),
+ IRBuilderCallbackInserter(
+ [&](Instruction *I) { NewInsts[I] = Epoche; })) {}
+
+ /// Instrument the module, public entry point.
+ bool instrument();
+
+private:
+ bool shouldInstrumentFunction(Function *Fn);
+ bool instrumentFunction(Function &Fn);
+ bool instrument(AllocaInst &I);
+
+ template <typename Ty> Constant *getCI(Type *IT, Ty Val) {
+ return ConstantInt::get(IT, Val);
+ }
+
+ std::string getRTName(StringRef Suffix) {
+ return (IC.Base.RuntimeName + Suffix).str();
+ }
+
+ DenseMap<unsigned, FunctionCallee> InstrumentationFunctions;
+ FunctionCallee getCallee(Instruction &I, SmallVectorImpl<Type *> &RTArgTypes,
+ SmallVectorImpl<std::string> &RTArgNames,
+ Type *RT = nullptr) {
+ FunctionCallee &FC = InstrumentationFunctions[I.getOpcode()];
+ if (!FC.getFunctionType()) {
+ FC = M.getOrInsertFunction(
+ getRTName(I.getOpcodeName()),
+ FunctionType::get(RT ? RT : VoidTy, RTArgTypes, /*IsVarArgs*/ false));
+
+ if (IC.Base.PrintRuntimeSignatures) {
+ printAsCType(outs(), FC.getFunctionType()->getReturnType());
+ outs() << FC.getCallee()->getName() << "(";
+ auto *FT = FC.getFunctionType();
+ for (int I = 0, E = RTArgNames.size(); I != E; ++I) {
+ if (I != 0)
+ outs() << ", ";
+ printAsCType(outs(), FT->getParamType(I)) << RTArgNames[I];
+ }
+ outs() << ");\n";
+ }
+ }
+ return FC;
+ }
+
+ /// Each instrumentation, i.a., of an instruction, is happening in a dedicated
+ /// epoche. The epoche allows to determine if instrumentation instructions
+ /// were already around, due to prior instrumentations, or have been
+ /// introduced to support the current instrumentation, i.a., compute
+ /// information about the current instruction.
+ unsigned Epoche = 0;
+
+ /// A mapping from instrumentation instructions to the epoche they have been
+ /// created.
+ DenseMap<Instruction *, unsigned> NewInsts;
+
+ /// The instrumentor configuration.
+ const InstrumentorConfig &IC;
+
+ /// The module and the LLVM context.
+ Module &M;
+ LLVMContext &Ctx;
+
+ /// A special IR builder that keeps track of the inserted instructions.
+ IRBuilder<ConstantFolder, IRBuilderCallbackInserter> IRB;
+
+ /// Commonly used values for IR inspection and creation.
+ ///{
+
+ const DataLayout &DL = M.getDataLayout();
+
+ Type *VoidTy = Type::getVoidTy(Ctx);
+ Type *IntptrTy = M.getDataLayout().getIntPtrType(Ctx);
+ PointerType *PtrTy = PointerType::getUnqual(Ctx);
+ IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
+ IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
+ IntegerType *Int64Ty = Type::getInt64Ty(Ctx);
+ ///}
+};
+
+} // end anonymous namespace
+
+bool InstrumentorImpl::shouldInstrumentFunction(Function *Fn) {
+ if (!Fn || Fn->isDeclaration())
+ return false;
+ return true;
+}
+
+bool InstrumentorImpl::instrument(AllocaInst &I) {
+ if (IC.Alloca.CB && !IC.Alloca.CB(I))
+ return false;
+
+ Instruction *IP = I.getNextNonDebugInstruction();
+ while (isa<AllocaInst>(IP))
+ IP = IP->getNextNonDebugInstruction();
+ IRB.SetInsertPoint(IP);
+
+ SmallVector<Type *> RTArgTypes;
+ SmallVector<Value *> RTArgs;
+ SmallVector<std::string> RTArgNames;
+
+ if (IC.Alloca.Value) {
+ auto *ArgTy = PtrTy;
+ RTArgTypes.push_back(ArgTy);
+ RTArgs.push_back(IRB.CreatePointerBitCastOrAddrSpaceCast(&I, ArgTy));
+ RTArgNames.push_back("Value");
+ }
+
+ if (IC.Alloca.AllocationSize) {
+ auto *ArgTy = Int64Ty;
+ RTArgTypes.push_back(ArgTy);
+ Value *SizeValue = nullptr;
+ TypeSize TypeSize = DL.getTypeAllocSize(I.getAllocatedType());
+ if (TypeSize.isFixed())
+ SizeValue = getCI(ArgTy, TypeSize.getFixedValue());
+ if (!SizeValue) {
+ auto *LHS = IRB.CreatePtrToInt(
+ IRB.CreateGEP(I.getAllocatedType(), &I, {getCI(Int32Ty, 1)}), ArgTy);
+ SizeValue = IRB.CreateSub(LHS, IRB.CreatePtrToInt(&I, ArgTy));
+ }
+ if (I.isArrayAllocation())
+ SizeValue = IRB.CreateMul(
+ SizeValue, IRB.CreateZExtOrBitCast(I.getArraySize(), ArgTy));
+ RTArgs.push_back(SizeValue);
+ RTArgNames.push_back("AllocationSize");
+ }
+
+ if (IC.Alloca.Alignment) {
+ auto *ArgTy = Int64Ty;
+ RTArgTypes.push_back(ArgTy);
+ RTArgs.push_back(getCI(ArgTy, I.getAlign().value()));
+ RTArgNames.push_back("Alignment");
+ }
+
+ Type *RetTy = IC.Alloca.ReplaceValue ? PtrTy : nullptr;
+ FunctionCallee FC = getCallee(I, RTArgTypes, RTArgNames, RetTy);
+ auto *CI = IRB.CreateCall(FC, RTArgs);
+ if (IC.Alloca.ReplaceValue)
+ I.replaceUsesWithIf(
+ IRB.CreatePointerBitCastOrAddrSpaceCast(CI, I.getType()), [&](Use &U) {
+ return NewInsts.lookup(cast<Instruction>(U.getUser())) != Epoche;
+ });
+
+ return true;
+}
+
+bool InstrumentorImpl::instrumentFunction(Function &Fn) {
+ bool Changed = false;
+ if (!shouldInstrumentFunction(&Fn))
+ return Changed;
+
+ ReversePostOrderTraversal<Function *> RPOT(&Fn);
+ for (auto &It : RPOT) {
+ for (auto &I : *It) {
+ // Skip instrumentation instructions.
+ if (NewInsts.contains(&I))
+ continue;
+
+ // Count epochs eagerly.
+ ++Epoche;
+
+ switch (I.getOpcode()) {
+ case Instruction::Alloca:
+ if (IC.Alloca.Instrument)
+ instrument(cast<AllocaInst>(I));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return Changed;
+}
+
+bool InstrumentorImpl::instrument() {
+ bool Changed = false;
+
+ for (Function &Fn : M)
+ Changed |= instrumentFunction(Fn);
+
+ return Changed;
+}
+
+PreservedAnalyses InstrumentorPass::run(Module &M, ModuleAnalysisManager &MAM) {
+ InstrumentorImpl Impl(IC, M);
+ if (!readInstrumentorConfigFromJSON(IC))
+ return PreservedAnalyses::all();
+ writeInstrumentorConfig(IC);
+ if (!Impl.instrument())
+ return PreservedAnalyses::all();
+ assert(!verifyModule(M, &errs()));
+ return PreservedAnalyses::none();
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/alloca.ll b/llvm/test/Instrumentation/Instrumentor/alloca.ll
new file mode 100644
index 0000000000000..1cb90c521ff42
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/alloca.ll
@@ -0,0 +1,62 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instrumentor -S | FileCheck %s
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+define ptr @foo() {
+; CHECK-LABEL: define ptr @foo() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 8, i64 8)
+; CHECK-NEXT: ret ptr [[TMP0]]
+;
+entry:
+ %a = alloca ptr
+ ret ptr %a
+}
+define ptr @bar(i1 %c) {
+; CHECK-LABEL: define ptr @bar(
+; CHECK-SAME: i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[B:%.*]] = alloca i32, i32 5, align 4
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__instrumentor_alloca(ptr [[B]], i64 20, i64 4)
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 4, i64 4)
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], ptr [[TMP1]], ptr [[TMP0]]
+; CHECK-NEXT: ret ptr [[S]]
+;
+entry:
+ %a = alloca i32
+ %b = alloca i32, i32 5
+ %s = select i1 %c, ptr %a, ptr %b
+ ret ptr %s
+}
+define ptr @baz(i32 %v) {
+; CHECK-LABEL: define ptr @baz(
+; CHECK-SAME: i32 [[V:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca ptr, i32 [[V]], align 8
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[V]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = mul i64 8, [[TMP0]]
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 [[TMP2]], i64 8)
+; CHECK-NEXT: ret ptr [[TMP1]]
+;
+entry:
+ %a = alloca ptr, i32 %v
+ ret ptr %a
+}
+define ptr @fizz() {
+; CHECK-LABEL: define ptr @fizz() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca <vscale x 2 x i32>, align 8
+; CHECK-NEXT: [[TMP1:%.*]] = getelementptr <vscale x 2 x i32>, ptr [[A]], i32 1
+; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = call ptr @__instrumentor_alloca(ptr [[A]], i64 [[TMP3]], i64 8)
+; CHECK-NEXT: ret ptr [[TMP4]]
+;
+entry:
+ %a = alloca <vscale x 2 x i32>
+ ret ptr %a
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/default_config.json b/llvm/test/Instrumentation/Instrumentor/default_config.json
new file mode 100644
index 0000000000000..587931254a049
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/default_config.json
@@ -0,0 +1,13 @@
+{
+ "Base": {
+ "RuntimeName": "__instrumentor_",
+ "PrintRuntimeSignatures": true
+ },
+ "Alloca": {
+ "Instrument": true,
+ "Value": true,
+ "AllocationSize": true,
+ "Alignment": true,
+ "ReplaceValue": true
+ }
+}
\ No newline at end of file
diff --git a/llvm/test/Instrumentation/Instrumentor/just_calls_config.json b/llvm/test/Instrumentation/Instrumentor/just_calls_config.json
new file mode 100644
index 0000000000000..4f1608318fc71
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/just_calls_config.json
@@ -0,0 +1,14 @@
+{
+ "Base": {
+ "RuntimeName": "__just_calls_",
+ "PrintRuntimeSignatures": false
+ },
+ "Alloca": {
+ "Instrument": true,
+ "Value": false,
+ "AllocationSize": false,
+ "Alignment": false,
+ "ArraySize": false,
+ "ReplaceValue": false
+ }
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/read_config.ll b/llvm/test/Instrumentation/Instrumentor/read_config.ll
new file mode 100644
index 0000000000000..d5a29afebb4c7
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/read_config.ll
@@ -0,0 +1,22 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instrumentor -instrumentor-read-config-file=%S/just_calls_config.json -S | FileCheck %s
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+define ptr @bar(i1 %c) {
+; CHECK-LABEL: define ptr @bar(
+; CHECK-SAME: i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[B:%.*]] = alloca i32, i32 5, align 4
+; CHECK-NEXT: call void @__just_calls_alloca()
+; CHECK-NEXT: call void @__just_calls_alloca()
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: ret ptr [[S]]
+;
+entry:
+ %a = alloca i32
+ %b = alloca i32, i32 5
+ %s = select i1 %c, ptr %a, ptr %b
+ ret ptr %s
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/write_config.ll b/llvm/test/Instrumentation/Instrumentor/write_config.ll
new file mode 100644
index 0000000000000..0a79ca65fb08e
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/write_config.ll
@@ -0,0 +1,3 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instrumentor -instrumentor-write-config-file=%t.json
+; RUN: diff %t.json %S/default_config.json
>From ac8406e9f880de65ff21a524d154d27c99bb1bae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 31 Jul 2025 15:44:58 +0200
Subject: [PATCH 3/5] [Instrumentor] `getNextNonDebugInstruction()` is
equivalent to `getNextNode()` now
---
llvm/lib/Transforms/Instrumentation/Instrumentor.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp b/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
index 6c9242126bff7..87cd4cf003b19 100644
--- a/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
+++ b/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
@@ -237,9 +237,9 @@ bool InstrumentorImpl::instrument(AllocaInst &I) {
if (IC.Alloca.CB && !IC.Alloca.CB(I))
return false;
- Instruction *IP = I.getNextNonDebugInstruction();
+ Instruction *IP = I.getNextNode();
while (isa<AllocaInst>(IP))
- IP = IP->getNextNonDebugInstruction();
+ IP = IP->getNextNode();
IRB.SetInsertPoint(IP);
SmallVector<Type *> RTArgTypes;
>From d4e77c768e761406bd2406b0411c5af870d8e33c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 31 Jul 2025 17:29:36 +0200
Subject: [PATCH 4/5] [Instrumentor] Wire up in reference plugin and adjust
tests
---
clang/test/Driver/pass-plugin-entrypoints.c | 59 ++++-------
clang/test/Driver/pass-plugin-params.c | 22 ++---
llvm/lib/Passes/PassBuilder.cpp | 1 -
llvm/lib/Passes/PassRegistry.def | 1 -
.../Transforms/Instrumentation/CMakeLists.txt | 1 -
llvm/test/lit.cfg.py | 7 ++
llvm/test/lit.site.cfg.py.in | 1 +
.../plugins-shlib}/alloca.ll | 3 +-
.../plugins-shlib}/default_config.json | 0
.../plugins-shlib}/just_calls_config.json | 0
.../plugins-shlib}/read_config.ll | 2 +-
.../plugins-shlib}/write_config.ll | 2 +-
llvm/tools/plugins-shlib/CMakeLists.txt | 1 +
.../plugins-shlib}/Instrumentor.cpp | 6 +-
.../plugins-shlib}/Instrumentor.h | 8 +-
.../plugins-shlib}/InstrumentorConfig.def | 0
llvm/tools/plugins-shlib/ReferencePlugin.cpp | 98 +++----------------
17 files changed, 59 insertions(+), 153 deletions(-)
rename llvm/test/{Instrumentation/Instrumentor => tools/plugins-shlib}/alloca.ll (94%)
rename llvm/test/{Instrumentation/Instrumentor => tools/plugins-shlib}/default_config.json (100%)
rename llvm/test/{Instrumentation/Instrumentor => tools/plugins-shlib}/just_calls_config.json (100%)
rename llvm/test/{Instrumentation/Instrumentor => tools/plugins-shlib}/read_config.ll (82%)
rename llvm/test/{Instrumentation/Instrumentor => tools/plugins-shlib}/write_config.ll (54%)
rename llvm/{lib/Transforms/Instrumentation => tools/plugins-shlib}/Instrumentor.cpp (98%)
rename llvm/{include/llvm/Transforms/Instrumentation => tools/plugins-shlib}/Instrumentor.h (87%)
rename llvm/{include/llvm/Transforms/Instrumentation => tools/plugins-shlib}/InstrumentorConfig.def (100%)
diff --git a/clang/test/Driver/pass-plugin-entrypoints.c b/clang/test/Driver/pass-plugin-entrypoints.c
index 1af0f0375d628..1eb44ee1e8d4d 100644
--- a/clang/test/Driver/pass-plugin-entrypoints.c
+++ b/clang/test/Driver/pass-plugin-entrypoints.c
@@ -1,66 +1,41 @@
// REQUIRES: pass-plugins
// UNSUPPORTED: system-windows
-// Test default entry-point
+// Default entry-point is Pipeline-EarlySimplification
+//
// RUN: %clang -O0 -fpass-plugin=%pass_plugin_reference \
// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
-// RUN: FileCheck --check-prefix=EP-EARLY-SIMPL %s
+// RUN: FileCheck --check-prefix=EP-EARLY %s
//
// RUN: %clang -O2 -fpass-plugin=%pass_plugin_reference \
// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
-// RUN: FileCheck --check-prefixes=EP-EARLY-SIMPL,LTO-NONE %s
+// RUN: FileCheck --check-prefix=EP-EARLY %s
//
// RUN: %clang -c -flto=full -O2 -fpass-plugin=%pass_plugin_reference \
// RUN: -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
-// RUN: FileCheck --check-prefixes=EP-EARLY-SIMPL,LTO-FULL %s
+// RUN: FileCheck --check-prefix=EP-EARLY %s
//
// RUN: %clang -c -flto=thin -O2 -fpass-plugin=%pass_plugin_reference \
// RUN: -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | \
-// RUN: FileCheck --check-prefixes=EP-EARLY-SIMPL,LTO-THIN %s
-//
-// EP-EARLY-SIMPL: Running pass: TestModulePass
-// EP-EARLY-SIMPL: Entry-point: registerPipelineEarlySimplificationEPCallback
+// RUN: FileCheck --check-prefix=EP-EARLY %s
//
-// LTO-NONE: LTO-phase: None
-// LTO-FULL: LTO-phase: FullLTOPreLink
-// LTO-THIN: LTO-phase: ThinLTOPreLink
+// EP-EARLY: Running pass: InstrumentorPass
+// EP-EARLY: Running pass: AlwaysInlinerPass
// Pass doesn't run if default entry-point is disabled
// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
-// RUN: %clang -fpass-plugin=%pass_plugin_reference -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck %s
-//
-// CHECK-NOT: Running pass: TestModulePass
-
-// Test pipeline-start entry-point
-// RUN: env registerPipelineStartEPCallback=On \
-// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
-// RUN: %clang -fpass-plugin=%pass_plugin_reference -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=EP-START %s
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -S -emit-llvm \
+// RUN: -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck %s
//
-// EP-START: Running pass: TestModulePass
-// EP-START: Entry-point: registerPipelineStartEPCallback
+// CHECK-NOT: Running pass: InstrumentorPass
-// Test optimizer entry-points
+// Pass runs twice if we add entry-point Opt-Early
// RUN: env registerOptimizerEarlyEPCallback=On \
-// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
-// RUN: %clang -fpass-plugin=%pass_plugin_reference -O2 -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=OPT-EARLY %s
+// RUN: %clang -fpass-plugin=%pass_plugin_reference -S -emit-llvm \
+// RUN: -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=OPT-EARLY %s
//
-// OPT-EARLY: Running pass: TestModulePass
-// OPT-EARLY: Entry-point: registerOptimizerEarlyEPCallback
-// OPT-EARLY: Running pass: LowerConstantIntrinsicsPass
-
-#if LLVM_VERSION_MAJOR > 20
-// RUN: env registerOptimizerLastEPCallback=On \
-// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
-// RUN: %clang -fpass-plugin=%pass_plugin_reference -O2 -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=OPT-LAST %s
-//
-// OPT-LAST: Running pass: LowerConstantIntrinsicsPass
-// OPT-LAST: Running pass: TestModulePass on [module]
-// OPT-LAST: Entry-point: registerOptimizerLastEPCallback
-#endif
-
-// TODO: Why is late optimizer entry-point reached in LTO-mode??
-// RUN: env registerOptimizerLastEPCallback=On \
-// RUN: env registerPipelineEarlySimplificationEPCallback=Off \
-// RUN: %clang -fpass-plugin=%pass_plugin_reference -O2 -flto -c -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1
+// OPT-EARLY: Running pass: InstrumentorPass
+// OPT-EARLY: Running pass: AlwaysInlinerPass
+// OPT-EARLY: Running pass: InstrumentorPass
int main() { return 0; }
diff --git a/clang/test/Driver/pass-plugin-params.c b/clang/test/Driver/pass-plugin-params.c
index 328f315e8cec7..03661f3dc7233 100644
--- a/clang/test/Driver/pass-plugin-params.c
+++ b/clang/test/Driver/pass-plugin-params.c
@@ -1,23 +1,13 @@
// REQUIRES: pass-plugins
// UNSUPPORTED: system-windows
-// RUN: %clang -fpass-plugin=%pass_plugin_reference \
-// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-FALSE %s
+// FIXME: This is supposed to work, but right now it doesn't. We need the extra -load like below.
+// RUN: not %clang -fsyntax-only -fpass-plugin=%pass_plugin_reference \
+// RUN: -mllvm -instrumentor-write-config-file=%t_cfg.json %s 2>&1 | FileCheck %s
//
-// TODO: Can we make this work? (i.e. without the extra -mllvm from below)
-// RUN: not %clang -fpass-plugin=%pass_plugin_reference -wave-goodbye \
-// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-ERR-CLANG %s
+// RUN: %clang -fsyntax-only -fpass-plugin=%pass_plugin_reference -Xclang -load -Xclang %pass_plugin_reference \
+// RUN: -mllvm -instrumentor-write-config-file=%t_cfg.json %s
//
-// FIXME: This is supposed to work (i.e. without the extra -load from below)
-// RUN: not %clang -fpass-plugin=%pass_plugin_reference -mllvm -wave-goodbye \
-// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-ERR-LLVM %s
-//
-// RUN: %clang -fpass-plugin=%pass_plugin_reference -Xclang -load -Xclang %pass_plugin_reference -mllvm -wave-goodbye \
-// RUN: -S -emit-llvm -Xclang -fdebug-pass-manager %s -o /dev/null 2>&1 | FileCheck --check-prefix=PARAM-TRUE %s
-//
-// PARAM-ERR-CLANG: error: unknown argument
-// PARAM-ERR-LLVM: Unknown command line argument
-// PARAM-TRUE: Plugin parameter value -wave-goodbye=true
-// PARAM-FALSE: Plugin parameter value -wave-goodbye=false
+// CHECK: Unknown command line argument
int main() { return 0; }
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 0276798f3b662..f810368a84940 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -244,7 +244,6 @@
#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
-#include "llvm/Transforms/Instrumentation/Instrumentor.h"
#include "llvm/Transforms/Instrumentation/KCFI.h"
#include "llvm/Transforms/Instrumentation/LowerAllowCheckPass.h"
#include "llvm/Transforms/Instrumentation/MemProfInstrumentation.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index b14a347c66241..1b111dc20d35c 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -100,7 +100,6 @@ MODULE_PASS("instrprof", InstrProfilingLoweringPass())
MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass())
MODULE_PASS("print<ctx-prof-analysis>", CtxProfAnalysisPrinterPass(errs()))
MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass())
-MODULE_PASS("instrumentor", InstrumentorPass())
MODULE_PASS("iroutliner", IROutlinerPass())
MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass())
MODULE_PASS("lower-emutls", LowerEmuTLSPass())
diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
index 6f8dad9799489..15fd421a41b0f 100644
--- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -12,7 +12,6 @@ add_llvm_component_library(LLVMInstrumentation
NumericalStabilitySanitizer.cpp
IndirectCallPromotion.cpp
InstrProfiling.cpp
- Instrumentor.cpp
KCFI.cpp
LowerAllowCheckPass.cpp
PGOCtxProfFlattening.cpp
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 143cc3817bd08..2e2dd3c4ae6a8 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -700,3 +700,10 @@ def host_unwind_supports_jit():
if config.has_logf128:
config.available_features.add("has_logf128")
+
+# Run tests with opt if the reference pass-plugin exists
+plugin_name = f"{config.llvm_plugin_prefix}ReferencePlugin{config.llvm_plugin_ext}"
+plugin_shlib = os.path.join(config.llvm_shlib_dir, plugin_name)
+if os.path.exists(plugin_shlib):
+ config.available_features.add("pass-plugins")
+ config.substitutions.append(("%pass_plugin_reference", plugin_shlib))
diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in
index 893e2cbd4f62b..0394044b0b580 100644
--- a/llvm/test/lit.site.cfg.py.in
+++ b/llvm/test/lit.site.cfg.py.in
@@ -11,6 +11,7 @@ config.llvm_lib_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.llvm_plugin_prefix = "@CMAKE_SHARED_LIBRARY_PREFIX@"
config.llvm_exe_ext = "@EXEEXT@"
config.lit_tools_dir = path(r"@LLVM_LIT_TOOLS_DIR@")
config.errc_messages = "@LLVM_LIT_ERRC_MESSAGES@"
diff --git a/llvm/test/Instrumentation/Instrumentor/alloca.ll b/llvm/test/tools/plugins-shlib/alloca.ll
similarity index 94%
rename from llvm/test/Instrumentation/Instrumentor/alloca.ll
rename to llvm/test/tools/plugins-shlib/alloca.ll
index 1cb90c521ff42..81297b4343380 100644
--- a/llvm/test/Instrumentation/Instrumentor/alloca.ll
+++ b/llvm/test/tools/plugins-shlib/alloca.ll
@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
-; RUN: opt < %s -passes=instrumentor -S | FileCheck %s
+; RUN: opt < %s -load-pass-plugin=%pass_plugin_reference -passes=instrumentor -S | FileCheck %s
+; REQUIRES: pass-plugins
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/llvm/test/Instrumentation/Instrumentor/default_config.json b/llvm/test/tools/plugins-shlib/default_config.json
similarity index 100%
rename from llvm/test/Instrumentation/Instrumentor/default_config.json
rename to llvm/test/tools/plugins-shlib/default_config.json
diff --git a/llvm/test/Instrumentation/Instrumentor/just_calls_config.json b/llvm/test/tools/plugins-shlib/just_calls_config.json
similarity index 100%
rename from llvm/test/Instrumentation/Instrumentor/just_calls_config.json
rename to llvm/test/tools/plugins-shlib/just_calls_config.json
diff --git a/llvm/test/Instrumentation/Instrumentor/read_config.ll b/llvm/test/tools/plugins-shlib/read_config.ll
similarity index 82%
rename from llvm/test/Instrumentation/Instrumentor/read_config.ll
rename to llvm/test/tools/plugins-shlib/read_config.ll
index d5a29afebb4c7..b320cc85d739b 100644
--- a/llvm/test/Instrumentation/Instrumentor/read_config.ll
+++ b/llvm/test/tools/plugins-shlib/read_config.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
-; RUN: opt < %s -passes=instrumentor -instrumentor-read-config-file=%S/just_calls_config.json -S | FileCheck %s
+; RUN: opt < %s -load-pass-plugin=%pass_plugin_reference -passes=instrumentor -instrumentor-read-config-file=%S/just_calls_config.json -S | FileCheck %s
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/llvm/test/Instrumentation/Instrumentor/write_config.ll b/llvm/test/tools/plugins-shlib/write_config.ll
similarity index 54%
rename from llvm/test/Instrumentation/Instrumentor/write_config.ll
rename to llvm/test/tools/plugins-shlib/write_config.ll
index 0a79ca65fb08e..459344876ce60 100644
--- a/llvm/test/Instrumentation/Instrumentor/write_config.ll
+++ b/llvm/test/tools/plugins-shlib/write_config.ll
@@ -1,3 +1,3 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
-; RUN: opt < %s -passes=instrumentor -instrumentor-write-config-file=%t.json
+; RUN: opt < %s -load-pass-plugin=%pass_plugin_reference -passes=instrumentor -instrumentor-write-config-file=%t.json
; RUN: diff %t.json %S/default_config.json
diff --git a/llvm/tools/plugins-shlib/CMakeLists.txt b/llvm/tools/plugins-shlib/CMakeLists.txt
index f0d5a86383772..76e32046c8f5e 100644
--- a/llvm/tools/plugins-shlib/CMakeLists.txt
+++ b/llvm/tools/plugins-shlib/CMakeLists.txt
@@ -1,4 +1,5 @@
add_llvm_library(ReferencePlugin SHARED INSTALL_WITH_TOOLCHAIN
+ Instrumentor.cpp
ReferencePlugin.cpp
DEPENDS
diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp b/llvm/tools/plugins-shlib/Instrumentor.cpp
similarity index 98%
rename from llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
rename to llvm/tools/plugins-shlib/Instrumentor.cpp
index 87cd4cf003b19..dac6c601f3e6e 100644
--- a/llvm/lib/Transforms/Instrumentation/Instrumentor.cpp
+++ b/llvm/tools/plugins-shlib/Instrumentor.cpp
@@ -8,7 +8,7 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/Transforms/Instrumentation/Instrumentor.h"
+#include "Instrumentor.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallVector.h"
@@ -77,7 +77,7 @@ void writeInstrumentorConfig(InstrumentorConfig &IC) {
J.objectEnd(); \
J.attributeEnd();
-#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+#include "InstrumentorConfig.def"
J.objectEnd();
}
@@ -128,7 +128,7 @@ bool readInstrumentorConfigFromJSON(InstrumentorConfig &IC) {
#define CONFIG_INTERNAL(SECTION, TYPE, NAME, DEFAULT_VALUE)
#define SECTION_END(SECTION)
-#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+#include "InstrumentorConfig.def"
return true;
}
diff --git a/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h b/llvm/tools/plugins-shlib/Instrumentor.h
similarity index 87%
rename from llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
rename to llvm/tools/plugins-shlib/Instrumentor.h
index 0da4562be6a7a..bb017cc96d554 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/Instrumentor.h
+++ b/llvm/tools/plugins-shlib/Instrumentor.h
@@ -10,8 +10,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
-#define LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
+#ifndef LLVM_TOOLS_PLUGINS_SHLIB_INSTRUMENTOR_H
+#define LLVM_TOOLS_PLUGINS_SHLIB_INSTRUMENTOR_H
#include "llvm/IR/Instruction.h"
#include "llvm/IR/PassManager.h"
@@ -40,7 +40,7 @@ struct InstrumentorConfig {
} \
SECTION;
-#include "llvm/Transforms/Instrumentation/InstrumentorConfig.def"
+#include "InstrumentorConfig.def"
};
class InstrumentorPass : public PassInfoMixin<InstrumentorPass> {
@@ -53,4 +53,4 @@ class InstrumentorPass : public PassInfoMixin<InstrumentorPass> {
};
} // end namespace llvm
-#endif // LLVM_TRANSFORMS_INSTRUMENTATION_INSTRUMENTOR_H
+#endif // LLVM_TOOLS_PLUGINS_SHLIB_INSTRUMENTOR_H
diff --git a/llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def b/llvm/tools/plugins-shlib/InstrumentorConfig.def
similarity index 100%
rename from llvm/include/llvm/Transforms/Instrumentation/InstrumentorConfig.def
rename to llvm/tools/plugins-shlib/InstrumentorConfig.def
diff --git a/llvm/tools/plugins-shlib/ReferencePlugin.cpp b/llvm/tools/plugins-shlib/ReferencePlugin.cpp
index aa9164449375f..630e983870a75 100644
--- a/llvm/tools/plugins-shlib/ReferencePlugin.cpp
+++ b/llvm/tools/plugins-shlib/ReferencePlugin.cpp
@@ -1,4 +1,4 @@
-//===- lib/plugins-shlib/ReferencePlugin.cpp ------------------------------===//
+//===- tools/plugins-shlib/ReferencePlugin.cpp ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,6 +6,8 @@
//
//===----------------------------------------------------------------------===//
+#include "Instrumentor.h"
+
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
@@ -16,9 +18,6 @@
using namespace llvm;
-static cl::opt<bool> Wave("wave-goodbye", cl::init(false),
- cl::desc("wave good bye"));
-
static std::optional<std::string> getEnv(const std::string &Var) {
const char *Val = std::getenv(Var.c_str());
if (!Val)
@@ -39,107 +38,42 @@ static bool getEnvBool(const std::string &VarName, bool Default = false) {
return Default;
}
-static std::string getLTOPhaseStr(ThinOrFullLTOPhase P) {
- switch (P) {
- case ThinOrFullLTOPhase::None: return "None";
- case ThinOrFullLTOPhase::ThinLTOPreLink: return "ThinLTOPreLink";
- case ThinOrFullLTOPhase::ThinLTOPostLink: return "ThinLTOPostLink";
- case ThinOrFullLTOPhase::FullLTOPreLink: return "FullLTOPreLink";
- case ThinOrFullLTOPhase::FullLTOPostLink: return "FullLTOPostLink";
- }
-}
-
-struct TestModulePass : public PassInfoMixin<TestModulePass> {
- std::string EP;
- std::string LTOPhase;
- TestModulePass(StringRef EntryPoint, ThinOrFullLTOPhase Phase = ThinOrFullLTOPhase::None) : EP(EntryPoint.str()), LTOPhase(getLTOPhaseStr(Phase)) {}
- PreservedAnalyses run(Module &, ModuleAnalysisManager &) {
- fprintf(stderr, "Entry-point: %s\n", EP.c_str());
- fprintf(stderr, "LTO-phase: %s\n", LTOPhase.c_str());
- return PreservedAnalyses::all();
- }
-};
-
-struct TestFunctionPass : public PassInfoMixin<TestFunctionPass> {
- std::string EP;
- TestFunctionPass(StringRef EntryPoint) : EP(EntryPoint.str()) {}
- PreservedAnalyses run(Function &, FunctionAnalysisManager &) {
- fprintf(stderr, "Entry-point: %s\n", EP.c_str());
- return PreservedAnalyses::all();
- }
-};
-
static void registerCallbacks(PassBuilder &PB) {
- printf("Plugin parameter value -wave-goodbye=%s\n", Wave ? "true" : "false");
-
// Entry-points for module passes
if (getEnvBool("registerPipelineStartEPCallback"))
PB.registerPipelineStartEPCallback(
[](ModulePassManager &MPM, OptimizationLevel Opt) {
- MPM.addPass(TestModulePass("registerPipelineStartEPCallback"));
+ MPM.addPass(InstrumentorPass());
return true;
});
if (getEnvBool("registerPipelineEarlySimplificationEPCallback", true))
PB.registerPipelineEarlySimplificationEPCallback(
[](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
- MPM.addPass(TestModulePass("registerPipelineEarlySimplificationEPCallback", Phase));
+ MPM.addPass(InstrumentorPass());
return true;
});
if (getEnvBool("registerOptimizerEarlyEPCallback"))
PB.registerOptimizerEarlyEPCallback(
[](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
- MPM.addPass(TestModulePass("registerOptimizerEarlyEPCallback", Phase));
+ MPM.addPass(InstrumentorPass());
return true;
});
if (getEnvBool("registerOptimizerLastEPCallback"))
PB.registerOptimizerLastEPCallback(
[](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
- MPM.addPass(TestModulePass("registerOptimizerLastEPCallback", Phase));
- return true;
- });
- if (getEnvBool("registerFullLinkTimeOptimizationEarlyEPCallback"))
- PB.registerFullLinkTimeOptimizationEarlyEPCallback(
- [](ModulePassManager &MPM, OptimizationLevel Opt) {
- MPM.addPass(TestModulePass("registerFullLinkTimeOptimizationEarlyEPCallback"));
+ MPM.addPass(InstrumentorPass());
return true;
});
- if (getEnvBool("registerFullLinkTimeOptimizationLastEPCallback"))
- PB.registerFullLinkTimeOptimizationLastEPCallback(
- [](ModulePassManager &MPM, OptimizationLevel Opt) {
- MPM.addPass(TestModulePass("registerFullLinkTimeOptimizationLastEPCallback"));
- return true;
- });
-
- // Entry-points for function passes
- if (getEnvBool("registerPeepholeEPCallback"))
- PB.registerPeepholeEPCallback(
- [](FunctionPassManager &FPM, OptimizationLevel Opt) {
- FPM.addPass(TestFunctionPass("registerPeepholeEPCallback"));
- return true;
- });
- if (getEnvBool("registerScalarOptimizerLateEPCallback"))
- PB.registerScalarOptimizerLateEPCallback(
- [](FunctionPassManager &FPM, OptimizationLevel Opt) {
- FPM.addPass(TestFunctionPass("registerScalarOptimizerLateEPCallback"));
- return true;
+ if (getEnvBool("registerPipelineParsingCallback"), true)
+ PB.registerPipelineParsingCallback(
+ [](StringRef Name, ModulePassManager &PM,
+ ArrayRef<llvm::PassBuilder::PipelineElement>) {
+ if (Name == "instrumentor") {
+ PM.addPass(InstrumentorPass());
+ return true;
+ }
+ return false;
});
- if (getEnvBool("registerVectorizerStartEPCallback"))
- PB.registerVectorizerStartEPCallback(
- [](FunctionPassManager &FPM, OptimizationLevel Opt) {
- FPM.addPass(TestFunctionPass("registerVectorizerStartEPCallback"));
- return true;
- });
-
-#if LLVM_VERSION_MAJOR > 20
- if (getEnvBool("registerVectorizerEndEPCallback"))
- PB.registerVectorizerEndEPCallback(
- [](FunctionPassManager &FPM, OptimizationLevel Opt) {
- FPM.addPass(TestFunctionPass("registerVectorizerEndEPCallback"));
- return true;
- });
-#endif
-
- // TODO: registerLateLoopOptimizationsEPCallback, registerCGSCCOptimizerLateEPCallback
}
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
>From 570f109c162a9ec5e14c3b6d70488513ba893b8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 31 Jul 2025 18:19:11 +0200
Subject: [PATCH 5/5] clang-format
---
llvm/tools/plugins-shlib/ReferencePlugin.cpp | 25 +++++++++++---------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/llvm/tools/plugins-shlib/ReferencePlugin.cpp b/llvm/tools/plugins-shlib/ReferencePlugin.cpp
index 630e983870a75..2dad9f61c6590 100644
--- a/llvm/tools/plugins-shlib/ReferencePlugin.cpp
+++ b/llvm/tools/plugins-shlib/ReferencePlugin.cpp
@@ -48,22 +48,25 @@ static void registerCallbacks(PassBuilder &PB) {
});
if (getEnvBool("registerPipelineEarlySimplificationEPCallback", true))
PB.registerPipelineEarlySimplificationEPCallback(
- [](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
+ [](ModulePassManager &MPM, OptimizationLevel Opt,
+ ThinOrFullLTOPhase Phase) {
MPM.addPass(InstrumentorPass());
return true;
});
if (getEnvBool("registerOptimizerEarlyEPCallback"))
- PB.registerOptimizerEarlyEPCallback(
- [](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
- MPM.addPass(InstrumentorPass());
- return true;
- });
+ PB.registerOptimizerEarlyEPCallback([](ModulePassManager &MPM,
+ OptimizationLevel Opt,
+ ThinOrFullLTOPhase Phase) {
+ MPM.addPass(InstrumentorPass());
+ return true;
+ });
if (getEnvBool("registerOptimizerLastEPCallback"))
- PB.registerOptimizerLastEPCallback(
- [](ModulePassManager &MPM, OptimizationLevel Opt, ThinOrFullLTOPhase Phase) {
- MPM.addPass(InstrumentorPass());
- return true;
- });
+ PB.registerOptimizerLastEPCallback([](ModulePassManager &MPM,
+ OptimizationLevel Opt,
+ ThinOrFullLTOPhase Phase) {
+ MPM.addPass(InstrumentorPass());
+ return true;
+ });
if (getEnvBool("registerPipelineParsingCallback"), true)
PB.registerPipelineParsingCallback(
[](StringRef Name, ModulePassManager &PM,
More information about the llvm-commits
mailing list