数据流分析总结

本文深入解析了数据流分析中的活性分析、定值到达分析、可用表达式分析和VeryBusy表达式分析,介绍了转移函数、汇集函数和不同分析的用途,如寄存器分配、优化和错误检测。通过实例和方程阐述概念,并讨论了它们在编译器和程序优化中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文旨在总结常见数据流分析:活跃性(liveness)分析,常用表达式(Available Expressions)分析,very busy 表达式(VeryBusy Expressions)分析,定值到达(Reaching Defs)分析,文章中的各类概念定义并不是严格定义,主要是帮助自己理解。

数据流分析

数据流分析是编译器,程序分析,垃圾回收等技术的公共基础,数据流分析的直觉感受可以认为是,在程序的控制流图上(CFG),将每条语句视为CFG上的节点(又叫程序点),计算出节点前后的数据流信息(liveness, Reaching Defs,VeryBusy Expressions,Available Expressions)的情况,即节点前驱或后继的边上的信息,仿佛数据流信息在CFG上的边上“流动“”。

值得注意的是每种数据流分析重要的关注点有以下共同几点:
1 . 转移函数 (transfer function): 表示节点前后数据流信息关系的方程
2 . 汇集函数(Merging Function): 在程序分支点(条件语句,while语句)(convergence)前后的数据流信息的关系
3. 迭代分析方向,前向或者后向
4 . 算法初始值

个人理解:

  1. 数据流分析时,每种数据流计算的信息是不一样的,脑海中一定要清楚:经过程序点后,那些是新生成的信息(gen),那些旧的信息需要删除(kill),这些gen-kill信息一定会在转移函数上体现。

1. 活跃性分析

1.1 概念定义

if a variable is used at a program point p, then it must be alive immediately before p.
翻译:一个变量v在程序点p使用,那么在程序点p之前,它都是活跃的。

补充理解:

一个变量v从定义点到使用点,只要中间没有重定义x,x的生命周期就是定义点到使用点,

示例代码:

var x,y,z;
x = 2;  (定义点)
......   // n条语句,但不包括对x的赋值语句
z = x-4; (使用点)
//在使用点之前,x一定是活跃的,变量的生命周期从def到use,是左开右闭(def,use].
1.2 数据流分析方程

p : v = E

IN ( p ) = (OUT( p ) ∖ \setminus {v} ) ∪ \cup vars(E) (transfer function)
OUT ( p) = ∪ \cup IN( p s p_s ps) , p s p_s ps ∈ \in succ( p ) (merging function)

  • IN ( p ) : 经过程序点之前活跃变量的集合
  • OUT( p ) :经过程序点之后活跃变量的集合
  • vars(E) :表达式中使用的变量
  • succ( p ):在CFG中,程序点p的后继节点

解释:

  • 每个程序点有两个集合, IN ( p ) 和OUT ( p) ,代表了经过程序点之前和之后活跃变量的集合。
  • 转移函数:
  1. 首先从等式可以看出,它是从OUT信息推出IN信息,所以它是一个Backward(后向)分析,
  2. 另外,等式中的差运算 {v},表示等式左边的{v}, 活跃性在程序点p杀死了,需要删除(kill);并运算vars(E),程序点右边表达式使用的变量,在程序点之前一定是活跃的(Gen),所以加入集合IN ( p )。
  • 汇集函数: 汇集函数对于后向分析,比较关注多个后继节点IN ( p ) 对该节点OUT( p )的影响,因为liveness它是一个may的信息,所以汇集函数使用并运算。
1.3 用途
  1. 活跃性分析最重要的一个优化就是寄存器分配,该优化关心每个变量的生命周期,从体系结构最基本的知识,我们知道寄存器是处理器上读写速度最快的资源,寄存器分配要确保最经常使用的变量放在寄存器,不经常使用的放入内存,这部分的优化也是另外一个复杂问题,在我博客寄存器分配算法有相关笔记。
  2. 活跃性分析,可以编译报错阶段,找到未定义的变量,输出debug信息。

2. 定值到达分析

2.1 概念定义

if a program point p defines a variable v, then v reaches the point immediately after p.
如果程序点p定义了一个变量v,那么变量v的定义可以到达p以后的程序点。

补充理解:

对变量的赋值在编译器术语中又叫定值,在赋值号右边的变量叫变量的使用,定值到达分析,关心赋值语句有没有到达它的使用点, 例如下面的代码片段,x的定义有两个点,我们比较关心到底哪个的赋值语句,才是真正对use有用的。

示例代码:

