其他分享
首页 > 其他分享> > 控制流分析1

控制流分析1

作者:互联网

控制流分析

1.基本概念

1.1支配性问题

支配结点:如果每一条从流图的入口结点到结点n的路径都经过结点d,那么我们可称d支配n,记作d dom n。t特别地,每个结点都可支配其本身。

1.1.1 构建支配树

对于龙书上,其寻找支配结点算法如下:
输入:一个流图G,G的结点集合为N,边集合为E,入口结点是ENTRY;
输出:对于N中的每个结点n,给出D(n),即支配n的所有结点集合;
方法: 求出如下框架中数据流问题的解。输入流图的基本块就是结点。对于N中的所有结点n, D(n) = OUT[n];

域: N的幂集;

方向:前向;
传递函数: \(f_B(x) = x \bigcup \{B\}\)
边界条件:OUT[ENTRY] = {ENTRY};
交汇运算: \(\bigcap\);
方程式:\(OUT[B] = f_B(IN[B])\)
\(IN[B] = \bigwedge_{P, pred(B)}OUT[P]\)
初始化设置:OUT[B] = N;
在编译器设计中给出了具体的算法伪码:

n = |N| - 1; //表示所有的结点数目;
//初始化;
Dom(0) = {0}; //入口结点的支配结点初始化;
for (int i 1 to n) {
   Dom(i) = N;
}

//迭代更新的标记;
bool change = true;
while (change) {
change = false; //处理时先假定本次会处理干净,即不会发生变化;
for (int i = 1 to n) {
     temp = {i} + (\(\bigcap _{j \in preds(i)} Dom(j)\))
     if temp \(\not ={ Dom(i)}\) {
      Dom(i) = temp;
     change = true;
    }
}   

1.2顺序问题

深度优先搜索: 逐一访问流图中的所有结点,搜索过程从入口结点开始,并首先访问离入口结点最远的结点。这样搜索的过程形成一个深度优先生成树(Depth-first Spanning Tree, DFST) .。
先序遍历首先访问一个结点,然后从左到右递归地访问该结点的子结点。
后序遍历:首先递归的从左到右访问一个结点的子结点,然后再访问该结点本身。
深度优先排序(depth-first ordering):其会首先访问一个结点,然后在遍历该结点的最右子结点,在遍历这个结点的左边子节点,以此类推。其顺序和后序遍历顺序完全相反。在为流图生成构造生成树之前,可以选择把一个结点的哪个后继作为树中的最右子节点,哪个作为树中的下一个子结点。 这个在编译器设计中也叫作 逆后序(RPO, Reverse PostOrder).
下面是龙书上的相关算法:
输入: 一个流图G
输出:G中的一个DFST树和G中的一个深度优先排序
方法:递归调用过程search(n),这个算法首先把G的所有结点初始化为"unvisited", 然后调用search(\(n_0\)), 其中\(n_0\)是G的入口结点。 当它调用search(n)时,会先把n标记为"visited",避免再次访。c作为计数器,从G的结点总数一直倒计数到1。在算法执行是把c的值赋给结点n的深度优先编号dfn[n]。边的集合T形成了G的深度优先生成树。

void search(n)
{
  将n标记为"visited"
 for (n的各个后继s) {
   if (s为"unvisited")
   将边n->s加入到T中;
   search(s);
 }
  dfn[n] = c;
 c--;
}

main() {
 T = $\Phi; //边集合 $
  for (G的各个结点n) {
  把n标记为"unvisited";
  }
   c = G的结点个数;
   search(\(n_0\));
}

1.2.1深度优先生成树中的边

当一个流图构造DFST时,流图的边可以分为三大类:

  1. 前进边(advance edge),指的是那些从一个结点m到达m在树中的一个真后代结点的边。    
  2. 有些边从结点m到达m在树中的某个祖先(包括m本身),称这些边为后退边(retreating edge)。
  3. 对于有些边,在DFST中m和n都不是对方祖先,这种边称之为交叉边。交叉边一个重要的性质是:如果我们把一个结点的子节点按照他们被加入到树中的顺序从左到右排列,那么所有的交叉边都是从右到左的。

1.2.2 回边和可归约性

回边:指一条边a->b,它的头b支配了它的尾a。对于任何流图而言,任何回边都是后退边,但并不是所有的后退边都是回边。对于一个流图而言,其对应的任何深度优先生成树中所有后退边都是回边,那么称该流图是可归约的(reducible)。
意思就是,一个流图是可归约的,那么其所有DFST的后退边的集合都是相同的,并且就是流图的回边集合。但如果是不可归约的,那么必然存在一些后退边不是回边,那么这样的后退边集合在不同DFST中表现不同。因此,如果删除流图中所有回边后得到的流图仍然是带环的,那么该图就是不可归约的。反过来也成立。
在实际中,出现的流图几乎都是可归约的。如果只使用if-else-then/while-do/continue和break语句这样的结构化控制流语句,那么得到的程序的流图总是可归约的,即使是使用了goto语句,程序也经常是可归约的,因为程序员在逻辑上会使用循环和分支的方式思考问题。

1.2.3自然循环

从程序分析角度看,循环在源代码中以什么形式出现并不太重要,重要的是它们是否具有易被优化的性质。我们特别关系在循环中是否只有一个唯一的入口结点。如果是这样,编译器分析可以假设某些初始化条件在循环中的每次迭代的开头成立。这种优化机会引发定义"自然循环"的需求。
自然循环通过两种重要的性质来定义:

  1. 它必须具有一个唯一的入口结点,称为循环头。这个入口结点支配了循环中的所有结点,否则它就不会称为循环的唯一入口。
  2. 必须存在一条进入循环头的回边,否则控制流就不可能从"循环"中直接回到循环头,也就是说实际上并没有循环。

构造一条回边的自然循环:
输入:一个流图G和一条回边n->d;
输出:由回边n->d的自然循环中所有结点组成的集合loop;
方法: 令loop = {n, d}, 把d标记为visited,这样在搜索时不至于会越过结点d。从结点n开始对输入的反向控制流图进行深度优先搜索。把所有访问到的结点都加入到loop中,这个过程可以找到所有不经过d就可以到达n的结点。
除非两个循环具有相同的循环头,否则其要么是分离的,要么是一个嵌套在另一个中。
当自然循环具有相同的循环头且没有哪一个循环真正包含在另一个循环中,它们将被合并在一起,当做一个循环处理。

标签:分析,回边,结点,控制流,入口,归约,循环,流图
来源: https://www.cnblogs.com/zhanghanLeo/p/15362584.html