こんにちは!HHVMとHackのディープで魅力的な世界へようこそ。
Hackを書き始めたばかりの皆さんの中には、「HackってPHPに似ているのに、なぜMeta(旧Facebook)のような超巨大インフラを支えられるほど圧倒的に速いんだろう?」と疑問に思ったことがある方も多いのではないでしょうか。
その秘密の心臓部にあるのが、仮想マシン HHVM(HipHop Virtual Machine) と、その内部で駆動する JIT(Just-In-Time)コンパイラ です。
今回は、HackコードがCPUで直接実行されるマシン語(ネイティブコード)へと生まれ変わる「直前の姿」である 中間表現「HHIR(HipHop Intermediate Representation)」 の世界を一緒に探検してみましょう!
内部構造を少し覗くだけで、「なぜ型をしっかり書くと実行速度まで爆速になるのか?」というHackの神髄が手に取るようにわかるようになりますよ。リラックスして読み進めてみてくださいね。
—
1. Hackコードがマシン語になるまでの全体マップ
まずは、私たちが書いた `.hack` ファイルが、どのような旅路を経てCPUで実行されるのか、全体像を俯瞰してみましょう。
[ あなたが書いた Hack ソースコード ]
│
▼ ① コンパイル(Parse & Compile)
[ HHBC (HHVM Bytecode) ] ── スタックマシン形式の仮想命令
│
▼ ② JITコンパイラが起動 (Translate)
[ HHIR (HHVM Intermediate Representation) ] ── ★今回の主役!SSA形式のIR
│
▼ ③ 最適化エンジン (Optimize: 型特化・不要コード削除・インライン化)
[ 最適化された HHIR ]
│
▼ ④ コード生成 (Code Generation / Vreg & Register Alloc)
[ x86_64 / AArch64 ネイティブマシン語 ] ── CPUが直接爆速実行!
普段の開発では、静的型チェッカー(`hh_client`)が構文や型エラーを事前に検知してくれますが、実行時のHHVM内部では上記のようなパイプラインが動いています。
注目してほしいのは、HHBC(バイトコード)から直接マシン語にするのではなく、一度「HHIR」という中間表現を挟んでいる 点です。ここがHHVMの頭脳にあたる部分です。
—
2. なぜ「HHIR」が必要なの?
「バイトコードから直接マシン語に変換したほうが手っ取り早いのでは?」と思うかもしれません。しかし、高度な最適化を行うためには HHIR が絶対に欠かせない理由があります。
1. SSA形式(静的単一代入形式)であること
すべての変数が「1度だけ代入される」形に分解されます。これにより、データの流れ(データフロー)が明確になり、不要な計算の削除や定数畳み込みが劇的にやりやすくなります。
2. 型情報(Type Knowledge)を保持できること
バイトコード(HHBC)は汎用的な命令ですが、HHIRになると「この変数は絶対に `int` だ」「このオブジェクトは `User` クラスだ」といった詳細な型情報が命令レベルで刻み込まれます。
3. ガード(Type Guard)と脱最適化の制御
実行時に型をチェックする命令(Guard)を最小限に減らす最適化がここで行われます。
—
3. 実例で見る:HackコードがHHIRに変わる瞬間
それでは、具体的なHackコードを見ながら、HHIRがどのような形をしているのか見てみましょう!
元のHackコード
<<__EntryPoint>>
function main(): void {
$a = 10;
$b = 20;
$c = add_numbers($a, $b);
echo $c;
}
function add_numbers(int $x, int $y): int {
return $x + $y;
}
ステップ1:HHBC(バイトコード)への変換
まず、関数 `add_numbers` は以下のようなバイトコード(HHBC)になります。スタックマシン形式なので、値を積んで足すという単純な流れです。
// HHBCのイメージ
FPushFuncD “add_numbers”
CGetL2 $x // 変数 $x をスタックに積