var x,y,z;
x = 2;  (定义点1......   // n条语句,但不包括对x的赋值语句
x = 4;   (定义点2......   // n条语句,但不包括对x的赋值语句
z = x-4; (使用点)
2.2 数据流方程

p : v = E
OUT ( p) = (IN ( p ) ∖ \setminus {defs(v)} ) ∪ \cup {p} (transfer function)
IN ( p ) = ∪ \cup OUT( p s p_s ps) , p s p_s ps ∈ \in pred( p ) (merging function)

  • IN ( p ) : 经过程序点之前到达定值(reaching-defs)集合
  • OUT( p ) :经过程序点之后到达定值(reaching-defs)集合
  • defs(E) :表达式中的到达定值,
  • pred( p ):在CFG中,程序点p的前驱节点

解释:

  • 每个程序点有两个集合, IN ( p ) 和OUT ( p) ,代表了经过程序点之前和之后到达定值的集合。
  • 转移函数:
  1. 首先从等式可以看出,它是从IN 信息推出OUT信息,所以它是一个Forward(前向) 分析。
  2. 另外,等式中的差运算 {v},表示等式左边的{v}的到达定值在程序点p杀死了,需要删除(kill);并运算{p} , 程序点新生成的defs需要加入集合,这里有个小技巧,直接将defs等价于程序点,所以是并运算程序点。
  • 汇集函数: 汇集函数对于前向分析,比较关注多个前驱节点的OUT ( p)对该节点的IN ( p ) 的影响,因为Available Expression它是一个may的信息,所以汇集函数使用并运算。
2.3 用途

小结:

  1. 到达定值和活跃性分析,感觉上似乎很像,但是这两个分析从实例代码可以看出,它们的关注点是不同的,活跃性关心,变量从使用点,倒推回定义点,关心这个变量的生命周期;而定值到达分析,关心程序点的defs可以走多远。
  2. 到达定值分析可以做一些死代码删除,不过值得提出的是,在程序中间表示有SSA形式以后,活跃性分析和定值到达分析直接在ssa形式上表现出来了,详见我的博文SSA生成算法

3. 可用表达式分析

3.1 概念定义

if an expression is used at a point p, then it is available immediately after p, as along as p does not redefine any the variables that the expression uses.
翻译:如果一个表达式在程序点p使用(出现),那么在程序点p以后,它都是可用的,只要程序点p没有重定义表达式使用的变量。

补充理解:

如果一个表达式在多个程序点出现,我们自然会想到,它们是不是相同的,因为如果是相同的话,只需计算一次表达式的值,后面的程序点直接使用该值就可以了。如下面的例子,程序点1,2,3都用了相同的表达式(a+b), 唯一的区别就是程序点12之间没有更改表达式里面变量的值,而程序点23之间,变量a的值改变了.

示例代码:

var x,y,z,a,b;
x = a+b;         // 程序点 1
y = a*b;
if (y > a+b){   // 程序点 2
	a = a+1
	x = a+b;    // 程序点 3
}
3.2 数据流方程

p : v = E
OUT ( p) = (IN ( p ) ∪ \cup {E} ) ∖ \setminus {Expr(v)} (transfer function)
IN ( p ) = ∩ \cap OUT( p s p_s ps) , p s p_s ps ∈ \in pred( p ) (merging function)

  • IN ( p ) : 经过程序点之前可用表达式(available expressions)集合
  • OUT( p ) :经过程序点之后可用表达式(available expressions)集合
  • Expr(v) :含变量v的可用表达式集合
  • pred( p ):在CFG中,程序点p的前驱节点

解释:

  • 转移函数:
  1. 首先从等式可以看出,它是从IN 信息推出OUT信息,所以它是一个Forward(前向) 分析。
  2. 另外,并运算 {E} , 表示程序点使用的表达式需要加入集合;等式中的差运算Expr(v),表示,需要删除(kill)含变量v的可用表达式;
  • 汇集函数: 汇集函数对于前向分析,比较关注多个前驱节点的OUT ( p)对该节点的IN ( p ) 的影响,因为Available Expression它是一个Must的信息,所以汇集函数使用交运算。

值得注意的是条件表达式也是表达式,需要加入计算,而活跃性分析与定制到达分析只关心define的表达式,不包含条件表达式计算。

3.3 分析用途

条件表达式分析之后的优化可以做一些表达式化简相关的操作。

4.VeryBusy 表达式分析

4.1 概念定义

if an expression E is very busy at a program point if it will be computed before the program terminates along any path that goes from that point to he end of the program.

补充理解:
非常忙(verybusy)表达式是可用表达式一种特殊情况,当某个表达式在控制流图的各个分支中都有出现,我们针对这个表达式做一些化简操作,但是如何得到这个VeryBusy的表达式呢?从后向前倒退出各个程序点前后的可用表达式信息,如果在各个分支都有出现的表达式,后面就可以做优化操作,强调一下,Verybusy分析,只是从后往前分析出各程序点的可用表达式,判断是否在各分支都有,不是Verybusy表达式数据流分析的工作。

示例代码:

var x,a,b;
x = input;         
a = x-1;
b = x-2;
while(x>0){
  print a*b-x;  //程序点1: a*b
  x = x-1;
}
print a*b;  //程序点2: a*b
4.2 数据流方程

p : v = E
OUT ( p) = (IN ( p ) ∖ \setminus {Expr(v)} ) ∪ \cup {E} (transfer function)
IN ( p ) = ∩ \cap OUT( p s p_s ps) , p s p_s ps ∈ \in succ( p ) (merging function)

  • IN ( p ) : 经过程序点之前非常忙表达式(very busy expressions)集合
  • OUT( p ) :经过程序点之后非常忙表达式(very busy expressions)集合
  • succ( p ):在CFG中,程序点p的后驱节点
  • Expr(v) :含变量v的Verybusy表达式集合

解释:

  • 转移函数:
  1. 首先从等式可以看出,它是从IN 信息推出OUT信息,所以它是一个Forward(前向) 分析。
  2. 另外,等式中的差运算Expr(v),表示,需要删除(kill)含变量v的非常忙表达式;并运算 {E} , 表示程序点使用的表达式需要加入集合;
  • 汇集函数:汇集函数对于后向分析,比较关注多个后继节点IN ( p ) 对该节点OUT( p )的影响,因为Available Expression它是一个Must的信息,所以汇集函数使用交运算。

值得注意的是条件表达式也是表达式,需要加入计算,而活跃性分析与定制到达分析只关心define的表达式,不包含条件表达式计算。

4.3 分析用途

可用表达式和非常忙表达式分析都是关注于表达式是否可化简, 差别在于一个是前向分析,一个是后向分析。

5. 总结

数据流分析,是编译器知识领域比较重要和成熟的领域,在60-70年代就基本完成了问题框架的理论构建,2007图灵奖就颁给了Frances Allen,表彰她在数据流分析的贡献。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值