顯示具有 cfg 標籤的文章。 顯示所有文章
顯示具有 cfg 標籤的文章。 顯示所有文章

2012年10月9日 星期二

LLVM : Control Flow Graph

主要紀錄 LLVM Basic Block 走訪方式

建議可搭配 LLVM 寫一個 pass - 教學入門篇 一起玩
另外這是 GCC 的對照組GCC : Control Flow Graph

0. 使用前注意

通常在 LLVM 裡面你會是在某個 Pass 裡面去走訪 CFG,
而這個時候你必須明確告訴 LLVM 你有使用 CFG, 然後他在呼叫你的 pass 之前就一定會幫你準備好
要作到這件事則必須把以下函數加入到你的 Pass 的那個 struct/class 中
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
  AU.setPreservesCFG();
}

1. 走訪該函數所有的 Basic Block

Function F;

for (Function::iterator itr = F.begin();
     itr != F.end();
     ++itr) {
  BasicBlock &bb = *itr;
}

2. 走訪 basic block 的所有 successor

BasicBlock bb;
for (succ_iterator itr = succ_begin(&bb);
     itr != succ_end(&bb);
     ++itr) {
     BasicBlock *succ = *itr;

}

3. 走訪 basic block 的所有 Predecessor

BasicBlock bb;
for (pred_iterator itr = pred_begin(&bb);
     itr != pred_end(&bb);
     ++itr) {
     BasicBlock *pred = *itr;

}

4. 取得 Function 的 Entry Basic Block

要注意的是收的變數最好是 Reference !
Function F;

BasicBlock &bb = F.getEntryBlock();

5. Basic Block中所有指令

BasicBlock bb;
for (BasicBlock::iterator itr = bb.begin();
     itr != bb.end();
     ++itr) {
  Instruction &inst = *itr;
}

跳過所有 PHI Node

BasicBlock bb;
for (BasicBlock::iterator itr = bb.getFirstNonPHI();
     itr != bb.end();
     ++itr) {
  Instruction &inst = *itr;
}
另外還有getFirstNonPHIOrDbg () 及 getFirstNonPHIOrDbgOrLifetime () 可以跳過除錯資訊及Lifetime 的資訊

6. 走訪一個函數裡面所有 BB 中的指令之FunctionPass 範例程式碼

#define DEBUG_TYPE "hello"
#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CFG.h"
using namespace llvm;

STATISTIC(HelloCounter, "Counts number of functions greeted");

namespace {
  // Hello - The first implementation, without getAnalysisUsage.
  struct Hello : public FunctionPass {
    static char ID; // Pass identification, replacement for typeid
    Hello() : FunctionPass(ID) {}

    virtual bool runOnFunction(Function &F) {
      ++HelloCounter;
      errs() << "Hello: ";
      errs() << F.getName() << '\n';
      for (Function::iterator itr = F.begin();
           itr != F.end();
           ++itr) {
        BasicBlock &bb = *itr;
        errs() << "Basic block :" << bb.getName () << "\n";
        errs() << "successor : {";
        for (succ_iterator ssitr = succ_begin(&bb);
             ssitr != succ_end(&bb);
             ++ssitr) {
          BasicBlock *ss = *ssitr;
          errs() << ss->getName() <<", ";
        }
        errs() << "}\n";
        errs() << "predecessors : {";
        for (pred_iterator ssitr = pred_begin(&bb);
             ssitr != pred_end(&bb);
             ++ssitr) {
          BasicBlock *ss = *ssitr;
          errs() << ss->getName() <<", ";
        }
        errs() << "}\n";
        for (BasicBlock::iterator iitr = bb.begin();
             iitr != bb.end();
             ++iitr) {
          Instruction &inst = *iitr;
          errs() << inst << '\n';
        }

      }
      return false;
    }
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
    }
  };
}

char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass");

2012年8月15日 星期三

GCC : Control Flow Graph

趁著記憶新鮮的時候先來紀錄一下

