版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lidan113lidan/article/details/119336214

更多内容可关注微信公众号 

一、需要了解的基本概念

在gcc优化的过程中,不论是循环的优化还是数据流分析,支配树在其中都起到至关重要的作用:

* 对于循环来说,支配树对找出代码中的循环非常有用

* 对数据流分析来说,支配树对计算SSA_NAME中的phi函数插入点十分重要

支配树中的主要概念包括:

1) 必经结点(Dominator,又称支配结点): 如果从(如函数的)起始节点S0到结点n的所有有效路径都经过结点d, 那么结点d则成为结点n的必经结点,同时每一个结点都是自己的必经结点

简单的说,必经结点的意思就是,从S0开始,如果控制流走到了n,那么在此之前其一定走过了结点d, 此时记为dom(n) = d;

2) 严格必经结点: 若d是n的必经结点,且d!=n,则d是n的严格必经结点;

  3) 直接必经结点(Immediate Dominator,又称直接支配结点):

首先对于毕竟节点有定理如下: 如果d,e都是n的必经结点,则d一定是e的必经结点或e一定是d的必经结点;

而直接必经结点的定义为: 若i是n的严格必经结点,且i不是n的其他必经结点的必经结点,则i是n的直接必经结点;

简单说就是 i是距离n最近的n的(非!=n的)必经结点, 即为 idom(n) = i;

  4) 必经结点树(Dominator Tree,又称支配树):

一棵树中包含图中的所有节点, 但只为每个做一条从其直接支配节点到自身的边(也就是对于所有节点n,只有 idom(n) => n的边), 这样构成的一颗树即为此图的必经结点树;

对于流图来说, 由于其中的每一个结点都至少有一个必经结点(即起始结点), 且每个结点都只能有一个直接必经结点,故一个流图一定能画出其对应的必经结点树.且若流图中每个节点均可达,则对应的必经结点树中的每个结点也必可达. 以[1] C18.1.2为例,其中流图和其对应的支配树的关系如图:

   5) 必经结点边界(Dominance Frontier, 又称支配结点边界): 结点x的必经结点边界是所有符合下面条件的结点w的集合: x是w前驱的必经结点,但不是w的严格必经结点;

简单说就是: 首先支配结点边界是针对某个结点来说, 其是多个节点的集合; 支配结点是当前结点和当前结点前驱的其他后继结点可能的控制流汇集处(也是在转SSA过程中需要插入phi函数的位置), 以[1] C19.1.2为例:

其中结点5的必经结点为{4,5,12,13}, 这四个结点都是结点5和其他结点控制流的汇集处;


二、gcc中支配树相关结构体

支配树是针对流图的,而在gcc中则是针对函数的控制流图(CFG)的, 故一个函数的支配树信息最终是保存在函数内的(实际上是各个bb->dom中), 在gcc中和支配树相关的结构体有三个:

1) enum dom_state: 此枚举型中记录当前函数中支配树的状态

enum dom_state
{DOM_NONE,              /* 代表当前函数的支配树还没有计算 *DOM_NO_FAST_QUERY,     /* 代表当前函数的支配树已经计算好了(记录在各个bb->dom中), 但尚未建立快速查询 */DOM_OK                 /* 代表当前函数支配树的快速查询也建立好了(更新好了bb->dom.dfs_num_in/out字段), 只有支配树和快速查询均建立好了才会有DOM_OK状态 */
};

2) class dom_info: 此类可以保存一个支配树的完整信息,但在gcc中通常都是临时的保存支配树算法的结果(最终结果保存在bb->dom中)

