Skip to content

Commit f617ab1

Browse files
committed
[NPM] Resolve llvmGetPassPluginInfo to the plugin being loaded
Dynamically loaded plugins for the new pass manager are initialised by calling llvmGetPassPluginInfo. This is defined as a weak symbol so that it is continually redefined by each plugin that is loaded. When loading a plugin from a shared library, the intention is that llvmGetPassPluginInfo will be resolved to the definition in the most recent plugin. However, using a global search for this resolution can fail in situations where multiple plugins are loaded. Currently: * If a plugin does not define llvmGetPassPluginInfo, then it will be silently resolved to the previous plugin's definition. * If loading the same plugin twice with another in between, e.g. plugin A/plugin B/plugin A, then the second load of plugin A will resolve to llvmGetPassPluginInfo in plugin B. * The previous case can also occur when a dynamic library defines both NPM and legacy plugins; the legacy plugins are loaded first and then with `-fplugin=A -fpass-plugin=B -fpass-plugin=A`: A will be loaded as a legacy plugin and define llvmGetPassPluginInfo; B will be loaded and redefine it; and finally when A is loaded as an NPM plugin it will be resolved to the definition from B. Instead of searching globally, restrict the symbol lookup to the library that is currently being loaded. Differential Revision: https://reviews.llvm.org/D104916
1 parent 434bd5b commit f617ab1

File tree

5 files changed

+155
-27
lines changed

5 files changed

+155
-27
lines changed

llvm/lib/Passes/PassPlugin.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ Expected<PassPlugin> PassPlugin::Load(const std::string &Filename) {
2323
inconvertibleErrorCode());
2424

2525
PassPlugin P{Filename, Library};
26+
27+
// llvmGetPassPluginInfo should be resolved to the definition from the plugin
28+
// we are currently loading.
2629
intptr_t getDetailsFn =
27-
(intptr_t)Library.SearchForAddressOfSymbol("llvmGetPassPluginInfo");
30+
(intptr_t)Library.getAddressOfSymbol("llvmGetPassPluginInfo");
2831

2932
if (!getDetailsFn)
3033
// If the symbol isn't found, this is probably a legacy plugin, which is an

llvm/unittests/Passes/CMakeLists.txt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Needed by LLVM's CMake checks because this file defines multiple targets.
2-
set(LLVM_OPTIONAL_SOURCES PluginsTest.cpp TestPlugin.cpp PassBuilderBindingsTest.cpp)
2+
set(LLVM_OPTIONAL_SOURCES PluginsTest.cpp TestPlugin.cpp DoublerPlugin.cpp PassBuilderBindingsTest.cpp)
33

44
# If plugins are disabled, this test will disable itself at runtime. Otherwise,
55
# reconfiguring with plugins disabled will leave behind a stale executable.
@@ -20,23 +20,25 @@ if (NOT WIN32)
2020
target_link_libraries(PluginsTests PRIVATE LLVMTestingSupport)
2121

2222
set(LLVM_LINK_COMPONENTS)
23-
add_llvm_library(TestPlugin MODULE BUILDTREE_ONLY
24-
TestPlugin.cpp
25-
)
23+
foreach(PLUGIN TestPlugin DoublerPlugin)
24+
add_llvm_library(${PLUGIN} MODULE BUILDTREE_ONLY ${PLUGIN}.cpp)
2625

27-
# Put plugin next to the unit test executable.
28-
set_output_directory(TestPlugin
29-
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
30-
LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
31-
)
32-
set_target_properties(TestPlugin PROPERTIES FOLDER "Tests")
26+
# Put PLUGIN next to the unit test executable.
27+
set_output_directory(${PLUGIN}
28+
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
29+
LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
30+
)
31+
set_target_properties(${PLUGIN} PROPERTIES FOLDER "Tests")
32+
33+
add_dependencies(${PLUGIN} intrinsics_gen)
34+
add_dependencies(PluginsTests ${PLUGIN})
35+
endforeach()
3336

34-
add_dependencies(TestPlugin intrinsics_gen)
35-
add_dependencies(PluginsTests TestPlugin)
3637
endif()
3738

