[llvm] [CodeGen] Expose the extensibility of PassConfig to plugins (PR #139059)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 6 09:54:45 PDT 2025


https://github.com/Tcc100 updated https://github.com/llvm/llvm-project/pull/139059

>From 088cdd3f03734d1b08e2e8cd3db72a67c7622e94 Mon Sep 17 00:00:00 2001
From: T <T>
Date: Fri, 6 Jun 2025 18:51:13 +0200
Subject: [PATCH] [CodeGen] Expose the extensibility of PassConfig to plugins

---
 llvm/docs/WritingAnLLVMPass.rst               |  17 +++
 llvm/include/llvm/Target/TargetMachine.h      |  14 +++
 llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp |   3 +
 llvm/lib/Target/TargetMachine.cpp             |   3 +
 .../CodeGen/CGPluginTest/CMakeLists.txt       |  18 +++
 .../CGPluginTest/Plugin/CMakeLists.txt        |  16 +++
 .../CGPluginTest/Plugin/CodeGenTestPass.cpp   |  32 +++++
 .../CGPluginTest/Plugin/CodeGenTestPass.h     |  33 +++++
 .../CodeGen/CGPluginTest/Plugin/Plugin.cpp    |  24 ++++
 .../CodeGen/CGPluginTest/PluginTest.cpp       | 114 ++++++++++++++++++
 llvm/unittests/CodeGen/CMakeLists.txt         |   1 +
 11 files changed, 275 insertions(+)
 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..770f5f6acd115 100644
--- a/llvm/docs/WritingAnLLVMPass.rst
+++ b/llvm/docs/WritingAnLLVMPass.rst
@@ -442,6 +442,23 @@ 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++
+
+  TargetMachine::registerTargetPassConfigCallback(
+    [](TargetMachine &TM, PassManager &PM, TargetPassConfig *TPC) {
+      TPC->insertPass(/* ... */);
+      TPC->substitutePass(/* ... */);
+    }
+  );
+
+
 .. _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 906926729ed74..bcc1ce29b8282 100644
--- a/llvm/include/llvm/Target/TargetMachine.h
+++ b/llvm/include/llvm/Target/TargetMachine.h
@@ -20,10 +20,12 @@
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/PGOOptions.h"
 #include "llvm/Target/CGPassBuilderOption.h"
 #include "llvm/Target/TargetOptions.h"
 #include "llvm/TargetParser/Triple.h"
+#include <functional>
 #include <optional>
 #include <string>
 #include <utility>
@@ -72,6 +74,10 @@ namespace yaml {
 struct MachineFunctionInfo;
 } // namespace yaml
 
+class TargetMachine;
+using PassConfigCallback =
+    std::function<void(TargetMachine &, PassManagerBase &, TargetPassConfig *)>;
+
 //===----------------------------------------------------------------------===//
 ///
 /// Primary interface to the complete machine description for the target
@@ -119,6 +125,9 @@ class TargetMachine {
   std::optional<PGOOptions> PGOOption;
 
 public:
+  static ManagedStatic<SmallVector<PassConfigCallback, 1>>
+      TargetPassConfigCallbacks;
+
   mutable TargetOptions Options;
 
   TargetMachine(const TargetMachine &) = delete;
@@ -518,6 +527,11 @@ class TargetMachine {
 
   // MachineRegisterInfo callback function
   virtual void registerMachineRegisterInfoCallback(MachineFunction &MF) const {}
+
+  // TargetPassConfig callback function
+  static void registerTargetPassConfigCallback(const PassConfigCallback &C) {
+    TargetPassConfigCallbacks->push_back(C);
+  }
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp b/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp
index 4a3503a2da7db..336f1db776036 100644
--- a/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp
+++ b/llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp
@@ -119,6 +119,9 @@ addPassesToGenerateCode(CodeGenTargetMachineImpl &TM, PassManagerBase &PM,
   PM.add(PassConfig);
   PM.add(&MMIWP);
 
+  for (auto& C : *TargetMachine::TargetPassConfigCallbacks)
+    C(TM, PM, PassConfig);
+
   if (PassConfig->addISelPasses())
     return nullptr;
   PassConfig->addMachinePasses();
diff --git a/llvm/lib/Target/TargetMachine.cpp b/llvm/lib/Target/TargetMachine.cpp
index 69b6e26e602f6..c43e2ba00f733 100644
--- a/llvm/lib/Target/TargetMachine.cpp
+++ b/llvm/lib/Target/TargetMachine.cpp
@@ -332,3 +332,6 @@ std::pair<int, int> TargetMachine::parseBinutilsVersion(StringRef Version) {
     Version.consumeInteger(10, Ret.second);
   return Ret;
 }
+
+// TargetPassConfig callbacks
+ManagedStatic<SmallVector<PassConfigCallback, 1>> TargetMachine::TargetPassConfigCallbacks{};
diff --git a/llvm/unittests/CodeGen/CGPluginTest/CMakeLists.txt b/llvm/unittests/CodeGen/CGPluginTest/CMakeLists.txt
new file mode 100644
index 0000000000000..2d2fc8668be63
--- /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)
+add_dependencies(CGPluginTest intrinsics_gen)
+
+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..c8dabe4fd18c1
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CMakeLists.txt
@@ -0,0 +1,16 @@
+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(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..07b7b43350fc9
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/CodeGenTestPass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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) {
+  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..f5b3db19c604e
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/Plugin/Plugin.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+
+__attribute__((constructor)) static void initCodeGenPlugin() {
+  initializeCodeGenTestPass(*PassRegistry::getPassRegistry());
+
+  TargetMachine::registerTargetPassConfigCallback(
+      [](auto &TM, auto &PM, auto *TPC) {
+        TPC->insertPass(&GCLoweringID, &CodeGenTest::ID);
+      });
+}
diff --git a/llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp b/llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp
new file mode 100644
index 0000000000000..4328a95e2ba06
--- /dev/null
+++ b/llvm/unittests/CodeGen/CGPluginTest/PluginTest.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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);
+
+  ASSERT_EQ(TargetMachine::TargetPassConfigCallbacks->size(), 0u);
+  std::string Error;
+  auto Library =
+      sys::DynamicLibrary::getPermanentLibrary(PluginPath->c_str(), &Error);
+  ASSERT_TRUE(Library.isValid()) << Error;
+  ASSERT_EQ(TargetMachine::TargetPassConfigCallbacks->size(), 1u);
+  TargetMachine::TargetPassConfigCallbacks->clear();
+}
+
+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;
+
+  ASSERT_EQ(TargetMachine::TargetPassConfigCallbacks->size(), 0u);
+  TargetMachine::registerTargetPassConfigCallback(
+      [&](auto &TM, auto &PM, auto *TPC) {
+        CallbackExecuted = true;
+        TPC->insertPass(&GCLoweringID, &CodeGenTest::ID);
+      });
+  ASSERT_EQ(TargetMachine::TargetPassConfigCallbacks->size(), 1u);
+
+  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";
+  }
+
+  TargetMachine::TargetPassConfigCallbacks->clear();
+}
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