[llvm] [CodeGen] Expose the extensibility of PassConfig to plugins (PR #139059)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 2 10:03:31 PDT 2025
https://github.com/Tcc100 updated https://github.com/llvm/llvm-project/pull/139059
>From d5e6be9b911cdcf570c2d7b672611daa18cfc4fc Mon Sep 17 00:00:00 2001
From: T <T>
Date: Wed, 2 Jul 2025 19:02:33 +0200
Subject: [PATCH] [CodeGen] Expose the extensibility of PassConfig to plugins
---
llvm/docs/WritingAnLLVMPass.rst | 21 ++++
llvm/include/llvm/Target/TargetMachine.h | 34 ++++++
llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp | 1 +
llvm/lib/Target/TargetMachine.cpp | 4 +
llvm/test/Other/codegen-plugin-loading.ll | 7 ++
.../CodeGen/CGPluginTest/CMakeLists.txt | 18 +++
.../CGPluginTest/Plugin/CMakeLists.txt | 17 +++
.../CGPluginTest/Plugin/CodeGenTestPass.cpp | 34 ++++++
.../CGPluginTest/Plugin/CodeGenTestPass.h | 33 ++++++
.../CodeGen/CGPluginTest/Plugin/Plugin.cpp | 23 ++++
.../CodeGen/CGPluginTest/PluginTest.cpp | 106 ++++++++++++++++++
llvm/unittests/CodeGen/CMakeLists.txt | 1 +
12 files changed, 299 insertions(+)
create mode 100644 llvm/test/Other/codegen-plugin-loading.ll
create mode 100644 llvm/unittests/CodeGen/CGPluginTest/CMakeLists.txt
create mode 100644 llvm/unittests/CodeGen/CGPluginTest/Plugin/CMakeLists.txt
create mode 100644 llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.cpp
create mode 100644 llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.h
create mode 100644 llvm/unittests/CodeGen/CGPluginTest/Plugin/Plugin.cpp
create mode 100644 llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp
diff --git a/llvm/docs/WritingAnLLVMPass.rst b/llvm/docs/WritingAnLLVMPass.rst
index 484227bac38b5..50e111a5dc355 100644
--- a/llvm/docs/WritingAnLLVMPass.rst
+++ b/llvm/docs/WritingAnLLVMPass.rst
@@ -442,6 +442,27 @@ in certain circumstances (such as calling the ``Pass::dump()`` from a
debugger), so it should only be used to enhance debug output, it should not be
depended on.
+Scheduling a MachineFunctionPass
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Backends create a ``TargetPassConfig`` and use ``addPass`` to schedule
+``MachineFunctionPass``\ es. External plugins can register a callback to modify
+and insert additional passes:
+
+.. code-block:: c++
+
+ RegisterTargetPassConfigCallback X{[](auto &TM, auto &PM, auto *TPC) {
+ TPC->insertPass(/* ... */);
+ TPC->substitutePass(/* ... */);
+ }};
+
+Note that passes still have to be registered:
+
+.. code-block:: c++
+ __attribute__((constructor)) static void initCodeGenPlugin() {
+ initializeExamplePass(*PassRegistry::getPassRegistry());
+ }
+
.. _writing-an-llvm-pass-interaction:
Specifying interactions between passes
diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h
index 7a37eb7391b31..367df346b4c0b 100644
--- a/llvm/include/llvm/Target/TargetMachine.h
+++ b/llvm/include/llvm/Target/TargetMachine.h
@@ -25,6 +25,7 @@
#include "llvm/Target/CGPassBuilderOption.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/TargetParser/Triple.h"
+#include <functional>
#include <optional>
#include <string>
#include <utility>
@@ -531,6 +532,39 @@ class LLVM_ABI TargetMachine {
}
};
+using PassConfigCallback =
+ std::function<void(TargetMachine &, PassManagerBase &, TargetPassConfig *)>;
+
+class TargetPassConfigCallbackRegistry : public PassConfigCallback {
+protected:
+ static SmallVector<PassConfigCallback *, 1> Callbacks;
+
+ explicit TargetPassConfigCallbackRegistry(PassConfigCallback &&C)
+ : PassConfigCallback(std::move(C)) {}
+
+public:
+ static void invokeCallbacks(TargetMachine &TM, PassManagerBase &PM,
+ TargetPassConfig *PassConfig) {
+ for (const PassConfigCallback *C : Callbacks)
+ (*C)(TM, PM, PassConfig);
+ }
+};
+
+class RegisterTargetPassConfigCallback
+ : public TargetPassConfigCallbackRegistry {
+public:
+ explicit RegisterTargetPassConfigCallback(PassConfigCallback &&C)
+ : TargetPassConfigCallbackRegistry(std::move(C)) {
+ Callbacks.push_back(this);
+ }
+
+ ~RegisterTargetPassConfigCallback() {
+ const auto &It = find(Callbacks, this);
+ if (It != Callbacks.end())
+ Callbacks.erase(It);
+ }
+};
+
} // end namespace llvm
#endif // LLVM_TARGET_TARGETMACHINE_H
diff --git a/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp b/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp
index 125ffd597bf23..4132f3e5cf4dc 100644
--- a/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp
+++ b/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp
@@ -118,6 +118,7 @@ addPassesToGenerateCode(CodeGenTargetMachineImpl &TM, PassManagerBase &PM,
PassConfig->setDisableVerify(DisableVerify);
PM.add(PassConfig);
PM.add(&MMIWP);
+ TargetPassConfigCallbackRegistry::invokeCallbacks(TM, PM, PassConfig);
if (PassConfig->addISelPasses())
return nullptr;
diff --git a/llvm/lib/Target/TargetMachine.cpp b/llvm/lib/Target/TargetMachine.cpp
index 69b6e26e602f6..41e26deda9383 100644
--- a/llvm/lib/Target/TargetMachine.cpp
+++ b/llvm/lib/Target/TargetMachine.cpp
@@ -332,3 +332,7 @@ std::pair<int, int> TargetMachine::parseBinutilsVersion(StringRef Version) {
Version.consumeInteger(10, Ret.second);
return Ret;
}
+
+// TargetPassConfig callbacks
+SmallVector<PassConfigCallback *, 1>
+ TargetPassConfigCallbackRegistry::Callbacks{};
diff --git a/llvm/test/Other/codegen-plugin-loading.ll b/llvm/test/Other/codegen-plugin-loading.ll
new file mode 100644
index 0000000000000..652bbcdef36c3
--- /dev/null
+++ b/llvm/test/Other/codegen-plugin-loading.ll
@@ -0,0 +1,7 @@
+; RUN: llc -load %llvmshlibdir/../unittests/CodeGen/CGPluginTest/CGTestPlugin%pluginext %s -o - | FileCheck %s
+; REQUIRES: native, system-linux, llvm-dylib
+
+; CHECK: CodeGen Test Pass running on main
+define void @main() {
+ ret void
+}
diff --git a/llvm/unittests/CodeGen/CGPluginTest/CMakeLists.txt b/llvm/unittests/CodeGen/CGPluginTest/CMakeLists.txt
new file mode 100644
index 0000000000000..51ef85ab279cd
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ Support
+ Core
+ Target
+ CodeGen
+ )
+
+add_llvm_unittest(CGPluginTest
+ PluginTest.cpp
+ Plugin/CodeGenTestPass.cpp
+
+ EXPORT_SYMBOLS
+ )
+target_link_libraries(CGPluginTest PRIVATE LLVMTestingSupport)
+
+unset(LLVM_LINK_COMPONENTS)
+add_subdirectory(Plugin)
diff --git a/llvm/unittests/CodeGen/CGPluginTest/Plugin/CMakeLists.txt b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CMakeLists.txt
new file mode 100644
index 0000000000000..04f7738507b0d
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CMakeLists.txt
@@ -0,0 +1,17 @@
+if (NOT WIN32 AND NOT CYGWIN)
+ unset(LLVM_LINK_COMPONENTS)
+ add_llvm_library(CGTestPlugin MODULE BUILDTREE_ONLY
+ CodeGenTestPass.cpp
+ Plugin.cpp
+ )
+
+ # Put PLUGIN next to the unit test executable.
+ set_output_directory(CGTestPlugin
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../
+ LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../
+ )
+ set_target_properties(CGTestPlugin PROPERTIES FOLDER "Tests")
+
+ add_dependencies(CGTestPlugin intrinsics_gen vt_gen)
+ add_dependencies(CGPluginTest CGTestPlugin)
+endif ()
diff --git a/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.cpp b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.cpp
new file mode 100644
index 0000000000000..990d23258891f
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "CodeGenTestPass.h"
+
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "codegen-test"
+#define CODEGEN_TEST_NAME "CodeGen Test Pass"
+
+CodeGenTest::CodeGenTest() : MachineFunctionPass(ID) {}
+
+bool CodeGenTest::runOnMachineFunction(MachineFunction &MF) {
+ outs() << CODEGEN_TEST_NAME << " running on " << MF.getName()
+ << "\n"; // used for the lit test
+ if (RunCallback)
+ RunCallback();
+ return true;
+}
+
+StringRef CodeGenTest::getPassName() const { return CODEGEN_TEST_NAME; }
+
+char CodeGenTest::ID = 0;
+std::function<void()> CodeGenTest::RunCallback;
+
+INITIALIZE_PASS(CodeGenTest, DEBUG_TYPE, CODEGEN_TEST_NAME, false, false)
diff --git a/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.h b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.h
new file mode 100644
index 0000000000000..8eaf6d5339657
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CODEGEN_TEST_PASS
+#define CODEGEN_TEST_PASS
+
+#include <llvm/CodeGen/MachineFunctionPass.h>
+
+using namespace llvm;
+
+namespace llvm {
+void initializeCodeGenTestPass(PassRegistry &);
+} // namespace llvm
+
+class CodeGenTest : public MachineFunctionPass {
+public:
+ static char ID;
+
+ CodeGenTest();
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ StringRef getPassName() const override;
+
+ static std::function<void()> RunCallback;
+};
+
+#endif // CODEGEN_TEST_PASS
diff --git a/llvm/unittests/CodeGen/CGPluginTest/Plugin/Plugin.cpp b/llvm/unittests/CodeGen/CGPluginTest/Plugin/Plugin.cpp
new file mode 100644
index 0000000000000..49cb108669982
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/Plugin.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "CodeGenTestPass.h"
+
+#include <llvm/CodeGen/Passes.h>
+#include <llvm/CodeGen/TargetPassConfig.h>
+#include <llvm/Target/TargetMachine.h>
+
+using namespace llvm;
+
+RegisterTargetPassConfigCallback X{[](auto &TM, auto &PM, auto *TPC) {
+ TPC->insertPass(&GCLoweringID, &CodeGenTest::ID);
+}};
+
+__attribute__((constructor)) static void initCodeGenPlugin() {
+ initializeCodeGenTestPass(*PassRegistry::getPassRegistry());
+}
diff --git a/llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp b/llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp
new file mode 100644
index 0000000000000..c7f3041149923
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "Plugin/CodeGenTestPass.h"
+
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/Config/config.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+void anchor() {}
+
+std::optional<std::string> libPath(const std::string &Name) {
+ const auto &Argvs = testing::internal::GetArgvs();
+ if (Argvs.size() <= 0)
+ return std::nullopt;
+ void *Ptr = (void *)(intptr_t)anchor;
+ std::string Path = sys::fs::getMainExecutable(Argvs[0].c_str(), Ptr);
+ SmallString<256> Buf{sys::path::parent_path(Path)};
+ sys::path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str());
+ return std::string(Buf.str());
+}
+} // namespace
+
+namespace llvm {
+class CGPluginTests : public testing::Test {
+protected:
+ static void SetUpTestCase() {
+ InitializeAllTargetMCs();
+ InitializeAllTargetInfos();
+ InitializeAllTargets();
+ }
+};
+} // namespace llvm
+
+TEST_F(CGPluginTests, LoadPlugin) {
+#if !defined(LLVM_ENABLE_PLUGINS)
+ // Skip the test if plugins are disabled.
+ GTEST_SKIP();
+#endif
+
+ auto PluginPath = libPath("CGTestPlugin");
+ ASSERT_TRUE(PluginPath.has_value());
+ ASSERT_NE("", PluginPath);
+
+ std::string Error;
+ auto Library = sys::DynamicLibrary::getLibrary(PluginPath->c_str(), &Error);
+ ASSERT_TRUE(Library.isValid()) << Error;
+ sys::DynamicLibrary::closeLibrary(Library);
+}
+
+TEST_F(CGPluginTests, ExecuteCallback) {
+#if !defined(LLVM_ENABLE_PLUGINS)
+ // Skip the test if plugins are disabled.
+ GTEST_SKIP();
+#endif
+
+ volatile bool CallbackExecuted = false;
+ volatile bool MPassExecuted = false;
+
+ RegisterTargetPassConfigCallback reg{[&](auto &TM, auto &PM, auto *TPC) {
+ CallbackExecuted = true;
+ TPC->insertPass(&GCLoweringID, &CodeGenTest::ID);
+ }};
+
+ CodeGenTest::RunCallback = [&] { MPassExecuted = true; };
+
+ TargetOptions Options;
+ std::unique_ptr<MCContext> MCC;
+ for (auto T : TargetRegistry::targets()) {
+ if (!T.hasTargetMachine())
+ continue;
+ Triple TT{T.getName(), "", ""};
+ auto *TM = T.createTargetMachine(TT, "", "", Options, std::nullopt,
+ std::nullopt, CodeGenOptLevel::Default);
+ ASSERT_TRUE(TM);
+
+ legacy::PassManager PM;
+ MCC.reset(new MCContext(TT, TM->getMCAsmInfo(), TM->getMCRegisterInfo(),
+ TM->getMCSubtargetInfo()));
+ auto *PtrMCC = MCC.get();
+ CallbackExecuted = false;
+ MPassExecuted = false;
+ if (TM->addPassesToEmitMC(PM, PtrMCC, outs()) == true)
+ continue;
+ ASSERT_TRUE(CallbackExecuted) << T.getName() << " callback failed";
+ ASSERT_TRUE(MPassExecuted) << T.getName() << " MachinePass failed";
+ }
+}
diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt
index 8b025219c46cf..d19b122676c9b 100644
--- a/llvm/unittests/CodeGen/CMakeLists.txt
+++ b/llvm/unittests/CodeGen/CMakeLists.txt
@@ -51,5 +51,6 @@ add_llvm_unittest(CodeGenTests
)
add_subdirectory(GlobalISel)
+add_subdirectory(CGPluginTest)
target_link_libraries(CodeGenTests PRIVATE LLVMTestingSupport)
More information about the llvm-commits
mailing list