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 が無いので、モジュール内の関数へのポインタも取っておかないと、バッファなどの後始末ができなくなるので、微妙なインタフェースになってしまいました。
今回はコードだけでしたが、次回はデータやローカル変数などを見てみたいと思います。