每個 Compiler 裡面的重要基礎設施之一就是 Control Flow Graph, 也就是 Basic Block 及其 Edge, 而這邊大概介紹一下 GCC 內部的資料結構及走訪方式, 主要參考來源是 GCC Source Code 及 GCC Internal.
GCC 的 Basic Block 在實作上要比較注意的一點是 GIMPLE 及 RTL 都會使用同樣得資料結構來表示, 但 RTL 的部份照慣例一樣是 expand 完後才會有, 而且同一個時間點只會是 GIMPLE 或 RTL

詳細的程式碼則可到 gcc/basic-block.h 裡面撈

1. Basic Block

在整個 CFG 中有兩個特別的 Block 一個是 ENTRY_BLOCK, 一個是 EXIT_BLOCK 他們分別可由 ENTRY_BLOCK_PTR EXIT_BLOCK_PTR 取得

在 basic_block 這個 struct 中有 next_bb 及 prev_bb 兩個欄位, 但沒事請直接別碰這兩個傢伙, 要走訪所有的 Basic Block 或要找兒子/父親的話下面有一些範例程式碼

走訪所有 Basic Block

basic_block bb;

FOR_EACH_BB (bb) {
  /* do something */
}

要注意的一點是有個東西叫 FOR_ALL_BB, 差別在於他會走到 ENTRY_BLOCK 跟 EXIT_BLOCK 而 FOR_EACH_BB 不會
basic_block bb;

FOR_ALL_BB (bb) {
  /* do something */
}

找父親

basic_block bb;
edge e;
edge_iterator ei;
FOR_EACH_EDGE(e, ei, bb->preds){
  /*
   * get src by `e->src`
   * get dest by `e->dest`
   */
}


找兒子


basic_block bb;
edge e;
edge_iterator ei;
FOR_EACH_EDGE(e, ei, bb->succs){
  /*
   * get src by `e->src`
   * get dest by `e->dest`
   */
}

走訪該 BB 的所有 RTL INSN

rtx insn;
basic_block bb;
FOR_BB_INSNS(bb, insn) {
  /* do something with insn */
}

走訪該 BB 的所有 GIMPLE stmt 及 PHI node


basic_block bb;
gimple_stmt_iterator si;

for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) {
  gimple phi = gsi_stmt (si);
  print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
}
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) {
  gimple stmt = gsi_stmt (si);
  print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}


2. Edges

Edge flag 如下
以下是從 basic-block.h 那邊抽出來的, 跟 GCC Internal 描述的是不太一樣的東西
  1. EDGE_FALLTHRU : 這個 edge 不會產生額外的 jump, 代表 src/dest 之後一定會放在一起
  2. EDGE_ABNORMAL : Indirect jump 或著是 Exception Handling 的 Edge
  3. EDGE_ABNORMAL_CALL : 呼叫一個可能會發生 Exception 的函數或著是 No return 的函數(例如呼叫 exit 或 abort)
  4. EDGE_EH : 處理 Exception Handling 的 Edge
  5. EDGE_FAKE : 他是一個不是 edge 的 edge! 拿來給 profiling 時用的
  6. EDGE_DFS_BACK : 是一個 back edge
  7. EDGE_CAN_FALLTHRU : 可以弄成 EDGE_FALLTHRU
  8. EDGE_IRREDUCIBLE_LOOP : irreducible loop 裡面的 edge
  9. EDGE_SIBCALL : 摳到 sibcall
  10. EDGE_LOOP_EXIT : 迴圈的終點
  11. EDGE_TRUE_VALUE : 當 Conditional Branch 的 Condition 為 True 時的目的地
  12. EDGE_FALSE_VALUE : 當 Conditional Branch 的 Condition 為 False 時的目的地
  13. EDGE_EXECUTABLE : 作 SSA-CCP 最佳化的時候用的, 其他時候請不要亂用
  14. EDGE_CROSSING : Cold code 與 Hot code 交界點
  15. EDGE_PRESERVE : 這個 Edge 不准砍!!!
  16. EDGE_ALL_FLAGS : 代表所有 flag
  17. EDGE_COMPLEX : EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH | EDGE_PRESERVE, 很複雜就對了!