Scm_VMCallCC (11)

VMのスタック操作の_環境のセーブ_の項。以下の記述あり。

lambda フォームによりクロージャが作成されると、その時点で環境フレームのリストを、env ポインタから順に手繰ってヒープにコピーする。

CLOSURE 認定なソレからそんな処理は見えんがのぅ、と言いつつ以下をよく見ると

            CASE(SCM_VM_CLOSURE) {
                ScmObj body;
                FETCH_OPERAND(body);
                INCR_PC;

                /* preserve environment */
                VAL0 = Scm_MakeClosure(body, get_env(vm));
                NEXT1;
            }

get_env な手続きがナニ。

static ScmEnvFrame *get_env(ScmVM *vm)
{
    ScmEnvFrame *e;
    ScmContFrame *c;
    
    e = save_env(vm, vm->env);
    if (e != vm->env) {
        vm->env = e;
        for (c = vm->cont; IN_STACK_P((ScmObj*)c); c = c->prev) {
            if (FORWARDED_ENV_P(c->env)) {
                c->env = FORWARDED_ENV(c->env);
            }
        }
    }
    return e;
}

この save_env って save_cont からも呼び出されている事を確認済み。ってか確かに lambda な手続きオブジェクトが作られる瞬間の環境は保存されてないと駄目ですね。上記の CLOSURE な分岐で言えば val0 に格納される Scm_MakeClosure な手続きの戻り (手続きオブジェクト) にはそれが内包されてないとマズいのは分かります。
ただ、

gosh> (disasm (lambda () (call/cc (lambda (c) (set! cont c) 5))))
main_code (name=#f, code=0x80d71f8, size=5, const=2, stack=4):
args: #f
     0 CLOSURE #<lambda 0>      ; (lambda (c) (set! cont c) 5)
     2 PUSH-GREF-TAIL-CALL(1) #<identifier user#call/cc>; (call/cc (lambda (c) (set! cont c) 5))
     4 RET 
internal_closure_0 (name=#f, code=0x80d7210, size=5, const=1 stack=0):
args: #f
     0 LREF0                    ; c
     1 GSET #<identifier user#cont>; cont
     3 CONSTI(5) 
     4 RET 
#<undef>
gosh> 

な例で言えば

  • 0 な CLOSURE で環境 save
  • 2 な call/cc でも環境 save

というナニもですがVMのスタック操作の_環境のセーブ_の項にもあるように最適化に課題がある模様。save_env が書いてある vm.c の該当箇所にも_Performance note_という事にて_I'm not sure I can optimize this routine further without a radical change in stack management code._なコメントがあります。

それは良いとして

save_env 手続きの中身なんですが、ちょっと脳がリハビリできてない感じ。次とか前とかそのあたり。意識がマトモな内に業務な_ほうれんそう_なナニの必要があるんで一旦ここで出力しますが、業務対応のあとに余裕があればもう少し見て何かを output するかも。