半格与数据流分析的五个要素(D、V、F、I、Λ)

五个要素的含义

方向(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 的所有路径上都计算该表达式,并且之后没有重新定义 xy

方向(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 被生成(前提是之后没有重新定义ab)。

相遇运算(Λ)

当多个前驱路径汇聚到同一个基本块时,使用交集合并信息:

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的入口值取交集。最终可计算出每个点可用的表达式。
相关推荐
liann1191 小时前
4.3.2_WEB——WEB后端语言——PHP
开发语言·前端·网络·安全·web安全·网络安全·php
董厂长2 小时前
用 LangGraph 实现 Small-to-Big 分块检索策略
人工智能·算法·rag
大江东去浪淘尽千古风流人物2 小时前
【Sensor】IMU传感器选型车轨级 VS 消费级
人工智能·python·算法·机器学习·机器人
元让_vincent2 小时前
DailyCoding C++ | SLAM里的“幽灵数据”:从一个未初始化的四元数谈C++类设计
开发语言·c++·slam·构造函数·类设计·激光里程计
A9better2 小时前
C++——指针与内存
c语言·开发语言·c++·学习
坚持编程的菜鸟2 小时前
互质数的个数
c语言·算法
ICscholar2 小时前
具身智能‘Affordance‘理解
人工智能·学习·算法
wangwangmoon_light2 小时前
1.2 LeetCode总结(线性表)_双指针
算法·leetcode·职场和发展
琢磨先生David2 小时前
Java算法每日一题
java·开发语言·算法