40429行
JITコンパイラが吐くコード中の任意の箇所に(制限はあるものの)任意のアセンブラコードを挿入することができる仕組みの実装を始めた。CPUの性能カウンタをサンプリングするコードを任意の箇所に挿入したい、というのが元々の動機。しかし、monitorenterやmonitorexitをアセンブラで書いたコードにインライン展開する際にも利用できそうだ。
現在のところの実装方針は、
- コードの断片はアセンブラで記述し、共有ライブラリの形にコンパイルする。
- 起動時に必要な共有ライブラリをdlopenする。どの共有ライブラリをロードするかは起動時の引数で与える設定ファイル中で自由に指定できる。
- JITコンパイルの最後のコード生成の際に、指定された箇所に共有ライブラリ(のメモリ上のイメージ)からコードをコピーする。アドレスはdlsymでわかる。
という感じ。アセンブラコード中の絶対アドレスによる参照はdlopenが解決してくれる。相対アドレスによる外部参照は用いないこと。Global offset tableやprocedure linkage tableは用いない。従って、共有ライブラリと言いつつ、position independent codeでないので共有はされない。
コードのサイズや、その他再配置が必要なオフセットなどはアセンブラコード中に頭にRJJasm__を付けた特別なシンボルを定義することで解決する。共有ライブラリにコンパイルしてからnmコマンドで抽出して設定ファイルにまとめる。
UltraSPARCで性能カウンタを読み出すコードの例:
.section ".text" .align 4 .type start_count, #function .global rjj_read_pic_start rjj_read_pic_start: rd %pic, %g1 RJJasm__rjj_read_pic_start__stackd0_p0_immhi22_0 = . - rjj_read_pic_start sethi %hi(0), %g2 RJJasm__rjj_read_pic_start__stackd0_p0_imm10_0 = . - rjj_read_pic_start or %g2, %lo(0), %g2 stx %g1, [%fp+%g2] RJJasm__rjj_read_pic_start__size = . - rjj_read_pic_start RJJasm__rjj_read_pic_start__clobber_g1g2 = 0 .type stop_count, #function .global rjj_read_pic_stop rjj_read_pic_stop: rd %pic, %o2 RJJasm__rjj_read_pic_stop__stackd0_p0_immhi22_0 = . - rjj_read_pic_stop sethi %hi(0), %g1 RJJasm__rjj_read_pic_stop__stackd0_p0_imm10_0 = . - rjj_read_pic_stop or %g1, %lo(0), %g1 ld [%fp+%g1], %o1 RJJasm__rjj_read_pic_stop__stackd0_p4_immhi22_0 = . - rjj_read_pic_stop sethi %hi(0), %g1 RJJasm__rjj_read_pic_stop__stackd0_p4_imm10_0 = . - rjj_read_pic_stop or %g1, %lo(0), %g1 ld [%fp+%g1], %o0 sethi %hi(rjj_print_perf_count_sparc), %g1 or %g1, %lo(rjj_print_perf_count_sparc), %g1 call %g1 srlx %o2, 32, %o3 RJJasm__rjj_read_pic_stop__size = . - rjj_read_pic_stop RJJasm__rjj_read_pic_stop__clobber_g1_g3o0_o7f0_f31 = 0
RJJasm__で始まるシンボルを抽出して整形すると、
rjj_read_pic_start:clobber_g1g2=0 rjj_read_pic_start:size=16 rjj_read_pic_start:stackd0_p0_imm10_0=8 rjj_read_pic_start:stackd0_p0_immhi22_0=4 rjj_read_pic_stop:clobber_g1_g3o0_o7f0_f31=0 rjj_read_pic_stop:size=44 rjj_read_pic_stop:stackd0_p0_imm10_0=8 rjj_read_pic_stop:stackd0_p0_immhi22_0=4 rjj_read_pic_stop:stackd0_p4_imm10_0=20 rjj_read_pic_stop:stackd0_p4_immhi22_0=16
となる。clobberは破壊されるレジスタ。sizeはコードのサイズ。stackはスタックを参照する命令のオフセットで、コピーした後でimm10部分やimmhi22部分を上書きする。
これで正しく動作するのだろうか。関係ないが、4万行を越えた。