Scm_VMCallCC (16)

昨晩の続き。ぢつは 2200 から呑み会らしいのでそれまでの時間限定です。ちなみに現在 2030 です。

昨晩の継続を補足

以下なソレが題材。

gosh> (disasm (lambda () (list 1 2 3 (call/cc (lambda (c) (set! cont c) 4)) 5 6)))
main_code (name=#f, code=0x8117ea8, size=14, const=2, stack=13):
args: #f
     0 CONSTI-PUSH(1) 
     1 CONSTI-PUSH(2) 
     2 CONSTI-PUSH(3) 
     3 PRE-CALL(1) 9
     5 CLOSURE #<lambda 0>      ; (lambda (c) (set! cont c) 4)
     7 PUSH-GREF-CALL(1) #<identifier user#call/cc>; (call/cc (lambda (c) (set! cont c) 4))
     9 PUSH 
    10 CONSTI-PUSH(5) 
    11 CONSTI(6) 
    12 LIST(6)                  ; (list 1 2 3 (call/cc (lambda (c) (set! c ...
    13 RET 
internal_closure_0 (name=#f, code=0x80d7b88, size=5, const=1 stack=0):
args: #f
     0 LREF0                    ; c
     1 GSET #<identifier user#cont>; cont
     3 CONSTI(4) 
     4 RET 
#<undef>
gosh> 

上記 7 番の途中です。call_entry: なラベルが breakpoint で停止してる状態。ちなみに stack の状態が以下。

     sp>|        |
   argp>|てつづき| ;; lambda な手続きオブジェクト
        +--------+
        |        | ;; base
        |        | ;; pc   next-pc は CLOSURE?
        |   3    | ;; size 本当かどうか微妙
        |   *3   | ;; argp
        |   *2   | ;; env
   cont>|   *1   | ;; prev
        +--------+
        |   3    |
        |   2    |
     *3>|   1    |
        +--------+
        |        |
*2(env)>|  ENV   |
        +--------+
        |        |
     *1>|  CONT  |
        +--------+

argc に SP - ARGP な値が格納される、という事は 1 ですか。確かに引数の数は一つですね。call/cc は subr なので以下な命令列が実行されるはず。

                if (proctype == SCM_PROC_SUBR) {
                    /* We don't need to complete environment frame.
                       Just need to adjust sp, so that stack-operating
                       procs called from subr won't be confused. */
                    ADJUST_ARGUMENT_FRAME(VAL0, argc);
                    SP = ARGP;
                    PC = PC_TO_RETURN;

                    SCM_PROF_COUNT_CALL(vm, VAL0);
                    VAL0 = SCM_SUBR(VAL0)->func(ARGP, argc,
                                                SCM_SUBR(VAL0)->data);
                    /* the subr may substituted pc, so we need to check
                       if we can pop the continuation immediately. */
                    if (TAIL_POS()) RETURN_OP();
                    NEXT;
                }

SP に ARGP の値が代入されて subr な本体が呼び出されています。がしかし、Scm_VMCallCC の定義は以下だったはずですが

ScmObj Scm_VMCallCC(ScmObj proc)
{

VAL0 に格納されている subr オブジェクトの func 属性に格納されている手続きは以下のはずです。stdlib.c にて定義。

static ScmObj stdlib_call_with_current_continuation(ScmObj *SCM_FP, int SCM_ARGCNT, void *data_)
{
  ScmObj proc_scm;
  ScmObj proc;
  SCM_ENTER_SUBR("call-with-current-continuation");
  proc_scm = SCM_ARGREF(0);
  proc = (proc_scm);
  {
{
ScmObj SCM_RESULT;
SCM_RESULT = Scm_VMCallCC(proc);
SCM_RETURN(SCM_OBJ_SAFE(SCM_RESULT));
}
  }
}

これは stdlib.stub から生成されたナニ。ARGP が指す領域に格納されたナニを Scm_VMCallCC 手続きに渡している。この手続きに定義されている命令列が以下。

    ScmObj contproc;
    ScmEscapePoint *ep;
    ScmVM *vm = theVM;

    save_cont(vm);
    ep = SCM_NEW(ScmEscapePoint);
    ep->prev = NULL;
    ep->ehandler = SCM_FALSE;
    ep->cont = vm->cont;
    ep->handlers = vm->handlers;
    ep->cstack = vm->cstack;

    contproc = Scm_MakeSubr(throw_continuation, ep, 0, 1,
                            SCM_MAKE_STR("continuation"));
    return Scm_VMApply1(proc, contproc);

なんとなくざっくり上手く動きそうなカンジですがメモが取れぬ。明日になったら忘れてる可能性大。一応 ReadingGauche 方面に feedback は可能になったのでしょうか。
ちょっとだけメモを以下に箇条書き。

  • save_cont 手続きで stack にある継続フレームと環境フレームが heap に退避
    • vm->cont も heap を指す形になってるはず
  • contproc に格納される手続きオブジェクトは退避された継続フレームへのポインタを属性として持つ ScmEscapePoint なポインタを属性として持つ
  • 最後に引数で渡された lambda な手続きに上記の継続な手続きオブジェクトを渡して apply
  • 継続って退避された継続オブジェクトを復帰して RET で POP_CONT する
    • ここはもう少し確認必要?

というあたり。でも POP_CONT を見ると確かに

gosh> (list 1 2 3 (call/cc (lambda (c) (set! cont c) 4)) 5 6)

で捕捉された継続に -1 を渡したら

(1 2 3 -1 5 6)

が戻りますな。むむむ。
ちょっとスッキリした所で呑み会に出かけます。