电子说
为了有效地优化代码,编译器需要在程序的各个节点建立并求解与信息有关的方程来收集数据流信息,并将这些信息分发给流程图的每个块,这个过程被称为数据流分析。
通过检查程序的一部分(基本程序块),编译器可以执行一些优化。如下图所示:
所以该基本程序块可以被优化为:x = 3。
但是有一些优化必须通过检查整个程序来实现。如下图所示:
从图中可以看出:d3对c的初始化赋值是无用的,x的值恒定为6,d6可以简化为:c = 7。
从上面这个例子可以看出,仅通过一两个连续语句,编译器无法发现这些优化信息。必须通过更加全面的数据流分析,以便编译器在程序的每个节点都了解以下内容:
这篇文章将会介绍一种数据流分析方法——区域分析,在正式开始之前,我们先来了解几个概念:
以下图为例,如果想要到达2、3或4,必须要先通过1,所以在{1,2,3,4}集合中1占支配地位,1严格支配集合{2,3,4}。
流图的区域是节点N和控制流边E的集合,必须满足以下条件:
以下图为例,节点B1、B2、B3和B4都可以单独看做一个区域(图(a));节点B1和B2以及边B1->B2和循环边B2->B1组成的循环也是一个区域(图(b));根据区域的第三个条件,循环边可以不包含在区域内,所以节点B1和B2以及边B1->B2也可以形成一个区域(图(c))。
然而,图(d)中节点B3、B4以及边B3->B4组成的子图(虚线部分)不形成区域,因为控制流既可以通过节点B3处进入子图,也可以通过节点B4进入子图。B3和B4都无法做到完全支配另一个,不符合区域的第一个条件。即使我们选择B3作为头h,也不符合区域的第二个条件。因为B1可以不经过B3,沿着边B1->B4到达B4,根据区域的第二个条件,B1应该在该“区域”中,但这显然不正确。
在一个语句之前和之后的数据流值受该语句的语义约束,即语句前后的程序点的数据流值受该语句语义的约束,这种约束关系称为传递函数。基本块的传递函数表示为(以下都以Reaching Definitions 为例):F(x) = Gen U (x - Kill)
其中,x表示基本块B的输入;F(x)表示基本块B的输出;kill表示被基本块B中各语句杀死的变量的集合;Gen表示基本块中没有被各语句杀死的定值的集合。
上图显示了流图中各基本块的Gen和Kill集合。以基本块B1为例,该基本块有三条语句:
所以基本块B1的Gen为{d1,d2,d3},kill为{d4,d5,d6,d7}。
对于区域分析,构造更大的区域实际上就是更新了Gen和Kill集合的值。
为了使区域分析发挥作用,我们需要对区域中传递函数集的属性做出某些假设。具体来说,我们需要对传递函数进行三个基本操作: 组合 、汇聚和 闭包 。
节点序列的传递函数可以通过各个节点的传递函数的组合来导出。设F1和F2是两个节点的传递函数,执行F1后执行F2的效果用F2(F1(x))表示:
汇聚用于导出执行路径不同,但输入端点与输出端点相同的节点。一般用F1(x)∧F2(x)表示:
如果F表示循环的传递函数,那么Fn表示循环n次的效果。在迭代次数未知的情况下,我们必须假设循环可以执行0次或多次。我们用F*(x)表示这样一个循环的传递函数,则F的闭包如下图所示:
可简化流图是指那些可以通过以下两个规则转换简化为单个节点的流图:
如上图所示:R是执行T1规则后形成的新区域,原区域n的头结点H也是新区域R的头结点,n中H到每一个基本块B的传递函数为Fn,B,则在新区域R中的传递函数为:
T2:删除顶点
如果有一个节点n具有唯一的前置节点m,则将m和n合并[2]。
如上图所示:R是执行T2规则后形成的新区域,对于n中的基本块B,传递函数未改变(FR,B = Fn,B);对于m中的基本块:
为了构建区域的层次结构,我们需要识别循环。在可简化流图中(这里,我们假设流程图都是可简化的),任何两个循环要么是不相交的,要么一个嵌套在另一个循环中。可简化流图解析到循环层次结构时,先将每个基本块本身作为一个区域。我们将这些区域称为叶区域。然后,从里到外排序循环,即从最里面的循环开始。处理循环时,我们通过两个步骤将整个循环替换为一个节点:
通过重复上述操作,我们可以逐渐将大的循环减少到单个节点。由于可简化流图的循环是嵌套的或不相交的,因此循环区域的节点可以表示在此简化过程中构建的一系列流图中循环的所有节点。
最终,所有循环都被简化为单个节点。此时,流程图有两种情况:一是简化为单个节点;二是有几个节点剩余,具有HO循环(即,简化的流图是多个节点的非循环图)。在前一种情况下,我们完成了区域层次结构的构建,而在后一种情况下,我们为整个流图再次构建出一个主体区域。
以下图为例,图(a)为控制流图。此流程图中有一个循环边(B4->B2)。区域的层次结构如图(b)所示,共有8个区域:
对于不可简化流图,我们建议使用迭代数据流分析算法来进行数据流分析。但是如果只是偶尔处理不可简化流图的话,可以使用节点分裂方法进行分析。
下面图(a)就是一个典型的不可简化流图。R2和R3之间存在循环,但R2和R3又都不占支配地位,导致我们无法进一步解析该图。我们选择一些区域R(如R2),该区域R具有多个前置节点(R2的前置节点为R1和R3),并且不是整个流图的头。如果有k个前置节点,则制作流图R的k个副本,并将每个前置节点连接到R的不同副本。这里需要注意,只有区域的头才可能有该区域之外的前置节点。在识别新的循环边并构建其区域后,这种节点分裂使得区域数量减少。由此产生的流图可能仍然不可简化,但通过分裂阶与新的循环被识别并折叠到区域的阶段交替使用,我们最终只剩下一个区域,即流图已经被约化。
图(b)中所示的分裂将边R2b->R3变成了循环边,此时R3支配R2b,这两个区域可以合并为一个新区域。由此产生的三个区域(R1、R2a和新区域)形成一个非循环图。此时,我们可以将整个流程图简化为单个区域。一般来说,可能需要额外的拆分,在最坏的情况下,基本块的总数可能会成为原始流图中块数的指数。
本文简单介绍了一种解决数据流问题的方法——区域分析。在区域分析过程中,我们首先为基本块创建传递函数,然后通过组合、汇聚和闭包等操作总结加大区域的传递函数,最终构造出整个数据流图的传递函数。通过区域分析等数据分析的方法,可以发现更多的优化机会,如将代码从循环内部移动到循环外部、冗余的代码删除等。
全部0条评论
快来发表一下你的评论吧 !