JIT における手続き呼び出しの問題点

JIT コンパイルされた手続きが、別の手続きを呼び出す場合の扱いが理解できないので整理。

通常 VM インストラクションにおける処理

  • FRAME: vPC などを push
  • CALL: cprocedure なら native call 。closure なら vPC に jump
  • RETURN: スタックから pop した vPC に jump 。

JIT の場合

JIT コンパイルされた手続きが、JIT コンパイルされていない手続きを呼ぶとき
  • FRAME: callee からの RETURN で JIT コンパイルされていない手続きに戻ってしまう
    • callee の RETURN 命令を制御できない事による
  • CALL: 通常と同じ
  • RETURN: callee が発行するので制御できない


この場合、JIT 手続きは使用していない vPC でもきちんと同期してやらないといけない。

JIT コンパイルされた手続きが、JIT コンパイルされた手続きを呼ぶとき
  • FRAME: callee は C 関数なので FRAME は不要。ただし callee が C 関数かどうかはコンパイル時には分からないので実質必要。
  • CALL: 通常と同じ
  • RETURN: callee は RETURN を発行しない

ややこしい部分は?

callee からの戻り先が「どこか?」の部分。
一番望ましいのは、JIT コンパイルされたコードの中に戻ること。そうでなければ、せっかく JIT コンパイルしてもすぐに別の手続きを呼ぶだけで、JIT の恩恵を受けられなくなってしまう。

解 その1

FRAME 命令を 2種類用意する。JIT 用では vPC ではなく native PC などを PUSH しておく。
callee で発行される RETURN 命令では、スタックに積まれているもので JIT 用かどうか判別して RETURN する。

解 その2

JIT コンパイルされた手続きから、呼ばれる手続きも JIT コンパイルされている事を期待する。

スタックの問題

解 その2 は、ネイティブスタックを使う事になるので、元の手続きが再帰している場合にうれしくない。

所感

この手の話は、JIT の教科書でもあれば章を割いて書かれてそうな話だ。論文を読むのが良いのかな。