“程序切片要先说清楚保留什么。本页沿用程序依赖图的无循环结构化整数语言,并固定末尾 为观察准则 $c$。全部表达式纯粹且总定义,原程序每条路径的读取都有定义;无异常、堆、外部 I/O 和并发。…”
形式陈述
先固定程序和边的含义
程序依赖图把“哪条语句可能给这里提供值”和“哪个判断决定这里是否执行”放在同一张图里。本页采用无循环、单入口单出口的结构化顺序程序:命令只有局部赋值、二路条件和末尾唯一 return。表达式纯粹且总定义,变量取数学整数,所有读取在每条到达路径上都有定义;没有指针、异常、外部 I/O 或并发。因而任一输入都正常终止。
以语句与判断为节点,入口另提供输入变量的定义。数据边
控制边采用支配关系的出口版本。记
走这条分支后必须经过
从 CFG 实际构图
先按逆拓扑顺序计算
无环性保证处理
两项计算各有直接不变量。逆向处理完的后支配集合等于全部后继路径的交;前向处理完的定义集合包含所有可能最后写入。依赖图保留的是这些语义相关来源,并不复制全部顺序边。
直觉
一段程序里的三种箭头
下面的编号是节点身份,缩进给出嵌套关系;0是输入节点,10是正常出口。
0 input x, y
1 a := x + 1
2 junk := y * y
3 r := 0
4 if x > 0:
5 if y == a:
6 r := 1
else:
7 r := 2
8 junk := junk + 1
9 return r
CFG 有顺序边 a,也不受1的条件控制,所以依赖图没有
节点8位于所有分支的汇合后,每次执行都会到达它。4虽出现在8前面,却不直接控制8是否执行。这个区别使依赖图比“把前面的语句全留下”更适合分析相关性。
例子与边界
逐项复算控制边
只看分支附近,后支配集合为
4的真边到5,集合差为
6的执行间接依赖4,经
逐项复算数据边
除输入0分别向1、2、4、5提供 x,y,x,y 外,数据边还有
共9条数据边。3的 r:=0 仍能在4为假时到达9;不能因为其他路径有后续赋值就删去这个来源。8的右侧 junk 由2提供,而非由8这次尚未完成的赋值提供。
图路径可能包含不可能的条件组合。例如 if x>0 内再测 x<0,CFG 仍有通向内层真分支的边。保留它使静态依赖保守,路径求解可另行证明不可行;没做求解时不能默认删掉。
本图并非任意重排许可证
这里只建立值流和控制依赖,足以服务下页的删除式切片。若要任意交换赋值,还可能需要反依赖、输出依赖和内存效果边。例如 a:=x; x:=1 中第一句读取旧 x;仅有定义到使用的边不会禁止交换这两句。本页不从“图上没有边”推出可并行或可交换。
原论文采用严格后支配记法,本页采用含自身的反身集合并限制无环图,避免把文献中关于循环自控制的规则机械照搬。一般循环、多出口和异常退出需重新明确最大执行、终止和观察语义;旧支配页的出口警告仍然适用。
推论与应用
成本和下一步
设 CFG 有
其中后支配集合最多
静态程序切片从观察节点逆向遍历两种边,得到必须保留的定义和判断。此处的构图责任与下页的删减正确性责任分开:前者说明来源覆盖,后者说明怎样得到仍可执行且保持特定观察的程序。
迁移练习:把8改成 r:=junk。现在9的唯一到达定义是8,2通过 junk 成为相关节点;3、6、7不再给9提供最终值。请重新计算数据边,再问4和5是否还影响返回值。答案需要重新构图,不能只给旧图添一条边。
参考资料
[1] Jeanne Ferrante、Karl J. Ottenstein、Joe D. Warren,The Program Dependence Graph and Its Use in Optimization,ACM TOPLAS 9(3),1987,319–349页;§3.1给控制依赖与后支配构造,§3.2讨论数据依赖。本文的无环整数算例、集合成本及删减用途单独限定,不覆盖论文全部优化模型。