比較照合法による
コンパイラ最適化の正しさの検証
佐々研究室
03M37191
須藤 大二朗
研究の背景
コンパイラの最適化


目的コードの時間効率を向上
プログラム変換
 プログラムの意味を変えてはいけない


アルゴリズム
実装
最適化器のテスト

誤った最適化を行うと出力結果が異なる
 誤った変換場所を特定するのは困難

誤った最適化でも出力結果が一致する場合がある
研究の目的
最適化が正しく行われているか検証したい

検証
 最適化器の証明ではない
 正しいプログラム変換かどうか確認する

最適化の前後のプログラムを比較
 代入文の左辺の値
 条件式の比較演算の両辺の値
 関数呼び出しの引数の値

SSA最適化において実験する
 最新の最適化
 変数の対応関係を作るのが容易


変数名が字面上唯一 ・・・ 変数名により対応付け
検証時間には重きをおかない
比較照合法
Jaramilloが99年に提案

最適化したプログラムのデバッグ
最適化の前後の変数を比較


代入文の左辺の値
条件式の値
アルゴリズム



対応付け
注釈付け
比較照合
SSA形式
変数の定義がプログラムの字面上で唯一
変数に添字をつけて区別する
変数の異なった定義の合流点にφ関数を導入
a:=
a:=x+y
a:=a+3
b:=x+y
a1:=x0+y0
a2:=a1+3
b1:=x0+y0
通常形式
SSA形式
a:=
:=a
通常形式
a1:=
a2:=
a3:=φ(a1,a2)
:=a3
SSA形式
本研究の概要
最適化の前後のプログラム



代入文の左辺の値
条件式の比較演算の両辺の値
関数呼び出しの引数の値
SSA最適化

変数名により対応付け
エラー表示

中間コードにおけるエラー情報






基本ブロックの番号
基本ブロック内の命令番号
変数名
値
最適化ごと、関数ごとに表示
中間コードをC言語風に変換
ソース
フロントエンド
本手法の概要
中間コード
中間コードを複製
(最適化はopt1/opt2/opt3とする)
中間コード
中間コード
中間コード
中間コード
ループ変換
ループ変換
ループ変換
ループ変換
SSA変換
SSA変換
SSA変換
SSA変換
最適化なし
opt1
opt1/opt2
opt1/opt2/opt3
ループ解析
ループ解析
ループ解析
ループ解析
トレース出力文の挿入
トレース出力文の挿入
トレース出力文の挿入
トレース出力文の挿入
SSA逆変換
SSA逆変換
SSA逆変換
SSA逆変換
目的コード
目的コード
目的コード
目的コード
実行
実行
実行
実行
変数情報
変数情報
変数情報
変数情報
比較照合
比較照合
比較照合
エラー表示
エラー表示
エラー表示
本手法の詳細
L0
1. 中間コードの複製
a=10
b=20
c=0
d=0

