ネイティブ・イメージのLLVMバックエンド
ネイティブ・イメージでは、LLVM中間表現およびLLVMコンパイラを使用してネイティブ実行可能ファイルを生成する代替バックエンドが提供されます。このLLVMバックエンドを使用すると、ユーザーは、GraalVMネイティブ・イメージで直接サポートされていない代替アーキテクチャをターゲットにすることができます。ただし、このアプローチではパフォーマンス・コストがいくらか発生します。
インストールおよび使用方法
LLVMバックエンドは、ネイティブ・イメージとは別のコンポーネントとして出荷され、GraalVM Updaterを使用してインストールできます:
gu install native-image-llvm-backend
LLVMバックエンドを有効にするには、-H:CompilerBackend=llvm
オプションをnative-image
コマンドに渡します。
コード生成オプション
-H:+SpawnIsolates
: 分離を有効にします。(これは、LLVMバックエンドを使用する際にパフォーマンスの低下をもたらすため、デフォルトでは無効になっています。)-H:+BitcodeOptimizations
: LLVMビットコード・レベルで積極的な最適化を有効にします。これは試験段階であり、バグを引き起こす可能性があります。
デバッグ・オプション
-H:TempDirectory=
: ネイティブ・イメージによって生成されたファイルの保存場所を指定します。LLVMファイルは、このフォルダのSVM-<timestamp>/llvm
に保存されます。-H:LLVMMaxFunctionsPerBatch=
: コンパイル・バッチの最大サイズを指定します*。1に設定すると、すべての関数が個別にコンパイルされ、0に設定すると、すべてが単一のバッチとしてコンパイルされます。-H:DumpLLVMStackMap=
: コンパイル済関数とそれに対応するビットコード・ファイルの名前の間のマッピングなど、デバッグ情報をダンプするファイルを指定します。
バッチについて: LLVMコンパイルは次の4つのフェーズで実行されます:
- 関数ごとにLLVMビットコード・ファイル(
f0.bc
、f1.bc
など)が作成されます。 - ビットコード・ファイルがバッチ(
b0.bc
、b1.bc
など)にリンクされます。-H:LLVMMaxFunctionsPerBatch=1
が指定されている場合、このフェーズはスキップされます。 - バッチが(
b0o.bc
、b1o.bc
などに)最適化され、(b0.o
、b1.o
などに)コンパイルされます。 - コンパイルされたバッチが単一のオブジェクト・ファイル(
llvm.o
)にリンクされ、そのファイルが最終実行可能ファイルにリンクされます。
LLVMバックエンドを使用したGraalVMへのターゲット・アーキテクチャの追加方法
LLVMバックエンドの興味深いユース・ケースは、ネイティブ・イメージ用にまったく新しいバックエンドを実装することなく、新しいアーキテクチャをターゲットにすることです。現時点でこれを実現するために必要なステップは次のとおりです。
ターゲット固有のLLVM設定
場合によっては、GraalVMコードでLLVMのターゲット非依存の性質よりも深い部分を処理することが必要になります。これらの代表例は、直接レジスタ・アクセスおよび直接レジスタ・ジャンプ(トランポリンの場合)と、LLVMによって発行されるコードのスタック・フレームの構造に関する精度を実装するための、インライン・アセンブリ・スニペットです。このことは、十数個に満たない単純な値が新しいターゲットごとに設定されることを表します。
LLVMステートポイントのサポート
LLVMバックエンドでは、LLVMの一般的で十分にサポートされている機能が使用されますが、ガベージ・コレクション・サポートでは、LLVMの試験段階の機能であるステートポイント組込み関数が暗黙的に使用されます。つまり、新しいアーキテクチャをサポートするには、要求されたターゲットのLLVMにステートポイントを実装する必要があります。ほとんどのステートポイント・ロジックはビットコード・レベル、つまりターゲット非依存のステージで処理されることから、これには、ステートポイント組込み関数を弱める正しいタイプのコールの発行が大きく関係します。
オブジェクト・ファイルのサポート
GraalコンパイラのLLVMバックエンドで作成されたプログラムのデータ・セクションは、LLVMによって処理されるコードから独立して発行されます。つまり、LLVMでコンパイルされたコードをGraalVMで生成されたデータ・セクションにリンクできるようにするには、Graalコンパイラでターゲット・アーキテクチャのオブジェクト・ファイルの再配置が認識されている必要があります。
(例はELFMachine$ELFAArch64Relocations
を参照)