五个要素的含义
方向(Direction, D)
指明数据流分析是沿着控制图向前 还是 向后 传播信息
- 前向分析:从程序入口向出口传递信息(如可用表达式、到达定义)
- 后向分析:从程序出口向入口传递信息(如活跃变量、非常忙表达式)
值(Value, V)
每个程序点(基本块入口/出口) 上需要记录的数据事实,通常是一个集合或映射
所有可能的值构成一个格, 格中的元素具有偏序关系
例如:
- 可用表达式分析中,V 是表达式的集合。常数传播中,V是变量到常数值的映射
转换函数(Transfer Function, F)
描述一个基本块如何改变数据流值。
对于前向分析,函数 f(in) = out 。对于后向分析, f(out) = in
- 转换函数必须是单调的,以保证迭代收敛
- 例如,在可用表达式分析中,基本块会"生成"新表达式(如
a + b), 并杀死哪些含有被重新定义变量的表达式
相遇运算(Meet Operation, Λ)
当控制流汇聚时(如多个前驱到达同一基本块),需要将来来自不同路径的信息 合并 成一个结果。
相遇运算是格上的一个二次运算,通常取 最大下界(greatest lower bound), 即保留所有路径信息的共同部分。
- 例如,可用表达式分析使用
交集作为相遇运算,因为一个表达式只有在所有前驱路径上都可用,在当前点才可用
初始值(Initial Value, I)
分析开始的初始数据流值,通常设置在程序入口(前向) 或 出口(后向)。
初始值的选择需确保分析从最保守或最乐观的状态开始,并逐步逼近正确的结果
- 例如,可用表达式分析在入口处通常设为
空集(最精确,认为没有表达式可用),然后通过迭代逐渐加入可能可用的表达式
与格(Lattice) 的关联
数据流分析的值集合 V 和相遇运算 Λ 共同构成一个 格(更准确是, 半格)
偏序关系 ⊑
定义在 V 上,用于比较两个值的精确程度。
通常认为 信息越少越不精确,例如空集 ∅, 比全集U 更精确(因为空集表示"没有任何表达式可用",这是一个确定的、保守的事实)
因此,我们可以定义 ∅ ⊑ U,即 ∅ 是更小的元素(更精确)。
最大下界(meet)
相遇运算 Λ 就是取两个元素的最大下界,即最精确的、同时小于等于两者的那个元素。
这个符合数据流合并的直觉:合并后的结果应比任何一个路径的信息都更精确
Top 和 Bottom
格中有两个特殊元素------Top(最大元素,最不精确)和 Bottom(最小元素,最精确)。
初始值通常设为 Top(乐观)或 Bottom(保守),取决于分析策略。在可用表达式分析中,我们采用保守策略,初始值为 Bottom(空集)。
格理论保证了数据流迭代算法的终止性和正确性:由于值集合有限(或具有有限高度),且转换函数单调,从初始值开始反复应用转换函数和相遇运算,最终一定收敛到不动点
详细举例:可用表达式分析
可用表达式(Available Expressions) 的定义: 在程序 p , 表达式 x op y 是可用的,如果从程序入口到 p 的所有路径上都计算该表达式,并且之后没有重新定义 x 或 y
方向(D)
前向分析,从程序入口向出口传播信息。
值(V)
每个程序点上的数据流值是一个表达式集合,表示在该点可用的所有表达式。所有可能的集合构成一个幂集格。
集合按包含关系 ⊆ 排序:S1 ⊑ S2 当且仅当 S1 ⊆ S2(即 S1 更精确,因为可用表达式更少)。
Top = 所有可能表达式的全集(最不精确,认为所有表达式都可用)。
Bottom = 空集 ∅(最精确,认为没有任何表达式可用)。
这个格是交半格,因为最大下界(meet)就是集合的交集。
转换函数(F)
每个基本块 B 的转换函数 f_B(in) = out 定义为:
out = (in - kill_B) ∪ gen_B
kill_B: 在B中因变量被重新定义而杀死的表达式集合。例如,若 B 中定义了变量 a,则所有含有 a 的表达式都被杀死gen_B:在 B 中生成的新表达式集合。例如,若 B 中有语句t = a + b,则表达式a + b被生成(前提是之后没有重新定义a或b)。
相遇运算(Λ)
当多个前驱路径汇聚到同一个基本块时,使用交集合并信息:
text
in[B] = Λ_{p ∈ preds(B)} out[p] = ⋂_{p ∈ preds(B)} out[p]
这是因为一个表达式必须在所有前驱路径上都可用,才能在 B 的入口处可用。
初始值(I)
程序入口处,没有表达式可用,因此 in[entry] = ∅(Bottom)。
这表示我们从最精确的假设开始,然后随着路径的展开逐渐加入可能可用的表达式。
迭代求解
通过不断迭代应用转换函数和相遇运算,直到所有 in[B] 和out[B]不再变化,即可得到每个程序点的可用表达式集合。
1: a = x + y
2: b = a + z
3: if (...) goto 4 else goto 5
4: c = x + y
5: d = a + z
基本块划分:
- B1: 语句1、2
- B2: 语句3(条件分支)
- B3: 语句4
- B4: 语句5
分析可用表达式:
- gen_B1 = {x+y, a+z},kill_B1 = {}(没有重新定义x、y、z、a?注意a被定义,但a+z中的a是新定义的,实际上表达式a+z依赖于a,而a在语句1被定义,所以a+z在语句2之后才生成,但语句2本身是a+z,所以a+z在B1内部生成后,后续仍可用?需仔细处理)。
- 相遇运算:B2的两个后继B3和B4的入口值取交集。最终可计算出每个点可用的表达式。