L1
a>5
L2
b>10
L3
c=a+b
c=3
d=a+b
a=0
L4
a=1
L5
printf(c)
最適化の数+1個
2.自然なループへループ変換
L0
L0
a=10
b=20
c=0
d=0
a=10
b=20
c=0
d=0
L1
L1
a>5
a>5
L2
L2
b>10
b>10
L3
L3
c=a+b
c=3
d=a+b
a=0
L4
a=1
L5
c=a+b
c=3
d=a+b
a=0
L4
a=1
Lt
printf(c)
L5
printf(c)
3.SSA変換
L0
a_0 =10
b_0=20
c_0 =0
d_0=0
L0
a=10
b=20
c=0
d=0
L1
a_1=Φ(a_0,a_4)
c_1=Φ(c_0,c_4)
d_1=Φ(d_0,d_3)
a_1>5
L1
a>5
L2
L2
b_0 >10
b>10
L3
L3
c=a+b
c=3
d=a+b
a=0
L4
a=1
c_2=a_1+b_0
c_3=3
d_2=a_1+b_0
a_2=0
L4
a_3=1
Lt
Lt
a_4=Φ(a_2,a_3)
c_4=Φ(c_3,c_1)
d_3=Φ(d_2,d_1)
L5
printf(c)
L5
printf(c_1)
4-1.ループ不変式のループ外追い出し
L0
L0
a_0=10
b_0=20
c_0=0
d_0=0
c_3=3
a_2=0
a_3=1
a_0 =10
b_0=20
c_0 =0
d_0=0
L1
a_1=Φ(a_0,a_4)
c_1=Φ(c_0,c_4)
d_1=Φ(d_0,d_3)
a_1>5
L1
a_1= Φ(a_0,a_4)
c_1= Φ(c_0,c_4)
d_1= Φ(d_0,d_3)
a_1>5
L2
b_0 >10
L2
L3
c_2=a_1+b_0
c_3=3
d_2=a_1+b_0
a_2=0
b_0>10
L4
a_3=1
Lt
a_4=Φ(a_2,a_3)
c_4=Φ(c_3,c_1)
d_3=Φ(d_2,d_1)
L5
L3
c_2=a_1+b_0
d_2=a_1+b_0
L4
Lt
a_4= Φ(a_2,a_3)
c_4= Φ(c_3,c_1)
d_3= Φ(d_2,d_1)
L5
printf(c_1)
printf(c_1)
4-2.共通部分式の除去
L0
L0
a_0=10
b_0=20
c_0=0
d_0=0
c_3=3
a_2=0
a_3=1
a_0=10
b_0=20
c_0=0
d_0=0
c_3=3
a_2=0
a_3=1
L1
L1
a_1= Φ(a_0,a_4)
c_1= Φ(c_0,c_4)
d_1= Φ(d_0,d_3)
a_1>5
a_1= Φ(a_0,a_4)
c_1= Φ(c_0,c_4)
d_1= Φ(d_0,d_3)
a_1>5
L2
L2
b_0>10
L3
c_2=a_1+b_0
d_2=a_1+b_0
L4
Lt
b_0>10
L3
c_2=a_1+b_0
d_2=c_2
L4
Lt
a_4= Φ(a_2,a_3)
c_4= Φ(c_3,c_1)
d_3= Φ(d_2,d_1)
L5
a_4= Φ(a_2,a_3)
c_4= Φ(c_3,c_1)
d_3= Φ(d_2,d_1)
L5
printf(c_1)
printf(c_1)
4-3.無用命令の除去
L0
L0
a_0=10
b_0=20
c_0=0
d_0=0
c_3=3
a_2=0
a_3=1
a_0=10
b_0=20
c_0=0
c_3=3
a_2=0
a_3=1
L1
L1
a_1= Φ(a_0,a_4)
c_1= Φ(c_0,c_4)
d_1= Φ(d_0,d_3)
a_1>5
a_1= Φ(a_0,a_4)
c_1= Φ(c_0,c_4)
a_1>5
L2
L2
b_0>10
L3
c_2=a_1+b_0
d_2=c_2
L4
Lt
b_0>10
L3
L4
Lt
a_4= Φ(a_2,a_3)
c_4= Φ(c_3,c_1)
d_3= Φ(d_2,d_1)
L5
a_4= Φ(a_2,a_3)
c_4= Φ(c_3,c_1)
L5
printf(c_1)
printf(c_1)
本手法の詳細
5. ループ解析


ループ内の命令は実行時に
複数回計算される可能性がある
コード移動が起こる場合
6. トレース出力文の挿入
7. SSA逆変換

中間コード(SSA形式)を
C言語風に変換したものを出力
8. 実行

全てに同じ入力を与える
出力結果
9. 比較照合

