数据流分析的核心格(Lattice)系统

概述

数据流分析的核心格(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%zBottom 最精确(信息最多)

格上的偏序关系:
TopCopyOf(r)BottomTopBottom

即: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 XX 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 = %cregs = %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_nextBottom

当处理 %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 节点处决定最终格值
设计亮点 通过特殊参数 regsrego 处理 Phi 函数自引用的边界情况,避免过度保守地降为 Bottom
优化效果 识别可传播的复制关系,将 mv 指令消除,并辅助常量传播、死代码消除

这个 meet 函数是复制传播优化的推理引擎,它根据 Phi 节点各前驱的信息,精确计算出合并后的寄存器状态

从而让优化器知道哪些寄存器仅仅是其他寄存器的"别名",可以进行替换消除。

相关推荐
键盘鼓手苏苏1 小时前
Flutter for OpenHarmony 实战:Flutter Rust Bridge — 极致计算性能方案
开发语言·后端·flutter·华为·rust·json·harmonyos
he___H1 小时前
jvm41-47回
java·开发语言·jvm
琛説1 小时前
⚡PitchPPT:将PPT导出为高清全图PPT,并控制PPT文件大小在固定MB/GB以内【解析算法原理 · 作者谈】
windows·python·算法·github·powerpoint
csdn2015_1 小时前
MybatisPlus LambdaQueryChainWrapper 联合查询
开发语言·windows·python
骑猪撞地球QAQ1 小时前
Java在导出excel时中添加图片导出
java·开发语言·excel
We་ct1 小时前
LeetCode 25. K个一组翻转链表:两种解法详解+避坑指南
前端·算法·leetcode·链表·typescript
好家伙VCC1 小时前
# 发散创新:基于 Go 语言打造高性能服务网格的实践与突破在微服务架构
java·python·微服务·架构·golang
悦悦子a啊1 小时前
CSS 知识点
开发语言·前端·css
Hag_201 小时前
LeetCode Hot100 438.找到字符串中的所有字母异位词
算法·leetcode·职场和发展