概述
数据流分析的核心格(Lattice)系统。
它通过一个三值格(Top / CopyOf / Bottom)精确地跟踪每个寄存器是"某个其他寄存器的直接复制"还是"通过计算产生的新值",并定义了meet操作来合并来自不同控制流路径的信息(通常在Phi函数处)。
这种设计是稀疏条件复制传播(SCCP) 算法的基石。
代码示例
rust
// Lattice(格)枚举定义,表示寄存器值的复制关系
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Lattice {
Top, // 未知值(最不精确) - 表示我们不知道这个寄存器是否是其他寄存器的复制
CopyOf(VReg), // 是某个寄存器的复制 - 例如 CopyOf(rd1) 表示这个寄存器是 rd1 的复制
Bottom, // 非复制值(最精确) - 表示这个寄存器不是任何其他寄存器的复制(是计算得到的新值)
}
impl Lattice {
/// 复制传播优化中的格值交汇(meet)操作
/// 用于合并两个格值(Lattice)的信息,是数据流分析的核心部分。
/// meet操作:计算两个格值的最大下界(最精确的共同信息)
/// - self: 第一个格值
/// - other: 第二个格值
/// - regs: 当前Phi函数的结果寄存器(用于特殊情况的处理)
/// - rego: 另一个操作数寄存器(用于特殊情况的处理)
fn meet(&self, other: &Lattice, regs: VReg, rego: VReg) -> Self {
/// 匹配两个格值的所有可能组合
match (self, other) {
/// 情况1:两个都是CopyOf,即都是其他寄存器的复制
/// 例如:CopyOf(rd1) meet CopyOf(rd2)
(Lattice::CopyOf(reg), Lattice::CopyOf(reg2)) => {
/// 如果复制的源寄存器相同
if reg == reg2 {
/// 结果仍然是同一个寄存器的复制
/// 例如:CopyOf(rd1) meet CopyOf(rd1) = CopyOf(rd1)
Lattice::CopyOf(*reg)
} else {
/// 如果复制的源寄存器不同,产生冲突
/// 例如:CopyOf(rd1) meet CopyOf(rd2) = Bottom
/// 因为寄存器不能同时是rd1和rd2的复制
Lattice::Bottom
}
}
/// 情况2:两个都是Bottom,即都不是复制
/// 例如:Bottom meet Bottom = Bottom
(Lattice::Bottom, Lattice::Bottom) => Lattice::Bottom,
/// 情况3:第一个是Bottom,第二个是CopyOf
/// 例如:Bottom meet CopyOf(rd1)
(Lattice::Bottom, Lattice::CopyOf(reg)) => {
/// 特殊处理:如果当前Phi函数的结果寄存器(regs)等于复制源寄存器(reg)
if regs == *reg {
/// 例如:rd3 = φ(rd1, 计算值)
/// 如果rd3 == rd1,那么结果可以是CopyOf(rd1)
Lattice::CopyOf(regs)
} else {
/// 否则产生冲突,结果为Bottom
Lattice::Bottom
}
}
/// 情况4:第一个是CopyOf,第二个是Bottom
/// 例如:CopyOf(rd1) meet Bottom
(Lattice::CopyOf(reg), Lattice::Bottom) => {
/// 特殊处理:如果另一个操作数寄存器(rego)等于复制源寄存器(reg)
if rego == *reg {
/// 例如:rd3 = φ(计算值, rd1)
/// 如果rd3 == rd1,那么结果可以是CopyOf(rd1)
Lattice::CopyOf(rego)
} else {
/// 否则产生冲突,结果为Bottom
Lattice::Bottom
}
}
/// 情况5:至少有一个是Top(未知值)
/// 模式匹配:Top meet X 或 X meet Top
/// 用更精确的信息替换未知值
/// 例如:Top meet CopyOf(rd1) = CopyOf(rd1)
/// 例如:Top meet Bottom = Bottom
(Lattice::Top, o) | (o, Lattice::Top) => *o,
}
}
}
格值的语义
| 格值 | 含义 | 举例 | 精确度 |
|---|---|---|---|
| Top | 未知状态,尚未分析或尚无定论 |
初始状态,或未被任何路径定义 | 最不精确(信息最少) |
| CopyOf® | 该寄存器的值就是寄存器 r 的当前值(直接复制) |
%x = mv %y → %x 是 %y 的复制 |
精确(知道复制关系) |
| Bottom | 不是任何寄存器的复制(通过计算产生、常量、内存加载等) |
%z = add %a, %b → %z 是 Bottom |
最精确(信息最多) |
格上的偏序关系:
Top ⊑ CopyOf(r) ⊑ Bottom 且 Top ⊑ Bottom。
即:Top 信息最少,Bottom 信息最多,CopyOf 介于中间。
meet 操作的核心作用
在数据流分析中,当控制流汇聚到Phi函数时,需要将来自不同前驱路径的信息合并,得到该Phi函数结果寄存器的格值。meet 就是用来计算两个格值的最大下界(最精确的共同信息):
- 如果两条路径都说同一个事实 → 采纳该事实
- 如果两条路径给出冲突的事实 → 结果只能是 Bottom(无法成为复制)
- 如果一条路径未知(Top) → 采用另一条路径的信息
- 特殊优化:当Phi函数自身是某寄存器的复制且另一路径是Bottom时,可能仍然保留复制关系(即 regs == *reg 或 rego == *reg 的情况)
meet 函数的分支详解
rust
fn meet(&self, other: &Lattice, regs: VReg, rego: VReg) -> Self
regs:当前Phi函数的结果寄存器(Phi(x, ...)中的x)rego:当前正在合并的另一个操作数寄存器(用于非对称情况)
CopyOf(r1) meet CopyOf(r2)
如果 r1 == r2 → 结论:CopyOf(r1)
否则 → Bottom(冲突,不能同时是两个不同寄存器的复制)
Bottom meet Bottom → Bottom
Bottom meet CopyOf®
特殊情况:若 regs == r(即Phi结果寄存器正好等于这个复制源)→ 说明该Phi函数本质上是"自己复制自己",可以保留为 CopyOf(regs)
- 例如:%x = φ [%x, 前驱1], [计算值, 前驱2] → 仍然认为 %x 是 %x 的复制(实际上就是保留原值)
- 否则 → Bottom
CopyOf® meet Bottom
对称情况:若 rego == r(当前合并的操作数寄存器等于复制源)→ 结果为 CopyOf(rego)
否则 → Bottom
包含 Top 的情况
Top meet X 或 X meet Top -> 直接取 X (用已知信息替换未知)
完整示例:phi函数处的 meet 操作
llvm
; 控制流:两个前驱路径在汇合点使用 Phi 函数
; pred1 pred2
; \ /
; \ /
; \ /
; block3:
; %x = φ [%a, pred1], [%b, pred2]
; 前驱1:%a = mv %c → 格值 CopyOf(%c)
; 前驱2:%b = add %d, %e → 格值 Bottom
初始状态
result[%a] = CopyOf(%c)result[%b] = Bottom
phi 函数处理 在 (visit_op 中, 会遍历所有可执行前驱并调用 meet):
rust
// 第一次 meet:res = Top.meet(CopyOf(%c), %x, %a) = CopyOf(%c)
// 第二次 meet:res = CopyOf(%c).meet(Bottom, %x, %b)
进入 CopyOf(r).meet(Bottom, regs, rego):
r = %c,regs = %x,rego = %b- 条件
rego == r即%b == %c为假(因为%b是加法结果,不是%c) - 因此结果为
Bottom
最终:%x 的格值 = Bottom -> 无法作为复制传播,因为它的一条路径产生了新值,另一条路径虽然是复制但来源不同
特殊情形:phi 中的自复制
考虑一个更微妙的情况 - 循环中的phi 节点
llvm
loop:
%i = φ [%init, entry], [%i_next, loop]
%i_next = add %i, 1
我们希望保留 %i 是 %i 自身的复制关系(实际上它被更新了,但仍然是同一个 SSA 寄存器)。在第一次迭代中:
- 来自
entry的%init可能是CopyOf(%const)或Bottom - 来自
loop的%i_next是Bottom
当处理 %i = φ [%init, entry], [%i_next, loop] 时,我们需要合并 CopyOf(%const) 和 Bottom
但这里 rego = %i_next 且 %i_next 不等于 %const,按照普通规则会得到 Bottom。
然而,由于 Phi 的结果寄存器 %i 同时也是下一条指令 %i_next 的源,循环最终会收敛。这里 meet 的特殊分支(CopyOf(r) meet Bottom 中检查 rego == r)并不会触发,因为 rego 是 %i_next 而不是 %const。
实际上,在复制传播算法中,这种循环需要多次迭代才能稳定。特殊分支主要针对的是phi结果寄存器与复制源相同的情况, 例如:
llvm
%x = φ [%x, pred1], [%y, pred2]
如果 pred1 路径上 %x 的值是它自身(即没有改变),那么即使另一条路径是 Bottom,我们仍然可以认为 %x 是 %x 的复制(即未定义新值)。
meet 中的 regs == *reg 分支正是为此设计的。
总结
| 维度 | 说明 |
|---|---|
| 所属算法 | 稀疏条件复制传播(SCCP)、全局值编号(GVN)等数据流优化 |
| 核心数据结构 | Lattice 格,表示寄存器值是否为其他寄存器的直接复制 |
| 关键操作 | meet:合并两个路径的信息,在 Phi 节点处决定最终格值 |
| 设计亮点 | 通过特殊参数 regs、rego 处理 Phi 函数自引用的边界情况,避免过度保守地降为 Bottom |
| 优化效果 | 识别可传播的复制关系,将 mv 指令消除,并辅助常量传播、死代码消除 |
这个 meet 函数是复制传播优化的推理引擎,它根据 Phi 节点各前驱的信息,精确计算出合并后的寄存器状态
从而让优化器知道哪些寄存器仅仅是其他寄存器的"别名",可以进行替换消除。