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)
が戻りますな。むむむ。
ちょっとスッキリした所で呑み会に出かけます。