第九章 独立于机器的优化
description
Transcript of 第九章 独立于机器的优化
第九章 独立于机器的优化词法分析器语法分析器语义分析器
源程序
中间代码生成器
独立于机器的代码优化器代码生成器
依赖于机器的代码优化器目标机器代码
符号表
第九章 独立于机器的优化• 代码优化
– 通过程序变换(局部变换和全局变换)来改进程序,称为优化• 代码改进变换的标准
– 代码变换必须保程序的含义– 采取安全稳妥的策略– 变换减少程序的运行时间平均达到一个可度量的值– 变换所作的努力是值得的
第九章 独立于机器的优化• 本章介绍独立于机器的优化,即不考虑任何依赖目标机器性质的优化变换
– 通过实例来介绍代码改进的主要机会 – 数据流分析包括的几类重要的全局收集的信息– 数据流分析的一般框架– 和一般框架有区别的常量传播– 部分冗余删除的优化技术– 循环的识别和分析
9.1 优化的主要种类9.1.1 优化的主要源头• 程序中存在许多程序员无法避免的冗余运算
– 对于 A[i][j] 和 X.f1 这样访问数组元素和结构体的域的操作(例如, A[i][j] = A[i][j] + 10 )– 随着程序被编译,这些访问操作展开成多步低级算术运算– 对同一个数据结构的多次访问导致许多公共的低级运算– 程序员没有办法删除这些冗余
9.1 优化的主要种类9.1.2 一个实例i = m 1; j = n; v = a[n]; (1) i = m 1while (1) { (2) j = n do i = i +1; while(a[i]<v); (3) t1 = 4 n do j =j 1;while (a[j]>v); (4) v = a[t1] if (i >= j) break; (5) i = i + 1 x=a[i]; a[i]=a[j]; a[j]=x; (6) t2 = 4 i } (7) t3 = a[t2] x=a[i]; a[i]=a[n]; a[n]=x; (8) if t3 < v goto (5)
9.1 优化的主要种类9.1.2 一个实例i = m 1; j = n; v = a[n]; (9) j = j 1 while (1) { (10) t4 = 4 j do i = i +1; while(a[i]<v); (11) t5 = a[t4] do j =j 1;while (a[j]>v); (12) if t5>v goto (9) if (i >= j) break; (13) if i >=j goto (23) x=a[i]; a[i]=a[j]; a[j]=x; (14) t6 = 4 i} (15 ) x = a[t6]x=a[i]; a[i]=a[n]; a[n]=x; . . .
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]
i = i + 1t2 = 4 it3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
• 程序流图
9.1 优化的主要种类9.1.3 公共子表达式删除B5 x=a[i]; a[i]=a[j]; a[j]=x;
t6 = 4 ix = a[t6]t7 = 4 i t8 = 4 jt9 = a[t8]a[t7] = t9
t10 = 4 ja[t10] = xgoto B2
9.1 优化的主要种类局部公共子表达式删除 , 复写传播 , 删除死代码B5 x=a[i]; a[i]=a[j]; a[j]=x;
t6 = 4 ix = a[t6]t7 = 4 i t8 = 4 jt9 = a[t8]a[t7] = t9
t10 = 4 ja[t10] = xgoto B2
t6 = 4 ix = a[t6]t8 = 4 jt9 = a[t8]a[t6] = t9
a[t8] = xgoto B2
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]
i = i + 1t2 = 4 it3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
9.1 优化的主要种类全局公共子表达式删除 , 复写传播 , 删除死代码B5 x=a[i]; a[i]=a[j]; a[j]=x;
t6 = 4 ix = a[t6]t7 = 4 i t8 = 4 jt9 = a[t8]a[t7] = t9
t10 = 4 ja[t10] = xgoto B2
t6 = 4 ix = a[t6]t8 = 4 jt9 = a[t8]a[t6] = t9
a[t8] = xgoto B2
9.1 优化的主要种类全局公共子表达式删除 , 复写传播 , 删除死代码B5 x=a[i]; a[i]=a[j]; a[j]=x;
t6 = 4 ix = a[t6]t7 = 4 i t8 = 4 jt9 = a[t8]a[t7] = t9
t10 = 4 ja[t10] = xgoto B2
t6 = 4 ix = a[t6]t8 = 4 jt9 = a[t8]a[t6] = t9
a[t8] = xgoto B2
x = a[t2]t9 = a[t4]a[t2] = t9
a[t4] = xgoto B2
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]
i = i + 1t2 = 4 it3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
9.1 优化的主要种类全局公共子表达式删除 , 复写传播 , 删除死代码B5 x=a[i]; a[i]=a[j]; a[j]=x;
t6 = 4 ix = a[t6]t7 = 4 i t8 = 4 jt9 = a[t8]a[t7] = t9
t10 = 4 ja[t10] = xgoto B2
t6 = 4 ix = a[t6]t8 = 4 jt9 = a[t8]a[t6] = t9
a[t8] = xgoto B2
x = a[t2]t9 = a[t4]a[t2] = t9
a[t4] = xgoto B2
9.1 优化的主要种类全局公共子表达式删除 , 复写传播 , 删除死代码B5 x=a[i]; a[i]=a[j]; a[j]=x;
t6 = 4 ix = a[t6]t7 = 4 i t8 = 4 jt9 = a[t8]a[t7] = t9
t10 = 4 ja[t10] = xgoto B2
t6 = 4 ix = a[t6]t8 = 4 jt9 = a[t8]a[t6] = t9
a[t8] = xgoto B2
x = a[t2]t9 = a[t4]a[t2] = t9
a[t4] = xgoto B2x = t3
a[t2] = t5
a[t4] = xgoto B2
9.1 优化的主要种类公共子表达式删除、复写传播、删除死代码B6 x = a[i]; a[i] = a[n]; a[n] = x;
t11 = 4 ix = a[t11]t12 = 4 i t13 = 4 nt14 = a[t13]a[t12] = t14
t15 = 4 n a[t15] = x
x = t3
t14 = a[t1]a[t2] = t14
a[t1] = x
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]
i = i + 1t2 = 4 it3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
9.1 优化的主要种类B6 x = a[i]; a[i] = a[n]; a[n] = x;
a[t1] 能否作为公共子表达式?t11 = 4 ix = a[t11]t12 = 4 i t13 = 4 nt14 = a[t13]a[t12] = t14
t15 = 4 n a[t15] = x
x = t3
t14 = a[t1]a[t2] = t14
a[t1] = x
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]
i = i + 1t2 = 4 it3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
把 a[t1] 作为公共子表达式是不稳妥的 因为 B5 有对下标变量 a[t2]和 a[t4] 的赋值
9.1 优化的主要种类9.1.4 复写传播• 复写语句:形式为 f = g 的赋值• 优化过程中会大量引入复写
t = d + ea = t
删除局部公共子表达式期间引进复写
t = d + eb = t
c = tc = d + e
b = d + ea = d + e
9.1 优化的主要种类9.1.4 复写传播• 复写语句:形式为 f = g 的赋值• 优化过程中会大量引入复写• 复写传播变换的做法是在复写语句 f = g 后,尽可能用 g 代表 f
B5
x = t3
a[t2] = t5
a[t4] = t3
goto B2
x = t3
a[t2] = t5
a[t4] = xgoto B2
9.1 优化的主要种类9.1.4 复写传播• 复写语句:形式为 f = g 的赋值• 优化过程中会大量引入复写• 复写传播变换的做法是在复写语句 f = g 后,尽可能用 g 代表 f• 复写传播变换本身并不是优化,但它给其它优化带来机会
– 常量合并(编译时可完成的计算)– 死代码删除
pi = 3.14 …y = pi 5
9.1 优化的主要种类9.1.5 死代码删除• 死代码是指计算的结果决不被引用的语句• 一些优化变换可能会引起死代码例: 为便于调试,可能在程序中加打印语句,测试后改成右边的形式 debug = true; | debug = false; . . . | . . . if (debug) print … | if (debug) print …靠优化来保证目标代码中没有该条件语句部分
9.1 优化的主要种类9.1.5 死代码删除• 死代码是指计算的结果决不被引用的语句• 一些优化变换可能会引起死代码例:复写传播可能会引起死代码删除
B5
x = t3
a[t2] = t5
a[t4] = t3
goto B2
a[t2] = t5
a[t4] = t3
goto B2
x = t3
a[t2] = t5
a[t4] = xgoto B2
9.1 优化的主要种类9.1.6 代码外提• 代码外提是循环优化的一种• 循环优化的其它重要技术
– 归纳变量删除– 强度削弱例: while (i <= limit 2 ) … 代码外提后变换成
t = limit 2;while (i <= t ) …
9.1 优化的主要种类9.1.7 强度削弱和归纳变量删除• j 和 t4 的值步伐一致地变化• 这样的变量叫做归纳变量• 在循环中有多个归纳变量时, 也许只需要留下一个• 这个操作由归纳变量删除 过程来完成• 对本例可以先做强度削弱 它给删除归纳变量创造机会
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
B3
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]
B1
B2
j = j 1t4 = t4 4t5 = a[t4]if t5 > v goto B3
B4
B3
B5 B6
t4 = 4 j
if i >= j goto B6
j = j 1t4 = 4 jt5 = a[t4]if t5 > v goto B3
B3
除第一次外,t4 == 4 j 在 B3 的入口一定保持在 j = j 1 后,关系 t4 == 4 j + 4 也保持
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]t4 = 4 j
i = i + 1t2 = 4 it3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = t44t5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
B2 也可以进行类似的变换
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]t4 = 4 jt2 = 4 i
i = i + 1t2 = t2+ 4t3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = t44t5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]t4 = 4 jt2 = 4 i
i = i + 1t2 = t2+ 4t3 = a[t2]if t3 < v goto B2
B1
B2
j = j 1t4 = t44t5 = a[t4]if t5 > v goto B3
if i >= j goto B6B4
B3
B5 B6
循环控制条件可以用 t2 和 t4来表示
9.1 优化的主要种类i = m 1j = nt1 = 4 nv = a[t1]t2 = 4 it4 = 4 jt2 = t2 + 4t3 = a[t2]if t3 < v goto B2
B1
B2
t4 = t4 4t5 = a[t4]if t5 > v goto B3
if t2 >= t4 goto B6
a[t2] = t5a[t4] = t3goto B2
B4
B3
B5 t14 = a[t1]a[t2] = t14a[t1] = t3
B6
9.2 数据流分析介绍9.2.1 数据流抽象• 流图上的点
– 基本块中,两个相邻的语句之间为程序的一个点– 基本块的开始点和结束点
• 流图上的路径– 点序列 p1, p2, …, pn ,对 1 和 n - 1 间的每个 i ,满足(1) pi 是先于一个语句的点, pi + 1 是同一块中位于该语句后的点,或者(2) pi 是某块的结束点, pi + 1 是后继块的开始点
9.2 数据流分析介绍9.2.1 数据流抽象• 流图上路径实例 - (1, 2, 3, 4, 9) - (1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 9) - (1, 2, 3, 4, 5, 6, 7, 8,
3, 4, 5, 6, 7, 8, 3, 4, 9) - (1, 2, 3, 4, 5, 6, 7, 8,
3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, …)路径长度无限
- 路径数无限
B1(1)
d2: b = ad3: a = 243 goto B3
B4
B2
B3
if read()<=0 goto B4
d1: a = 1(2)
(3)(4)
(5)(6)(7)(8)(9)
9.2 数据流分析介绍9.2.1 数据流抽象 分析程序的行为时,必须在其流图上考虑所有的执行路径(在调用或返回语句被执行时,还需要考虑执行路径在多个流图之间的跳转) - 通常,从流图得到的程序执行路径数无限,且执行路径长度没有有限的上界
B1(1)
d2: b = ad3: a = 243 goto B3
B4
B2
B3
if read()<=0 goto B4
d1: a = 1(2)
(3)(4)
(5)(6)(7)(8)(9)
9.2 数据流分析介绍9.2.1 数据流抽象 分析程序的行为时,必须在其流图上考虑所有的执行路径(在调用或返回语句被执行时,还需要考虑执行路径在多个流图之间的跳转) - 每个程序点的不同状态数也可能无限程序状态:存储单元到值的映射
B1(1)
d2: b = ad3: a = 243 goto B3
B4
B2
B3
if read()<=0 goto B4
d1: a = 1(2)
(3)(4)
(5)(6)(7)(8)(9)
9.2 数据流分析介绍9.2.1 数据流抽象
把握所有执行路径上的所有程序状态一般来说是不可能的– 数据流分析并不打算区分到达一个程序点的不同执行路径,也不试图掌握该点每个完整的状态– 它从这些状态中抽取解决特定数据流分析所需信息,以总结出用于该分析目的的一组有限的事实– 并且这组事实和到达这个程序点的路径无关,即从任何路径到达该程序点都有这样的事实– 分析的目的不同,从程序状态提炼的信息也不同
9.2 数据流分析介绍9.2.1 数据流抽象• 点 (5) 所有程序状态:
– a {1, 243} – 由 {d1, d3} 定值
(1) 到达‑定值 - {d1, d3} 的定值 到达点 (5)(2) 常量合并 - a 在点 (5) 不是 常量
B1(1)
d2: b = ad3: a = 243 goto B3
B4
B2
B3
if read()<=0 goto B4
d1: a = 1(2)
(3)(4)
(5)(6)(7)(8)(9)
9.2 数据流分析介绍9.2.3 到达 ‑定值• 到达一个程序点的所有定值
– 可用来判断一个变量在某程序点是否为常量– 可用来判断一个变量在某程序点是否无初值
• 别名给到达 ‑定值的计算带来困难– 过程参数、数组访问、间接引用等都有可能引起别名例如:若 p==q ,则 p->next 和 q->next互为别名– 程序分析必须是稳妥的– 本章其余部分仅考虑变量无别名的情况
9.2 数据流分析介绍9.2.3 到达 ‑定值• 到达一个程序点的所有定值
– 可用来判断一个变量在某程序点是否为常量– 可用来判断一个变量在某程序点是否无初值
• 别名给到达 ‑定值的计算带来困难• 定值的注销
– 在一条执行路径上,对 x 的赋值注销先前对 x 的所有赋值
9.2 数据流分析介绍• gen 和 kill 分别表示一个基本块生成和注销的定值
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}gen [B4] = {d7}kill [B4] = {d1, d4}
9.2 数据流分析介绍• 基本块的 gen 和 kill 是怎样计算的
– 对三地址指令 d: u = v + w ,它的状态迁移函数是 fd(x) = gend (x killd)
– 若: f1(x) = gen1 (x kill1), f2(x) = gen2 (x kill2)则: f2(f1(x)) = gen2 (gen1 (x kill1) kill2)= (gen2 (gen1 kill2)) (x (kill1 kill2))
– 若基本块 B 有 n 条三地址指令killB = kill1 kill2 … killn genB = genn (genn1 killn) (genn2 killn1 killn) … (gen1 kill2 kill3 … killn)
9.2 数据流分析介绍• 到达 ‑定值的数据流等式
– genB : B 中能到达 B 的结束点的定值语句– killB :整个程序中决不会到达 B 结束点的定值– IN[B] :能到达 B 的开始点的定值集合– OUT[B] :能到达 B 的结束点的定值集合两组等式(根据 gen 和 kill 定义 IN 和 OUT )– IN[B] = P 是 B 的前驱 OUT[P]– OUT[B] = genB (IN[B] killB)– OUT[ENTRY] =
• 到达 ‑定值方程组的迭代求解
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 B2 000 0000 B3 000 0000
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 000 0000 B2 000 0000 B3 000 0000
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 000 0000 B3 000 0000
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000
B2 111 0000 000 0000 B3 000 0000
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000
B2 111 0000 001 1100 B3 000 0000
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0000 001 1100 B3 001 1100 000 0000
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0000 001 1100 B3 001 1100 000 1110
B4 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0000 001 1100 B3 001 1100 000 1110
B4 001 1110 000 0000
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0000 001 1100 B3 001 1100 000 1110
B4 001 1110 001 0111
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0111 001 1100 B3 001 1100 000 1110
B4 001 1110 001 0111
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0111 001 1110 B3 001 1100 000 1110
B4 001 1110 001 0111
gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍 IN [B] OUT [B] B1 000 0000 111 0000 B2 111 0111 001 1110 B3 001 1110 000 1110
B4 001 1110 001 0111不再继续演示迭代计算gen [B1] = {d1, d2, d3}kill [B1]={d4, d5, d6, d7}
gen [B2] = {d4, d5}kill [B2] = {d1, d2, d7}
gen [B3] = {d6}kill [B3] = {d3}
gen [B4] = {d7}kill [B4] = {d1, d4}
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍• 到达 ‑定值数据流等式是正向的方程
OUT [B] = gen [B] (IN [B] kill [B])IN [B] = P 是 B 的前驱 OUT [P]
某些数据流等式是反向的• 到达 ‑定值数据流等式的合流运算是求并集
IN [B] = P 是 B 的前驱 OUT [P]
某些数据流等式的合流运算是求交集• 对到达 ‑定值数据流方程,迭代求它的最小解某些数据流方程可能需要求最大解
9.2 数据流分析介绍9.2.2 数据流分析模式• 数据流值
– 数据流分析总把程序点和数据流值联系起来– 数据流值代表在程序点能观测到的所有可能程序状态集合的一个抽象– 语句 s前后两点数据流值用 IN[s] 和 OUT[s] 来表示– 数据流问题就是通过基于语句语义的约束(迁移函数)和基于控制流的约束来寻找所有语句 s 的
IN[s] 和 OUT[s] 的一个解
9.2 数据流分析介绍9.2.2 数据流分析模式• 迁移函数 f
– 语句前后两点的数据流值受该语句的语义约束– 若沿执行路径正向传播,则OUT[s] = fs(IN[s])– 若沿执行路径逆向传播,则 IN[s] = fs(OUT[s])
若基本块 B 由语句 s1, s2, …, sn 依次组成,则– IN[si+1] = OUT[si], i = 1, 2, …, n1 (逆向…)– fB = fn . . . f2 f1 (逆向 fB = f1 . . . fn - 1 fn )– OUT[B] = fB (IN[B]) (逆向 IN[B] = fB (OUT[B]) )
9.2 数据流分析介绍9.2.2 数据流分析模式• 控制流约束
– 正向传播 IN[B] = P 是 B 的前驱OUT[P] (可能用)
– 逆向传播 OUT[B] = S 是 B 的后继 IN[S] (可能用)
• 约束方程组的解通常不是唯一的– 求解的目标是要找到满足这两组约束(控制流约束和迁移约束)的最“精确”解
9.2 数据流分析介绍9.2.4 活跃变量• 定义
– x 的值在 p点开始的某条执行路径上被引用,则说 x 在 p点活跃,否则称 x 在 p点已经死亡
– IN[B] :块 B 开始点的活跃变量集合– OUT[B] :块 B 结束点的活跃变量集合– useB :块 B 中有引用且在引用前无定值的变量集– defB :块 B 中有定值的变量集
•应用– 一种重要应用就是基本块的寄存器分配
9.2 数据流分析介绍9.2.4 活跃变量• 例
– use[B2] = { i, j }– def[B2] = { i, j }
d1: i = m 1d2: j = nd3: a = u1
B1
B2
d7: i = u3 B4
d4: i = i + 1d5: j = j 1
d6: a = u2 B3
9.2 数据流分析介绍9.2.4 活跃变量• 活跃变量数据流等式
– IN [B] = useB (OUT [B] defB )– OUT[B] = S 是 B 的后继 IN [S]– IN [EXIT] =
• 和到达‑定值等式之间的联系与区别– 都以集合并算符作为它们的汇合算符– 信息流动方向相反, IN 和 OUT 的作用相互交换– use 和 def 分别取代 gen 和 kill– 仍然需要最小解
9.2 数据流分析介绍9.2.5 可用表达式 x = y + z x = y + z x = y + z . . . . y = … z = …
. . . p p p
y + z 在 p点 y + z 在 p 点 y + z 在 p点可用 不可用 不可用
9.2 数据流分析介绍9.2.5 可用表达式下面两种情况下, 4i 在 B3 的入口都可用
t1 = 4i
没有对i 赋值
t2 = 4i
B1
B2
B3
t1 = 4i
i =t0 = 4i
t2 = 4i
B1
B2
B3
9.2 数据流分析介绍9.2.5 可用表达式• 定义
– 若到点 p 的每条执行路径都计算 x + y ,并且计算后没有对 x 或 y 赋值,那么称 x + y 在点 p 可用– e_genB :块 B产生的可用表达式集合– e_killB : 块 B注销的可用表达式集合 – IN [B] : 块 B 入口的可用表达式集合– OUT [B] :块 B出口的可用表达式集合
• 应用– 公共子表达式删除
9.2 数据流分析介绍9.2.5 可用表达式• 数据流等式
– OUT [B] = e_genB (IN [B] e_killB )– IN [B] = P 是 B 的前驱 OUT [P] – IN [ENTRY] =
• 同先前的主要区别– 使用而不是作为这里数据流等式的汇合算符– 求最大解而不是最小解
9.2 数据流分析介绍• IN 集合的不同初值比较 (以 B2 为例)
B1
B2简写成:I j+1 = OUT[B1] O j
O j+1 = G (I j+1 K)O0 = (初值为空集) O0 = U (初值为全集)I 1 = I 1 = OUT[B1] O1 = G O1 = G (OUT[B1] K)I 2 = OUT[B1] GI2=(OUT[B1]G) (OUT[B1]K)O 2 = G O 2 = G (OUT[B1] K)
IN[B2]= OUT[B1] OUT[B2]OUT[B2] = G (IN[B2] K)
9.2 数据流分析介绍9.2.6 小结• 三个数据流问题
– 到达‑定值、活跃变量、可用表达式• 每个问题的组成
– 数据流值的论域、数据流的方向、迁移函数、边界条件、汇合算符、数据流等式• 见书上表 9.2
9.3 数据流分析的基础本节内容• 把各种数据流模式作为一个整体抽象地研究• 形式地回答数据流算法的下列几个基本问题
– 在什么情况下数据流分析中使用的迭代算法是正确的– 迭代算法所得解的精度如何– 迭代算法是否收敛– 数据流方程的解的含义是什么
9.3 数据流分析的基础• 数据流分析框架 (D, V, , F) 包括
– 数据流分析的方向 D ,它可以是正向或逆向– 数据流值的论域
半格 V 、汇合算子– V 到 V 的迁移函数族 F
包括适用于边界条件( ENTRY 和 EXIT结点)的常函数
9.3 数据流分析的基础9.3.1 半格• 半格 (V, ) 是一个集合 V 和一个二元交运算 (汇合运算 ) ,交运算满足下面三点性质
– 幂等性:对所有的 x , x x = x– 交换性:对所有的 x 和 y , x y = y x– 结合性:对所有的 x, y 和 z , x (y z) = (x y)
z• 半格有顶元 (可以还有底元)
– 对 V 中的所有 x , x = x– 对 V 中的所有 x , x =
9.3 数据流分析的基础9.3.1 半格• 偏序关系
集合 V上的关系– 自反性:对所有的 x , x x– 反对称性:对所有的 x 和 y ,如果 x y 并且 y
x,那么 x = y– 传递性:对所有的 x, y 和 z ,如果 x y 并且 y z,那么 x z此外,关系的定义
x y当且仅当 (x y) 并且 (x y)
9.3 数据流分析的基础9.3.1 半格• 半格和偏序关系之间的联系
– 半格 (V, ) 的汇合运算确定了半格值集 V上一种偏序 : 对 V 中所有的 x 和 y , x y当且仅当 x
y = x– 若 x y等于 g ,则 g就是 x 和 y 的最大下界
9.3 数据流分析的基础9.3.1 半格• 例 半格的论域 V 是先前全域 U 的幂集
– 集合并作为汇合运算:是顶元, U 是底元, x = x 并且 U x = U ,对应的偏序关系是
– 集合交作为汇合运算: U 是顶元, 是底元,对应偏序关系是 – 数据流方程组通常有很多解,但是按偏序意义上的最大解是最精确的
(1) 到达‑定值:最精确的解含最少定值(2) 可用表达式:最精确的解含最多表达式
9.3 数据流分析的基础9.3.1 半格• 格图
右边是定值子集形成的格– 这是一个格,本课程用半格概念就够了 是– x y 的最大下界 x y
{ }
{d1} {d2} {d3}
{d1, d2} {d1, d3} {d2, d3}
{d1, d2, d3}
( )
( )
9.3 数据流分析的基础9.3.1 半格• 积半格(定义略)
– 上一页数据流值的集合是定值集合的幂集– 可以用从每个变量的一个简单定值半格构造出的积半格来表示整个定值半格
• 半格的高度– 上升链是序列 x1 x2 … xn
– 半格的高度就是其中最长上升链中的个数– 若半格的高度有限,证明数据流分析迭代算法的收敛则非常容易
9.3 数据流分析的基础9.3.2 迁移函数• 迁移函数族 F : V V 有下列性质
– F 包括恒等函数 – F封闭于复合– 若 F 中所有函数 f 都有单调性,即
x y蕴涵 f(x) f(y) ,或 f(x y) f(x) f(y)则称框架 (D, V, , F) 是单调的
– 框架 (D, V, , F) 的分配性 对 F 中所有的 f , f(x y) = f(x) f(y)
9.3 数据流分析的基础9.3.2 迁移函数• 例 到达‑定值分析
若 f1(x) = G1 (x K1) , f2(x) = G2 (x K2)– 若 G 和 K 是空集,则 f 是恒等函数– f2(f1(x)) = G2 ((G1 (x K1)) K2)
= (G2 (G1 K2)) (x (K1 K2))因此 f1 和 f2 的复合 f 为 f = G (x K) 的形式– 分配性可以由检查下面的条件得到
G ((y z) K) = (G (y K)) (G (z K))分配性: f(y z) = f(y) f(z)
9.3 数据流分析的基础9.3.3 一般框架的迭代算法• 以正向数据流分析为例
(1) OUT[ENTRY] = vENTRY;(2) for ( 除了 ENTRY 以外的每个块 B) OUT[B] =;(3) while ( 任何一个 OUT出现变化 )(4) for ( 除了 ENTRY 以外的每个块 B) {(5) IN[B] = /\P 是 B 的前驱 OUT[P];(6) OUT[B] = fB(IN[B]);(7) }
9.3 数据流分析的基础9.3.3 一般框架的迭代算法• 算法的一些可以证明的性质
– 如果算法收敛,则结果是数据流方程组的一个解– 如果框架单调,则所求得的解是数据流方程组的最大不动点– 如果框架单调并且半格的高度有限,那么可以保证算法收敛
9.3 数据流分析的基础9.3.4 数据流解的含义• 结论:算法所得解是理想解的稳妥近似• 理想解所考虑的路径
– 执行路径集:流图上每一条路径都属于该集合 若流图有环,则执行路径数是无限的
– 程序可能的执行路径集:程序执行所走的路径属于该集合 — 这是理想解所考虑的路径集– 可能的执行路径集 执行路径集– 寻找所有可能执行路径是不可判定的
• 以下讨论以正向数据流分析为例
9.3 数据流分析的基础9.3.4 数据流解的含义• 理想解若路径 P = ENTRY B1 B2 … Bk ,定义– fP = fk1 … f2 f1
– IDEAL[B] = /\P 是从 ENTRY 到 B 的一条可能路径 fP(vENTRY)
• 有关理解解的结论– 任何大于理想解 IDEAL 的回答一定是不对的– 任何小于或等于 IDEAL 的值是稳妥的– 在稳妥的值中,越接近 IDEAL 的值越精确
9.3 数据流分析的基础9.3.4 数据流解的含义• 执行路径上的解( meet over paths )
– MOP[B] = /\P 是从 ENTRY 到 B 的一条路径 fP(vENTRY)– MOP解不仅汇集了所有可能路径的数据流值,而且还包括了那些不可能被执行路径的数据流值– 对所有的块 B , MOP[B] IDEAL[B] ,简写成
MOP IDEAL– MOP 的定义并没有通向一个直接算法
9.3 数据流分析的基础9.3.4 数据流解的含义• 先前算法得到的最大不动点解MFP
– 不是先找出到达一个块的所有路径,然后用汇合运算,而是– 访问每个基本块,并且不一定按照程序执行时的次序– 在每个汇合点,把汇合运算作用在到目前为止得到的数据流值上,其中所用一些初值是人工引入的
MFP: maximal fixed point
B1
ENTRY
B4
B3
B2
9.3 数据流分析的基础9.3.4 数据流解的含义• MFP 与 MOP 的联系
– MFP 访问块未必遵循次序由各块的初值和迁移函数
的单调性保证结果一致– MFP较早地使用汇合运算
迭代算法的 IN[B4] = f3(f1(vENTRY) f2(vENTRY)) 而 MOP[B4] = (f3 f1) (vENTRY) (f3 f2) (vENTRY)
数据流分析框架具有分配性时,结果是一样的• MFP MOP IDEAL
B1
ENTRY
B4
B3
B2
9.4 常 量 传 播9.4.1 常量传播框架的数据流值• 单个变量的数据流值半格
– 变量的类型所允许的所有常量– 值 NAC 表示不是常量– 值 UNDEF 表示没有定义
• 程序中各变量的半格的积– 把程序中每个变量映射到 该半格上的一个“值”
UNDEF
NAC
… 3 2 1 0 1 2 3 …
9.4 常 量 传 播9.4.2 常量传播框架的迁移函数
令 fs 是语句 s 的迁移函数, m = fs(m) ,用 m 和 m之间的联系来表达 fs (m 是变量到“值”的映射 )(1) 如果 s 不是赋值语句,则 fs 是恒等函数(2) 若 s 对变量 x 赋值,则对所有 v x , m(v) = m(v),再看m(x) :– 若 s 的右部是一个常量 c ,则m(x) = c– 若 s 的右部是 y + z
m(x) = m(y) + m(z) ,若m(y) 和 m(z)都是常量值 m(x) = NAC ,若m(y) 或 m(z) 是 NAC m(x) = UNDEF , 否则
9.4 常 量 传 播9.4.3 常量传播框架的单调性
m(y) m(z) m(x)UNDEF UNDEF UNDEF c2 UNDEFNAC NACUNDEF UNDEF c1 c2 c1 + c2
NAC NACUNDEF NACNAC c2 NACNAC NAC
(1) 赋值语句的形式不是 x = y + z fs 不改变 m(x) 的值,或者改变成一个常量 在这些情况下, fs肯定是单调的
9.4 常 量 传 播9.4.3 常量传播框架的单调性
m(y) m(z) m(x)UNDEF UNDEF UNDEF c2 UNDEFNAC NACUNDEF UNDEF c1 c2 c1 + c2
NAC NACUNDEF NACNAC c2 NACNAC NAC
(1) 赋值语句的形式不是 x = y + z fs 不改变 m(x) 的值,或者改变成一个常量 在这些情况下, fs肯定是单调的(2) 赋值语句的形式是x = y + z见右边, fs单调
9.4 常 量 传 播9.4.4 常量传播框架的非分配性
– 不管取什么路径,在块 B3 的出口,z 的值是 5
– 迭代算法在块 B3 的入口而不是出口使用汇合运算
– f3(f1(m0) f2(m0)) f3(f1(m0)) f3(f2(m0))不具有分配性
– 算法未能发现 B3出口 z 是 5 ,这个结果虽不精确但安全
B1
EXIT
z = x + y
x = 2y = 3
B3
B2x = 3y = 2
9.4 常 量 传 播9.4.5 结果的解释
– ENTRY块置初值 UNDEF – 其它块置初值 UNDEF– x 在块 B4 入口是 10– 块 B5 的 x 引用可以用 10 代替– 和执行路径 B1 B3
B4 B5 不符– 若程序正确的话,认为 x 在块 B5 入口的值
只能为 10 是合适的
if Q goto B2 B1
B2
B4
B3x = 10
if Q goto B5
B5
B7
B6 = x
9.5 部 分 冗 余 删 除• 本节内容
– 冗余计算以公共子表达式的形式出现,或以循环不变表达式的形式出现– 冗余可能只出现在一部分路径上– 讨论最小化 x + y 这样表达式的计算次数的方法– 策略是,一个计算尽量不做,除非它不得不做– 首先讨论冗余的不同形式,以建立直观认识,然后描述广义的冗余删除问题,最后提出算法– 算法涉及到求解多个正向或逆向的数据流问题
9.5 部 分 冗 余 删 除9.5.1 冗余的根源• 公共子表达式
B1
B2
B4
B3b = 7d = b + c
a = b + c
e = b + c
B1
B2
B4
B3b = 7t = b + cd = t
t = b + ca = t
e = t
9.5 部 分 冗 余 删 除9.5.1 冗余的根源• 完全冗余
– 块 B4 的表达式 b + c 是完全冗余的,因为所有到达 B4 的路径都计算b + c ,并且 b 和 c都未重新定值– B2 和 B3 的出边的集合形成一个割集,若把该割集删掉,则从起点到 B4 的路径都被割断
B1
B2
B4
B3b = 7d = b + c
a = b + c
e = b + c
9.5 部 分 冗 余 删 除9.5.1 冗余的根源• 循环不变表达式
a = b + c
t = b + c
a = t
9.5 部 分 冗 余 删 除9.5.1 冗余的根源• 部分冗余表达式
B1
B2
B4
B3
a = b + c
d = b + c
B1
B2
B4
B3
t = b + ct = b + ca = t
d = t
9.5 部 分 冗 余 删 除9.5.1 冗余的根源• 放置 b + c 的副本来使 B4 中的 b + c 完全冗余
B1
B2
B4
B3
a = b + c
d = b + c
B1
B2
B4
B3
t = b + ct = b + ca = t
d = t
9.5 部 分 冗 余 删 除9.5.2 能否删除所有的冗余
B3 B4 是一条关键边:源结点有多个后继目标结点有多个前驱
B1
B2 B3
B4d = b + c
a = b + c
B5
…
B1
B2 B3
B6t = b + c
t = b + ca = t
B5
…
d = t B4增加新块来删除冗余
9.5 部 分 冗 余 删 除• 复制代码以删除冗余
B1
B2 B3
B4
a = b + c
B5 B6
B7
d = b + c
B1
B2 B3
B4
t = b + ca = t
B4
B5
d = t d = b + cB6 B6
B7
9.5 部 分 冗 余 删 除9.5.3 惰性代码移动问题• 部分冗余删除算法
优化后程序具有下列性质– 无需通过复制代码就可删除的冗余计算都被删除
若是部分冗余,则通过放置副本形成完全冗余– 优化后程序不会执行原来程序中没有的任何计算– 表达式的计算尽可能推迟 尽可能延迟值的计算就是最小化它的生存期,
也就是最小化它占用寄存器的时间
9.5 部 分 冗 余 删 除9.5.4 预期表达式 预期的 不可用的
B1
B2 B5
B4
c = 2
B3 B6
B7 d = b + c
a = b + c
e = b + c
B8
B9
B10
B11
b + c 部分冗余 B5 的 b + c 不能提前到 B1 计算 B5 的 b + c 计算不能再推迟 添加的计算最迟放在 B4
9.5 部 分 冗 余 删 除9.5.4 预期表达式 预期的 不可用的
B1
B2 B5
B4
c = 2
B3 B6
B7
t = b+c
d = t
t = b + ca = t
e = t
B8
B9
B10
B11
b + c 部分冗余 B5 的 b + c 不能提前到 B1 计算 B5 的 b + c 计算不能再推迟 添加的计算最迟放在 B4
期望的优化结果如图
9.5 部 分 冗 余 删 除9.5.4 预期表达式 预期的 不可用的
B1
B2 B5
B4
c = 2
B3 B6
B7 d = b + c
a = b + c
e = b + c
B8
B9
B10
B11
表达式 b + c在点 p 被预期: 若所有从 p 开始的路径上都从p点可用的 b 和
c来计算表达式b + c 的值
b + c 被块 B3,B4, B5, B6, B7 和B9( 的入口 )预期
9.5 部 分 冗 余 删 除9.5.5 惰性代码移动算法 预期的 不可用的
B1
B2 B5
B4
c = 2
B3 B6
B7 d = b + c
a = b + c
e = b + c
B8
B9
B10
B11
1 、使用预期来决定表达式可以放置的位置 可以建立数据流方程来求解预期表达式
9.5 部 分 冗 余 删 除9.5.5 惰性代码移动算法 预期的 不可用的
B1
B2 B5
B4
c = 2
B3 B6
B7 d = b + c
a = b + c
e = b + c
B8
B9
B10
B11
2 、表达式的副本被放置到该表达式最早被期望的程序点 (B3, B5) 需要定义可用表达式数据流问题 表达式在点 p可用,若在到达 p的所有路径上它都被预期
9.5 部 分 冗 余 删 除9.5.5 惰性代码移动算法
3 、在保语义且最小化冗余前提下,尽可能延迟表达式的计算 放置在从可延迟到不可延迟的边界上 (B4, B5) 需要定义可延迟表达式数据流问题
最早的 可延迟的B1
B2 B5
B4
c = 2
B3 B6
B7 d = b + c
a = b + c
e = b + c
B8
B9
B10
B11
9.5 部 分 冗 余 删 除9.5.5 惰性代码移动算法
4 、确定表达式被引用的位置,以便改成引用临时变量 需要定义引用表达式数据流问题,类似于活跃变量分析
最早的 可延迟的B1
B2 B5
B4
c = 2
B3 B6
B7 d = b + c
a = b + c
e = b + c
B8
B9
B10
B11
9.5 部 分 冗 余 删 除9.5.5 惰性代码移动算法
实施该算法后的结果
最早的 可延迟的B1
B2 B5
B4
c = 2
B3 B6
B7
t = b+c
d = t
t = b + ca = t
e = t
B8
B9
B10
B11
9.6 流图中的循环• 标识循环并对循环专门处理的重要性
– 程序执行的大部分时间消耗在循环上,改进循环性能的优化会对程序执行产生显著影响– 循环也会影响程序分析的运行时间
• 本节介绍– 介绍概念:支配结点、深度优先排序、回边、图的深度和可归约性– 用于寻找循环和迭代数据流分析收敛速度的讨论
9.6 流图中的循环9.6.1 支配结点d 是 n 的支配结点 : 若从初始结点起,每条到达 n 的路径都要经过 d ,写成 d dom n 结点是它本身的支配结点 循环的入口是循环中所有结点的支配结点 支配结点集的计算可以形式化为一个数据流问题
1
23
4
5 6
7
8
9 10
d
n
9.6 流图中的循环1
23
4
56
7
8
910
1
23
4
5 6
7
8
9 10
9.6.2 回边和可归约性
深度优 先表示
9.6 流图中的循环9.6.2 回边和可归约性• 深度优先表示
– 前进边 (深度优先生成树的边 )– 后撤边
4 3 、 7 4 、 10 7 、8 3 和 9 1
– 交叉边2 3 和 5 7
1
23
4
56
7
8
910
9.6 流图中的循环9.6.2 回边和可归约性•回边
– 如果有 a dom b ,那么边 b a 叫做回边
– 如果流图可归约,则后撤边正好就是回边
1
23
4
5 6
7
8
9 10
9.6 流图中的循环9.6.2 回边和可归约性•回边
– 如果有 a dom b ,那么边 b a 叫做回边
– 如果流图可归约,则后撤边正好就是回边
1
23
4
56
7
8
910
9.6 流图中的循环9.6.2 回边和可归约性• 可归约流图
– 如果把一个流图中所有回边删掉后,剩余的图无环
9.6 流图中的循环9.6.2 回边和可归约性• 可归约流图
– 如果把一个流图中所有回边删掉后,剩余的图无环• 例:不可归约流图
– 开始结点是 1– 2 3 和 3 2都不是回边– 该图不是无环的– 从结点 2 和 3两处都能进入
由它们构成的环 32
1
9.6 流图中的循环9.6.3 流图的深度• 深度是在无环路径上的最大后撤边数
– 深度不大于流图中循环嵌套的层数
– 该例深度为 310 7 4 3
1
23
4
56
7
8
910
9.6 流图中的循环9.6.4 自然循环• 自然循环的性质
– 有唯一的入口结点,叫做首结点,首结点支配该循环中所有结点– 至少存在一条回边进入该循环首结点
• 回边 n d确定的自然循环– d 加上不经过 d 能到达 n 的所有结点– 结点 d 是该循环的首结点
9.6 流图中的循环9.6.4 自然循环•回边 10 7 循环 {7, 8, 10}•回边 7 4 循环 {4, 5, 6, 7, 8, 10}•回边 4 3 和 8 3 循环 {3, 4, 5, 6, 7, 8, 10}•回边 9 1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
1
23
4
5 6
7
8
9 10
9.6 流图中的循环• 内循环
– 若一个循环的结点集合是另一个循环的结点集合的子集– 两个循环有相同的首结点 ,
但并非一个结点集是另一个的子集,则看成一个循环 1
2
3 4
本 章 要 点1 、对各类优化的理解,包括常量合并、公共子表达式删除、复写传播、死代码删除、循环优化等2 、到达-定值、活跃变量、可用表达式等几种常用的数据流抽象实例3 、把各种数据流模式作为整体来抽象地研究的共同理论框架4 、常量传播的特点5 、最小化表达式计算次数的一种方法—部分冗余删除方法6 、自然循环的概念及自然循环的识别
例 题 1UNIX 下的 C 编译命令 cc 的选择项 g 和 O 的解释如下 , 其中dbx 的解释是“ dbx is an utility for source-level debugging and execution of programs written in C”。试说明为什么用了选择项 g 后,选择项O 便被忽略。-g Produce additional symbol table information for dbx(1) and dbxtool(1) and pass -lg option to ld(1) (so as to include the g library, that is: /usr/lib/libg.a). When this option is given, the -O and -R options are suppressed.-O[level] Optimize the object code. Ignored when either -g, -go, or -a is used. ...
例 题 2一个 C 语言程序如下,右边是优化后的目标代码main() pushl %ebp { movl %esp,%ebp
int i,j,k; movl $1,%eax -- j=1 i=5; movl $6,%edx -- k=6 j=1; L4: while(j<100){ addl %edx,%eax -- j=j+6 k=i+1; cmpl $99,%eax j=j+k; jle .L4 -- while(j99) }
} 完成了哪些优化?
例 题 2一个 C 语言程序如下,右边是优化后的目标代码main() pushl %ebp { movl %esp,%ebp
int i,j,k; movl $1,%eax -- j=1 i=5; movl $6,%edx -- k=6 j=1; L4: while(j<100){ addl %edx,%eax -- j=j+6 k=i+1; cmpl $99,%eax j=j+k; jle .L4 -- while(j99) } 复写传播、常量合并、代码外提、删除无用赋值
} 对 i , j 和 k 分配内存单元也成为多余,从而被取消
例 题 3一个 C 语言程序 main(){
long i,j;
while (i) {if (j) { i = j; }
}} 生成的汇编码见右边为什么会有连续跳转?
pushl %ebpmovl %esp,%ebpsubl $8,%esp
.L2:cmpl $0,-4(%ebp)jne .L4jmp .L3
.L4:cmpl $0,-8(%ebp)je .L5movl -8(%ebp),%eaxmovl %eax,-4(%ebp)
.L5:jmp .L2
.L3:
例 题 3while E1 do S1L2 : E1 的代码 真转 L4 无条件转 L3L4 : S1 的代码
JMP L2L3:if E2 then S2 E2 的代码假转 L5
S2 的代码L5 :
例 题 3while E1 do S1 当它们嵌套时,代码结构变成 L2 : E1 的代码 L2 : E1 的代码 真转 L4 真转
L4 无条件转 L3 无条件转 L3L4 : S1 的代码 L4 : E2 的代码
JMP L2 假转 L5 L3: S2 的代码if E2 then S2 L5 : JMP L2 E2 的代码 L3 : 假转 L5
S2 的代码L5 :
例 题 3while E1 do S1 当它们嵌套时,代码结构变成 L2 : E1 的代码 L2 : E1 的代码 真转 L4 真转
L4 无条件转 L3 无条件转 L3L4 : S1 的代码 L4 : E2 的代码
JMP L2 假转 L5 L3: S2 的代码if E2 then S2 L5 : JMP L2 E2 的代码 L3 : 假转 L5
S2 的代码L5 :
例 题 3while E1 do S1 当它们嵌套时,代码结构变成 L2 : E1 的代码 L2 : E1 的代码 真转 L4 真转
L4 无条件转 L3 无条件转 L3L4 : S1 的代码 L4 : E2 的代码
JMP L2 假转 L5 L3: S2 的代码if E2 then S2 L5 : JMP L2 E2 的代码 L3 : 假转 L5
S2 的代码L5 :
例 题 3while E1 do S1 当它们嵌套时,代码结构变成 L2 : E1 的代码 L2 : E1 的代码 真转 L4 真转
L4 无条件转 L3 无条件转 L3L4 : S1 的代码 L4 : E2 的代码
JMP L2 假转 L5 L3: S2 的代码if E2 then S2 L5 : JMP L2 E2 的代码 L3 : 假转 L5
S2 的代码L5 :
可优化为假转
例 题 3一个 C 语言程序 main(){
long i,j;
while (i) {if (j) { i = j; }
}}优化编译的汇编码见右边
pushl %ebpmovl %esp,%ebp
.L7:testl %eax,%eaxje .L3testl %edx,%edxje .L7movl %edx,%eaxjmp .L7
.L3:
例 题 4求最大公约数的函数long gcd(p,q)long p,q;{
if (p%q == 0) return q;else return gcd(q, p%q);
}
其中的递归调用称为尾递归 对于尾递归,编译器可以产生和一般的函数调用不同的代码,使得目标程序运行时,这种递归调用所需的存储空间大大减少,也缩短了运行时间 对于尾递归,编译器应怎样产生代码 ?
例 题 4求最大公约数的函数long gcd(p,q)long p,q;{
if (p%q == 0) return q;else return gcd(q, p%q);
}
计算实在参数 q 和 p%q ,存放在不同的寄存器中 将上述寄存器中实在参数的值存入当前活动记录中形式参数 p 和 q 的存储单元 转到本函数第一条语句的起始地址继续执行
例 题 4求最大公约数的函数long gcd(p,q)long p,q;{
if (p%q == 0) return q;else return gcd(q, p%q);
}
movl 8(%ebp),%esi pmovl 12(%ebp),%ebx q
.L4:movl %esi,%eaxcltd 扩展为 64位idivl %ebxmovl %edx,%ecx p%qtestl %ecx,%ecx p%qje .L2movl %ebx,%esi qpmovl %ecx,%ebxp%qqjmp .L4
.L2:
C 语言程序引用 sizeof (求字节数运算符)时,该运算是在编译该程序时完成,还是在运行该程序时完成?说明理由。
例 题 5
Program StmtStmt id := Exp | read ( id ) | write ( Exp ) |
Stmt ; Stmt | while ( Exp ) do begin Stmt end |if ( Exp ) then begin Stmt end else begin Stmt end
Exp id | lit | Exp OP Exp定义 Stmt 的两个属性• MayDef 表示它可能定值的变量集合• MayUse 表示它可能引用的变量集合• 写一个语法制导定义或翻译方案,它计算 Stmt 的上
述 MayDef 和 MayUse属性
例 题 6
Stmt id := Exp{ Stmt.MayDef = {id.name} ; Stmt.MayUse = Exp.MayUse }
Stmt read ( id ){ Stmt.MayUse = ; Stmt.MayDef =
{id.name}}Stmt write ( Exp )
{ Stmt.MayDef = ; Stmt.MayUse = Exp.MayUse }
例 题 6
Stmt Stmt1 ; Stmt2
{ Stmt.MayUse = Stmt1.MayUse Stmt2.MayUse ; Stmt.MayDef = Stmt1.MayDef Stmt2.MayDef }
Stmt if ( Exp ) then begin Stmt1 end else begin Stmt2 end{ Stmt.MayUse = Stmt1.MayUse Stmt2.MayUse Exp.MayUse; Stmt.MayDef = Stmt1.MayDef Stmt2.MayDef }
例 题 6
Stmt while ( Exp ) do begin Stmt1 end{ Stmt.MayUse = Stmt1.MayUse Exp.MayUse; Stmt.MayDef = Stmt1.MayDef }
Exp id{ Exp.MayUse = {id.name} }
Exp lit{ Exp.MayUse = }
Exp Exp1 OP Exp2
{ Exp.MayUse = Exp1.MayUse Exp2.MayUse }
例 题 6
基于 MayDef 和 MayUse属性 , 说明Stmt1;Stmt2
和 Stmt2;Stmt1 在什么情况下有同样的语义Stmt1.MayDef Stmt2.MayUse = andStmt2.MayDef Stmt1.MayUse = andStmt1.MayDef Stmt2. MayDef =
例 题 6
习 题• 第一次: 9.12, 9.15• 第二次: 9.18, 9.25