Loop Lv.0
Loop Lv.0
0_1:a_0:10
0_2:b_0:20
0_3:c_0:0
0_4:d_0:0
5_1:CALL_printf_1_1_bef:3
5_1:CALL_printf_1_1_aft:3
Loop Lv.1
<L0>
1_1:a_1:10
1_2:c_1:0
1_3:d_1:0
1_4:JUMP_l:10
1_4:JUMP_r:5
2_1:JUMP_l:20
2_1:JUMP_r:10
3_1:c_2:30
3_2:c_3:3
3_3:d_2:30
3_4:a_2:0
t_1:a_4:0
t_2:c_4:3
t_3:d_3:30
1_1:a_1:0
1_2:c_1:3
1_3:d_1:30
1_4:JUMP_l:0
1_4:JUMP_r:5
</L0>
(a) 最適化なし
値が一致しない場合はエラーを表示
Loop Lv.0
0_1:a_0:10
0_2:b_0:20
0_3:c_0:0
0_4:d_0:0
0_5:c_3:3
0_6:a_2:0
0_7:a_3:1
5_1:CALL_printf_1_1_bef:3
5_1:CALL_printf_1_1_aft:3
Loop Lv.0
0_1:a_0:10
0_2:b_0:20
0_3:c_0:0
0_4:d_0:0
0_5:c_3:3
0_6:a_2:0
0_7:a_3:1
5_1:CALL_printf_1_1_bef:3
5_1:CALL_printf_1_1_aft:3
Loop Lv.1
Loop Lv.1
Loop Lv.1
<L0>
1_1:a_1:10
1_2:c_1:0
1_3:d_1:0
1_4:JUMP_l:10
1_4:JUMP_r:5
2_1:JUMP_l:20
2_1:JUMP_r:10
3_1:c_2:30
3_2:d_2:30
t_1:a_4:0
t_2:c_4:3
t_3:d_3:30
1_1:a_1:0
1_2:c_1:3
1_3:d_1:30
1_4:JUMP_l:0
1_4:JUMP_r:5
</L0>
(b) ループ不変式の
ループ外追い出し
0_1:a_0:10
0_2:b_0:20
0_3:c_0:0
0_4:c_3:3
0_5:a_2:0
0_6:a_3:1
5_1:CALL_printf_1_1_bef:3
5_1:CALL_printf_1_1_aft:3
<L0>
1_1:a_1:10
1_2:c_1:0
1_3:d_1:0
1_4:JUMP_l:10
1_4:JUMP_r:5
2_1:JUMP_l:20
2_1:JUMP_r:10
3_1:c_2:30
3_2:d_2:30
t_1:a_4:0
t_2:c_4:3
t_3:d_3:30
1_1:a_1:0
1_2:c_1:3
1_3:d_1:30
1_4:JUMP_l:0
1_4:JUMP_r:5
</L0>
(c) 共通部分式除去
<L0>
1_1:a_1:10
1_2:c_1:0
1_4:JUMP_l:10
1_4:JUMP_r:5
2_1:JUMP_l:20
2_1:JUMP_r:10
t_1:a_4:0
t_2:c_4:3
1_1:a_1:0
1_2:c_1:3
1_3:JUMP_l:0
1_3:JUMP_r:5
</L0>
(d) 無用命令の除去
誤りのある最適化例
実際に誤りを発見できた最適化
Common Subexpression Elimination With
Efficient Question Propagation
SSA変換後に3番地コードへ変換


main()
{
a = 1;
b = a + 2;
c = b + a * c;
x = a + (b + c) * a;
}
出力結果
0_1:a:1
0_2:t_0:1
0_3:b:3
0_4:t_1:3
0_5:t_2:1
0_6:t_3:0
0_7:t_4:0
0_8:c:3
0_9:t_5:1
0_10:t_6:3
0_11:t_7:3
0_12:t_8:6
0_13:t_9:1
0_14:t_10:6
0_15:x:7
3番地コード
0_1:a:1
0_2:t_0:1
0_3:b:3
0_4:t_1:3
0_5:t_2:1
0_6:t_3:0
0_7:t_4:0
0_8:c:3
0_9:t_5:1
0_10:t_6:3
0_11:t_7:0
0_12:t_8:3
0_13:t_9:1
0_14:t_10:3
0_15:x:4
CSEQP

t_7,t_8,t_10,xに誤り
エラー表示
-----NG!----opt::LoopLevel=0,BlkID=0,InstrNumber=11,t_7:0
unopt::LoopLevel=0,BlkID=0,InstrNumber=11,t_7:3
-----------------NG!----opt::LoopLevel=0,BlkID=0,InstrNumber=12,t_8:3
unopt::LoopLevel=0,BlkID=0,InstrNumber=12,t_8:6
-----------------NG!----opt::LoopLevel=0,BlkID=0,InstrNumber=14,t_10:3
unopt::LoopLevel=0,BlkID=0,InstrNumber=14,t_10:6
-----------------NG!----opt::LoopLevel=0,BlkID=0,InstrNumber=15,x:4
unopt::LoopLevel=0,BlkID=0,InstrNumber=15, x:7
-------------
_L2:
((int)(*(((int *)(unsigned char *)&(a))))) = ((int)( 1));
divexI32_0 = ((int)(((int)(*(((int *)(unsigned char *)&(a)))))));
((int)(*(((int *)(unsigned char *)&(b))))) = ((int)(((int)(divexI32_0 + 2))));
divexI32_1 = ((int)(((int)(*(((int *)(unsigned char *)&(b)))))));
divexI32_2 = ((int)(((int)(*(((int *)(unsigned char *)&(a)))))));
divexI32_3 = ((int)(((int)(*(((int *)(unsigned char *)&(c)))))));
divexI32_4 = ((int)(((int)(divexI32_2 * divexI32_3))));
((int)(*(((int *)(unsigned char *)&(c))))) = ((int)(((int)(divexI32_1 + divexI32_4))));
divexI32_5 = ((int)(((int)(*(((int *)(unsigned char *)&(a)))))));
divexI32_6 = ((int)(((int)(*(((int *)(unsigned char *)&(b)))))));
divexI32_7 = ((int)(((int)(*(((int *)(unsigned char *)&(c)))))));
divexI32_8 = ((int)(((int)(divexI32_6 + divexI32_7))));
divexI32_9 = ((int)(((int)(*(((int *)(unsigned char *)&(a)))))));
divexI32_10 = ((int)(((int)(divexI32_8 * divexI32_9))));
((int)(*(((int *)(unsigned char *)&(x))))) = ((int)(((int)(divexI32_5 + divexI32_10))));
_L2:
((int)(*(((int *)(unsigned char *)&(a))))) = ((int)( 1));
divexI32_0 = ((int)(((int)(*(((int *)(unsigned char *)&(a)))))));
((int)(*(((int *)(unsigned char *)&(b))))) = ((int)(((int)(divexI32_0 + 2))));
divexI32_1 = ((int)(((int)(*(((int *)(unsigned char *)&(b)))))));
divexI32_2 = ((int)(divexI32_0));
divexI32_3 = ((int)(((int)(*(((int *)(unsigned char *)&(c)))))));
divexI32_4 = ((int)(((int)(divexI32_2 * divexI32_3))));
((int)(*(((int *)(unsigned char *)&(c))))) = ((int)(((int)(divexI32_1 + divexI32_4))));
divexI32_5 = ((int)(divexI32_0));
divexI32_6 = ((int)(divexI32_1));
divexI32_7 = ((int)(divexI32_3));
divexI32_8 = ((int)(((int)(divexI32_6 + divexI32_7))));
divexI32_9 = ((int)(divexI32_0));
divexI32_10 = ((int)(((int)(divexI32_8 * divexI32_9))));
((int)(*(((int *)(unsigned char *)&(x))))) = ((int)(((int)(divexI32_5 + divexI32_10))));
中間コードをC言語風に出力
L0
L0
a=1
t_0=a
b=t_0+2
t_1=b
t_2=a
t_3=c
t_4=t_2*t_3
c=t_1+t_4
t_5=a
t_6=b
t_7=c
t_8=t_6+t_7
t_9=a
t_10=t_8*t_9
x=t_5+t_10
3番地コード
a=1
t_0=a
b=t_0+2
t_1=b
t_2=t_0
t_3=c
t_4=t_2*t_3
c=t_1+t_4
t_5=t_0
t_6=t_1
t_7=t_3
t_8=t_6+t_7
t_9=t_0
t_10=t_8*t_9
x=t_5+t_10
CSEQP
ループ構造の最適化への
ベクトル計算機、並列計算機
ループの構造を変える


インデックス変数の対応関係がとれないもの
がある →比較照合の対象としない
ループ内の変数名
○  変わらない最適化
・・・ループ分配、ループ融合、ループ展開、...
△  変わる最適化
・・・ループ傾斜、ループつぶし
実験
COINS上で実装および実験



CVSの2004年7月10日のバージョン
小さなプログラム700本
SPEC CPU2000 CINT 181.mcf
対象とする最適化器





Copy Propagation
Constant Propagation
Dead Code Elimination
Common Subexpression
Elimination
Partial Redundancy
Elimination(立川)





Hoisting Loop Invariant
Variables
Operator Strength Reduction
Common Subexpression
Elimination With Efficient
Question Propagation
Redundant Phi Elimination
Empty Block Elimination
実験結果
最適化器のバグを4つ発見



Operator Strength Reductionのバグを1つ発見
Common Subexpression Elimination With
Efficient Question Propagationのバグを1つ発見
立川のPartial Redundancy Eliminationのバグを2
つ発見
(知られていなかったバグ)
関連研究
Neculaらの方法


記号実行を使って検証
gcc2.7.2.2のバグを発見
 ループ展開、レジスタ割り当て
Zuck,Pnueliらの方法


計算モデルに対応付ける
validating toolで確認
大熊,南出らの方法


定理証明システムで各パスの定義を証明
コンパイラプログラムを自動生成
まとめ
比較照合法による最適化器の正しさの検証


最適化の前後の値
SSA最適化
最適化器のバグを発見できる


最適化器のバグ4つ
実装上
誤った変換場所を容易に探すことができる


エラー表示
中間コードをC言語風に出力
今後の課題
ループ構造の最適化

COINS上で実装、実験
ベンチマーク


COINSの最新版
SPEC CPU2000の181.mcf以外
ダウンロード

比較照合法による コンパイラ最適化の正しさの検証