class dom_info
{......TBB *m_dfs_parent;             /* Lengauer-Tarjan 算法中需要借助DFS来实现高效的支配树计算,而此结构体实际上是一个数组,其下标为函数CFG在DFS下每个结点的父节点编号 */TBB *m_key;TBB *m_path_min;TBB *m_bucket;TBB *m_next_bucket;TBB *m_dom;                    /* 这里同样是一个数组, 其中m_dom[x] 记录DFS中编号为x的结点的直接必经结点编号 */TBB *m_set_chain;unsigned int *m_set_size;TBB *m_set_child;TBB *m_dfs_order;              /* 记录基本块在DFS遍历中的编号,在DFS中编号从1开始 */TBB *m_dfs_last;               /* 指向 m_dfs_order中最后一个bb */basic_block *m_dfs_to_bb;      /* 记录DFS中每个节点对应的bb的 basic_block 结构体指针 */unsigned int m_nodes;bitmap m_fake_exit_edge;unsigned m_n_basic_blocks;     /* 记录当前支配树中的结点个数,实际上来自函数中基本块的个数 */bool m_reverse;                /* 代表当前记录的是否为后序支配信息, gcc中可以计算支配树和后序支配树两种, 此值来自参数 CDI_DOMINATORS/CDI_POST_DOMINATORS */basic_block m_start_block;     /* 记录当前函数的入口bb */basic_block m_end_block;       /* 记录当前函数的出口bb */
};

3) struct et_node: 此结构体实际上是代表元素树(Element Tree)中一个结点信息的,而在gcc中,每个bb均通过此元素来保存其支配树结点

struct basic_block_def {......struct et_node * dom[2];    /* 在基本块(bb)中, dom[0/1]分别记录此bb在其支配树和后序支配树(若有)中的结点信息, 整个函数的支配树信息实际上就是记录在由入口bb开始的各个bb的dom中 */......
}struct et_node
{void *data;                     /* 在支配树中,data指向当前支配结点对应的基本块bb的指针 */int dfs_num_in, dfs_num_out;    /* 记录此结点在其对应的树结点的DFS遍历过程中,进/出此结点时遍历到的结点编号. 对于支配树来说,这两个字段负责支配树的快速查询 */struct et_node *father;         /* 记录当前节点的父节点,在支配树中当前节点的父节点为其直接支配节点(idom)*/struct et_node *son;            /* 当前节点的第一个子节点指针,在支配树中则是最后插入的子节点 */struct et_node *left;           /* 当前结点的左右兄弟结点 */struct et_node *right;          /* The brothers of the node.  */struct et_occ *rightmost_occ;   /* The rightmost occurrence.  */struct et_occ *parent_occ;      /* The occurrence of the parent node.  */
};

三、gcc中支配树的计算

在gcc中是通过calculate_dominance_info来计算当前函数的支配树的, 在调用此函数之前要求当前全局的cfun记录当前要计算的函数, 计算结果会以一个个支配结点的形式记录在当前函数的各个基本块的bb->dom中, 而并不是以一个支配树结构体(如dom_info)保存的,函数定义如下:

