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を見てみます。