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

  1. int foo(int a) {
  2. if (a < 1)
  3. return a;
  4. else
  5. return 1;
  6. }

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

  1. ;; Function foo (foo, funcdef_no=0, decl_uid=1793, cgraph_uid=0, symbol_order=0)
  2. foo (int a)
  3. {
  4. int D.1800;
  5. if (a <= 0) goto <D.1798>; else goto <D.1799>;
  6. <D.1798>:
  7. D.1800 = a;
  8. goto <D.1801>;
  9. <D.1799>:
  10. D.1800 = 1;
  11. goto <D.1801>;
  12. <D.1801>:
  13. return D.1800;
  14. }

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

  1. (gdb) pt gimple_seq
  2. type = struct gimple {
  3. gimple_code code : 8;
  4. unsigned int no_warning : 1;
  5. unsigned int visited : 1;
  6. unsigned int nontemporal_move : 1;
  7. unsigned int plf : 2;
  8. unsigned int modified : 1;
  9. unsigned int has_volatile_ops : 1;
  10. unsigned int pad : 1;
  11. unsigned int subcode : 16;
  12. unsigned int uid;
  13. location_t location;
  14. unsigned int num_ops;
  15. basic_block bb;
  16. gimple *next;
  17. gimple *prev;
  18. } *
  19. (gdb) p debug(seq)
  20. if (a <= 0) goto <D.1798>; else goto <D.1799>;
  21. (gdb) p debug(seq->next)
  22. <D.1798>:
  23. (gdb) p debug(seq->next->next)
  24. D.1800 = a;
  25. (gdb) p debug(seq->next->next->next)
  26. 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。

  1. /* Insert SEQ after BB and build a flowgraph. */
  2. static basic_block
  3. make_blocks_1 (gimple_seq seq, basic_block bb)
  4. {
  5. gimple_stmt_iterator i = gsi_start (seq);
  6. gimple *stmt = NULL;
  7. bool start_new_block = true;
  8. bool first_stmt_of_seq = true;
  9. // 顺序遍历GIMPLE序列
  10. while (!gsi_end_p (i))
  11. {
  12. gimple *prev_stmt;
  13. prev_stmt = stmt;
  14. stmt = gsi_stmt (i);
  15. if (stmt && is_gimple_call (stmt))
  16. gimple_call_initialize_ctrl_altering (stmt);
  17. /* If the statement starts a new basic block or if we have determined
  18. in a previous pass that we need to create a new block for STMT, do
  19. so now. */
  20. if (start_new_block || stmt_starts_bb_p (stmt, prev_stmt))
  21. {
  22. if (!first_stmt_of_seq)
  23. gsi_split_seq_before (&i, &seq);
  24. bb = create_basic_block (seq, bb);
  25. start_new_block = false;
  26. }
  27. /* Now add STMT to BB and create the subgraphs for special statement
  28. codes. */
  29. gimple_set_bb (stmt, bb);
  30. /* If STMT is a basic block terminator, set START_NEW_BLOCK for the
  31. next iteration. */
  32. if (stmt_ends_bb_p (stmt))
  33. {
  34. ……
  35. start_new_block = true;
  36. }
  37. gsi_next (&i);
  38. first_stmt_of_seq = false;
  39. }
  40. return bb;
  41. }

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

  1. ;; Function foo (foo, funcdef_no=0, decl_uid=1793, cgraph_uid=0, symbol_order=0)
  2. ;; 1 loops found
  3. ;;
  4. ;; Loop 0
  5. ;; header 0, latch 1
  6. ;; depth 0, outer -1
  7. ;; nodes: 0 1 2 3 4 5
  8. ;; 2 succs { 3 4 }
  9. ;; 3 succs { 5 }
  10. ;; 4 succs { 5 }
  11. ;; 5 succs { 1 }
  12. foo (int a)
  13. {
  14. int D.1800;
  15. <bb 2> [0.00%]:
  16. if (a <= 0)
  17. goto <bb 3>; [0.00%]
  18. else
  19. goto <bb 4>; [0.00%]
  20. <bb 3> [0.00%]:
  21. D.1800 = a;
  22. goto <bb 5> (<L2>); [0.00%]
  23. <bb 4> [0.00%]:
  24. D.1800 = 1;
  25. <L2> [0.00%]:
  26. return D.1800;
  27. }

【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. WPF添加外边框,添加外边框虚线

    <Border Background="LightBlue" BorderBrush="Black"  BorderThickness="2&q ...

  2. 分布式事务与Seate框架(3)——Seata的AT模式实现原理

    前言 在上两篇博文(分布式事务与Seate框架(1)--分布式事务理论.分布式事务与Seate框架(2)--Seata实践)中已经介绍并实践过Seata AT模式,这里一些例子与概念来自这两篇(特别是 ...

  3. Kafka 总结学习

    Kafka Need No Keeper 最近在鹅厂工作中不断接触到Kafka,虽然以前也使用过,但是对其架构和发展过程总是模模糊糊,所以在回学校准备末考的时候找些资料总结一下. Kafka Need ...

  4. golang 写文件--详细解释

    1,不覆盖指定的文件 先看代码怎么写,下面再具体解释. func writeToFile(msg string) { f, err := os.OpenFile("/home/mingbai ...

  5. sql数据库新建作业,新建步骤时报错从 IClassFactory 为 CLSID 为 {AA40D1D6-CAEF-4A56-B9BB-D0D3DC976BA2} 的 COM 组件创建实例失败,原因是出现以下错误: c001f011。 (Microsoft.SqlServer.ManagedDTS)

    简单粗暴的重启sql数据库 其他网上找的方法 32位操作系统: 打开运行(命令提示符), 一.输入 cd c:\windows\system32 进入到c:\windows\system32路径中 二 ...

  6. 玩转STM32MP157-开发环境搭建

    (一)STM32MP 1.什么是 STM32MPU STM32MPU是 ST 推出的 Cortex-A7 + Cortex-M4 多核异构处理器 STM32MPU151 是单核 A7+M4,.STM3 ...

  7. 什么是WAF?

    1.什么是Web Application Firewall(WAF)? WAF或Web Application Firewall通过过滤和监控Web应用程序与Internet之间的HTTP流量来帮助保 ...

  8. 低代码开发LCDP,Power Apps系列 - 搭建入职选购电脑设备案例

    低代码简介 上世纪八十年代,美国就有一些公司和实验室开始了可视化编程的研究,做出了4GL"第四代编程语言",到后来衍生成VPL"Visual Programming La ...

  9. CentOS-Docker搭建MinIO(单点)

    下载镜像 $ docker pull minio/minio 创建相关目录 $ mkdir /home/minio/data /home/minio/config -p 运行镜像(自定义Access和 ...

  10. linux学习之路第五天(文件目录类第一部分)

    文件目录类 pwd 指令 基本语法 pwd (显示当前目录的绝对路径) Ls 指令 cd 指令 -代表的是上一级目录 mkdir指令 用于创建目录 基本语法 mkdir [选项] 要创建的目录 常用选 ...