/*此函数为当前函数cfun计算[后序]支配树信息(dir决定是否为后序),计算结果以支配结点的方式保存到cfun的各个bb->dom中,并将当前函数的支配树计算状态更新到 cfun->cfg->x_dom_computed (即dom_computed)中;若此函数已计算了支配树,则此函数需重新计算并验证之前计算的支配树的正确性,如果二者不匹配直接报错,匹配则返回不做任何修改.
*/
void calculate_dominance_info (cdi_direction dir)
{unsigned int dir_index = dom_convert_dir_to_idx (dir);      /* 先确定本次是要计算支配树(dir=0)还是后序支配树(dir=1),后序均以后序支配树举例 */if (dom_computed[dir_index] == DOM_OK)      /* 若当前函数已有支配树,则对当前函数重新计算支配树信息,并和原有的比较看是否正确 */{checking_verify_dominators (dir);       /* 重新计算的支配树(dom_info)和函数中已有的支配树(各bb->dom)信息匹配,则直接返回 */return;}if (!dom_info_available_p (dir))            /* 若当前函数的支配树是完全不可用的(DOM_NONE),则在此循环中重新计算支配树 */{gcc_assert (!n_bbs_in_dom_tree[dir_index]);          /* 支配树未计算时,cfg中支配节点数量应给为0 */basic_block b;FOR_ALL_BB_FN (b, cfun)                 /* 在开始计算前先为当前函数cfun的每个bb都分配一个元素树结点,以存储其在支配树中的支配结点的信息 */b->dom[dir_index] = et_new_tree (b);n_bbs_in_dom_tree[dir_index] = n_basic_blocks_for_fn (cfun);        /* 在cfg中记录当前支配树中结点的个数,支配树中结点个数正常和函数中的结点个数是相同的 */dom_info di (cfun, dir);                /* 创建并初始化一个临时的dom_info类,用来先保存整个支配树的计算结果,参数是要计算的函数和方向(支配树or后序支配树) */di.calc_dfs_tree ();                    /* DFS遍历当前函数中所有的结点,结果都存在这个临时的dom_info di中, 根据Lengauer-Tarjan 算法,快速计算必经节点的方法中首先就是要构建DFS */di.calc_idoms ();                       /* 按照LT算法, 计算出当前函数所有bb的直接支配结点,并将其保存在di.m_dom数组中 */FOR_EACH_BB_FN (b, cfun)                /* 遍历当前函数的所有bb, 将计算出的支配树信息转换为一个个支配结点的形式保存到当前函数的各个bb->dom中 */{if (basic_block d = di.get_idom (b))  /* 从dom_info di中获取基本块b的直接支配节点 d */et_set_father (b->dom[dir_index], d->dom[dir_index]);    /* 将idom(b) = d 这个信息记录到b/d两个基本块的 ->dom中(分别更新了d->dom[0]->son/ b->dom[0]->father等信息) */}dom_computed[dir_index] = DOM_NO_FAST_QUERY;    /* 标记当前函数的支配树状态为尚未开启快速查询 */}else                                        /* 若当前函数的支配树已经计算,只是尚未建立快速查询,则这里也是要重算并验证一遍支配树是否正确 */checking_verify_dominators (dir);         /* 重算并验证的流程实际上和if中的流程十分类似,只是最后的填充bb->dom变为了对比是否正确 */compute_dom_fast_query (dir);               /* 建立支配树的快速查询,实际上只是修改了各个bb->dom的 dfs_num_in/out字段, 以便于快速查找当前bb在dfs中包含的子结点编号 */
}

4. gcc中支配结点边界的计算

在gcc中支配结点边界的信息是通过compute_dominance_frontiers函数计算的,其定义如下:

void compute_dominance_frontiers (bitmap_head *frontiers)
{timevar_push (TV_DOM_FRONTIERS);compute_dominance_frontiers_1 (frontiers);timevar_pop (TV_DOM_FRONTIERS);
}

其具体实现就不展开了,此函数最关键的就是需要知道其返回的是一个n*n的二维数组,记录在frontiers指向的一个bitmap中, n为当前函数中基本块的个数, frontiers[x][y] = 1; 则代表对于基本块 x, 基本块y是其支配结点边界中的一个结点;


参考资料:

[1] 《现代编译原理—C语言描述》

