Scm_VMDynamicWind (2)
POP_CONT の動作がなんとなくイメージできた。
昨晩なエントリの焼き直しになりますが、ご容赦下さい。まず押さえておく必要があるのが昨晩エントリな記述で言えば Scm_VMDynamicWind() を呼び出す部分な_PC に RET を仕込んで val0 な関数 (stdlib_dynamic_wind) 呼び出して戻りを val0 格納_になる。
とりあえず Scm_VMDynamicWind()
Scm_VMPushCC() を呼び出して dynwind_before_cc を_次の RET で呼んでね_なおまじない。
Scm_VMPushCC(dynwind_before_cc, data, 3);
新たに積む継続フレームの pc 属性に引数で渡される dynwind_before_cc を設定。他はざっくり以下なカンジ。
s = SP; cc = (ScmContFrame*)s; s += CONT_FRAME_SIZE; cc->prev = CONT; cc->argp = NULL; cc->size = datasize; cc->pc = (ScmWord*)after; cc->base = BASE; cc->env = ENV; for (i=0; i<datasize; i++) { *s++ = SCM_OBJ(data[i]); } CONT = cc; ARGP = SP = s;
継続フレームの上に引数が積まれてます。で、以下。
return Scm_VMApply0(before);
これが Scm_VMDynamicWind() の戻りになります。戻った時点で PC が
TAIL-CALL RET
なナニを指してて val0 には before なポインタが格納されてます。ので before が呼ばれて云々、って引数とかどうなるんだと思ったら dynamic-wind に渡される手続きは全部 thunk な模様。そのまま続けます。
before が呼ばれて
戻って RET がナニ。基本的に継続フレームがあればそれを pop するので先で push した継続フレームが復活。pop されるのは C continuation (Scm_VMPushCC() 手続きでは継続フレームの argp 属性に NULL がセット) なので以下が適用される。
if (CONT->argp == NULL) { \ void *data__[SCM_CCONT_DATA_SIZE]; \ ScmCContinuationProc *after__; \ void **d__ = data__; \ void **s__ = (void**)((ScmObj*)CONT + CONT_FRAME_SIZE); \ int i__ = CONT->size; \ while (i__-- > 0) { \ *d__++ = *s__++; \ } \ after__ = (ScmCContinuationProc*)CONT->pc; \ if (IN_STACK_P((ScmObj*)CONT)) SP = (ScmObj*)CONT; \ ENV = CONT->env; \ ARGP = SP; \ PC = PC_TO_RETURN; \ CONT = CONT->prev; \ BASE = CONT->base; \ VAL0 = CALL_CCONT(after__, VAL0, data__); \ } else if (IN_STACK_P((ScmObj*)CONT)) { \
当初、CALL_CCONT というマクロは一体? というあたりにヒッカカッてたのですが、shiro さんのコメントを見た後に上記をニラんでて気がついたのが
- CALL_CCONT の定義は
#define CALL_CCONT(p, v, d) p(v, d)
-
- てコトは CALL_CCONT(after__, VAL0, data__) は after__(VAL0, data__) じゃん
- after__ の型は ScmCContinuationProc なポインタになってますな
- 定義は以下
typedef ScmObj ScmCContinuationProc(ScmObj result, void **data);
-
- これって例えば dynwind_before_cc なソレと合致
という事は before が呼ばれて RET する時点で dynwind_before_cc が引数とかも復帰された状態で呼びだされて、その中で今度は body が (ry
という流れになる訳か。このあたりは正に shiro さんがフォローしてくれていた部分ですね。ざくっと Scm_VMDynamicWind() 以降の流れを整理してみると
- Scm_VMDynamicWind()
- before, body, after を引数で dynamic_wind_cc を呼んでね、と継続フレームに push
- before な thunk 呼んでね、と vm にお願い
- before な処理が終わったら継続フレームから dynamic_wind_cc を取り出して呼び出し
- dynwind_before_cc()
- vm->handlers に (before after) なリストを push
- after と push する前の vm->handlers を引数で dynamic_body_cc を呼んでね、と継続フレームに push
- body な thunk 呼んでね、と vm にお願い
- body な処理が終わったら継続フレームから dynamic_body_cc を取り出して呼び出し
- dynwind_body_cc()
- push する前の vm->handlers に復帰
- val0 (result で渡される) と vm->numVals と多値な戻りを引数で dynwind_after_cc を呼んでね、と継続フレームに push
- after な thunk 呼んでね、と vm にお願い
- after な処理が終わったら継続フレームから dynwind_after_cc を取り出して呼び出し
- dynwind_after_cc()
- 戻りを設定して return
このあたりのイメージが
ある程度適切だとすると、Scm_VMCallCC() の掘削精度が上がったりなんかするのかな。余裕があればそちらに戻って掘削予定。
それにしても
shiro さんからのフォローに感謝です。