mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 04:32:44 +01:00
[Plugins] Add a slim plugin API to work together with the new PM
Summary: Add a new plugin API. This closes the gap between pass registration and out-of-tree passes for the new PassManager. Unlike with the existing API, interaction with a plugin is always initiated from the tools perspective. I.e., when a plugin is loaded, it resolves and calls a well-known symbol `llvmGetPassPluginInfo` to obtain details about the plugin. The fundamental motivation is to get rid of as many global constructors as possible. The API exposed by the plugin info is kept intentionally minimal. Reviewers: chandlerc Reviewed By: chandlerc Subscribers: bollu, grosser, lksbhm, mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D35258 llvm-svn: 329273
This commit is contained in:
parent
0112f6cd58
commit
d9afd01962
@ -503,4 +503,22 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
|
||||
#define LLVM_ENABLE_EXCEPTIONS 1
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_PLUGIN_IMPORT
|
||||
/// \brief Used to import the well-known entry point for registering loaded pass
|
||||
/// plugins
|
||||
#ifdef WIN32
|
||||
#define LLVM_PLUGIN_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
#define LLVM_PLUGIN_IMPORT
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_PLUGIN_EXPORT
|
||||
/// \brief Used to export the well-known entry point for registering loaded pass
|
||||
/// plugins
|
||||
#ifdef WIN32
|
||||
#define LLVM_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define LLVM_PLUGIN_EXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
114
include/llvm/Passes/PassPlugin.h
Normal file
114
include/llvm/Passes/PassPlugin.h
Normal file
@ -0,0 +1,114 @@
|
||||
//===- llvm/Passes/PassPlugin.h - Public Plugin API -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This defines the public entry point for new-PM pass plugins.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_PASSES_PASSPLUGIN_H
|
||||
#define LLVM_PASSES_PASSPLUGIN_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/DynamicLibrary.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace llvm {
|
||||
class PassBuilder;
|
||||
|
||||
/// \macro LLVM_PLUGIN_API_VERSION
|
||||
/// Identifies the API version understood by this plugin.
|
||||
///
|
||||
/// When a plugin is loaded, the driver will check it's supported plugin version
|
||||
/// against that of the plugin. A mismatch is an error. The supported version
|
||||
/// will be incremented for ABI-breaking changes to the \c PassPluginLibraryInfo
|
||||
/// struct, i.e. when callbacks are added, removed, or reordered.
|
||||
#define LLVM_PLUGIN_API_VERSION 1
|
||||
|
||||
extern "C" {
|
||||
/// Information about the plugin required to load its passes
|
||||
///
|
||||
/// This struct defines the core interface for pass plugins and is supposed to
|
||||
/// be filled out by plugin implementors. LLVM-side users of a plugin are
|
||||
/// expected to use the \c PassPlugin class below to interface with it.
|
||||
struct PassPluginLibraryInfo {
|
||||
/// The API version understood by this plugin, usually \c
|
||||
/// LLVM_PLUGIN_API_VERSION
|
||||
uint32_t APIVersion;
|
||||
/// A meaningful name of the plugin.
|
||||
const char *PluginName;
|
||||
/// The version of the plugin.
|
||||
const char *PluginVersion;
|
||||
|
||||
/// The callback for registering plugin passes with a \c PassBuilder
|
||||
/// instance
|
||||
void (*RegisterPassBuilderCallbacks)(PassBuilder &);
|
||||
};
|
||||
}
|
||||
|
||||
/// A loaded pass plugin.
|
||||
///
|
||||
/// An instance of this class wraps a loaded pass plugin and gives access to
|
||||
/// its interface defined by the \c PassPluginLibraryInfo it exposes.
|
||||
class PassPlugin {
|
||||
public:
|
||||
/// Attempts to load a pass plugin from a given file.
|
||||
///
|
||||
/// \returns Returns an error if either the library cannot be found or loaded,
|
||||
/// there is no public entry point, or the plugin implements the wrong API
|
||||
/// version.
|
||||
static Expected<PassPlugin> Load(const std::string &Filename);
|
||||
|
||||
/// Get the filename of the loaded plugin.
|
||||
StringRef getFilename() const { return Filename; }
|
||||
|
||||
/// Get the plugin name
|
||||
StringRef getPluginName() const { return Info.PluginName; }
|
||||
|
||||
/// Get the plugin version
|
||||
StringRef getPluginVersion() const { return Info.PluginVersion; }
|
||||
|
||||
/// Get the plugin API version
|
||||
uint32_t getAPIVersion() const { return Info.APIVersion; }
|
||||
|
||||
/// Invoke the PassBuilder callback registration
|
||||
void registerPassBuilderCallbacks(PassBuilder &PB) const {
|
||||
Info.RegisterPassBuilderCallbacks(PB);
|
||||
}
|
||||
|
||||
private:
|
||||
PassPlugin(const std::string &Filename, const sys::DynamicLibrary &Library)
|
||||
: Filename(Filename), Library(Library), Info() {}
|
||||
|
||||
std::string Filename;
|
||||
sys::DynamicLibrary Library;
|
||||
PassPluginLibraryInfo Info;
|
||||
};
|
||||
}
|
||||
|
||||
/// The public entry point for a pass plugin.
|
||||
///
|
||||
/// When a plugin is loaded by the driver, it will call this entry point to
|
||||
/// obtain information about this plugin and about how to register its passes.
|
||||
/// This function needs to be implemented by the plugin, see the example below:
|
||||
///
|
||||
/// ```
|
||||
/// extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
|
||||
/// LLVM_PLUGIN_EXPORT llvmGetPassPluginInfo() {
|
||||
/// return {
|
||||
/// LLVM_PLUGIN_API_VERSION, "MyPlugin", "v0.1", [](PassBuilder &PB) { ... }
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK LLVM_PLUGIN_IMPORT
|
||||
llvmGetPassPluginInfo();
|
||||
|
||||
#endif /* LLVM_PASSES_PASSPLUGIN_H */
|
@ -16,4 +16,5 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#include "llvm/Demangle/Compiler.h"
|
||||
|
@ -1,5 +1,6 @@
|
||||
add_llvm_library(LLVMPasses
|
||||
PassBuilder.cpp
|
||||
PassPlugin.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/Passes
|
||||
|
50
lib/Passes/PassPlugin.cpp
Normal file
50
lib/Passes/PassPlugin.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
//===- lib/Passes/PassPluginLoader.cpp - Load Plugins for New PM Passes ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Passes/PassPlugin.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
Expected<PassPlugin> PassPlugin::Load(const std::string &Filename) {
|
||||
std::string Error;
|
||||
auto Library =
|
||||
sys::DynamicLibrary::getPermanentLibrary(Filename.c_str(), &Error);
|
||||
if (!Library.isValid())
|
||||
return make_error<StringError>(Twine("Could not load library '") +
|
||||
Filename + "': " + Error,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
PassPlugin P{Filename, Library};
|
||||
auto *getDetailsFn =
|
||||
Library.SearchForAddressOfSymbol("llvmGetPassPluginInfo");
|
||||
|
||||
if (!getDetailsFn)
|
||||
// If the symbol isn't found, this is probably a legacy plugin, which is an
|
||||
// error
|
||||
return make_error<StringError>(Twine("Plugin entry point not found in '") +
|
||||
Filename + "'. Is this a legacy plugin?",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
P.Info = reinterpret_cast<decltype(llvmGetPassPluginInfo) *>(getDetailsFn)();
|
||||
|
||||
if (P.Info.APIVersion != LLVM_PLUGIN_API_VERSION)
|
||||
return make_error<StringError>(
|
||||
Twine("Wrong API version on plugin '") + Filename + "'. Got version " +
|
||||
Twine(P.Info.APIVersion) + ", supported version is " +
|
||||
Twine(LLVM_PLUGIN_API_VERSION) + ".",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
if (!P.Info.RegisterPassBuilderCallbacks)
|
||||
return make_error<StringError>(Twine("Empty entry callback in plugin '") +
|
||||
Filename + "'.'",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
return P;
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/Passes/PassPlugin.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
@ -41,6 +42,10 @@ static cl::opt<bool>
|
||||
DebugPM("debug-pass-manager", cl::Hidden,
|
||||
cl::desc("Print pass management debugging information"));
|
||||
|
||||
static cl::list<std::string>
|
||||
PassPlugins("load-pass-plugin",
|
||||
cl::desc("Load passes from plugin library"));
|
||||
|
||||
// This flag specifies a textual description of the alias analysis pipeline to
|
||||
// use when querying for aliasing information. It only works in concert with
|
||||
// the "passes" flag above.
|
||||
@ -210,6 +215,18 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
|
||||
PassBuilder PB(TM, P);
|
||||
registerEPCallbacks(PB, VerifyEachPass, DebugPM);
|
||||
|
||||
// Load requested pass plugins and let them register pass builder callbacks
|
||||
for (auto &PluginFN : PassPlugins) {
|
||||
auto PassPlugin = PassPlugin::Load(PluginFN);
|
||||
if (!PassPlugin) {
|
||||
errs() << "Failed to load passes from '" << PluginFN
|
||||
<< "'. Request ignored.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
PassPlugin->registerPassBuilderCallbacks(PB);
|
||||
}
|
||||
|
||||
// Register a callback that creates the debugify passes as needed.
|
||||
PB.registerPipelineParsingCallback(
|
||||
[](StringRef Name, ModulePassManager &MPM,
|
||||
|
@ -22,6 +22,7 @@ add_subdirectory(Object)
|
||||
add_subdirectory(BinaryFormat)
|
||||
add_subdirectory(ObjectYAML)
|
||||
add_subdirectory(Option)
|
||||
add_subdirectory(Passes)
|
||||
add_subdirectory(ProfileData)
|
||||
add_subdirectory(Support)
|
||||
add_subdirectory(Target)
|
||||
|
21
unittests/Passes/CMakeLists.txt
Normal file
21
unittests/Passes/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
set(LLVM_LINK_COMPONENTS Support Passes Core)
|
||||
|
||||
add_llvm_unittest(PluginsTests PluginsTest.cpp)
|
||||
export_executable_symbols(PluginsTests)
|
||||
|
||||
add_library(TestPlugin SHARED TestPlugin.cxx)
|
||||
|
||||
set_output_directory(TestPlugin
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
|
||||
LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
|
||||
)
|
||||
|
||||
set_target_properties(TestPlugin
|
||||
PROPERTIES PREFIX ""
|
||||
SUFFIX ".so"
|
||||
)
|
||||
|
||||
llvm_map_components_to_libnames(LLVMDependencies ${LLVM_LINK_COMPONENTS})
|
||||
target_link_libraries(TestPlugin ${LLVMDependencies})
|
||||
|
||||
add_dependencies(PluginsTests TestPlugin)
|
53
unittests/Passes/PluginsTest.cpp
Normal file
53
unittests/Passes/PluginsTest.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
//===- unittests/Passes/Plugins/PluginsTest.cpp ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Analysis/CGSCCPassManager.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/Passes/PassPlugin.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Transforms/Scalar/LoopPassManager.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "TestPlugin.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
void anchor() {}
|
||||
|
||||
static std::string LibPath(const std::string Name = "TestPlugin") {
|
||||
const std::vector<testing::internal::string> &Argvs =
|
||||
testing::internal::GetArgvs();
|
||||
const char *Argv0 = Argvs.size() > 0 ? Argvs[0].c_str() : "PluginsTests";
|
||||
void *Ptr = (void *)anchor;
|
||||
std::string Path = sys::fs::getMainExecutable(Argv0, Ptr);
|
||||
llvm::SmallString<256> Buf{sys::path::parent_path(Path)};
|
||||
sys::path::append(Buf, (Name + ".so").c_str());
|
||||
return Buf.str();
|
||||
}
|
||||
|
||||
TEST(PluginsTests, LoadPlugin) {
|
||||
auto PluginPath = LibPath();
|
||||
ASSERT_NE("", PluginPath);
|
||||
|
||||
Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath);
|
||||
ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath;
|
||||
|
||||
ASSERT_EQ(TEST_PLUGIN_NAME, Plugin->getPluginName());
|
||||
ASSERT_EQ(TEST_PLUGIN_VERSION, Plugin->getPluginVersion());
|
||||
|
||||
PassBuilder PB;
|
||||
ModulePassManager PM;
|
||||
ASSERT_FALSE(PB.parsePassPipeline(PM, "plugin-pass"));
|
||||
|
||||
Plugin->registerPassBuilderCallbacks(PB);
|
||||
ASSERT_TRUE(PB.parsePassPipeline(PM, "plugin-pass"));
|
||||
}
|
39
unittests/Passes/TestPlugin.cxx
Normal file
39
unittests/Passes/TestPlugin.cxx
Normal file
@ -0,0 +1,39 @@
|
||||
//===- unittests/Passes/Plugins/Plugin.cxx --------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/Passes/PassPlugin.h"
|
||||
|
||||
#include "TestPlugin.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
struct TestModulePass : public PassInfoMixin<TestModulePass> {
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
|
||||
return PreservedAnalyses::all();
|
||||
}
|
||||
};
|
||||
|
||||
void registerCallbacks(PassBuilder &PB) {
|
||||
PB.registerPipelineParsingCallback(
|
||||
[](StringRef Name, ModulePassManager &PM,
|
||||
ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
|
||||
if (Name == "plugin-pass") {
|
||||
PM.addPass(TestModulePass());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK LLVM_PLUGIN_EXPORT
|
||||
llvmGetPassPluginInfo() {
|
||||
return {LLVM_PLUGIN_API_VERSION, TEST_PLUGIN_NAME, TEST_PLUGIN_VERSION,
|
||||
registerCallbacks};
|
||||
}
|
2
unittests/Passes/TestPlugin.h
Normal file
2
unittests/Passes/TestPlugin.h
Normal file
@ -0,0 +1,2 @@
|
||||
#define TEST_PLUGIN_NAME "TestPlugin"
|
||||
#define TEST_PLUGIN_VERSION "0.1-unit"
|
Loading…
Reference in New Issue
Block a user