Clang教程之实现源源变化(4)
在这一节中,对loop-convert的例子进行了修改,以展示对ForStmt的处理。
这里主要是通过AST树结构的判断,增加对类似for(int i=0;i<5;i++)这种循环的循环界判断,在实际程序优化中,意义不是特别大,但是作为示例和练习还是不错的。
主要使用的test case是:
1 int foo(int a, int b, int *c) { 2 int ret = 0; 3 if (a > b) { 4 ret = a; 5 } else { 6 ret = b; 7 } 8 for (int temp = 0; temp < 100; ++temp) { 9 *c = (*c + temp); 10 } 11 return ret; 12 } 13 14 15 int main() { 16 int a = 1, b = 2; 17 int c = 0; 18 int d = foo(a, b, &c); 19 return 0; 20 }
对ForStmt的处理在Visitor的VisitForStmt中进行(前排提示,不要尝试在RecursiveASTVisitor中对节点进行修改,大的节点修改会破坏整个AST树的Context,这里的Visitor仅建议作为遍历读取使用)
1 //------------------------------------------------------------------------------ 2 // Tooling sample. Demonstrates: 3 // 4 // ForStmt Demo to show how to use ForStmt 5 // 6 // jourluohua (jourluohua@sina.com) 7 // This code is in the public domain 8 //------------------------------------------------------------------------------ 9 #include <sstream> 10 #include <string> 11 #include <map> 12 13 #include "clang/AST/AST.h" 14 #include "clang/AST/ASTConsumer.h" 15 #include "clang/AST/RecursiveASTVisitor.h" 16 #include "clang/Frontend/ASTConsumers.h" 17 #include "clang/Frontend/CompilerInstance.h" 18 #include "clang/Frontend/FrontendActions.h" 19 #include "clang/Rewrite/Core/Rewriter.h" 20 #include "clang/Tooling/CommonOptionsParser.h" 21 #include "clang/Tooling/Tooling.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 using namespace clang; 25 using namespace clang::driver; 26 using namespace clang::tooling; 27 28 static llvm::cl::OptionCategory ToolingSampleCategory("Tooling Sample"); 29 30 // By implementing RecursiveASTVisitor, we can specify which AST nodes 31 // we're interested in by overriding relevant methods. 32 class LoopBound { 33 public: 34 int initVal; 35 int maxVal; 36 int incVal; 37 LoopBound(){ 38 initVal = 0; 39 maxVal = 0; 40 incVal = 0; 41 } 42 }; 43 class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> { 44 public: 45 MyASTVisitor(Rewriter &R) : TheRewriter(R) {} 46 47 bool VisitForStmt(ForStmt *forStmt) { 48 // Only care about ForStmt statements. 49 // now we just judge for(int i=0;i<100;i++) case 50 VarDecl* initVar = nullptr; 51 Expr* initExpr = nullptr; 52 LoopBound* lpBound = new LoopBound(); 53 DeclStmt* DS = nullptr; 54 55 if ((DS = dyn_cast_or_null<DeclStmt>(dyn_cast<ForStmt>(forStmt)->getInit())) != 0 && DS->isSingleDecl() && 56 (initVar = dyn_cast<VarDecl>(DS->getSingleDecl())) != 0){ 57 initExpr = initVar->getInit(); 58 } 59 else { 60 return true; 61 } 62 IntegerLiteral* initInteg = dyn_cast_or_null<IntegerLiteral>(initExpr); 63 lpBound->initVal = initInteg->getValue().getSExtValue(); 64 65 Expr *expCond = dyn_cast<ForStmt>(forStmt)->getCond(); 66 if(isa<BinaryOperator>(expCond)) 67 { 68 BinaryOperator *binaryOpCond = dyn_cast<BinaryOperator>(expCond); 69 if(binaryOpCond->getOpcode() ==BO_LT) 70 { 71 Expr * condExpr = binaryOpCond ->getRHS(); 72 assert(condExpr && "condExpr "); 73 if(isa<IntegerLiteral>(condExpr)) 74 { 75 lpBound->maxVal=(dyn_cast<IntegerLiteral>(condExpr)->getValue()).getSExtValue(); 76 } 77 else 78 { 79 return true; 80 } 81 } 82 else 83 { 84 return true; 85 } 86 } 87 else 88 { 89 return true; 90 } 91 92 Expr *incExpr = dyn_cast<ForStmt>(forStmt)->getInc(); 93 assert(incExpr && "forStmt->getInc() cannot be NULL"); 94 if(isa<UnaryOperator>(incExpr)){ 95 UnaryOperator *unaryInc = dyn_cast<UnaryOperator>(incExpr); 96 if(unaryInc->isIncrementOp () ) //just like '++i' or 'i++', we think that they work same 97 { 98 lpBound->incVal = 1; 99 } 100 else 101 { 102 return true; 103 } 104 105 } 106 conditionMap[initVar] = lpBound; 107 return true; 108 } 109 110 std::map<VarDecl*, LoopBound*>& getCondMap(){ 111 return conditionMap; 112 } 113 114 115 private: 116 Rewriter &TheRewriter; 117 std::map<VarDecl*, LoopBound*> conditionMap; 118 }; 119 120 // Implementation of the ASTConsumer interface for reading an AST produced 121 // by the Clang parser. 122 class MyASTConsumer : public ASTConsumer { 123 public: 124 MyASTConsumer(Rewriter &R) : Visitor(R) {} 125 126 // Override the method that gets called for each parsed top-level 127 // declaration. 128 bool HandleTopLevelDecl(DeclGroupRef DR) override { 129 for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) { 130 // Traverse the declaration using our AST visitor. 131 Visitor.TraverseDecl(*b); 132 (*b)->dump(); 133 } 134 std::map<VarDecl*, LoopBound*> conditionMap = Visitor.getCondMap(); 135 if(conditionMap.size()>0){ 136 auto iter = conditionMap.begin(); 137 LoopBound *lpFirst = iter->second; 138 VarDecl* varFirst = iter->first; 139 llvm::errs()<<varFirst->getDeclName().getAsString()<<" ["<<lpFirst->initVal<<" " 140 <<lpFirst->maxVal <<" "<<lpFirst->incVal<<"]\n"; 141 } 142 else{ 143 llvm::errs()<<"conditionMap.size()==0\n"; 144 } 145 return true; 146 } 147 148 private: 149 MyASTVisitor Visitor; 150 }; 151 152 // For each source file provided to the tool, a new FrontendAction is created. 153 class MyFrontendAction : public ASTFrontendAction { 154 public: 155 MyFrontendAction() {} 156 void EndSourceFileAction() override { 157 SourceManager &SM = TheRewriter.getSourceMgr(); 158 llvm::errs() << "** EndSourceFileAction for: " 159 << SM.getFileEntryForID(SM.getMainFileID())->getName() << "\n"; 160 161 // Now emit the rewritten buffer. 162 TheRewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs()); 163 } 164 165 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 166 StringRef file) override { 167 llvm::errs() << "** Creating AST consumer for: " << file << "\n"; 168 TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); 169 return std::make_unique<MyASTConsumer>(TheRewriter); 170 } 171 172 private: 173 Rewriter TheRewriter; 174 }; 175 176 int main(int argc, const char **argv) { 177 llvm::Expected<CommonOptionsParser> op=CommonOptionsParser::create(argc, argv, ToolingSampleCategory); 178 179 ClangTool Tool(op.get().getCompilations(), op.get().getSourcePathList()); 180 181 // ClangTool::run accepts a FrontendActionFactory, which is then used to 182 // create new objects implementing the FrontendAction interface. Here we use 183 // the helper newFrontendActionFactory to create a default factory that will 184 // return a new MyFrontendAction object every time. 185 // To further customize this, we could create our own factory class. 186 return Tool.run(newFrontendActionFactory<MyFrontendAction>().get()); 187 }
结果展示如下: