2010年03月31日
LLVM の JIT を使ってみる (3)
LLVM の JIT API を使うと、関数を返す(作る)関数が書けます。
肝は ExecutionEngine の getPointerToFunction() API で、JIT 済みの関数(コード)へのポインタを取得できます。
HowToUseJIT.cpp で、ExecutionEngine を作り終わった後に、
// Now we create the JIT. ExecutionEngine* EE = EngineBuilder(M).create();
以下のようなコードを追加してみると、JIT 済みの関数を関数ポインタに受けて、そのまま通常の関数のように使用できることが確認できます。
int (*foo_ptr)(int); int result; foo_ptr = (int (*)(int))EE->getPointerToFunction(Add1F); result = foo_ptr(123); // 124
後は応用です。以下には必要な部分しか書いてませんが、HowToUseJIT プロジェクトと同じヘッダと設定でビルドできるはずです。
typedef int (*addn_func_t)(int); addn_func_t create_func(ExecutionEngine& ee, Module& mod, int n, Function **f) { /* 無名関数を作ります。 [](int x) -> int // C++0x syntax { return x + n; // n は定数 } */ LLVMContext& ctx = mod.getContext(); *f = cast<Function>(mod.getOrInsertFunction("", Type::getInt32Ty(ctx), Type::getInt32Ty(ctx), (Type *)0)); BasicBlock *bb = BasicBlock::Create(ctx, "", *f); Value *v = ConstantInt::get(Type::getInt32Ty(ctx), n); Argument *arg = (*f)->arg_begin(); Instruction *add = BinaryOperator::CreateAdd(arg, v, "", bb); ReturnInst::Create(ctx, add, bb); return (addn_func_t)ee.getPointerToFunction(*f); } int main() { InitializeNativeTarget(); LLVMContext ctx; Module* mod = new Module("", ctx); ExecutionEngine* ee = EngineBuilder(mod).create(); int n = 123; Function *fp; /* 関数(コード)を作る */ addn_func_t f = create_func(*ee, *mod, n, &fp); int x = 222; /* 関数を呼び出す */ int res = f(x); printf("f(%d) = %d\n", x, res); /* 関数を削除 */ ee->freeMachineCodeForFunction(fp); delete ee; llvm_shutdown(); return 0; }
関数ポインタを返すようにしてみたのですが、どのみち C++ は GC が無いので、モジュール内の関数へのポインタも取っておかないと、バッファなどの後始末ができなくなるので、微妙なインタフェースになってしまいました。
今回はコードだけでしたが、次回はデータやローカル変数などを見てみたいと思います。