Hi, I'm sorry for spamming. But could anyone review this? On Sat, Apr 23, 2011 at 11:25 PM, MORITA Hajime <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
