20151112 kutech lecture_ishizaki_public
TRANSCRIPT
© 2015 IBM Corporation
高知工科大学 情報学群 コンパイラ
コンパイラにおける最適化技術
2015年11月12日石崎 一明 [email protected](資料作成協力 井上 拓、緒方 一則、仲池 卓也)日本アイビーエム(株) 東京基礎研究所 (IBM Research - Tokyo)
IBM Research - Tokyo
© 2015 IBM Corporation
IBM Research - Tokyo
自己紹介
石崎 一明(いしざき かずあき) http://ibm.co/kiszk1992年3月 早稲田大学理工学研究科修士課程電気工学専攻を修了。
1992年4月 日本アイ・ビー・エム(株)入社、東京基礎研究所勤務。以来、並列化コンパイラ、動的コンパイラ、アプリケーション最適化、などの研究に従事。最近は、GPGPUのためのコンパイル技術の研究に従事。現在、同研究所リサーチ・スタッフ・メンバー。
1995年までは、IBM社の並列化Fortranコンパイラの研究開発をおこなってきました。
1996年以来、IBM社のJava処理系の研究開発にたずさわり、新規に研究開発したコンパイラ最適化を、IBM社のJava処理系に実装してきました。IBM社のJava処理系は、業界標準のベンチマークプログラムで、世界最高速を達成してきました。
2002年12月 早稲田大学理工学研究科にて、博士(情報科学)を取得。
2004年情報処理学会業績賞受賞。
2
© 2015 IBM Corporation
IBM Research - Tokyo
今日の講義内容
講義内容
– 基本的な最適化
• 実行前コンパイルでも、実行時コンパイルでも使います
– 実行時情報を使った最適化
– インタプリタが存在する場合の最適化
– Java言語特有の最適化
– 最近のJava処理系とコンパイラの話題
3
© 2015 IBM Corporation
IBM Research - Tokyo
コンパイラの構成
中間コード生成
プラットフォーム共通最適化
コード生成
プラットフォーム固有最適化
バイナリ生成
コントロールフローグラフ(CFG)
プラットフォーム固有の命令列
レジスタアロケーション、命令スケジューリング、etc.
字句・構文・意味解析現在、鵜川先生が授業で扱われている部分
今日の授業で扱う部分
少しだけここも触れます
基本的な最適化
© 2015 IBM Corporation
IBM Research - Tokyo
最適化(Optimization)とは?
目的
– 実行時間を短縮する
• 一台のコンピュータを使って この後はこの最適化について
• 複数台のコンピュータを使って(並列化)
– メモリ使用量を削減する
– 電力消費量を削減する
大前提
– 最適化前のコードと最適化後のコードで、(許されていないかぎり)プログラムの意味を変えないこと
• メソッド外部から観測可能な値を変えない
–rは観測可能、aとbは観測不可能
• メソッド外部から観測可能なイベントの発生順序を変えない
–Javaプログラムであれば、例外の発生順序(割算、剰余)を変えないこと
6
int foo(int i, int j) {int a, b, ra = i / jb = j % ir = a + breturn r
}
© 2015 IBM Corporation
IBM Research - Tokyo
最適化の方法
どこを
– ソースコードで見える部分
– ソースコードで見えない部分
どの範囲で?
– 基本ブロック内
•基本ブロック(Basic Block, BB)=制御の分岐も合流もない命令列
– 基本ブロック間
•Control Flow Graph(CFG)=プログラムの実行可能な全経路をBBをノードとする有向グラフで示したもの
– メソッド間
どうやって?
– 命令の実行回数を減らす
– 速い命令を使う7
x = 6
y = x+3BB4
x = 1x = 6
BB2 BB3
i > 0BB1
if (i > 0) {x = 6;
} else {x = 1;x = 6;
}y = x + 3
CFG
命令の実行時間を減らす
© 2015 IBM Corporation
IBM Research - Tokyo
命令の実行回数を減らす最適化の例
コンパイル時に前もって計算
– 定数の畳込み
– 定数伝播
冗長な命令の除去
– 複写伝播
– 不要命令除去
計算結果の再利用
– 部分共通式の削除
– 部分冗長性の削除
コードの移動
– コード上昇
– ループ不変値移動
プログラムの特殊化・専用化
– 末尾複製
– メソッド展開9
© 2015 IBM Corporation
IBM Research - Tokyo
定数の畳込み(constant folding)
定数計算をコンパイル時に行う。
– 言語によっては、決められたデータの精度や変数がとることができる値の範囲に考慮する必要がある
• 例:Javaではshortは-32768~32767の値をとるが、-32768/-1の答えは?
10
…
x = 2 * 3
…
…
x = 6
…
© 2015 IBM Corporation
IBM Research - Tokyo
定数伝播(constant propagation)
定数値を、後続の文に伝搬させる。
11
x = 6
… // この間にxの定義がない
y = x + 3
x = 6
…
y = 6 + 3
x = 6
y = x + 3BB3
x = 6BB1 BB2
y = 6 + 3
x = 6 x = 6
© 2015 IBM Corporation
IBM Research - Tokyo
複写伝播(copy propagation)
単純な代入命令を、右辺のオペランドに伝搬させる。
12
x = a // xの定義はここだけ
… // この間にaの定義がない
y = x + b
x = a
…
y = a + b
y = x + b
x = aBB1
z = x + cBB2 BB3
y=a+b z=a+c
x = a
© 2015 IBM Corporation
IBM Research - Tokyo
不要命令除去(dead code elimination)
メソッド中で使われない値を計算する命令を削除する。
実行されない文を削除する。
13
…
x = a + b // xはこの後使われない
…
…
x = a + b
…
…
if (false) {
y = 1
}
…
if (false) {
y = 1
}
© 2015 IBM Corporation
IBM Research - Tokyo
共通部分式の削除(common subexpression elimination)
一度計算された式の値を再利用する。
14
x = a*b
… // この間にa,b,xの// 定義がない
y = a*b + 3
x = a*b
…
y = x + 3
x = a*b
y = a*b + 3BB3
x = a*bBB1 BB2
y = x + 3
x = a*b x = a*b
© 2015 IBM Corporation
IBM Research - Tokyo
部分冗長性の削除(partial redundancy elimination)
一度計算された式の値を再利用するために、式が存在しなかった実行経路へ、式を実行順で考えた前方へ移動する。
– 副作用を持つ式の移動に注意
• 例外(0による除算など)を発生する演算
15
x = a*b
y = a*bBB3
BB1 BB2x = a*bt = x
y = tBB3
t = a*b
BB1 BB2
BB1→BB3、 BB2→BB3のどちらの経路でも、a*bは1回しか計算されない
BB1→BB3の経路を通るとa*bは2回計算される
© 2015 IBM Corporation
IBM Research - Tokyo
コード上昇(code hoisting)
式を実行順で前方へ移動する。
片方の経路でしか実行されない式でも移動可能
– 副作用を持つ式の移動に注意
16
y = a*s[0]
…BB1
z = a*s[0]
BB2 BB3
y = t
t = a*s[0]BB1
z = t
BB2 BB3
a * s[0]の処理に時間がかかる際に、特に効果が高い
© 2015 IBM Corporation
IBM Research - Tokyo
ループ内不変値移動(loop invariant code motion)
ループ内で定数の値をループ外へ移動する。
– 副作用を持つ式の移動に注意
17
i = 0
BB3
x += a*b + s[i]i = i + 1
BB1
BB2
i < 88
i = 0t = a*b
x += t + s[i]i = i + 1
i < 88
a*bの計算は1回だけループを回るたびにa*bの計算を行う
© 2015 IBM Corporation
IBM Research - Tokyo
メソッド展開(method inlining)
メソッド呼び出しの場所に、メソッド本体を展開する。
– 最適化の適用範囲が大きくなる。
– ソースコードには現れない、引数の受け渡しの処理や、メソッド先頭・末尾でのレジスタ待避・回復処理を削除出来る。
18
…
x = (a * a)
…
int foo(int i) {
return i * i
}
…
x = foo(a)
…
int foo(int i) {
return I * I
}
© 2015 IBM Corporation
IBM Research - Tokyo
末尾複製(tail duplication, splitting)
制御の合流部分以降を複製して、専用コードを作る。
– 変数の性質が一意に決定する、ので他の最適化が使える可能性が増える
• BB2→BB4’では、常にa=0、ということが分かる
19
a = 0
BB1
a = foo(a)
BB2 BB3
a = 0
BB1
a = foo(a)
BB2 BB3
BB4a < 0
a = ...b < 0
a = ...b < 0
a < 0 a < 0BB4’ BB4’’
速い命令を使う
© 2015 IBM Corporation
IBM Research - Tokyo
速い命令を使う最適化の例
命令強度軽減
スカラ置き換え
レジスタ割付
SIMD命令の利用
21
© 2015 IBM Corporation
IBM Research - Tokyo
命令強度軽減(strength reduction)
同じ演算を、CPUが持つ速い命令で置き換える。
– 例えば、多くのCPUでは、乗算・除算よりシフト・論理積命令の方が高速である。
22
…
x = a * 8 // 2のべき乗による乗算
y = b % 8 // 2のべき乗による剰余
…
…
x = a << 3
y = b & 7
…
© 2015 IBM Corporation
IBM Research - Tokyo
スカラ置き換え(scalar replacement)
メモリアクセス命令を、単純変数を使った命令に置き換える。
– 後のレジスタ割付で、単純変数がレジスタに割り付けられることを期待
23
class A {
int f
}
A a = new A()
a.f = 1 // a.fはメモリへのアクセス
a.f = a.f * 2
class A {
int f
}
A a = new A()
t = 1
t = t * 2
a.f = t
© 2015 IBM Corporation
IBM Research - Tokyo
レジスタ割付(register allocation)
メソッド中の単純変数を、プロセッサが持つレジスタにできるだけ割り付ける。
– レジスタは、1クロックでデータを読み書きできる高速な(スカラ)データ保持領域だが、プロセッサによって16または32個と制限がある
24
…
a = 1
b = 2
c = a + 3
a = c * 2
…
…
R1 = 1 // a: R1
[mem_b] = 2 // b: mem
R2 = R1 + 3 // c: R2
R1 = R2 * 2
…
変数は3つだが、レジスタは2個しかない →
aとcに割り当てる
© 2015 IBM Corporation
IBM Research - Tokyo
SIMD命令の活用
SIMD (Single Instruction Multiple Data)命令とは?
– 1つの命令で(Single Instruction)、複数のデータに(Multiple Data)、演算を行う
– 1つのベクタレジスタ内の、複数のデータに、同じ演算を行う、実装が一般的
25
A0
add
input 1
input 2
output
add grA,grB,grC vadd vrA,vrB,vrC
ベクタレジスタ
SIMDの各演算
通常のスカラ命令(1命令1演算) SIMD命令(1命令4演算)
B0
C0
A0
add
input 1
input 2
output
B0
C0
A1
add
B1
C1
A2
add
B2
C2
A3
add
B3
C3
32bit 32bit x 4
© 2015 IBM Corporation
IBM Research - Tokyo
SIMD命令の活用
同一処理が、別データに繰り返して適用される部分を、SIMD命令に置き換える
– ループ内での配列アクセス、が代表的
26
for (i = 0; i < 1024; i++)s[i] = t[i] + u[i]
for (i = 0; i < 1024; i += 4)vload vrB, t[i] // 4データ同時ロードvload vrC, u[i] // 4データ同時ロードvadd vrA, vrB, vrC // 4データ同時加算vstore vrA, s[i] // 4データ同時ストア
現実は、このような簡単な例ばかりではないのでコンパイラによる自動変換は限られた例に限られている
コンパイル時に実行時情報が利用できたら?
© 2015 IBM Corporation
IBM Research - Tokyo
実行時情報が利用できたら?
より正確にプログラムの挙動がわかる
– 実行頻度の高いメソッド
– メソッド内の実行頻度の高い実行経路
より詳しい情報がわかる
– メソッドに渡される引数
– ループの実行回数
より細かいプログラムの情報がわかる
– メモリアクセスの際のキャッシュミスは、どこで起きているか
28
© 2015 IBM Corporation
IBM Research - Tokyo
実行時情報の取得方法
Sampling – 頻繁に実行されるメソッド、コードの特定
– コンパイルされたコード内の適当な地点(メソッドの入り口、ループの先頭へのjump等)を通過したときに、ログを取る。
– OSのタイマー割り込みを使って、割り込みハンドラの中で実行されているプログラムの命令アドレスを取得する。
Instrumentation – 実行中の値の特定
– コンパイルされたコード内に値を記録するコードを生成する。
Hardware Performance Monitor Counter – キャッシュミス等の特定
– CPUが提供するパフォーマンス・モニタの値(キャッシュミス回数、発生したアドレス)を読み取る。
29
© 2015 IBM Corporation
IBM Research - Tokyo
実行情報を利用するための方法
実行時情報を取得するためのコードを生成してコンパイルしたバイナリを一度実行して、その情報を元に再コンパイルする
– ユーザが手動でリコンパイルを行う
• Profile Guided Optimization (PGO) in LLVM and GCC
– システムが自動的にリコンパイルを行う
• 主なJavaの処理系(IBM Java、Open JDK、Oracle JDK、その他)
30
動的コンパイラ
メソッドA
メソッドB
プロセッサ
プロファイラ
最初のコンパイル
コンパイルリクエスト
メソッドB
再々コンパイル
実行時情報
分岐トレースと命令サンプルCall A Call BInst in B
自動的にリコンパイルを行うシステムの例
メソッドB
再コンパイル
© 2015 IBM Corporation
IBM Research - Tokyo
実行情報を利用した最適化の例
経路の頻度情報に基づいた最適化
– よく実行されるメソッドに対して、メソッド展開を適用する
– メソッド内のよく実行される実行経路の命令数を減らす
• あまり実行されない実行経路の命令数は増えるかもしれない
特定した値に基づくコードの特殊化
– 定数伝搬
• 特定しなかった値が来た時のコードも必要
キャッシュミスを減らす
– データのプリフェッチ(事前にメモリからキャッシュに読み込んでおく)
– データの再配置
• よくアクセスされるデータを近くに配置する
31
実行時にインタプリタがあったら?
© 2015 IBM Corporation
IBM Research - Tokyo
インタプリタとは?
プログラムを機械語命令にコンパイルすることなく、実行できる
– Ruby、 Python、Perlの代表的な実装で使われている実行方式
実行時コンパイルに比べた利点
– 消費メモリが少ない
– プログラム起動の時間が速い
実行時コンパイルに比べた欠点
– プログラムの実行時間が遅い
33
© 2015 IBM Corporation
IBM Research - Tokyo
インタプリタとコンパイラが両方あったら
よく実行されるメソッドだけ、コンパイルすればよいのでは?
– あまり実行されないメソッドは、インタプリタで実行しても、全体の実行時間に影響を与えない
– コンパイルにかかる時間、必要なメモリも減る
さらに、インタプリタに実行時情報をとってもらえばよいのでは?
– 実行頻度の高いメソッドなら、すぐに分かる
– 実行頻度の高いメソッド内の経路、も分かるだろう
– 実行中のメソッド内の値、は記録すると実行時間が遅くなる
ついでに、メソッドをコンパイルする際に、あまり実行されない部分はインタプリタに実行をまかせてしまえばよいのでは?
– 後ほど、お話します34
クラス管理オブジェクト
管理
インタープリタJIT
コンパイラJITコンパイラ
生成コード
実行時プロファイラ
クラスデータ(Java バイト
コード)
Javaヒープ(Java オブジェクト)
Javaスタック(ローカル変数、
オペランドスタック)
Java仮想マシン(Java VM)
Java同期ランタイム
Java例外処理ランタイム
JVMランタイム
Java言語特有の最適化
© 2015 IBM Corporation
IBM Research - Tokyo
Java言語特有の機能と最適化
プログラムの安全性を保証するための例外検査
– 冗長な例外検査削除
プログラムの安全性を保証するための型検査
– 形解析による冗長な型検査の削除
多様性(polymorphism)の導入による、仮想メソッド呼び出し
– 仮想メソッド呼び出しの高速化と、メソッド展開の適用
カプセル化(encapsulation)による、オブジェクト内のフィールドへの参照
– 脱出解析による、オブジェクトのスタック割付
– スカラ置き換えによる、フィールドアクセスの高速化
言語が提供する同期操作
– 脱出解析による同期操作除去
36
© 2015 IBM Corporation
IBM Research - Tokyo
例外検査とその削除
プログラムの安全性を保証する手段の1つ
– ポインタの値の検査 – オブジェクトがnullでない
– 配列のインデックスの検査 – 配列のインデックスが配列の範囲内である
– その他、0での割り算チェックなど
例外検査の削除
– 例外検査のための、比較命令、分岐命令の削除
– 命令の移動機会を増やす
• 例外検査に依存する後続の命令は、例外検査より前に移動出来ない
37
void foo(String s) {int h = s.hashCode();...int i = s.charAt(88);
}
SがnullならNullPointeException
© 2015 IBM Corporation
IBM Research - Tokyo
単純に冗長な例外検査削除
同じ変数に対する、同一の例外検査を削除する。
38
nullcheck ax = a.f… // この間にaの定義がない
nullcheck ay = a.g
nullcheck ax = a.f …nullcheck ay = a.g
nullcheck ax = a.f
nullcheck ay = a.g
BB3
nullcheck az = a.h
BB1 BB2
nullcheck ay = a.g
nullcheck ax = a.f
nullcheck az = a.h
© 2015 IBM Corporation
IBM Research - Tokyo
仮想メソッド呼び出しの高速化と、メソッド展開の適用
複数のメソッドが呼び出される可能性があり、メソッドの呼び出し先を一意に特定することが難しい場合
– 仮想メソッド呼び出し
– インターフェースメソッド呼び出し
39
call s.add() Sub1.add()Super.add()
…... class Sub1 extends Super {
publicint add(int i) { ... }
public staticint calc1(Super s) {... s.add(2) ......
}}
class Super {publicint add(int i) { ... }
}
© 2015 IBM Corporation
IBM Research - Tokyo
直接メソッド呼び出し(direct method call)直接分岐命令(call address)の使用
– コンパイル時に、プログラム上の情報から呼び出し先が一意に決定可能
• メソッド展開を適用可能
40
...call method_div...
method_div:...ret
class Super {public static finalint div(int i) { ... }
public static int calc() {... Super.div(2) ......
}}
プログラム 機械語命令
© 2015 IBM Corporation
IBM Research - Tokyo
仮想メソッド呼び出し(virtual method call)実行時に呼び出し先を決定する機構(表引き、など)の使用
– コンパイル時に、呼び出し先が一意に決定できない
• メソッド展開ができなくなり、最適化の範囲が狭くなる
41
Super s
class Sub1 extends Super {publicint add(int i) { ... }
public staticint calc1(Super s) {... s.add(2) ......
}}
...// r3 = object of Superld r2, (r3+offset_class_in_object)ld r1, (r2+offset_vtableadd_in_class)ld r0, (r1+offset_code_in_method)call r0...
プログラム 機械語命令
class Sub1
mul
foo
virtual table
add
code
method Sub1.addr3 r2 r1
binary code
r0sub同じメソッドはクラス階層内で同じオフセットを持つ
class Superadd()
class Sub1add()
© 2015 IBM Corporation
IBM Research - Tokyo
Devirtualizationによって、呼び出し先を特定
複数の呼び出し先を、特定した1つとその他、に分ける
– 仮想呼び出しではなくなっている(直接呼び出し)、のでdevirtualization(脱仮想化)
分ける方法
– Guarded devirtualization• Class testなど
– Direct devirtualization
42
call Sub1.add()
s==Sub1
call s.add()call s.add() Sub1.add()
Super.add()
…...
ガード
Y
N
© 2015 IBM Corporation
IBM Research - Tokyo
Class testによる手法
元のコード
直接メソッド呼び出しのコードを呼び出してよいか、ガードのクラスの一致に関する比較結果に基づいて判断する。
43
Super s
...// r3 = object in sld r2, offset_class_in_object(r3)ld r1, offset_vtableadd_in_class(r2)ld r0, offset_code_in_method(r3)call r0...
class Sub1
virtual table
add
code
method Sub1.add
// r3 = object in sld r2, offset_class_in_object(r3)if (r2 == #address_of_classSub1) {call Sub1.add // exec direct call
} else {ld r1, offset_vtableadd_in_class(r2)ld r0, offset_code_in_method(r1)call r0
}
機械語命令
© 2015 IBM Corporation
IBM Research - Tokyo
Guarded devirtualization+メソッド展開による問題点
メソッド展開した場合と仮想メソッド呼び出しの制御が合流する
– データーフロー解析結果(rの値は?)の精度が悪くなる→0であるか、値はわからない
解決手段
– 末尾複製
– Deoptimization44
r = Sub1.add(-2)-> -2 + 2 = 0
s==Sub1
r = s.add(-2)
class Sub1 extends Super {public int add(int i) { return i + 2; }
public static int calc2(Super s) {...int r = s.add(-2);if (r != 0) { ... // 長いコード }return r;
}}
r != 0
// 長いコード
r = 0
Y
© 2015 IBM Corporation
IBM Research - Tokyo
末尾複製による合流点の変更
コードを複製して、制御の合流点の位置を変更して、データフロー解析結果の精度を下げない(条件分岐におけるrの値は?)
– 場合によっては、複製するコード量が増えることがある
45
r = Sub1.add(-2)-> -2 + 2 = 0
s==Sub1
r = s.add(-2)class Sub1 extends Super {public int add(int i) { return i+2; }
public static int calc2(Super s) {...int r = s.add(-2);if (r != 0) { ... // 長いコード }return r;
}}
r != 0
// 長いコード
r = 0
r != 0
r != 0
Y
© 2015 IBM Corporation
IBM Research - Tokyo
末尾複製に伴うコード削除
左側のパスは、r = 0が確定しているので、条件分岐を除去可能
– でも、あまり実行されない(=インライン展開していない)パスにある、長いコードをコンパイルしないといけない
46
r = Sub1.add(-2)-> -2 + 2 = 0
s==Sub1
r = s.add(-2)class Sub1 extends Super {public int add(int i) { return i+2; }
public static int calc2(Super s) {...int r = s.add(-2);if (r != 0) { ... // 長いコード }return r;
}}
r != 0
// 長いコード
r = 0 r != 0
Y
© 2015 IBM Corporation
IBM Research - Tokyo
(さきほど話した)もしもインタープリタがいたら?
コンパイルコードとインタープリタ間の実行の遷移について
– インタープリタからコンパイルコードへ
– コンパイルコードからインタープリタへ
• 通常はメソッドの先頭で遷移する
• メソッドの実行途中に遷移できたらうれしいことがある
47
コンパイルされたコードbar() {......
}
インタープリタのコードfoo() {...bar();...
}
インタープリタのコードfoo() { bar() {... ...bar(); ...... }
}
コンパイルされたコードbar() {... ...
}
© 2015 IBM Corporation
IBM Research - Tokyo
Deoptimization最適化されたコードから、最適化されていないコード(この場合はインタプリタ)
へ移行する
コンパイラ内部表現は、条件を満たさなかったとき、分岐先がコンパイルコード外となる
– コンパイルするコード量が減る
– コンパイラコード内の制御合流点が減る
使い方の一例
– Guarded devirtualization+メソッド展開
4848
r = Sub1.add(-2)-> -2 + 2 = 0
s==Sub1
r = s.add(-2)
class Sub1 extends Super {public int add(int i) { return i+2; }
public static int calc2(Super s) {...int r = s.add(-2);if (r != 0) { ... // 長いコード }return r;
}}
r != 0
// 長いコード
r = 0
インタプリタで実行
Y
© 2015 IBM Corporation
IBM Research - Tokyo
Deoptimizationによるコード最適化
コンパイルコード内では、rの値が0と決まるので、条件分岐と条件が成立しない場合のコードを削除可能
4949
r = Sub1.add(-2)-> -2 + 2 = 0
s==Sub1
r = s.add(-2)
class Sub1 extends Super {public int add(int i) { return i+2; }
public static int calc2(Super s) {...int r = s.add(-2);if (r != 0) { ... // 長いコード }return r;
}}
インタプリタで実行
Y
最近のJava処理系とコンパイラの話題
© 2015 IBM Corporation
IBM Research - Tokyo
最近のJava処理系とコンパイラの話題
起動時間を短くする
– プログラムの開発時間の短縮
メモリ消費量を少なくする
– 同じ物理メモリのマシンの元で、クラウドやDockerで使えるインスタンス数が増える
新しいハードウェアを利用して実行時間を短縮する
– SIMD
– GPGPU
プロセッサをより効率的に使用する
– Javaから、Java以外のメモリを操作する
• 異なるプロセッサ上で動かしても、同じ方法で操作したい
51
© 2015 IBM Corporation
IBM Research - Tokyo
まとめ
基本的な最適化
実行時情報を使った最適化
インタプリタが存在する場合の最適化
Java言語特有の最適化
最近のJava処理系とコンパイラの話題
52
付録
データフロー解析
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析
プログラム内における変数の定義・参照位置で、取りうる値の集合に関する情報を特定する。
– 具体的には、CFG上のBBに関するデータフロー方程式をたてて解く。
• データフロー方程式の例
– in[b]: BB bの入口に到達する定義の集合out[b]: BB bの出口にある定義の集合
–解釈:自分のBB bについて、前に実行されるBBの情報の和を取る和をとった情報(in[b])を使って、BB bの出口での情報(out[b])を求める
解析結果を用いて、基本ブロック間のある種の最適化を効率良く行うことができる
– 定数の畳込み、定数伝播、不要命令除去、複写伝播、など55
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
STEP1: プログラムを見て、genとkillを求める
– genは、自分のBBで定義して、自分のBBの出口に定義が到達する文
– killは、自分のBBで定義した変数を、BBに関わらず無効にしてしまう文
56
s4: x = 4s5: y = 5
s7: a = xs8: b = ys9: c = z
BB4
s6: y = 6BB2 BB3
BB1gen[BB1]={s1,s2,s3},gen[BB2]={s4,s5},gen[BB3]={s6},gen[BB4]={s7,s8,s9}
kill[BB1]={s4,s5,s6}kill[BB2]={s1,s2,s6}kill[BB3]={s2,s5}kill[BB4]={}
s1: x = 1s2: y = 2s3: z = 3
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
STEP2: BB1からデータフロー方程式を適用していく
– genとkillが分かっていれば、BB内のことは気にしなくてよい
57
gen[BB1]={s1,s2,s3},gen[BB2]={s4,s5},gen[BB3]={s6},gen[BB4]={s7,s8,s9}
kill[BB1]={s4,s5,s6}kill[BB2]={s1,s2,s6}kill[BB3]={s2,s5}kill[BB4]={}
in[BB1] = {}
s4: x = 4s5: y = 5
s7: a = xs8: b = ys9: c = z
BB4
s6: y = 6BB2 BB3
BB1 s1: x = 1s2: y = 2s3: z = 3 out[BB1]= gen[BB1](in[BB1]–kill[BB1])
= {s1,s2,s3} {}
in out
BB1 {s1,s2,s3}
BB2
BB3
BB4
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
STEP3: BB2に適用
58
gen[BB1]={s1,s2,s3},gen[BB2]={s4,s5},gen[BB3]={s6},gen[BB4]={s7,s8,s9}
kill[BB1]={s4,s5,s6}kill[BB2]={s1,s2,s6}kill[BB3]={s2,s5}kill[BB4]={}
s4: x = 4s5: y = 5
s7: a = xs8: b = ys9: c = z
BB4
s6: y = 6BB2 BB3
BB1 s1: x = 1s2: y = 2s3: z = 3
in[BB2] = out[BB1] = {s1,s2,s3}
out[BB2]= gen[BB2](in[BB2]–kill[BB2])= {s4,s5} ({s1,s2,s3}–{s1,s2,s6})
in out
BB1 {s1,s2,s3}
BB2 {s1,s2,s3} {s3,s4,s5}
BB3
BB4
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
STEP4: BB3に適用
59
gen[BB1]={s1,s2,s3},gen[BB2]={s4,s5},gen[BB3]={s6},gen[BB4]={s7,s8,s9}
kill[BB1]={s4,s5,s6}kill[BB2]={s1,s2,s6}kill[BB3]={s2,s5}kill[BB4]={}
s4: x = 4s5: y = 5
s7: a = xs8: b = ys9: c = z
BB4
s6: y = 6BB2 BB3
BB1 s1: x = 1s2: y = 2s3: z = 3
in[BB3] = out[BB1] = {s1,s2,s3}
out[BB3]= gen[BB3](in[BB3]–kill[BB3])= {s6} ({s1,s2,s3}–{s2,s5})
in out
BB1 {s1,s2,s3}
BB2 {s1,s2,s3} {s3,s4,s5}
BB3 {s1,s2,s3} {s1,s3,s6}
BB4
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
STEP5: BB4に適用
60
gen[BB1]={s1,s2,s3},gen[BB2]={s4,s5},gen[BB3]={s6},gen[BB4]={s7,s8,s9}
kill[BB1]={s4,s5,s6}kill[BB2]={s1,s2,s6}kill[BB3]={s2,s5}kill[BB4]={}
s4: x = 4s5: y = 5
s7: a = xs8: b = ys9: c = z
BB4
s6: y = 6BB2 BB3
BB1 s1: x = 1s2: y = 2s3: z = 3
in[BB4] = out[BB2]out[BB3]= {s3,s4,s5}{s1,s3,s6}
in out
BB1 {s1,s2,s3}
BB2 {s1,s2,s3} {s3,s4,s5}
BB3 {s1,s2,s3} {s1,s3,s6}
BB4 {s1,s3,s4,s5,s6} {...}
out[BB4]= gen[BB4](in[BB4]–kill[BB4])= ...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
STEP6: in[BB4]で、xに関する定義はs1とs4が、yに関する定義はs5とs6が、とそれぞれ複数到達している。zに関する定義は、唯一s3だけが到達しているので、BB4におけるzの使用をs3の定義で置き換える。
61
s4: x = 4s5: y = 5
s7: a = xs8: b = ys9: c = z3
BB4
s6: y = 6BB2 BB3
BB1 s1: x = 1s2: y = 2s3: z = 3
in out
BB1 {s1,s2,s3}
BB2 {s1,s2,s3} {s3,s4,s5}
BB3 {s1,s2,s3} {s1,s3,s6}
BB4 {s1,s3,s4,s5,s6} {...}