静态单赋值形式(进阶)
复习
- 数据流分析:追踪信息并迭代到稳定
- 控制流图:分支与汇合
- 名字解析:每个名字指向一个定义
本章为进阶内容,零基础读者可以跳过,不影响后续阅读。
TL;DR
- SSA 要求每个变量只被赋值一次
- 它让“一个名字对应一个定义”变得明确
- 分支汇合处用 φ 函数选择来源
- SSA 让很多分析和优化都更简单
正文
数据流分析很有用,但如果程序里变量被反复赋值,分析起来就很费劲。静态单赋值形式(SSA,Static Single Assignment)就是为了解决这个麻烦而生的。
每个变量只赋值一次
在普通程序里,同一个名字可能被改来改去:
x = 1
x = x + 2
x = x * 3
要问“某处的 x 到底是第几次赋值的结果”,就得费一番功夫。
SSA 的规则很干脆:每个变量只被赋值一次。 需要多次赋值时,就起新的名字,比如:
x1 = 1
x2 = x1 + 2
x3 = x2 * 3
这样,每个名字都唯一对应一个定义。想知道某个 x3 从哪来?直接看它那一次赋值就行了,定义与使用的关系一目了然。
汇合处用 φ 函数
那分支汇合时怎么办?比如:
if 条件:
x = 1
else:
x = 2
用 x
两条路给 x 赋了不同的值,汇合之后“用 x”到底用哪个?SSA 引入一个虚构的 φ 函数(读作 phi)来处理:
x3 = φ(x1, x2)
它的意思是:如果从左边来,就取 x1;从右边来,就取 x2。 这样,汇合点上也有了唯一的定义 x3。
为什么它让分析变简单
SSA 的好处,是让编译器做分析和优化时轻松很多:
- 定义—使用关系清清楚楚:谁用了谁,一眼可见
- 数据流分析更简单:很多分析在 SSA 上可以直接“跟着定义走”,不必反复迭代
- 优化更精准:常量传播、死代码删除等,在 SSA 上都更直接
代价是:SSA 是一种“中间形态”,程序最终还是要转回普通形式,才能生成机器码——因为真实机器就是一个变量被反复赋值。这段转换要妥善处理 φ 函数,是编译器的一个技术难点。
为了分析方便先换个形式,生成代码前再换回来——这也算是“加一层、再撤一层”的灵活运用。到这里,中端分析的工具就齐了。下一组,正式进入优化。
思考题 1
SSA 中“每个变量只赋值一次”,为什么能简化分析?
思考题 2
分支汇合处的 φ 函数,是用来做什么的?
小结
知识点
- SSA 要求每个变量只被赋值一次
- 多次赋值改用新名字,定义唯一
- 汇合处用 φ 函数选择来自哪条路径的值
- SSA 让分析与优化更简单,但生成代码前需转回普通形式
参考资料
- Wikipedia(zh):静态单赋值形式:每个变量只赋值一次的中间表示
- Wikipedia(zh):数据流分析:SSA 常被用于简化的分析
思考题答案(仅供参考)
思考题 1
因为每个名字只对应一个定义,使用某个名字时,它的来源是唯一确定的,不需要在多个可能的赋值点之间推理。定义—使用关系变得明确,很多分析可以直接顺着定义进行,无需反复迭代。
思考题 2
它用来在控制流的汇合点“选择来源”:根据实际是从哪条路径到达汇合点,决定取对应分支上的那个值。这样汇合点上仍然只有一个定义,保持 SSA 的性质。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