Scm_VMCallCC (18)

捕捉した継続を呼び出した時にどうなるかを確認。
イメージ的には

gosh> (define cont #f)                 
cont
gosh> (list 1 2 (call/cc (lambda (c) (set! cont c) 3)) 4)
(1 2 3 4)
gosh> (cont -1)
(1 2 -1 4)
gosh> 

の (cont -1) の時のソレ。disasm したナニが以下。

gosh> (disasm (lambda () (cont -1)))
main_code (name=#f, code=0x80d67c0, size=4, const=1, stack=4):
args: #f
     0 CONSTI-PUSH(-1) 
     1 GREF-TAIL-CALL(1) #<identifier user#cont>; (cont -1)
     3 RET 
#<undef>
gosh> 

結局呼び出されるのは以下の式で作られた subr です。

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

これが以下な命令列でナニ。

                    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;

実質呼び出されるのは throw_continuation 関数。引数フレームなポインタを渡している模様。VAL0 が指す領域 (ScmSubr 型なナニ) の data 属性 (ScmEscapePoint 型の領域のポインタ) も渡されています。

端折りつつどんどん進める

handlers 云々はスルー。基本的に

  • PC に PC_TO_RETURN がセット
    • RET です
    • ので RETURN_OP にて POP_CONT されます。
  • CONT には ep の cont 属性がセット
    • 上記の通り pop されます
  • handlers も復帰

で、面白いのが最後の処理。

    nargs = Scm_Length(args);
    if (nargs == 1) {
        return SCM_CAR(args);
    } else if (nargs < 1) {
        return SCM_UNDEFINED;
    } else if (nargs >= SCM_VM_MAX_VALUES) {
        Scm_Error("too many values passed to the continuation");
    }

    for (i=0, ap=SCM_CDR(args); SCM_PAIRP(ap); i++, ap=SCM_CDR(ap)) {
        vm->vals[i] = SCM_CAR(ap);
    }
    vm->numVals = nargs;
    return SCM_CAR(args);

これって直前エントリな多値のナニなフレーバがあったりする気が。確かに何回かこれって見てるんですが、若干不思議な印象があったんだよなぁ、と。

そりゃええんですが

引数が return されてます。ちょっと微妙に道に迷ってるカンジです。

あら?

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

なナニであれば復帰は 8?
そうだとしたら val0 なソレを push して続き、になりますな。継続が push されるのは 2 な時点で次の PC は 8 ってコトなのか。

なんと言えばよいやら

上記ビンゴだとしたらこれはこれで凄いな。2, 4, 6, 8 なあたり。