Clang教程之实现源源变化(5)

Clang教程之实现源源变化(5)

其实我也没想到会有这一节。一直有人在说AST上只有抽象的语法结构,没有CFG信息,不能实现某某功能等等,但就实际来说,目前的clang上边,通过AST的Anslysis也能实现一些控制流相关的东西,确实没有IR上进行比较方便和功能丰富。

先介绍下这一节要用到的一个库CFG,在clang/lib/Analysis文件夹下边,在这个文件夹下,除了CFG还有可达分析(ReachabilityAnalysis)、活跃变量(LiveVariable)、ThreadSafety和未初始化(UninitizedValues)分析等工具,本版本(13.0)功能相比之前4.0版本有了非常大的提升。

先介绍下使用的测试代码,其实是从Clang的UserManual上拷过来的。https://clang.llvm.org/docs/InternalsManual.html#basic-blocks

 1 int foo(int x) {
 2   x = x + 1;
 3   if (x > 2)
 4     x++;
 5   else {
 6     x += 2;
 7     x *= 2;
 8   }
 9 
10   return x;
11 }
View Code

其对应AST为

 

 

 从CFG(这里提供了一个CFGBlock的功能)的角度来说,因为是foo函数,可以大胆猜一下,肯定会有一个函数入口,一个函数出口,然后函数体里边有一个“x = x + 1;”的Block块,然后if/else的TrueBody和FalseBody又是两个Block,可能还有一个条件判断的Block(也可能是条件判断和“x = x +1;”这个Block合并到了一起)。

下边给出主要的实现代码:

  1 //------------------------------------------------------------------------------
  2 // Tooling sample. Demonstrates:
  3 //
  4 // CFG Demo to show how to use CFG
  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 #include "clang/Analysis/CFG.h"
 24 #include "clang/Analysis/CFGStmtMap.h"
 25 #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
 26 #include "clang/Analysis/Analyses/CalledOnceCheck.h"
 27 #include "clang/Analysis/Analyses/Consumed.h"
 28 #include "clang/Analysis/Analyses/ReachableCode.h"
 29 #include "clang/Analysis/Analyses/ThreadSafety.h"
 30 #include "clang/Analysis/Analyses/UninitializedValues.h"
 31 #include "clang/Analysis/AnalysisDeclContext.h"
 32 
 33 using namespace clang;
 34 using namespace clang::driver;
 35 using namespace clang::tooling;
 36 
 37 
 38 static llvm::cl::OptionCategory ToolingSampleCategory("Tooling Sample");
 39 
 40 // Implementation of the ASTConsumer interface for reading an AST produced
 41 // by the Clang parser.
 42 class MyASTConsumer : public ASTConsumer {
 43 public:
 44   MyASTConsumer(Rewriter &R) {
 45   }
 46 
 47   // Override the method that gets called for each parsed top-level
 48   // declaration.
 49   bool HandleTopLevelDecl(DeclGroupRef DR) override {
 50     for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) {
 51       // Traverse the declaration using our AST visitor.
 52       (*b)->dump();
 53       // Construct the analysis context with the specified CFG build options.
 54       AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, *b);
 55       // Don't generate EH edges for CallExprs as we'd like to avoid the n^2
 56       // explosion for destructors that can result and the compile time hit.
 57       if(&AC == nullptr){
 58         continue;
 59       }
 60       AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true;
 61       AC.getCFGBuildOptions().AddEHEdges = false;
 62       AC.getCFGBuildOptions().AddInitializers = true;
 63       AC.getCFGBuildOptions().AddImplicitDtors = true;
 64       AC.getCFGBuildOptions().AddTemporaryDtors = true;
 65       AC.getCFGBuildOptions().AddCXXNewAllocator = false;
 66       AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
 67 
 68       AC.getCFGBuildOptions().setAllAlwaysAdd();
 69 
 70       if(CFG *cfg = AC.getCFG()){
 71         cfg->dump(LangOptions(), false);
 72       }
 73     }
 74     return true;
 75   }
 76 
 77 private:
 78 };
 79 
 80 // For each source file provided to the tool, a new FrontendAction is created.
 81 class MyFrontendAction : public ASTFrontendAction {
 82 public:
 83   MyFrontendAction() {}
 84   void EndSourceFileAction() override {
 85     SourceManager &SM = TheRewriter.getSourceMgr();
 86     llvm::errs() << "** EndSourceFileAction for: "
 87                  << SM.getFileEntryForID(SM.getMainFileID())->getName() << "\n";
 88 
 89     // Now emit the rewritten buffer.
 90     TheRewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs());
 91   }
 92 
 93   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
 94                                                  StringRef file) override {
 95     llvm::errs() << "** Creating AST consumer for: " << file << "\n";
 96     TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
 97     return std::make_unique<MyASTConsumer>(TheRewriter);
 98   }
 99 
100 private:
101   Rewriter TheRewriter;
102 };
103 
104 int main(int argc, const char **argv) {
105   llvm::Expected<CommonOptionsParser> op=CommonOptionsParser::create(argc, argv, ToolingSampleCategory);
106   
107   ClangTool Tool(op.get().getCompilations(), op.get().getSourcePathList());
108 
109   // ClangTool::run accepts a FrontendActionFactory, which is then used to
110   // create new objects implementing the FrontendAction interface. Here we use
111   // the helper newFrontendActionFactory to create a default factory that will
112   // return a new MyFrontendAction object every time.
113   // To further customize this, we could create our own factory class.
114   return Tool.run(newFrontendActionFactory<MyFrontendAction>().get());
115 }

从代码中可以看到主要修改的是DeclRef的循环处理部分,大概流程分为以下几步:

1. 新建一个针对Decl*的AnalysisDeclContext

2. 添加针对分析的Options(尤其是setAllAlwaysAdd)

3. 得到针对这个Decl的CFG

最后得到的CFG如下:

 

posted @ 2021-07-14 17:53  转换无极限  阅读(411)  评论(0编辑  收藏  举报