“一个范围受控的独立接口是线性函数测试替换:先用已证明的仿射递推改写循环比较,再检查计数器除自身更新外不再被体内、条件或出口读取,最后成对删除初始化与递增。它为这一个封闭分量另给状态关系证明,…”
形式陈述
只剩比较在使用旧计数器
经过仿射归纳变量强度削弱,循环中的实际数据计算可能已全部读取新载体 h,i 却仍每轮加一次,只为判断 i<B。既然在每个条件检查点都有 h=mi+c,能否直接用 h 检查边界,再让 i 退出程序?
这包含两项不同义务。先证明新比较与旧比较有相同真假;随后确认 i 的其余使用真的消失,才能删除其初始化和更新。数值关系成立并不意味着源程序从未在返回值里观察 i。
沿用前页的规范数学整数循环、纯总运算、体尾唯一归纳更新与不变步长、偏移。源条件必须为 i θ B,其中
B 的全部变量在入环前已定义且体内不写。斜率 m 是已知非零整数,偏移 c 是循环不变表达式。要求 h=mi+c 在每次旧条件检查点成立,而不是只在某个体内计算位置成立。
比较怎样随斜率变换
先在入环初始化段计算新界 L=mB+c。m>0时保持比较符号,m<0时反转四种有序比较;等于与不等于保持符号:
例如 m=−2 时,i<13 对应 −2i+9>−17。这里反转的是斜率符号,不是步长符号。循环可以递增或递减,只要同一个仿射关系在检查点成立,新旧比较仍逐点等价。
m=0时,h=c 丢失了 i 的信息,不能把所有 i 与 B 的比较还原出来。参考器拒绝此种测试替换,即使之前的强度削弱仍可合法维护这个常值。它不会用测试输入中偶然一致的真假代替非零斜率条件。
改测试之后,再检查能否删变量
把条件换成 h θ′ L 后,检查循环体中除 i 自身递增之外是否仍有任何读取 i 的位置,也检查所有返回表达式。如果还有读取,则本接口拒绝删除 i,不试图猜测该使用“不重要”。
删除仍须满足死代码消除的纯总运算与观察限制。不过旧页的普通活跃性迭代可能让自读递增一直保活,本页不能声称调用那个保守实现就会自动删掉 i。这里另有已检查的封闭结构:除自身递增外,i 不再被任何体内表达式、条件或返回读取;本页因而成对删除初始化和递增,并用下文的状态关系证明正确。其余源赋值保持原位置。
初始 h 原来在 i 初始化后读取 i。为删除 i:=i₀,把这项初始化写成 h:=m i₀+c。输入合同保证 i 的初始化是原初始化段最后一条,二者之间没有另一条源写入会改变 i₀ 的右侧值,因此这次替代取得同一个初值。更一般的跨赋值复制传播需要自己的到达定义证据。
参考函数从实际源程序重新执行强度削弱,建立递推证据,再检查本页条件;不接受用户传来的未经核验“h=mi+c”标记。成功返回完整新程序、新旧条件、界初始化和被移除变量。失败返回 refused、精确原因和已经验证的前阶段报告,最终 program 为 null;不得把前阶段程序误当成测试替换已经通过。
直觉
i 与 h 像同一条刻度尺上的两套刻度。正斜率把顺序保持住;负斜率把刻度方向翻转;零斜率则把全部刻度压成一点,无法判断原来的先后。转换边界时,也必须用同一套比例和偏移。
能够用新刻度判断循环,并不保证旧刻度无人需要。如果函数最后还返回 i,删除它就丢掉观察结果。本页先改比较,再做使用检查,把这两步分开。
例子与边界
从i<13到h<61
前页源程序 i 从2起,每轮加3,j=5i−4,sum 累加 j。强度削弱后 h 从6起,每轮加15;返回只含 sum、j,没有 i。新界是5×13−4=61,因此循环改成:
sum := 0
j := 99
h := 5*start+bias
d := 15
L := 5*limit+bias
while h < L:
j := h
sum := sum+j
h := h+d
return (sum,j)
h 的五个条件检查值为6、21、36、51、66,对61的比较为真、真、真、真、假;旧 i 对13的比较也恰是这串真假。两者都返回 (114,51)。目标不再执行 i 的初始化和四次递增,但保留 j 的实际赋值。
源、仅强度削弱、再替换测试三者的乘法次数分别为4、1、2;最后一步额外计算新界,不是免费常量。加法次数为12、13、10。若start、limit和bias都是运行时参数,新界乘法要在每次调用时支付;不能把删除四次 i 递增说成“目标所有操作都减少”。
负斜率要求反转方向
保持 i 的起点2、步长3和上界13,改成 j=−2i+9。四轮 j 为5、−1、−7、−13,sum 最终为−16,返回 (−16,−13)。h 从5开始每轮减6,新界为−17。
正确条件是 h>−17;检查5、−1、−7、−13时为真,检查−19时为假。若忘记反转仍写 h<−17,第一次检查就为假,错误返回 (0,99)。步长仍为正,却必须反转比较,决定因素是斜率−2。
跨过界值,不等于到达界值
正斜率例中 h 从51跳到66,越过61而不等于61。把正确的 h<61擅自改成 h!=61,会一直继续,因为6+15k永远不是61。源只执行4轮,错误目标却不再正常退出。
本页保留比较种类,只在负斜率时作对应反转。若源本来就是 i!=B,则新条件 h!=mB+c确实等价;它不承诺源一定会到达B。例如 i₀=2、s=3、B=13,方程2+3k=13没有非负整数解,所以这份具体源循环发散,正确目标也发散。
循环结束后的计数器仍可能有用
让正斜率主例返回 (sum,j,i)。源结果为 (114,51,14),不是把界13填到最后一项。强度削弱保持这个返回;测试替换接口报告 induction live at exit,不能交付缺失 i 的程序。
当然可以另设计退出值重建,借 h 和非零m求回 i;但那需要定义整除与观察时刻,核对所有退出边,并计入新增成本。本页不做这项扩展,拒绝比未经证明的“修复”更准确。
回绕的仿射关系不保持整数顺序
设所有值按8位无符号数回绕,源从 i=0 起,条件 i<100,每轮 i加1。源执行100轮;用 h=4i的模256递推并不破坏这个模关系。
但若把新界400也回绕成144,再用 h<144作条件,目标在36轮后就停止:此时 i=36仍小于100,h=144却不小于144。这说明“递推仍对”不等于“比较仍对”。本接口只接收数学整数模型,没有一个能偷偷切到回绕语义的选项;要优化机器整数,须先给出足以排除相关回绕的范围证明或其他专门规则。
推论与应用
一个逐点比较证明
对任意数学整数 i、B,若m>0,由整数有序环性质,i<B当且仅当mi+c<mB+c,其他非严格或反向不等式同理。若m<0,乘以负数使顺序反转,随后加同一个c不再改变真假。
相等与不等于使用的是单射性:m(i−B)=0在m非零的整数域上当且仅当i=B。m=0恰好使这步失效。因此表中的每项都有对应依据,不是只凭“线性函数一般不会改变测试”。
在每次循环头,前页递推已给出h=mi+c,新界又因B、c不变而一直等于mB+c。将逐点命题代入,就得到每次新旧条件相同。证明不要求i按正方向前进,也不要求存在某个有限退出轮次。
删除计数器后的状态关系
源与目标不再拥有完全相同的变量集合。关系要求:除被证明没有外部使用的i以外,所有原变量仍同值;目标的h对应源mi+c。初始h用i₀求值建立关系,删除i初始化不影响其他源赋值。
一轮开始时条件相同。保留下来的体内表达式不读取i,其余源状态又相同,因此逐条得到相同赋值结果。源尾部更新i,目标尾部更新h;由仿射递推恢复下一头的关系。退出返回不读取i,故观察结果相同。
每个匹配轮次都是有限工作,因而有限退出与无限循环都保持。执行器把“检查条件后还需执行更多轮,但预算用完”报告为budget_exhausted;它提供相同数量的循环头前缀,不能自行判定任意循环永不终止。
实际成本与适用范围
若可靠递推关系和使用索引已给出,改写比较、生成新界与删除两条定义只需常数数量的IR动作;确认没有其他使用仍按所检查语法的大小付费。新界表达式不是复制动态循环次数,而是在初始化段出现一次。
参考器为可复查而从源重新建立强度削弱结果,再扫描体内及返回的所有读取,并重验输出初始化合同。沿用前页A及常量折叠工作F,保守费用为O(A²+F),工作空间O(A²),输出语法O(A)。这包含重复验证与结构扫描,不把“改一条分支”冒充整个过程的成本。
对于N轮主例,删除原i更新节约N次加法,但新界增加一次乘法和一次偏移加法。N=0时源返回(0,99),最终目标也一样,却新增两次初始化乘法和两次加法。语义合法、操作数变化与硬件性能应分开报告。
可执行终点要求同时提交正负斜率的比较真假序列、零斜率拒绝、活跃出口拒绝、36对100的回绕反例及!=不可到达界的前缀。只有所有使用都受控时,消失的旧计数器才真正是死代码。
参考资料
- Keith D. Cooper、L. Taylor Simpson、Christopher A. Vick,Operator Strength Reduction,作者预稿,§5,预稿PDF第18–19页,介绍沿削弱变换链替换被比较变量和界,并由后续优化删除旧归纳变量;Figure2为基本组合。正式发表为 ACM TOPLAS 23(5),2001,pp.603–625。本页补全规范数学整数模型中的符号、单射性、零轮和外部使用条件,不声称移植全部SSA算法或机器整数比较规则
- Adrian Sampson,Cornell CS6120: Loop Optimization,归纳变量优化后仍需复制传播、死代码删除的教学背景;本页的比较表与拒绝条件由上述模型单独证明