JIT の CALL インストラクションで手を抜きたい
VM ループの CALL 命令は
case (CALL): { #include "長いコード" }
のようになっている。include しているのは CALL の合成命令、例えば REFER_GLOBAL_CALL などで同じコードを使いたいから。
さて CALL 命令を JIT 化するには長いコードをアセンブリでごりごり書かないといけないんだけど、とても面倒で心が折れそう。さらに長いのでメンテナンスが大変そう。というわけで気が進まない。
それなら「長いコード」の部分を C++ の関数にしてしまえば JIT 化が楽だよねと思いつく。call すれば良いだけだから。
問題は CALL インストラクション毎に call/ret のコストが増えてしまう事。でもこれは実際に計測してみないと分からないのでやってみた。
ベンチマークに使ったのは
- R6RS test suite
- fib, triangle, clos
など。環境は X86-64 。引数は2つなのでレジスタ渡し。
結論としては triangle が 1 割ほど遅くなった以外は全く差がなかった。実践的なコードでは CALL インストラクションが発行される割合が少ないからだと思われる。これが例えばローカル変数参照の REFER_LOCAL とかだと結果が違ったかもしれない。