Scm_VMCallCC (9)

なんか昨晩のエントリはへろへろ杉で最後らへんに書いてるソレが微妙。多分ポインタなんですな、という事が言いたかったのではないかと。

ScmContFrame 型

base という属性はVMのスタック操作な文書の図には出てきてないので新たに追加された属性らしい。

    ScmCompiledCode *base;        /* base register value */

ちょっとだけメモっておくと closure な CALL の処理にて

                    vm->base = SCM_COMPILED_CODE(SCM_CLOSURE(VAL0)->code);
                    PC = vm->base->code;

みたいな事をしている。

_引数フレームの頭を積む_というナニは順番が変更になっている模様。以下のナニで言うと

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> 

順に push してって GREF_CALL から CALL の中に jmp して fold が closure なので以下なソレが適用されるはず。

                if (proctype == SCM_PROC_CLOSURE) {
                    ADJUST_ARGUMENT_FRAME(VAL0, argc);
                    if (argc) {
                        FINISH_ENV(SCM_PROCEDURE_INFO(VAL0),
                                   SCM_CLOSURE(VAL0)->env);
                    } else {
                        ENV = SCM_CLOSURE(VAL0)->env;
                        ARGP = SP;
                    }
                    vm->base = SCM_COMPILED_CODE(SCM_CLOSURE(VAL0)->code);
                    PC = vm->base->code;
                    CHECK_STACK(vm->base->maxstack);
                    SCM_PROF_COUNT_CALL(vm, SCM_OBJ(vm->base));
                    NEXT;
                }

上記、argc が non zero な場合に FINISH_ENV マクロが記述されてますが、定義が以下。(なんども引用してる気がしますがスルーで)

/* push environment header to finish the environment frame.
   env, sp, argp is updated. */
#define FINISH_ENV(info_, up_)                  \
    do {                                        \
        ScmEnvFrame *e__ = (ScmEnvFrame*)SP;    \
        e__->up = up_;                          \
        e__->info = info_;                      \
        e__->size = SP - ARGP;                  \
        SP += ENV_HDR_SIZE;                     \
        ARGP = SP;                              \
        ENV = e__;                              \
    } while (0)

ええと gauche/vm.h にて ScmEnvFrame 型が定義されてますが、その部分を以下に。

/*
 * Environment frame
 *
 *   :        :
 *   +--------+
 *   | size=N |
 *   |  info  |
 *   |...up...|<--- ScmEnvFrame* envp
 *   |arg[N-1]|
 *   |arg[N-2]|
 *   :        :
 *   | arg[0] |
 *   +--------+
 *   :        :
 */

typedef struct ScmEnvFrameRec {
    struct ScmEnvFrameRec *up;  /* static link */
    ScmObj info;                /* reserved */
    ScmWord size;               /* size of the frame (excluding header) */
} ScmEnvFrame;

#define ENV_HDR_SIZE   3        /* envframe header size */

属性は全部 4bytes なんで SIZE が 3 という事で。FINISH_ENV 後のナニは以下?

 *   :        :<--- SP, ARGP
 *   +--------+
 *   | size=N |
 *   |  info  |
 *   |...up...|<--- ENV
 *   |arg[N-1]|
 *   |arg[N-2]|
 *   :        :
 *   | arg[0] |
 *   +--------+
 *   :        :

FINISH_ENV する直前を類推。PUSH_CONT した直後は

 *   :        :<--- SP
 *   +--------+
 *   |arg[N-1]|
 *   |arg[N-2]|
 *   :        :
 *   | arg[0] |<---ARGP
 *   +--------+
 *   :        :

なはず。ARGP って微妙なカンジ ...
これ、参照はどうなんだ、と言いつつ LREF 系のナニを確認。

            CASE(SCM_VM_LREF0)  { VAL0 = ENV_DATA(ENV, 0); NEXT1; }
            CASE(SCM_VM_LREF1)  { VAL0 = ENV_DATA(ENV, 1); NEXT1; }
            CASE(SCM_VM_LREF2)  { VAL0 = ENV_DATA(ENV, 2); NEXT1; }

ENV_DATA の定義が以下。

#define ENV_DATA(env, num) (*(((ScmObj*)(env))-(num)-1))

これって最後の引数は LREF0 で参照ってコトかな、と言いつつ確認。

gosh> (disasm (lambda (a b c) (+ a b c)))
main_code (name=#f, code=0x80f6c40, size=7, const=0, stack=1):
args: #f
     0 LREF2-PUSH               ; a
     1 LREF1                    ; b
     2 NUMADD2                  ; (+ a b c)
     3 PUSH 
     4 LREF0                    ; c
     5 NUMADD2                  ; (+ a b c)
     6 RET 
#<undef>
gosh>

一応ビンゴな模様。引数ゼロなケースも確認しとく必要あり、とは言えここではスルーします。

もう少し

引数フレームの up な属性って何だっけ? と言いつつ色々見てたら引数フレームは環境じゃんか、という事に遅ればせながら気づいた次第で。(とほほ
いっちゃん最後に_引数フレームの up リンクにクロージャの環境へのリンクをセット_とありますな。確かに FINISH_ENV でもそうしてる、というか CALL なナニで以下なコトをしております。

                    if (argc) {
                        FINISH_ENV(SCM_PROCEDURE_INFO(VAL0),
                                   SCM_CLOSURE(VAL0)->env);
                    } else {
                        ENV = SCM_CLOSURE(VAL0)->env;
                        ARGP = SP;
                    }

これって closure が作られたトキの_環境_なんだっけ...
でも良く考えたら closure が外の環境なソレを参照ってどうなんでしょ、と言いつつ

gosh> (define a 1)
a
gosh> (define b (lambda (x) (+ x a)))
b
gosh> (b 1)
2
gosh> (define c (let ((a 3)) (b a)))
c
gosh> c
4
gosh> (let ((a (+ a 1))) (b a))
3
gosh> 

なんとなく成程。(を
良く考えたらこれって SICP で嫌っていうほど出てきた気がしてきた。

やれやれ

継続なナニで save される継続云々なソレに到達するまでどの位かかるんでしょ。体力不足を感じつつ、今週も待機なんで頑張ってみる方向。
あと今回のスルーが以下

  • クロージャが呼び出されて引数ゼロな場合のスタックの様子

ええと明日以降は末尾呼び出しなソレでしょうか。