プロジェクト

全般

プロフィール

高徹JITネタ

高橋 徹 さんが12日前に追加

JavaVMのJIT(Just In Time)コンパイラについて


返答 (3)

高徹 こJITが生成する機械語のアセンブリコードを確認したい - 高橋 徹 さんが12日前に追加

JavaVMのJITコンパイラが生成するネイティブの機械語のアセンブリコードを見ることができれば、どのような最適化がなされているか分かります。

JavaVMには、-XX:+PrintAssembly オプションがあり、JITがコンパイルした機械語のアセンブリコードを参照する基本的な枠組みがあります。

C:\work> java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly MulDivShift

ただし、普通にOpenJDKディストリビューションを入れただけでは、このオプションを指定しても機械語の16進数値しか表示されません。アセンブリコードを見るには、さらに hsdis(HotSpot DISassembler)が必要になります。hsdisは、OpenJDKプロジェクトに含まれるJavaVMのプラグインライブラリで、JITが生成した機械語からアセンブリコードに逆アセンブルします。ただし hsdisのバイナリはほとんどのOpenJDKディストリビューションには含まれていないので、次のどちらかで用意します。

  • ビルド済みのhsdisライブラリを探して入手する
  • OpenJDKのソースコードを入手して自分でビルドする

ビルド済みのhsdisライブラリ

ビルド済みのhsdisライブラリを公式に提供しているサイトはないので、野良ビルド的なものを取得することになります。このhsdisはOpenJDKのバージョンに依存する可能性があり、使用するJDKバージョンと互換性のあるバージョンのhsdisを取得します。が、互換性情報は中々見出せず、動くかどうか試してみるしかなさそうです。
次のサイトでは、本日(2026-03-03)現在、OpenJDK 17のソースコードからビルドしたhsdisライブラリを公開しています。

https://chriswhocodes.com/hsdis/

Windows OSの場合、このサイトから hsdis-amd64.dll を入手し、OpenJDK 17ディストリビューションをインストールし、OpenJDK 17のインストールディレクトリ\bin\serverの下にhsdis-amd64.dllを保存します。

C:\work> java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly MulDivShift
  :
[Verified Entry Point]
  # {method} {0x000001e56a4002e0} 'calc' '(I)I' in 'MulDivShift'
  # parm0:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x000001e57b228b80:   sub    $0x18,%rsp
  0x000001e57b228b87:   mov    %rbp,0x10(%rsp)              ;*synchronization entry
                                                            ; - MulDivShift::calc@-1 (line 9)
  0x000001e57b228b8c:   mov    %edx,%eax
  0x000001e57b228b8e:   sar    $0x1f,%eax
  0x000001e57b228b91:   shr    $0x1e,%eax
  0x000001e57b228b94:   add    %edx,%eax
  0x000001e57b228b96:   sar    $0x2,%eax                    ;*idiv {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - MulDivShift::calc@2 (line 9)
  0x000001e57b228b99:   add    $0x10,%rsp
  0x000001e57b228b9d:   pop    %rbp
  0x000001e57b228b9e:   cmp    0x348(%r15),%rsp             ;   {poll_return}
  0x000001e57b228ba5:   ja     0x000001e57b228bac
  0x000001e57b228bab:   ret    

このアセンブリコード(ニーモニック)はAT&T記法です。Intel記法で出すには、-XX:PrintAssemblyOptions=intel を指定します。

C:\work> java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:PrintAssemblyOption=intel MulDivShift

JITコンパイラは賢いので、短いメソッドは呼び出し元の箇所へインライン化したり、ある一定回数以上実行されたコードでないとJITコンパイルしないとか、第1段階では軽い最適化(C1コンパイル)で第2段階で深い最適化(C2コンパイル)するなどの振る舞いを持ちます。JITが吐き出すアセンブリコードを、インライン化させずに、C2コンパイル結果のものを Intelニーモニック形式で見るには、
次のようにオプションを指定します。

C:\work> java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:PrintAssemblyOptions=intel ^
 -XX:-TieredCompilation -XX:-Inline MulDivShift
  :
[Verified Entry Point]
  # {method} {0x000001cd7d4002e8} 'calc' '(I)I' in 'MulDivShift'
  # parm0:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x000001cd5efb3280:   sub    rsp,0x18
  0x000001cd5efb3287:   mov    QWORD PTR [rsp+0x10],rbp     ;*synchronization entry
                                                            ; - MulDivShift::calc@-1 (line 9)
  0x000001cd5efb328c:   mov    eax,edx
  0x000001cd5efb328e:   sar    eax,0x1f
  0x000001cd5efb3291:   shr    eax,0x1d
  0x000001cd5efb3294:   add    eax,edx
  0x000001cd5efb3296:   sar    eax,0x3                      ;*idiv {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - MulDivShift::calc@3 (line 9)

この hsdis バイナリを JDK 21および JDK 25のbin\serverの下に置いたところ、ニーモニックが表示されました。

高徹 hsdisについて補足 - 高橋 徹 さんが11日前に追加

hsdisが逆アセンブルを行う際に利用するライブラリは、hsdisのビルド時に次の3つから選択します。

  • GNU binutils
  • LLVM
  • Capstone

hsdisの開発当初はGNU binutilsを利用して逆アセンブルを行っていました。その後LLVMとCapstoneを利用することが可能になりました(OpenJDK 19で追加、安定してきたのは21あたりの模様)。
GNU binutilsはGPLv3のライセンスで提供され、OpenJDKのGPLv2とは互換性がありません。そのため、hsdisのバイナリを提供するとなると、ちょっと困ったことになります。これが hsdis のバイナリが大っぴらに提供されていない理由かもしれません。また、binutilsのABIがバージョンアップで不安定、libbfdが重い、Windows OS環境ではMSYS2とMinGW64を使う必要がある、などの課題もあります。

LLVMはコンパイラ基盤で、その逆アセンブラ機能(MC Disassembler API)を使います。
ビルドにはLLVMをインストールする必要がありますが、そこそこ大きなソフトウェアです。

Capstoneは、逆アセンブラ機能そのものです。

高徹 Windows OS用の hsdis をソースからビルドする方法を調査 - 高橋 徹 さんが7日前に追加

https://github.com/openjdk/jdk/blob/master/src/utils/hsdis/README.md

2026年3月時点(OpenJDK 21/25)において、Windows OS上で動く hsdis をビルドするのは中々に難しい作業といえます。

GNU binutils

Visual Studio ツール系では binutils をビルドすることはできず、mingw コンパイラを使う必要がありビルド環境を整えるのに cygwin を利用することになります。

LLVM

Windows OS用のLLVMインストールは不完全でインクルードファイルが不足しているなど、LLVMビルド環境を構築するのに問題があると記載されています。(少なくともLLVM 13までは)
LLVMは完全なコンパイラキットではなく、コンパイルフレームワークなので、ベースに別途Cのランタイム(とstdio.hなどのインクルードファイル)が必要で、WindowsOSでは、Visual Studioツール系もしくはMingwツール系のどちらかを使用します。

Capstone

Windows用のCapstone Core Engineをダウンロードすると記載されていますが、現時点でCore Engineは https://www.capstone-engine.org/download.html には存在せず、Capstone Python モジュールのWindows版を入手し、その中からライブラリ(capstone.dll)を取り出すなどの回避策を取らざるを得ないようです。その上で、Visual StudioかMingwのコンパイラ、CMakeツールを使用します。

    (1-3/3)