Make it possible for AST plugins to enable themselves by default

Currently when an AST plugin is loaded it must then be enabled by passing
-plugin pluginname or -add-plugin pluginname to the -cc1 command line. This
patch adds a method to PluginASTAction which allows it to declare that the
action happens before, instead of, or after the main AST action, plus the
relevant changes to make the plugin action happen at that time automatically.

Differential Revision: http://reviews.llvm.org/D17959

llvm-svn: 263546
This commit is contained in:
John Brawn 2016-03-15 12:51:40 +00:00
parent 27fc7a7b47
commit 6c78974b29
10 changed files with 158 additions and 34 deletions

View File

@ -54,6 +54,10 @@ the `latest version of PrintFunctionNames.cpp
Running the plugin
==================
Using the cc1 command line
--------------------------
To run a plugin, the dynamic library containing the plugin registry must be
loaded via the :option:`-load` command line option. This will load all plugins
that are registered, and you can select the plugins to run by specifying the
@ -88,3 +92,19 @@ source tree:
Also see the print-function-name plugin example's
`README <http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/README.txt?view=markup>`_
Using the clang command line
----------------------------
Using :option:`-fplugin=plugin` on the clang command line passes the plugin
through as an argument to :option:`-load` on the cc1 command line. If the plugin
class implements the ``getActionType`` method then the plugin is run
automatically. For example, to run the plugin automatically after the main AST
action (i.e. the same as using :option:`-add-plugin`):
.. code-block:: c++
// Automatically run the plugin after the main AST action
PluginASTAction::ActionType getActionType() override {
return AddAfterMainAction;
}

View File

@ -0,0 +1,52 @@
//===- AnnotateFunctions.cpp ----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Example clang plugin which adds an annotation to every function.
//
//===----------------------------------------------------------------------===//
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
using namespace clang;
namespace {
class AnnotateFunctionsConsumer : public ASTConsumer {
public:
bool HandleTopLevelDecl(DeclGroupRef DG) override {
for (auto D : DG)
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
FD->addAttr(AnnotateAttr::CreateImplicit(FD->getASTContext(),
"example_annotation"));
return true;
}
};
class AnnotateFunctionsAction : public PluginASTAction {
public:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
llvm::StringRef) override {
return llvm::make_unique<AnnotateFunctionsConsumer>();
}
bool ParseArgs(const CompilerInstance &CI,
const std::vector<std::string> &args) override {
return true;
}
PluginASTAction::ActionType getActionType() override {
return AddBeforeMainAction;
}
};
}
static FrontendPluginRegistry::Add<AnnotateFunctionsAction>
X("annotate-fns", "annotate functions");

View File

@ -0,0 +1,9 @@
add_llvm_loadable_module(AnnotateFunctions AnnotateFunctions.cpp)
if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
target_link_libraries(AnnotateFunctions ${cmake_2_8_12_PRIVATE}
clangAST
clangFrontend
LLVMSupport
)
endif()

View File

@ -8,3 +8,4 @@ add_subdirectory(analyzer-plugin)
endif()
add_subdirectory(clang-interpreter)
add_subdirectory(PrintFunctionNames)
add_subdirectory(AnnotateFunctions)

View File

@ -249,6 +249,19 @@ public:
/// CompilerInstance's Diagnostic object to report errors.
virtual bool ParseArgs(const CompilerInstance &CI,
const std::vector<std::string> &arg) = 0;
enum ActionType {
Cmdline, //< Action is determined by the cc1 command-line
ReplaceAction, //< Replace the main action
AddBeforeMainAction, //< Execute the action before the main action
AddAfterMainAction //< Execute the action after the main action
};
/// \brief Get the action type for this plugin
///
/// \return The action type. If the type is Cmdline then by default the
/// plugin does nothing and what it does is determined by the cc1
/// command-line.
virtual ActionType getActionType() { return Cmdline; }
};
/// \brief Abstract base class to use for preprocessor-based frontend actions.

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/StringRef.h"
#include <string>
#include <vector>
#include <unordered_map>
namespace llvm {
class MemoryBuffer;
@ -227,15 +228,12 @@ public:
/// The name of the action to run when using a plugin action.
std::string ActionName;
/// Args to pass to the plugin
std::vector<std::string> PluginArgs;
/// Args to pass to the plugins
std::unordered_map<std::string,std::vector<std::string>> PluginArgs;
/// The list of plugin actions to run in addition to the normal action.
std::vector<std::string> AddPluginActions;
/// Args to pass to the additional plugins
std::vector<std::vector<std::string> > AddPluginArgs;
/// The list of plugins to load.
std::vector<std::string> Plugins;

View File

@ -1051,18 +1051,10 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.Plugins.emplace_back(A->getValue(0));
Opts.ProgramAction = frontend::PluginAction;
Opts.ActionName = A->getValue();
for (const Arg *AA : Args.filtered(OPT_plugin_arg))
if (AA->getValue(0) == Opts.ActionName)
Opts.PluginArgs.emplace_back(AA->getValue(1));
}
Opts.AddPluginActions = Args.getAllArgValues(OPT_add_plugin);
Opts.AddPluginArgs.resize(Opts.AddPluginActions.size());
for (int i = 0, e = Opts.AddPluginActions.size(); i != e; ++i)
for (const Arg *A : Args.filtered(OPT_plugin_arg))
if (A->getValue(0) == Opts.AddPluginActions[i])
Opts.AddPluginArgs[i].emplace_back(A->getValue(1));
for (const Arg *AA : Args.filtered(OPT_plugin_arg))
Opts.PluginArgs[AA->getValue(0)].emplace_back(AA->getValue(1));
for (const std::string &Arg :
Args.getAllArgValues(OPT_ftest_module_file_extension_EQ)) {

View File

@ -141,28 +141,46 @@ FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
if (!Consumer)
return nullptr;
if (CI.getFrontendOpts().AddPluginActions.size() == 0)
// If there are no registered plugins we don't need to wrap the consumer
if (FrontendPluginRegistry::begin() == FrontendPluginRegistry::end())
return Consumer;
// Make sure the non-plugin consumer is first, so that plugins can't
// modifiy the AST.
// Collect the list of plugins that go before the main action (in Consumers)
// or after it (in AfterConsumers)
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Consumers.push_back(std::move(Consumer));
for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size();
i != e; ++i) {
// This is O(|plugins| * |add_plugins|), but since both numbers are
// way below 50 in practice, that's ok.
for (FrontendPluginRegistry::iterator
it = FrontendPluginRegistry::begin(),
ie = FrontendPluginRegistry::end();
it != ie; ++it) {
if (it->getName() != CI.getFrontendOpts().AddPluginActions[i])
continue;
std::unique_ptr<PluginASTAction> P = it->instantiate();
if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
Consumers.push_back(P->CreateASTConsumer(CI, InFile));
std::vector<std::unique_ptr<ASTConsumer>> AfterConsumers;
for (FrontendPluginRegistry::iterator it = FrontendPluginRegistry::begin(),
ie = FrontendPluginRegistry::end();
it != ie; ++it) {
std::unique_ptr<PluginASTAction> P = it->instantiate();
PluginASTAction::ActionType ActionType = P->getActionType();
if (ActionType == PluginASTAction::Cmdline) {
// This is O(|plugins| * |add_plugins|), but since both numbers are
// way below 50 in practice, that's ok.
for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size();
i != e; ++i) {
if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) {
ActionType = PluginASTAction::AddAfterMainAction;
break;
}
}
}
if ((ActionType == PluginASTAction::AddBeforeMainAction ||
ActionType == PluginASTAction::AddAfterMainAction) &&
P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs[it->getName()])) {
std::unique_ptr<ASTConsumer> PluginConsumer = P->CreateASTConsumer(CI, InFile);
if (ActionType == PluginASTAction::AddBeforeMainAction) {
Consumers.push_back(std::move(PluginConsumer));
} else {
AfterConsumers.push_back(std::move(PluginConsumer));
}
}
}
// Add to Consumers the main consumer, then all the plugins that go after it
Consumers.push_back(std::move(Consumer));
for (auto &C : AfterConsumers) {
Consumers.push_back(std::move(C));
}
return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));

View File

@ -66,7 +66,9 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
it != ie; ++it) {
if (it->getName() == CI.getFrontendOpts().ActionName) {
std::unique_ptr<PluginASTAction> P(it->instantiate());
if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs))
if ((P->getActionType() != PluginASTAction::ReplaceAction &&
P->getActionType() != PluginASTAction::Cmdline) ||
!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs[it->getName()]))
return nullptr;
return std::move(P);
}
@ -194,6 +196,18 @@ bool clang::ExecuteCompilerInvocation(CompilerInstance *Clang) {
<< Path << Error;
}
// Check if any of the loaded plugins replaces the main AST action
for (FrontendPluginRegistry::iterator it = FrontendPluginRegistry::begin(),
ie = FrontendPluginRegistry::end();
it != ie; ++it) {
std::unique_ptr<PluginASTAction> P(it->instantiate());
if (P->getActionType() == PluginASTAction::ReplaceAction) {
Clang->getFrontendOpts().ProgramAction = clang::frontend::PluginAction;
Clang->getFrontendOpts().ActionName = it->getName();
break;
}
}
// Honor -mllvm.
//
// FIXME: Remove this, one day.

View File

@ -0,0 +1,7 @@
// RUN: %clang -fplugin=%llvmshlibdir/AnnotateFunctions%pluginext -emit-llvm -S %s -o - | FileCheck %s
// REQUIRES: plugins, examples
// CHECK: [[STR_VAR:@.+]] = private unnamed_addr constant [19 x i8] c"example_annotation\00"
// CHECK: @llvm.global.annotations = {{.*}}@fn1{{.*}}[[STR_VAR]]{{.*}}@fn2{{.*}}[[STR_VAR]]
void fn1() { }
void fn2() { }