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/