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; }
-
- 上記はVMのスタック操作の末尾呼び出しの項の 4. にある記述な処理かな ...
個別に確認
先に_通常の_の方から。手順としてはざっくり
- PUSH_CONT する
- 引数を前側から順に push
- GREF-CALL
- #t を戻す
というカンジ。
うーん
ちょっと微妙なトコなんですが、続きは明日で。