静态单赋值(一)—gcc中的支配树相关推荐

  1. 一文入门Go静态单赋值(SSA)

    在上一篇文章<通过实例理解Go内联优化>[1]中,我们探讨了Go编译器在编译中端进行的内联优化.内联优化基于IR中间表示进行,不过Go编译过程不止有一种IR表示,这点和龙书<编译原理 ...

  2. 静态单赋值形式-概念简介

    文章目录 前言 静态单赋值(static single assignment)的概念 静态单赋值算法 前言 视频:北大-软件分析-公开课 文档:课件' | 静态单赋值形式-wiki 这里简单记录下. ...

  3. 数据流分析与 SSA | 什么是静态单赋值 SSA

    什么是静态单赋值 SSA. SSA 是 static single assignment 的缩写,也就是静态单赋值形式. 顾名思义,就是每个变量只有唯一的赋值.

  4. SSA(static single assignment)(静态单赋值)

    SSA属于控制流分析,所以有些相关的概念如控制流图,控制树,n控制m,直接控制和真控制需要参考如下文章: 控制流分析 引入 例如: SSA 一个问题: 这是肯定的,有了SSA,做出上面的判断不难. 上 ...

  5. 控制流分析之构建支配树

    控制流分析之构建支配树 引言 1 分析有向图 2 构建支配树 2.1 求最小半支配点 2.2 求最近支配点 引言 如上一个带有起始入口点的有向图为例,从A到Q的必经结点有A.L.M.Q,我们称其为Q的 ...

  6. layui 子页面写弹出框覆盖父页面,以及给弹框中的表单赋值

    咋说呢,因为对 layui 不太熟悉,这个弹出框搞了好久,看了好多解决方案,大致尝试了一下其中几种,在坑中无法自拔...总之终于搞出来了,在这里分享一下我的笔记. 着急的直接 戳这里 看解决代码. 尝 ...

  7. GCC源代码分析(1): GCC中的树

    转自:http://blog.chinaunix.net/uid-13800995-id-67956.html 本文分析GCC4.3.1的源代码.如某位牛人所说,我并不打算做"参考手册&qu ...

  8. 数据结构 - 静态单链表的实行(C语言)

    静态单链表的实现 1 静态链表定义 静态链表存储结构的定义如下: /* 线性表的静态链表存储结构 */ #define MAXSIZE 1000 /* 假设链表的最大长度是1000 */ typede ...

  9. 懵了,Java枚举单例模式比DCL和静态单例要好???

    点击关注公众号,实用技术文章及时了解 来源:liuchenyang0515.blog.csdn.net/article/ details/121049426 文章目录 双重校验锁单例(DCL) 为什么 ...

  10. [转] GCC 中的编译器堆栈保护技术

    以堆栈溢出为代表的缓冲区溢出已成为最为普遍的安全漏洞.由此引发的安全问题比比皆是.早在 1988 年,美国康奈尔大学的计算机科学系研究生莫里斯 (Morris) 利用 UNIX fingered 程序 ...

最新文章

  1. c语言报告程序分析报告,2012C语言程序分析报告.doc
  2. 基于订阅的服务通讯架构体系
  3. android tab 点击,TabLayout.Tab点击事件
  4. [ActionScript 3.0] 安全沙箱的类型sandboxType,判断当前程序是AIR还是web程序
  5. 手写注解实现SpringMVC
  6. 在UI程序设计中使用BackgroundWorker进行多线程异步处理
  7. UVA 167 - The Sultan's Successors
  8. hadoop 注解之 interfaceAudience interfaceStability
  9. 关于Excel常用函数做数据分析
  10. ubuntu 换软件源
  11. 详解FAT12文件系统
  12. java文件怎么保存_java 文件保存和打开
  13. 数据结构几个最简单的阐述完整版(带你入门:链表,栈,队列,哈希表,树)通俗易懂简单明了
  14. 计算机微机维修工四级理论知识试卷,计算机维修工中级理论知识试卷2
  15. linux文件查找操作
  16. 绝地求生玩家排名预测
  17. 浪潮nf5220服务器做系统,【浪潮NF5220参数】浪潮NF5220系列服务器参数-ZOL中关村在线...
  18. 牛客 小米校招 小明的字符串 循环队列
  19. 听李维谈JB2007有感 CodeGear能否重现辉煌?
  20. CRM系统五大技巧集成Excel为销售流程赋能

热门文章

  1. C# 程序开机自动启动
  2. 西部陆海新通道海铁联运通达中国六省市
  3. Office 2010 excel在打开两个表格的时候,仅能在一个窗口显示
  4. [转载] 【冬瓜哥手绘雄文】集群文件系统架构演变终极深度梳理图解
  5. m苹果放n篮子_M个相同苹果放到N个相同篮子里有多少种放法
  6. 2段代码实现在所有浏览器下显示特殊字体(附字体转换工具)
  7. python中文转化gb2321_使用Python进行中文繁简转换的实现代码
  8. java中输入密码为星号_怎么用c语言把输入的密码变成星号
  9. 智齿科技获投B轮融资5000万人民币
  10. Autumn中文文档3:接收客户端数据