2013-03-04
あなとみー おぶ mrubyのJIT (その8)
かなり間が空いちゃいました。その間にmrubyのJITをコーティングしたりお祭りの資料を作ったりしていました。結構内容が変わって、例えばこれまで説明していたものがjit.cからvm.cに移ったりしています。そんなこんなで結構速度が上がって(多くがwannabe53さんのおかげ)、もうすぐオリジナルの2倍速くらいか(単純なループなら4倍くらいだけどメソッドコールが入ると速度が落ちる)というところまで来ています。
さて、今回はjitcode.ccの説明です。
const void *
mrbjit_emit_code(mrb_state *mrb, mrbjit_vmstatus *status)
{
MRBJitCode *code = (MRBJitCode *)mrb->compile_info.code_base;
const void *rc = mrbjit_emit_code_aux(mrb, status, code);
if (rc == NULL && code == NULL) {
mrb->compile_info.code_base = NULL;
}
return rc;
}
これが、コード生成部のトップレベルmrbjit_emit_codeです。最初に、
MRBJitCode *code = (MRBJitCode *)mrb->compile_info.code_base;
でCレベルで持っているvoid *型のXbyakのCodeGeneratorオブジェクトをC++のオブジェクトに変換します。変換って言っても単にキャストですが。なんか、すごい勢いで怒られそうなコードですがとりあえず動いているからいいかって感じです。
const void *rc = mrbjit_emit_code_aux(mrb, status, code);
if (rc == NULL && code == NULL) {
mrb->compile_info.code_base = NULL;
}
ここで、実際の処理を行うmrbjit_emit_code_auxを呼び出します。mrbjit_emit_code_auxはコード生成をするとそのコードの命令列の先頭のアドレスを返します。コードが生成され合い場合はNULLを返します。
その後のif文はなぜいるのか忘れてしまった説明出来ないです。すみません・・・。これがないと動かないのですが…
次に、mrbjit_emit_code_auxです。
static const void *
mrbjit_emit_code_aux(mrb_state *mrb, mrbjit_vmstatus *status, MRBJitCode *code)
{
mrb_irep *irep = *status->irep;
mrb_value *regs = *status->regs;
mrb_code **ppc = status->pc;
const void *entry;
if (code == NULL) {
code = the_code;
mrb->compile_info.code_base = code;
entry = code->gen_entry(mrb, irep);
}
初めてmrbjit_emit_codeを呼び出したとき、codeはNULLになっているのでCodeGeneratorオブジェクトを設定します。今のところ、CodeGeneratorオブジェクトは1つでthe_codeというグローバルのstatic変数に入っています。ただ、複数のCodeGeneratorオブジェクトを管理できるようにするため、the_codeを直接アクセスする箇所を出来る限り減らしています。code->gen_entry(mrb, irep)は今のところ何もしないのですが、CodeGenratorオブジェクトになんか設定する場合とかを想定しています。
switch(GET_OPCODE(**ppc)) {
case OP_NOP:
return code->emit_nop(mrb, irep, ppc);
case OP_MOVE:
return code->emit_move(mrb, irep, ppc);
case OP_LOADL:
return code->emit_loadl(mrb, irep, ppc);
次に実行しようとする命令に対応するコードを生成します。emit_*というメソッドはjitcode.hに定義されていてほとんどはそれを呼び出すだけです。
例外は、OP_ENTERとOP_RETURNでそれぞれ次のようになっています。
case OP_ENTER:
mrb->compile_info.nest_level++;
return code->emit_enter(mrb, status);
case OP_RETURN:
mrb->compile_info.nest_level--;
if (mrb->compile_info.nest_level < 0) {
return code->emit_return(mrb, status);
}
else {
return code->emit_return_inline(mrb, status);
}
OP_ENTERはメソッドの先頭、OP_RETURNはメソッドの最後の処理です。mrb->compile_info.nest_levelは現在のメソッドの呼び出しのネストレベルを表します。このレベルはネイティブコードで実行する場合で途中でVMに戻る場合は0に戻されます。また、メソッドがインライン化しない場合も0戻されます。つまり、OP_RETURNの時点でnest_levelが1以上の場合はOP_ENTERからOP_RETURNまでVMに戻らず、しかもインライン化するメソッドということになります。そういう場合は特別扱いして高速なコードを生成します。特別扱いするコード生成のメソッドが、emit_return_inlineです。
default:
mrb->compile_info.nest_level = 0;
return NULL;
}
普通この手のdefaultはnot reachedでエラーチェックのためにあるって感じですが、現時点のmrubyのJITは普通に実行されます。サポートされていない命令はVMのJITで実行されます。ちなみに、emit_*においてもNULLを返すとその命令はVMで実行されます。例えば、OP_SENDで可変引数とかコード生成が超面倒な割には使用頻度が少ないのでNULLを返してVMで実行してもらっています。
そういうことで、今回は終わり
gdgdですがまあ仕方がない。次回はあるのか???
多分続く
- 50 http://t.co/HBHIkrinZa
- 22 https://www.google.co.jp/
- 8 http://www.rubyist.net/~kazu/samidare/
- 7 http://t.co/TNu3RQ0aq4
- 6 http://www.google.co.jp/url?sa=t&rct=j&q=jemallocを読んでみる&source=web&cd=1&cad=rja&ved=0CDAQFjAA&url=http://d.hatena.ne.jp/miura1729/20080328/1206702996&ei=8Mk6UYnZOM_VkAXw2IHYAQ&usg=AFQjCNH6yOHiptD
- 5 http://reader.livedoor.com/reader/
- 5 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&ved=0CD0QFjAC&url=http://d.hatena.ne.jp/miura1729/20080610/1213091022&ei=Ze41UYbJO8fvlAWVx4H4Dg&usg=AFQjCNE05QRueqNDOoUk7mTb70YSXdeW2Q&bvm=bv.43148975,d.dGI
- 5 http://www.google.co.jp/url?sa=t&rct=j&q=rfc1213&source=web&cd=2&sqi=2&ved=0CDUQFjAB&url=http://d.hatena.ne.jp/miura1729/20080610/1213091022&ei=dqc2UbSzDc6YkgXDsoGwBA&usg=AFQjCNE05QRueqNDOoUk7mTb70YSXdeW2Q&bvm=bv.43148975,bs.1,d.dGI
- 4 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0CDgQFjAB&url=http://d.hatena.ne.jp/miura1729/20080610/1213091022&ei=rXk1UbLJKI7RlAW-7oGoAw&usg=AFQjCNE05QRueqNDOoUk7mTb70YSXdeW2Q&bvm=bv.43148975,d.dGI
- 3 http://www.google.co.jp/