GraalでのOSRを見たとき、"Control Flow Graph: 制御フローグラフ"(CFG)が出てきました。
Register AllocationとCFGは密接に関わります。さて、JavaでのCFGを検索すると論文が出てきました。
Thomas Würthingerさんの"Visualization of Java Control Flow Graphs"です。 https://pdfs.semanticscholar.org/1f9f/d3658f77aaa4d144d8b7e59bfe4cd63d4475.pdf
またまたヨハネス・ケプラー大学の論文です。前回読んだlinear scan register allocationの論文もこちらの大学でした。そして、Graalな方はピンときたでしょう。そう、Thomas Würthingerさん。現在はOracle LabsでGraalの開発をしておられる方です!
http://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-SO12-Polygot-interview.pdf
この論文では、Javaで-XX:+PrintCFGToFileした際のファイルを可視化するツールを作成しています。それは、現在までc1visualizerというツールで公開されています。
起動すると、ヨハネス・ケプラー大学のエンブレムがあることがわかります。ただ、以前はjava.netで公開されていましたが、java.netは閉鎖されました。今c1visualizerをダウンロードできる場所について、Davidさんがツイートしてくれています。
PSA: C1Visualizer can currently be found here. For some reason, 1.5/1.6 don't seem to work with HotSpot GFG files. https://t.co/AbxBcPB3Fk
— David Buck (@DavidBuckJP) 2017年8月4日
at(オーストリア)ドメイン、URLから推測するにヨハネス・ケプラー大学のサーバのようです。c1visualizerはダウンロードして解凍するだけでOKです。ひとまず1.4を使いました。
さてCFGを出力するため、サンプルコードを使います。
class Demo { public static void main(String[] args) { while (true) { new Demo().workload(14, 2); } } private int workload(int a, int b) { return a + b; } }
-XX:+PrintCFGToFileして実行しましょう。
$ java -XX:+PrintCFGToFile Demo Error: VM option 'PrintCFGToFile' is develop and is available only in debug version of VM. Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.
デバッグバージョンでしかこのオプションは使えません。OpenJDKをデバッグビルドします。こちらを参考にしてください。基本的には--enable-debugをつけてconfigureするだけです。
$ java -XX:+PrintCFGToFile -XX:CompileOnly=Demo.workload -Xcomp -XX:-Inline Demo
-XX:+PrintCFGToFileはC1コンパイルでのCFGを出力します。C2だけだと何もでません。今はデフォルトでTieredCompilationが有効ですが、そうでない場合は-XX:+TieredCompilationで有効にしてください。
無限ループなので適当に停止します。CompileOnlyなしだと全メソッドのCFGが出て出力量が多くなりすぎりので、指定します。-XcompはインタープリトせずすぐにJITコンパイルします。-XX:-Inlineはインライン化をしない設定です。
すると.cfgファイルが出力されます。
$ ls -lat ... -rw-r--r-- 1 jyukutyo jyukutyo 13452 12 15 19:00 output_tid23299_pid22562.cfg ...
このファイルはテキストファイルです。
begin_compilation
name " Demo::workload"
method "virtual jint Demo.workload(jint, jint)"
date 1513329125814
end_compilation
begin_cfg
name "BlockListBuilder virtual jint Demo.workload(jint, jint)"
begin_block
name "B0"
from_bci 0
to_bci -1
predecessors
successors
xhandlers
flags "std"
end_block
end_cfg
...
CFGとしてブロックとそのブロックの各predecessors(先行ブロック)/successors(後続ブロック)、bci(バイトコードインデックス)といった情報が出力されます。こういった情報をテキストで見ても、わかりづらいですね。なのでc1visualizerで可視化します。binディレクトリにあるc1visualizerコマンドを実行します。Java 9だとそのままでは動作しないので、8以前で起動します。
c1visualizerでさきほどの.cfgファイルを読み込みます。

メソッドのJITコンパイルに至るまでの各状態を見ることができます。たとえば"After Generation of HIR"でHIRの状態が見れます。
virtual jint Demo.workload(jint, jint)
2017/12/15 18:12:05
After Generation of HIR
B1 -> B2 [0, 0]
Locals size 3 [virtual jint Demo.workload(jint, jint)]
0 a1 [method parameter]
1 i2 [method parameter]
2 i3 [method parameter]
_p__bci__use__tid__instruction__________________________________________________ (HIR)
. 0 0 v9 std entry B2
B2 <- B1 -> B0 [0, 0] std
Locals size 3 [virtual jint Demo.workload(jint, jint)]
1 i2
2 i3
_p__bci__use__tid__instruction__________________________________________________ (HIR)
. 0 0 v8 goto B0
B0 <- B2 [0, 3] std
Locals size 3 [virtual jint Demo.workload(jint, jint)]
1 i2
2 i3
_p__bci__use__tid__instruction__________________________________________________ (HIR)
2 0 i4 i2 + i3
. 3 0 i5 ireturn i4
3つのブロックB0、B1、B2があり、B1からB2、B2からB0へ遷移して最後returnという流れです。
"After Register Allocation"では、レジスタ割り当てが終わったあとの各intervalの状態が見れます。

各行がintervalに対応しています。Number 0にはrsiを割り当てています。今回の割り当てではspillしているintervalはありませんでした。
LIRも見れます。
B1 -> B2 [0, 0] _nr__instruction________________________________________________________________ (LIR) 0 label [label:0x00007fa62983b890] 2 std_entry 4 move [rsi|L] [R569|L] 6 move [rdx|I] [R570|I] 8 move [rcx|I] [R571|I] 10 move [metadata:0x000000012722f128|M] [R572|M] 12 move [Base:[R572|M] Disp: 300|I] [R573|I] 14 add [R573|I] [int:8|I] [R573|I] 16 move [R573|I] [Base:[R572|M] Disp: 300|I] 18 branch [AL] [CounterOverflowStub: 0x00007fa62a1194d0] 20 label [label:0x00007fa62a119500] 22 branch [AL] [B2]
かなりマシンコードに近い表現になっています。c1visualizerでCFGやHIR、LIRを可視化できます。
さらに各ダイアグラムを右クリックすると"Open Control Flow Graph"があります。これを選択すると、CFGを見れます。

この例ではCFGが単純すぎました。次回以降でもう少し複雑なコードでCFGを見てみます。