[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