[cfe-commits] [PATCH] AST plugin invocation before default action
MORITA Hajime
morrita at gmail.com
Tue Apr 26 11:47:39 PDT 2011
Hi,
I'm sorry for spamming. But could anyone review this?
On Sat, Apr 23, 2011 at 11:25 PM, MORITA Hajime <morrita at gmail.com> wrote:
> Hi folks,
>
> I read some discussions about AST modification using plugin on
> cfe-dev, and read the code around FrontendAction and AST.
>
> Now with some understanding what the limitations are, I think AST
> modification will be useful even if with that limitations. So I tried
> to write a patch for inserting plugin before the default action like
> code generation.
>
> Here is how:
>
> * Added PluginASTAction::GetASTConsumerOrder() to control the
> invocation order of that plugin. The default is "BeforeDefault".
>
> * Changed FrontendAction::CreateWrappedASTConsumer() to recognize the
> order of each plugin. After this change, "BeforeDefault" plugin
> actions are listed before the default action.
>
> * Extracted PluginASTActionList for simplify the code.
>
> This change also includes simple example called Multiplier, which
> multiplies the return value of int value function with specified
> number.
>
> My goal is to allow plugins to insert some instrumentation function
> calls. And we might need some more hooks for that type of
> operation. But I think this is a good small first step for that.
>
> Some other alternative options, like tree cloning (in functional
> fashion) and injecting TreeTransform class, seem to need big change.
> Thus that will be risky and controversial, at least in this stage.
> I prefer a small start.
> What do you think?
> Any suggestions are appreciated!
>
>
> Regards,
> --
> morrita
>
> ---------------------------
> diff --git a/examples/Makefile b/examples/Makefile
> index 8cb431d..3fbcdf1 100644
> --- a/examples/Makefile
> +++ b/examples/Makefile
> @@ -9,6 +9,6 @@
>
> CLANG_LEVEL := ..
>
> -PARALLEL_DIRS := clang-interpreter PrintFunctionNames
> +PARALLEL_DIRS := clang-interpreter PrintFunctionNames Multiplier
>
> include $(CLANG_LEVEL)/Makefile
> diff --git a/examples/Multiplier/CMakeLists.txt
> b/examples/Multiplier/CMakeLists.txt
> new file mode 100644
> index 0000000..2c008fe
> --- /dev/null
> +++ b/examples/Multiplier/CMakeLists.txt
> @@ -0,0 +1,15 @@
> +set(MODULE TRUE)
> +
> +set( LLVM_USED_LIBS
> + clangFrontend
> + clangAST
> + )
> +
> +set( LLVM_LINK_COMPONENTS support mc)
> +
> +add_clang_library(Multiple Multiple.cpp)
> +
> +set_target_properties(Multiple
> + PROPERTIES
> + LINKER_LANGUAGE CXX
> + PREFIX "")
> diff --git a/examples/Multiplier/Makefile b/examples/Multiplier/Makefile
> new file mode 100644
> index 0000000..3cf35a6
> --- /dev/null
> +++ b/examples/Multiplier/Makefile
> @@ -0,0 +1,28 @@
> +##===- examples/PrintFunctionNames/Makefile ----------------*-
> Makefile -*-===##
> +#
> +# The LLVM Compiler Infrastructure
> +#
> +# This file is distributed under the University of Illinois Open Source
> +# License. See LICENSE.TXT for details.
> +#
> +##===----------------------------------------------------------------------===##
> +
> +CLANG_LEVEL := ../..
> +LIBRARYNAME = Multiplier
> +
> +# If we don't need RTTI or EH, there's no reason to export anything
> +# from the plugin.
> +ifneq ($(REQUIRES_RTTI), 1)
> +ifneq ($(REQUIRES_EH), 1)
> +EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/Multiplier.exports
> +endif
> +endif
> +
> +LINK_LIBS_IN_SHARED = 0
> +SHARED_LIBRARY = 1
> +
> +include $(CLANG_LEVEL)/Makefile
> +
> +ifeq ($(OS),Darwin)
> + LDFLAGS=-Wl,-undefined,dynamic_lookup
> +endif
> diff --git a/examples/Multiplier/Multiplier.cpp
> b/examples/Multiplier/Multiplier.cpp
> new file mode 100644
> index 0000000..41f065c
> --- /dev/null
> +++ b/examples/Multiplier/Multiplier.cpp
> @@ -0,0 +1,131 @@
> +//===- PrintFunctionNames.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 simply prints the names of all the
> top-level decls
> +// in the input file.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "clang/Frontend/FrontendPluginRegistry.h"
> +#include "clang/AST/ASTConsumer.h"
> +#include "clang/AST/AST.h"
> +#include "clang/AST/DeclVisitor.h"
> +#include "clang/AST/StmtVisitor.h"
> +#include "clang/Frontend/CompilerInstance.h"
> +#include "llvm/Support/raw_ostream.h"
> +using namespace clang;
> +
> +namespace {
> +
> +class MultiplierVisitor : public DeclVisitor<MultiplierVisitor>,
> + public StmtVisitor<MultiplierVisitor>
> +{
> +private:
> + ASTContext& Context;
> + int Scale;
> +
> +public:
> + MultiplierVisitor(ASTContext& C, int S) : Context(C), Scale(S) {}
> +
> + bool isAcceptable(const QualType& T) const {
> + return T.getTypePtr()->isIntegerType();
> + }
> +
> + void VisitFunctionDecl(FunctionDecl *ND) {
> + Stmt* B = ND->getBody();
> + if (B && isAcceptable(ND->getResultType())) {
> + for (Stmt::child_iterator SI = B->child_begin(); SI !=
> B->child_end(); ++SI) {
> + static_cast<StmtVisitor<MultiplierVisitor>*>(this)->Visit(*SI);
> + }
> + }
> + }
> +
> + void VisitReturnStmt(ReturnStmt *Node) {
> + Expr* OriginalRet = Node->getRetValue();
> + llvm::APInt ScaleValue(32, Scale, 10);
> + Expr* RHS = new (Context) IntegerLiteral(Context, ScaleValue,
> OriginalRet->getType(), SourceLocation());
> + BinaryOperator* NewRet =
> + new (Context) BinaryOperator(OriginalRet, RHS, BO_Mul,
> + OriginalRet->getType(),
> + VK_RValue, OK_Ordinary,
> + SourceLocation());
> + Node->setRetValue(NewRet);
> + }
> +};
> +
> +class MultiplierConsumer : public ASTConsumer {
> + ASTContext* Context;
> + int Scale;
> +
> +public:
> + virtual void Initialize(ASTContext &C) {
> + Context = &C;
> + }
> +
> + virtual void HandleTopLevelDecl(DeclGroupRef DG) {
> + MultiplierVisitor V(*Context, Scale);
> + for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; ++i) {
> + static_cast<DeclVisitor<MultiplierVisitor>&>(V).Visit(*i);
> + }
> + }
> +
> + explicit MultiplierConsumer(int S) : Scale(S) {}
> +};
> +
> +
> +class MultiplierAction : public PluginASTAction {
> + int Scale;
> +protected:
> + ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) {
> + return new MultiplierConsumer(Scale);
> + }
> +
> + bool ParseArgs(const CompilerInstance &CI,
> + const std::vector<std::string>& args) {
> + if (0 == args.size()) {
> + Diagnostic &D = CI.getDiagnostics();
> + unsigned DiagID = D.getCustomDiagID(
> + Diagnostic::Error, "Need scale argument");
> + D.Report(DiagID);
> + return false;
> + }
> +
> + if (args[0] == "help") {
> + PrintHelp(llvm::errs());
> + return false;
> + }
> +
> + llvm::StringRef FirstArg(args[0]);
> + if (FirstArg.getAsInteger(10, Scale)) {
> + Diagnostic &D = CI.getDiagnostics();
> + unsigned DiagID = D.getCustomDiagID(
> + Diagnostic::Error, "Invalid argument '" + args[0] + "'");
> + D.Report(DiagID);
> + return false;
> + }
> +
> + return true;
> + }
> +
> + virtual ConsumerOrderKind GetASTConsumerOrder() {
> + return PluginASTAction::BeforeDefault;
> + }
> +
> + void PrintHelp(llvm::raw_ostream& ros) {
> + ros << "This plugin explains the most simple AST modification\n";
> + }
> +
> +public:
> + MultiplierAction() : Scale(0) {}
> +};
> +
> +}
> +
> +static FrontendPluginRegistry::Add<MultiplierAction>
> +X("multiply", "multiply integer return values");
> diff --git a/examples/Multiplier/Multiplier.exports
> b/examples/Multiplier/Multiplier.exports
> new file mode 100644
> index 0000000..0ff590d
> --- /dev/null
> +++ b/examples/Multiplier/Multiplier.exports
> @@ -0,0 +1 @@
> +_ZN4llvm8Registry*
> diff --git a/examples/Multiplier/README.txt b/examples/Multiplier/README.txt
> new file mode 100644
> index 0000000..7ea0e1a
> --- /dev/null
> +++ b/examples/Multiplier/README.txt
> @@ -0,0 +1,11 @@
> +This is another simple example demonstrating how to use clang's facility for
> +providing AST consumers using a plugin.
> +
> +Build the plugin by running `make` in this directory.
> +
> +Once the plugin is built, you can run it using:
> +--
> +Linux:
> +$ clang -Xclang -load -Xclang
> ../../Debug+Asserts/lib/libMultiplier.so -Xclang -add-plugin -Xclang
> multiply -Xclang -plugin-arg-multiply -Xclang 42
> +Mac:
> +$ clang -Xclang -load -Xclang
> ../../Debug+Asserts/lib/libMultiplier.dylib -Xclang -add-plugin
> -Xclang multiply -Xclang -plugin-arg-multiply -Xclang 42
> diff --git a/include/clang/Frontend/FrontendAction.h
> b/include/clang/Frontend/FrontendAction.h
> index ee0863a..67f7b65 100644
> --- a/include/clang/Frontend/FrontendAction.h
> +++ b/include/clang/Frontend/FrontendAction.h
> @@ -225,11 +225,16 @@ public:
> };
>
> class PluginASTAction : public ASTFrontendAction {
> -protected:
> +public:
> + /// ConsumerOrderKind - Specifies the invocation order of the ASTConsumer.
> + enum ConsumerOrderKind {
> + BeforeDefault, ///< The consumer is invoked before the default consumer.
> + AfterDefault ///< The consumer is invoked before the default consumer.
> + };
> +
> virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
> llvm::StringRef InFile) = 0;
>
> -public:
> /// ParseArgs - Parse the given plugin command line arguments.
> ///
> /// \param CI - The compiler instance, for use in reporting diagnostics.
> @@ -238,6 +243,18 @@ public:
> /// CompilerInstance's Diagnostic object to report errors.
> virtual bool ParseArgs(const CompilerInstance &CI,
> const std::vector<std::string> &arg) = 0;
> +
> + /// GetASTConsumerOrder - Determines the invocation order for a
> consumer which
> + /// is created by CreateASTConsumer() method. ASTConsumers which are invoked
> + /// earlier can affect AST observed by later one.
> + ///
> + /// Note that ASTConsumer invocation is one-pass, thus there are limitations
> + /// for the AST modification: You can make some kind of local modification
> + /// like modifying statements inside the given function. But you cannot make
> + /// global changes including global function addition or removal.
> + /// Also note that Type object modification is not possible because Type
> + /// objects are immutable.
> + virtual ConsumerOrderKind GetASTConsumerOrder() { return AfterDefault; }
> };
>
> /// PreprocessorFrontendAction - Abstract base class to use for preprocessor
> diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
> index 42da44c..206e754 100644
> --- a/lib/Frontend/FrontendAction.cpp
> +++ b/lib/Frontend/FrontendAction.cpp
> @@ -76,6 +76,56 @@ public:
> }
> };
>
> +/// \Manages the lifetime of list of PluginASTAction instances
> +class PluginASTActionList {
> + typedef std::vector<PluginASTAction*> ActionList;
> + typedef std::vector<ASTConsumer*> ConsumerList;
> +
> + ActionList Actions;
> + CompilerInstance& CI;
> + llvm::StringRef InFile;
> +
> +public:
> + PluginASTActionList(FrontendPluginRegistry::iterator begin,
> + FrontendPluginRegistry::iterator end,
> + CompilerInstance &CI,
> + llvm::StringRef InFile)
> + : CI(CI), InFile(InFile) {
> + for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size();
> + i != e; ++i) {
> + for (FrontendPluginRegistry::iterator it = begin;
> + it != end; ++it) {
> + if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) {
> + llvm::OwningPtr<PluginASTAction> P(it->instantiate());
> + if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
> + Actions.push_back(P.take());
> + }
> + }
> + }
> + }
> +
> + ~PluginASTActionList() {
> + for (size_t i = 0; i < Actions.size(); ++i)
> + delete Actions[i];
> + }
> +
> + ConsumerList CreateASTConsumers(ASTConsumer* DefaultConsumer) {
> + ConsumerList Consumers;
> +
> + for (ActionList::iterator it = Actions.begin(), ie = Actions.end();
> + it != ie; ++it) {
> + PluginASTAction& A = (**it);
> + if (A.GetASTConsumerOrder() == PluginASTAction::BeforeDefault)
> + Consumers.push_back(A.CreateASTConsumer(CI, InFile));
> + Consumers.push_back(DefaultConsumer);
> + if (A.GetASTConsumerOrder() == PluginASTAction::AfterDefault)
> + Consumers.push_back(A.CreateASTConsumer(CI, InFile));
> + }
> +
> + return Consumers;
> + }
> +};
> +
> } // end anonymous namespace
>
> FrontendAction::FrontendAction() : Instance(0) {}
> @@ -98,28 +148,10 @@ ASTConsumer*
> FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
> if (CI.getFrontendOpts().AddPluginActions.size() == 0)
> return Consumer;
>
> - // Make sure the non-plugin consumer is first, so that plugins can't
> - // modifiy the AST.
> - std::vector<ASTConsumer*> Consumers(1, 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]) {
> - llvm::OwningPtr<PluginASTAction> P(it->instantiate());
> - FrontendAction* c = P.get();
> - if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
> - Consumers.push_back(c->CreateASTConsumer(CI, InFile));
> - }
> - }
> - }
> -
> - return new MultiplexConsumer(Consumers);
> + PluginASTActionList L(FrontendPluginRegistry::begin(),
> + FrontendPluginRegistry::end(),
> + CI, InFile);
> + return new MultiplexConsumer(L.CreateASTConsumers(Consumer));
> }
>
> bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
>
--
morita
More information about the cfe-commits
mailing list