【GCC编译器】将GIMPLE序列划分成基本块(Basic block),并构造控制流图

1. 首先介绍测试用例,这是一个简单的if-then-else结构,输入为 int 类型的单变量,输出为 int 类型的结果。如果条件 a < 1 成立,则将输入直接返回;如果条件不成立,则返回 1。

int foo(int a) {
    if (a < 1)
        return a;
    else
        return 1;
}

2. 我们用GCC编译上述用例,打印出构造CFG之前的GIMPLE序列。在划分BB块之前,所有GIMPLE表达式在同一个序列中。

;; Function foo (foo, funcdef_no=0, decl_uid=1793, cgraph_uid=0, symbol_order=0)

foo (int a)
{
  int D.1800;

  if (a <= 0) goto <D.1798>; else goto <D.1799>;
  <D.1798>:
  D.1800 = a;
  goto <D.1801>;
  <D.1799>:
  D.1800 = 1;
  goto <D.1801>;
  <D.1801>:
  return D.1800;
}

3. GIMPLE 序列是一段线性结构,以双向链表的形式保存。next指向下一条gimple表达式,prev指向上一条gimple表达式,label也是单独的一条gimple表达式。

(gdb) pt gimple_seq
type = struct gimple {
    gimple_code code : 8;
    unsigned int no_warning : 1;
    unsigned int visited : 1;
    unsigned int nontemporal_move : 1;
    unsigned int plf : 2;
    unsigned int modified : 1;
    unsigned int has_volatile_ops : 1;
    unsigned int pad : 1;
    unsigned int subcode : 16;
    unsigned int uid;
    location_t location;
    unsigned int num_ops;
    basic_block bb;
    gimple *next;
    gimple *prev;
} *

(gdb) p debug(seq)
if (a <= 0) goto <D.1798>; else goto <D.1799>;
(gdb) p debug(seq->next)
<D.1798>:
(gdb) p debug(seq->next->next)
D.1800 = a;
(gdb) p debug(seq->next->next->next)
goto <D.1801>;

4.  BB块构造算法:

    4.1 顺序遍历GIMPLE序列,把正在访问的stmt 的bb属性设置为当前BB。如果 stmt 是 label,则建立 label -> bb 的 map 关系,便于在 make_edges 时快速查找到 label 所在的 bb。

    4.2 如果遇到符合stmt_starts_bb_p 和stmt_ends_bb_p 条件的stmt,则创建一个新的BB。

/* Insert SEQ after BB and build a flowgraph.  */
static basic_block
make_blocks_1 (gimple_seq seq, basic_block bb)
{
  gimple_stmt_iterator i = gsi_start (seq);
  gimple *stmt = NULL;
  bool start_new_block = true;
  bool first_stmt_of_seq = true;
  // 顺序遍历GIMPLE序列
  while (!gsi_end_p (i))
    {
      gimple *prev_stmt;
      prev_stmt = stmt;
      stmt = gsi_stmt (i);
      if (stmt && is_gimple_call (stmt))
        gimple_call_initialize_ctrl_altering (stmt);

      /* If the statement starts a new basic block or if we have determined
         in a previous pass that we need to create a new block for STMT, do
         so now.  */
      if (start_new_block || stmt_starts_bb_p (stmt, prev_stmt))
        {
           if (!first_stmt_of_seq)
           gsi_split_seq_before (&i, &seq);
           bb = create_basic_block (seq, bb);
           start_new_block = false;
         }
      /* Now add STMT to BB and create the subgraphs for special statement
         codes.  */
      gimple_set_bb (stmt, bb);
      /* If STMT is a basic block terminator, set START_NEW_BLOCK for the
         next iteration.  */
      if (stmt_ends_bb_p (stmt))
        {
           ……
           start_new_block = true;
        }
      gsi_next (&i);
      first_stmt_of_seq = false;
    }
  return bb;
}

5. 完成BB的构造后,编译器会遍历每个BB的最后一条stmt(带有目标BB的label信息),根据stmt的GIMPLE_CODE类型,调用不同接口建立BB之间的edges。此后,GIMPLE序列整体的组织方式发生了改变,由线性序列变成了以BB块为节点的图结构。在每个BB 的内部,GIMPLE表达式仍然以链表的形式线性保存。

;; Function foo (foo, funcdef_no=0, decl_uid=1793, cgraph_uid=0, symbol_order=0)

;; 1 loops found
;;
;; Loop 0
;;  header 0, latch 1
;;  depth 0, outer -1
;;  nodes: 0 1 2 3 4 5
;; 2 succs { 3 4 }
;; 3 succs { 5 }
;; 4 succs { 5 }
;; 5 succs { 1 }
foo (int a)
{
  int D.1800;

  <bb 2> [0.00%]:
  if (a <= 0)
    goto <bb 3>; [0.00%]
  else
    goto <bb 4>; [0.00%]

  <bb 3> [0.00%]:
  D.1800 = a;
  goto <bb 5> (<L2>); [0.00%]

  <bb 4> [0.00%]:
  D.1800 = 1;

<L2> [0.00%]:
  return D.1800;

}
上一篇:HCIA复习


下一篇:Vue笔记(一):组件(component)