Xbyakを使ったJITの続き。前回と同様、gcc4.2.1 (x86-64)でのみ動作確認。

JITで生成するコード内から外部の関数を呼び出す:

class CallCFunc : public Xbyak::CodeGenerator {
public:
  CallCFunc(int (*f)(int)) {
    // エントリシーケンス
    push(rbp);
    mov(rbp, rsp);
    // ローカル変数を使いたい場合は、その分rspをずらして、
    //   sub(rsp, 16);
    // rbp相対でアクセスする
    //   mov(dword [rbp - 4], 1234);
    // 関数本体
    mov(rax, (size_t)f);
    call(rax);
    // 終了シーケンス
    mov(rsp, rbp);
    pop(rbp);
    ret();
  }
  int (*get() const)(int) { return getCode<int(*)(int)>(); }
};

#include <math.h>
int isqrt(int x) {
  return (int)sqrt(x);
}

int main() {
  cout << CallCFunc(isqrt).get()(1234321) << endl;
  // => 1111
}
  • x86-64やその呼出規約とかまるでわかってないので勘違いしてる可能性大なんだけど。
  • 64ビット環境なので、スタックポインタやベースポインタはrsprbpと、64ビットのレジスタを使う。
  • 関数へのポインタも64ビットになるけど、直接呼び出す方法はないのかな?いったんraxに代入してコールする。これがx86-64のインストラクションの制約なのかxbyakの制約なのかわかってない。
  • 生成する関数でローカル変数を扱いたい場合は、rspをマイナスして領域を確保して、rbp相対でアクセスする。rspのアライメントは16単位? x86 calling conventionsには、「GCC4.5以降は16、それ以前は4」と書かれている
  • 逆にローカル変数を使わない場合は、push命令でrbpを保存しなくても動くんじゃないかと思うんだけど、試した限りsegmentation fault動いたり動かなかったり、よくわからなかった。