3839
set(LLVM_LINK_COMPONENTS Support Passes Core Target native AllTargetsInfos)
3940
add_llvm_unittest(PassesBindingsTests
4041
PassBuilderBindingsTest.cpp
4142
)
4243
target_link_libraries(PassesBindingsTests PRIVATE LLVMTestingSupport)
44+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===- unittests/Passes/DoublerPlugin.cpp
2+
//--------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "llvm/Passes/PassBuilder.h"
11+
#include "llvm/Passes/PassPlugin.h"
12+
13+
using namespace llvm;
14+
15+
struct DoublerModulePass : public PassInfoMixin<DoublerModulePass> {
16+
17+
// Double the value of the initializer
18+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
19+
auto *GV = cast<GlobalVariable>(M.getNamedValue("doubleme"));
20+
auto *Init = GV->getInitializer();
21+
auto *Init2 = ConstantExpr::getAdd(Init, Init);
22+
GV->setInitializer(Init2);
23+
24+
return PreservedAnalyses::none();
25+
}
26+
27+
static void registerCallbacks(PassBuilder &PB) {
28+
PB.registerPipelineParsingCallback(
29+
[](StringRef Name, ModulePassManager &PM,
30+
ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
31+
if (Name == "doubler-pass") {
32+
PM.addPass(DoublerModulePass());
33+
return true;
34+
}
35+
return false;
36+
});
37+
}
38+
};
39+
40+
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
41+
llvmGetPassPluginInfo() {
42+
return {LLVM_PLUGIN_API_VERSION, "DoublerPlugin", "2.2-unit",
43+
DoublerModulePass::registerCallbacks};
44+
}

