Scm_VMCallCC (7)

VMのスタック操作を色々見てます。disasm でインストラクションをナニしてみる方向で色々試すも、良い例に行きあたらず。
subr は微妙だったんで closure な fold を使ってみたんですが、偶然ハマッた感じ。でも最初こんなコトしてて

gosh> (disasm (lambda () (fold + 0 '(1 2 3))))
main_code (name=#f, code=0x80f6ea0, size=8, const=3, stack=6):
args: #f
     0 GREF-PUSH #<identifier user#+>; +
     2 CONSTI-PUSH(0) 
     3 CONST-PUSH (1 2 3)
     5 GREF-TAIL-CALL(3) #<identifier user#fold>; (fold + 0 '(1 2 3))
     7 RET 
#<undef>
gosh>

これって_通常の関数呼び出し_にはあたらんな、と言いつつ以下。

gosh> (disasm (lambda () (fold + 0 '(1 2 3)) #t))
main_code (name=#f, code=0x80f7f30, size=11, const=3, stack=12):
args: #f
     0 PRE-CALL(3) 9
     2 GREF-PUSH #<identifier user#+>; +
     4 CONSTI-PUSH(0) 
     5 CONST-PUSH (1 2 3)
     7 GREF-CALL(3) #<identifier user#fold>; (fold + 0 '(1 2 3))
     9 CONST-RET #t
#<undef>
gosh> 

末尾呼び出しとそうでないナニで結構違います。差分は以下。

  • 末尾呼び出しの場合、PRE-CALL しない
    • 言い換えれば PUSH_CONT しない
    • VMのスタック操作でも_コンティニュエーションを積まずに、直接引数フレームを積む_とある
  • GREF-TAIL-CALL と GREF-CALL の違い
    • (クロージャ限定ですが) GREF-CALL では FINISH_ENV マクロで環境フレームを締めくくる
    • よく見たら TAIL-CALL と CALL の差分って以下? (若干乱暴気味
              tail_call_entry:
                argc = SP - ARGP;

                if (IN_STACK_P((ScmObj*)CONT)) {
                    to = CONT_FRAME_END(CONT);
                } else {
                    /* continuation has been saved, which means the
                       stack has no longer useful information. */
                    to = vm->stackBase;
                }
                if (argc) {
                    ScmObj *t = to, *a = ARGP;
                    int c;
                    /* The destintation and the source may overlap, but
                       in such case the destination is always lower than
                       the source, so we can safely use incremental copy. */
                    for (c=0; c<argc; c++) *t++ = *a++;
                }
                ARGP = to;
                SP = to + argc;
                /* We discarded the current env, so make sure we don't have
                   a dangling env pointer. */
                ENV = NULL;
            }

個別に確認

先に_通常の_の方から。手順としてはざっくり

  • PUSH_CONT する
  • 引数を前側から順に push
  • GREF-CALL
  • #t を戻す

というカンジ。

うーん

ちょっと微妙なトコなんですが、続きは明日で。