“如果使用 x 的地方只看到一个定义 d,可以连接这次使用与 d;还不能仅凭这一点,把 d 的右侧文本原样搬来。例如 d 是 ,随后 a 被改写,再使用 x。x 仍来自 d,却等于旧 a+b。…”
形式陈述
要传播的是当时的等值事实
固定单过程局部标量 IR,复制不产生可观察事件,变量没有通过地址被外部修改。所有使用都有定义,指令按顺序读取右侧再写左侧。输出观察包括返回值和控制选择;本页不改写存储别名、volatile、寄存器约定或调试变量。
执行 y:=x 后记录有向复制对
例如 x 初值1,执行 y:=x; x:=9; return y 必须返回1。错误地把最后的 y 换成 x 会返回9。只在目标 y 被覆盖时删事实、却忘记来源 x 的重定义,是最常见的错误之一。
可用复制对的方程
候选全集 C 收集本过程实际出现的非自复制 v:=e:
先删 KILL,再在当前语句是 v:=x 且 v:=v 不产生新关系;它也可保守地删掉旧相关事实,尽管更精细的实现可以证明这次自复制不改值而保留它们。
合流用交集,因为替换必须对所有到达路径都成立。人工入口事实为空,其他可达块从 C 初始化,经前向 must 求解逐步收紧。基本块按语句顺序组合转移,不把其中全部复制无序保留。
替换一条语句时先看它的 IN 事实,只改右侧和分支、返回的读取,不改赋值左侧。一个简单实现每个操作数只替换一步,并按原始指令更新事实;这样证明最直接,也不会因循环的名字替换链而在编译器里无限循环。下载实现只变换入口可达块,不可达块原样保留;分析集合仅为可达块分配,因此改写也必须遍历同一可达集合。删除不可达块是另一个明确的步骤。若进一步沿链追到代表元,需要检测环并证明链上每一条同时成立。
直觉
y:=x; return y 可以写成 y:=x; return x。但在中间插入 x:=9 就不一定可以:y 保存的是复制那一刻的旧 x。复制传播把一次读取换成另一个名字,前提是在这次读取发生时,两者仍被证明相等。它不会使 y 从此自动跟着 x 变化。
例子与边界
两条分支怎样给出同一替换
先用公共子表达式消除得到下面的 OPT-8 中间代码:
E: x := a+b
y := x
if p goto T else F
T: u := x
goto J
F: u := x
goto J
J: z := x
dead := z+1
r := u+y
return r
E 出口有 {(y,x)}。T 与 F 的出口都加入同一个 (u,x),所以 J 入口为 {(y,x),(u,x)}。虽然 u 的赋值有两个不同身份,它们建立的是同一个数值关系。
J.1 执行后又有 (z,x)。因此 J.2 的 z 可改成 x,J.3 的两个输入可同时改成 x,得到 dead:=x+1; r:=x+x。x 从 E 到这里没有被覆盖,所以替换成立。y、u、z 的复制此时仍保留;是否删除它们由死代码消除另做。
若 F 改为 u:=0,该路没有 (u,x),交集便只剩 (y,x)。J.3 可以把 y 换成 x,却不能把 u 换成 x。不能因为 T 路提供了关系就忽略 F。
推论与应用
可靠性是不变关系的保持
对任意到达当前点的具体执行,证明分析集合里的每一对
把一个读取 y 换成同值 x,不改变该原语的输入;确定性原语因此产生同一结果。对整个运行逐步归纳,分支读取也不变,所以经过同样的块并返回同值。替换时不能从“当前分析预计将来相等”倒用一项尚未建立的事实;必须使用语句之前的集合。
这份证明同样说明它为何保守:若两条路分别执行 u:=x 与 u:=x+0,本分析只在第一路生成复制对,于是不会传播 u,尽管数学整数下两者确实相等。更强的值编号或关系分析能发现它;保守性不是实现错误。
成本与边界
候选对有 Q 项,每个块事实至多删除 Q 次。位集求解可以预计算每个被写变量要杀掉的对。设 N 个块、M 条边、最大入度 Δ,朴素“每次重扫全部前驱”的工作队列上界为
在 SSA 中,变量没有重新定义,许多复制能沿唯一的定义—使用链直接消除;仍须保持支配和 φ 的前驱槽位含义。现有SSA 构造与消解已经给出回边上的复制传播,本页的贡献是没有 SSA 保证时的全路径条件。
迁移任务:在 T 的 u:=x 后增加 x:=9,F 不变。J 入口的 (y,x) 与 (u,x) 都应消失,因为 T 路改写了右端。取 a=2、b=3、p=true,y和u仍为5,r应为10;错误传播会算出18。再把新增语句改为 q:=9,两对均保留,说明 KILL 应围绕实际写变量,而不是“见到赋值就全部清空”。
参考资料
- Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools, 2nd ed., 2007,§9.1 的全局优化、§9.2 的数据流分析。本文使用显式可用复制对实现一个保守版本。
- Steven S. Muchnick, Advanced Compiler Design and Implementation, Morgan Kaufmann, 1997,§12.5 “Copy Propagation”。复制传播、失效条件与后续优化的教材背景。