朝イチのエントリ
正にナグリ書き。
にも関わらず shiro さんからフォロー。なんとゆーか勿体無い。
で
Gauche:VMの最適化 なドキュメント見てみたら、今の実装は run_loop() 手続きで自動生成されたコードを #include している模様。比叡山の延暦寺 (何
とりあえず基本的な思想は変わってない事を信じつつ、リトライ。
一般的な例として
朝に例示した以下のナニをサンプルにしてみます。
gosh> (disasm (lambda (x y) (x 1 2) (y 3 4))) main_code (name=#f, code=0x80f7ed0, size=11, const=0, stack=11): args: #f 0 PRE-CALL(2) 6 2 CONSTI-PUSH(1) 3 CONSTI-PUSH(2) 4 LREF1 ; x 5 CALL(2) ; (x 1 2) 6 CONSTI-PUSH(3) 7 CONSTI-PUSH(4) 8 LREF0 ; y 9 TAIL-CALL(2) ; (y 3 4) 10 RET #<undef> gosh>
PRE_CALL なインストラクションは vm.c にて以下。
CASE(SCM_VM_PRE_CALL) { ScmWord *next; CHECK_STACK_PARANOIA(CONT_FRAME_SIZE); FETCH_LOCATION(next); PUSH_CONT(next); INCR_PC; NEXT; }
これ、微妙ですね。インストラクションに付いてる引数っぽいソレがナニ。いくつか試験。
gosh> (disasm (lambda (x y z) (x 1) (y 2 3) (z 4 5 6))) main_code (name=#f, code=0x814efa0, size=17, const=0, stack=11): args: #f 0 PRE-CALL(1) 5 2 CONSTI-PUSH(1) 3 LREF2 ; x 4 CALL(1) ; (x 1) 5 PRE-CALL(2) 11 7 CONSTI-PUSH(2) 8 CONSTI-PUSH(3) 9 LREF1 ; y 10 CALL(2) ; (y 2 3) 11 CONSTI-PUSH(4) 12 CONSTI-PUSH(5) 13 CONSTI-PUSH(6) 14 LREF0 ; z 15 TAIL-CALL(3) ; (z 4 5 6) 16 RET #<undef> gosh> (disasm (lambda (x y z) (x 1) (y 2) (z 3))) main_code (name=#f, code=0x8149f50, size=14, const=0, stack=10): args: #f 0 PRE-CALL(1) 5 2 CONSTI-PUSH(1) 3 LREF2 ; x 4 CALL(1) ; (x 1) 5 PRE-CALL(1) 10 7 CONSTI-PUSH(2) 8 LREF1 ; y 9 CALL(1) ; (y 2) 10 CONSTI-PUSH(3) 11 LREF0 ; z 12 TAIL-CALL(1) ; (z 3) 13 RET #<undef> gosh>
あるいはこんなのとか。
gosh> (disasm (lambda (x y z) (x (y 1 2 3) (z 4)))) main_code (name=#f, code=0x813ef40, size=16, const=0, stack=13): args: #f 0 PRE-CALL(3) 7 2 CONSTI-PUSH(1) 3 CONSTI-PUSH(2) 4 CONSTI-PUSH(3) 5 LREF1 ; y 6 CALL(3) ; (y 1 2 3) 7 PUSH-PRE-CALL(1) 12 9 CONSTI-PUSH(4) 10 LREF0 ; z 11 CALL(1) ; (z 4) 12 PUSH 13 LREF2 ; x 14 TAIL-CALL(2) ; (x (y 1 2 3) (z 4)) 15 RET #<undef> gosh>
どうも PRE-CALL に渡されるソレは引数の数みたく見える。それを元に継続なインストラクションの位置を勘案してる、んスかねぇ。
あるいは PRE-CALL なインストラクションでは渡される引数を完全スルーしているように見えるのも微妙。これって compile.scm 見れ、って世界なのでしょうか。
と言いつつ
以前のエントリを compile.scm で検索してみたら出てきた。やはり compile.scm を確認しないと根拠にはならぬ、と言いつつ以下。
全然記憶にないのですが、上記エントリにて CALL なインストラクションの確認をしています。ポインタだけでは微妙なので上記エントリにて申し述べてるソレを以下に。
例えば以下のケイスだと
gosh> (disasm (lambda (x y z) (x 1) (y 2) (z 3))) main_code (name=#f, code=0x8149f50, size=14, const=0, stack=10): args: #f 0 PRE-CALL(1) 5 2 CONSTI-PUSH(1) 3 LREF2 ; x 4 CALL(1) ; (x 1) 5 PRE-CALL(1) 10 7 CONSTI-PUSH(2) 8 LREF1 ; y 9 CALL(1) ; (y 2) 10 CONSTI-PUSH(3) 11 LREF0 ; z 12 TAIL-CALL(1) ; (z 3) 13 RET #<undef> gosh>
- 0 の PRE-CALL(1)
- 5 の PRE-CALL(1) ;; PUSH_CONT されて INCR_PC されるので評価されない
- 2 の CONSTI-PUSH(1)
- 3 の LREF2
- 4 の CALL
- 5 の PRE-CALL(1) ;; 継続フレームから取り出されて評価
- 10 の CONSTI-PUSH(3) ;; PUSH_CONT されて INCR_PC されるので評価されない
- 7 の CONSTI-PUSH(2)
- 8 の LREF1
- 9 の CALL(1)
- 10 の CONSTI-PUSH(3) ;; 継続フレームから取り出されて評価
- 11 の LREF0
- 12 の TAIL-CALL(1)
- 13 の RET
上記、抜け番が埋まっているのがポイント、と件のエントリに記述あり。自分で色々確認したコト自体を忘れてるあたり、微妙と言わざるを得ません。
む
これって ReadingGauche にフィードバックした方が良い? って根拠はないですね。やはり compile.scm ですか。
話を元に
戻します。再度引用しますが、PRE_CALL なインストラクションは vm.c にて以下。
CASE(SCM_VM_PRE_CALL) { ScmWord *next; CHECK_STACK_PARANOIA(CONT_FRAME_SIZE); FETCH_LOCATION(next); PUSH_CONT(next); INCR_PC; NEXT; }
ここでは next なインストラクションは push すべきものになっております (いるはず)。push した後に無条件で ++ して次のインストラクションに、という形。
次のインストラクションは CONSTI-PUSH(1) ですがコードが以下。
CASE(SCM_VM_CONSTI_PUSH) { long imm = SCM_VM_INSN_ARG(code); PUSH_ARG(SCM_MAKE_INT(imm)); NEXT; }
引数げとして int に直して push して次。継続フレームを push した直後に引数が push されております。shiro さんからフォロー頂いたのは正にこのあたりのはず。
例示しているインストラクションも再掲しとくと以下にて
gosh> (disasm (lambda (x y) (x 1 2) (y 3 4))) main_code (name=#f, code=0x80f7ed0, size=11, const=0, stack=11): args: #f 0 PRE-CALL(2) 6 2 CONSTI-PUSH(1) 3 CONSTI-PUSH(2) 4 LREF1 ; x 5 CALL(2) ; (x 1 2) 6 CONSTI-PUSH(3) 7 CONSTI-PUSH(4) 8 LREF0 ; y 9 TAIL-CALL(2) ; (y 3 4) 10 RET #<undef> gosh>
3 で push して 4 で x を取り出して val0 に格納した後に CALL する形。で、CALL の中で (クロージャ前提で) スタックの後始末をしているのが以下なはず。
if (argc) { FINISH_ENV(SCM_PROCEDURE_INFO(VAL0), SCM_CLOSURE(VAL0)->env); } else { ENV = SCM_CLOSURE(VAL0)->env; ARGP = SP; }
多々微妙な部分がございますので、そのあたりは別途。