1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +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:
Philip Pfaffe 2018-04-05 11:29:37 +00:00
parent 0112f6cd58
commit d9afd01962
11 changed files with 317 additions and 0 deletions

View File

@ -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

View 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 */

View File

@ -16,4 +16,5 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/Compiler.h"

View File

@ -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
View 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;
}

View File

@ -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,

View File

@ -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)

View 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)

View 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"));
}

View 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};
}

View File

@ -0,0 +1,2 @@
#define TEST_PLUGIN_NAME "TestPlugin"
#define TEST_PLUGIN_VERSION "0.1-unit"