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; }

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

  1. ZOJ 3963 Heap Partition set维护。给一个序列,将其划分成尽量少的序列,使每一个序列满足按照顺序构造二叉树,父母的值<=孩子的值。

    Heap Partition Time Limit: Seconds Memory Limit: KB Special Judge A sequence S = {s1, s2, ..., sn} i ...

  2. GCC编译器基础入门

    导语 GCC(GNU Compiler Collection,GNU 编译器套件) 是由 GNU 开发的编程语言编译器,支持C.C++.Objective-C.Fortran.Java.Ada和Go语 ...

  3. 字符编码与gcc 编译器的编码问题

    最近在 vscode 中借助 gcc 编译器来配置 c 语言开发环境时,发现中文编码存在乱码问题.再加上最近学习到多字节字符与宽字符,搅在一起,搞得很乱,就把自己的理解写下来,供有需者参考吧. 1. ...

  4. 利用GCC编译器生成动态链接库和静态链接库

    转载请标明:http://www.cnblogs.com/winifred-tang94/ 1.编译过程 gcc –fPIC –c xxx.c 其中-fPIC是通知gcc编译器产生位置独立的目标代码. ...

  5. GCC编译器编译链接

    在gcc编译器环境下,常见的文件扩展名的含义如下: .c:C源程序,经过预编译后的源程序也为.c文件,它可以通过-E参数输出. .h:头文件 .s:经过编译得到的汇编程序代码,它可以通过-S参数输出. ...

  6. GCC编译器使用

    一.GCC简介 通常所说的GCC是GUN Compiler Collection的简称,除了编译程序之外,它还含其他相关工具,所以它能把易于人类使用的高级语言编写的源代码构建成计算机能够直接执行的二进 ...

  7. GCC编译器和GDB调试器常用选项

    http://blog.csdn.net/u014328976/article/details/46745349 GCC编译器 gcc hello.c -o hello                 ...

  8. gcc编译器与基本类型3

    C语言发展史 1969年贝尔实验室 肯尼斯·蓝·汤普逊,丹尼斯·李奇开发了B语言 ->Unix,New B语言,改名C语言83年提出C语言标准 1989年十二月正式通过C语言标准,C89标准 C ...

  9. 如何使用gcc编译器

    开始... 首先,我们应该知道如何调用编译器.实际上,这很简单.我们将从那个著名的第一个C程序开始. #include <stdio.h> int main() { printf(&quo ...

随机推荐

  1. 环境安装——JDK安装

    @ 目录 开发环境-JDK安装 1. 下载地址 2. 安装JDK 3. 配置系统环境 开发环境-JDK安装 无论在我们开始学习Java或者入职第一天安装环境,这个你都是必备滴!下面是下载和安装JDK的 ...

  2. external-provisioner源码分析(3)-组件启动参数分析

    更多ceph-csi其他源码分析,请查看下面这篇博文:kubernetes ceph-csi分析目录导航 external-provisioner源码分析(3)-组件启动参数分析 本文将对extern ...

  3. 3、SpringBoot整合之SpringBoot整合JDBC

    SpringBoot整合JDBC 一.创建SpringBoot项目 选择Spring Web.JDBC API.MySQL Driver 二.在pom配置文件中修改JDBC版本,导入lombok &l ...

  4. 为什么要使用MongoDB?

    1.Mongo与Mysql简单对比 关系型数据库-MySQL 1.在不同的引擎上有不同的存储方式. 2.查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高. 3.开源数据库的份额在不断增加 ...

  5. 18、通过yum命令只下载rpm包不安装

    18.1.说明: 经常遇到服务器没有网络的情况下部署环境,或者创建自己的 yum 仓库等,这时就需要下载 rpm 包. 18.2.方法一,yumdownloader(推荐): 如果只想通过 yum 下 ...

  6. SpringCloud 微服务最佳开发实践

    Maven规范 所有项目必须要有一个统一的parent模块 所有微服务工程都依赖这个parent,parent用于管理依赖版本,maven仓库,jar版本的统一升级维护 在parent下层可以有 co ...

  7. AcWing 829. 模拟队列

    实现一个队列,队列初始为空,支持四种操作: (1) "push x" – 向队尾插入一个数x: (2) "pop" – 从队头弹出一个数: (3) " ...

  8. MyBatis:条件构造器QueryWrapper方法详解

    QueryWrapper 说明:      继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及 LambdaQueryWrapper, 可以通过 n ...

  9. Spring 依赖注入(DI) 的三种方式 和 对集合类型的注入

    // 分别省略了getter setter public class Student { private String name; private int age; private Teacher t ...

  10. XSS challenges 1-10

    学长发的xss靶场,刚好js学完了,上手整活. 这个提示说非常简单,直接插入就完事了 <script>alert(document.domain)</script> 第二关. ...