• 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() << "
    ";
     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 << "
    ";
     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如下:

  • 相关阅读:
    设计模式---适配器模式
    【面经】2019-4-1 杭州边锋网络面经
    web前端基础——jQuery编程进阶
    web前端基础——jQuery编程基础
    web前端基础——初识HTML DOM编程
    web前端基础——初识JavaScript
    web前端基础——初识CSS
    web前端基础——初识HTML
    CentOS 6.5上安装python2.7、pip以及Python命令行补全和yum冲突解决
    Python中常用技巧整理
  • 原文地址:https://www.cnblogs.com/jourluohua/p/15012176.html
Copyright © 2020-2023  润新知