llvm/unittests/Passes/PluginsTest.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/Analysis/CGSCCPassManager.h"
10+
#include "llvm/AsmParser/Parser.h"
1011
#include "llvm/Config/config.h"
12+
#include "llvm/IR/GlobalVariable.h"
1113
#include "llvm/IR/PassManager.h"
1214
#include "llvm/Passes/PassBuilder.h"
1315
#include "llvm/Passes/PassPlugin.h"
@@ -58,3 +60,80 @@ TEST(PluginsTests, LoadPlugin) {
5860
Plugin->registerPassBuilderCallbacks(PB);
5961
ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, "plugin-pass"), Succeeded());
6062
}
63+
64+
// Test that llvmGetPassPluginInfo from DoublerPlugin is called twice with
65+
// -fpass-plugin=DoublerPlugin -fpass-plugin=TestPlugin
66+
// -fpass-plugin=DoublerPlugin.
67+
TEST(PluginsTests, LoadMultiplePlugins) {
68+
#if !defined(LLVM_ENABLE_PLUGINS)
69+
// Disable the test if plugins are disabled.
70+
return;
71+
#endif
72+
73+
auto DoublerPluginPath = LibPath("DoublerPlugin");
74+
auto TestPluginPath = LibPath("TestPlugin");
75+
ASSERT_NE("", DoublerPluginPath);
76+
ASSERT_NE("", TestPluginPath);
77+
78+
Expected<PassPlugin> DoublerPlugin1 = PassPlugin::Load(DoublerPluginPath);
79+
ASSERT_TRUE(!!DoublerPlugin1)
80+
<< "Plugin path: " << DoublerPlugin1->getFilename();
81+
82+
Expected<PassPlugin> TestPlugin = PassPlugin::Load(TestPluginPath);
83+
ASSERT_TRUE(!!TestPlugin) << "Plugin path: " << TestPlugin->getFilename();
84+
85+
// If llvmGetPassPluginInfo is resolved as a weak symbol taking into account
86+
// all loaded symbols, the second call to PassPlugin::Load will actually
87+
// return the llvmGetPassPluginInfo from the most recently loaded plugin, in
88+
// this case TestPlugin.
89+
Expected<PassPlugin> DoublerPlugin2 = PassPlugin::Load(DoublerPluginPath);
90+
ASSERT_TRUE(!!DoublerPlugin2)
91+
<< "Plugin path: " << DoublerPlugin2->getFilename();
92+
93+
ASSERT_EQ("DoublerPlugin", DoublerPlugin1->getPluginName());
94+
ASSERT_EQ("2.2-unit", DoublerPlugin1->getPluginVersion());
95+
ASSERT_EQ(TEST_PLUGIN_NAME, TestPlugin->getPluginName());
96+
ASSERT_EQ(TEST_PLUGIN_VERSION, TestPlugin->getPluginVersion());
97+
// Check that the plugin name/version is set correctly when loaded a second
98+
// time
99+
ASSERT_EQ("DoublerPlugin", DoublerPlugin2->getPluginName());
100+
ASSERT_EQ("2.2-unit", DoublerPlugin2->getPluginVersion());
101+
102+
PassBuilder PB;
103+
ModulePassManager PM;
104+
const char *PipelineText = "module(doubler-pass,plugin-pass,doubler-pass)";
105+
ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Failed());
106+
TestPlugin->registerPassBuilderCallbacks(PB);
107+
DoublerPlugin1->registerPassBuilderCallbacks(PB);
108+
DoublerPlugin2->registerPassBuilderCallbacks(PB);
109+
ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded());
110+
111+
LLVMContext C;
112+
SMDiagnostic Err;
113+
std::unique_ptr<Module> M =
114+
parseAssemblyString(R"IR(@doubleme = constant i32 7)IR", Err, C);
115+
116+
// Check that the initial value is 7
117+
{
118+
auto *GV = M->getNamedValue("doubleme");
119+
auto *Init = cast<GlobalVariable>(GV)->getInitializer();
120+
auto *CI = cast<ConstantInt>(Init);
121+
ASSERT_EQ(CI->getSExtValue(), 7);
122+
}
123+
124+
ModuleAnalysisManager MAM;
125+
// Register required pass instrumentation analysis.
126+
MAM.registerPass([&] { return PassInstrumentationAnalysis(); });
127+
PM.run(*M, MAM);
128+
129+
// Check that the final value is 28 because DoublerPlugin::run was called
130+
// twice, indicating that the llvmGetPassPluginInfo and registerCallbacks
131+
// were correctly called.
132+
{
133+
// Check the value was doubled twice
134+
auto *GV = M->getNamedValue("doubleme");
135+
auto *Init = cast<GlobalVariable>(GV)->getInitializer();
136+
auto *CI = cast<ConstantInt>(Init);
137+
ASSERT_EQ(CI->getSExtValue(), 28);
138+
}
139+
}

llvm/unittests/Passes/TestPlugin.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===- unittests/Passes/Plugins/Plugin.cpp --------------------------------===//
1+
//===- unittests/Passes/TestPlugin.cpp --------------------------------===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
@@ -17,22 +17,22 @@ struct TestModulePass : public PassInfoMixin<TestModulePass> {
1717
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
1818
return PreservedAnalyses::all();
1919
}
20-
};
2120

22-
void registerCallbacks(PassBuilder &PB) {
23-
PB.registerPipelineParsingCallback(
24-
[](StringRef Name, ModulePassManager &PM,
25-
ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
26-
if (Name == "plugin-pass") {
27-
PM.addPass(TestModulePass());
28-
return true;
29-
}
30-
return false;
31-
});
32-
}
21+
static void registerCallbacks(PassBuilder &PB) {
22+
PB.registerPipelineParsingCallback(
23+
[](StringRef Name, ModulePassManager &PM,
24+
ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
25+
if (Name == "plugin-pass") {
26+
PM.addPass(TestModulePass());
27+
return true;
28+
}
29+
return false;
30+
});
31+
}
32+
};
3333

3434
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
3535
llvmGetPassPluginInfo() {
3636
return {LLVM_PLUGIN_API_VERSION, TEST_PLUGIN_NAME, TEST_PLUGIN_VERSION,
37-
registerCallbacks};
37+
TestModulePass::registerCallbacks};
3838
}

0 commit comments

Comments
 (0)