Scm_VMCallCC (2)

の前に以下の一連のナニを復 (予) 習。

     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

以下にポイント列挙

PRE-CALL

4 にあたる PC に上記であれば 7 の call/cc から RET (あるいは 5 な CLOSURE からの RET?) する時の戻りである 9 なソレが格納されてて、PRE-CALL の中の PUSH_CONT で ScmContFrame 型の pc 属性にセットされてスタックに push される。
PUSH_CONT マクロは以下なカンジ

/* Push a continuation frame.  next_pc is the PC from where execution
   will be resumed.  */
#define PUSH_CONT(next_pc)                              \
    do {                                                \
        ScmContFrame *newcont = (ScmContFrame*)SP;      \
        newcont->prev = CONT;                           \
        newcont->env = ENV;                             \
        newcont->argp = ARGP;                           \
        newcont->size = SP - ARGP;                      \
        newcont->pc = next_pc;                          \
        newcont->base = BASE;                           \
        CONT = newcont;                                 \
        SP += CONT_FRAME_SIZE;                          \
        ARGP = SP;                                      \
    } while (0)

定義は vm.c の中。ちなみに ScmContFrame 型は gauche/vm.h で定義されてて以下。

typedef struct ScmContFrameRec {
    struct ScmContFrameRec *prev; /* previous frame */
    ScmEnvFrame *env;             /* saved environment */
    ScmObj *argp;                 /* saved argument pointer */
    int size;                     /* size of argument frame */
    SCM_PCTYPE pc;                /* next PC */
    ScmCompiledCode *base;        /* base register value */
} ScmContFrame;

CLOSURE

ここでも 6 にあたる次のナニに ScmObj なコンパイル済みの lambda 手続きなオブジェクトのポインタが格納されていると思われる。それを取り出して Scm_MakeClosure 手続きな戻りを val0 に格納。

PUSH-GREF-CALL

  • val0 を push
  • 8 にあたる次のナニから束縛を解決して val0 に
  • CALL する
    • ここで CALL される call/cc の中身がどうなっているか、がある意味今のテーマ

PUSH

  • 戻りな val0 をスタックに push

CALL?

む。とりあえず call/cc は subr なナニなので

gosh> call/cc
#<subr call-with-current-continuation>
gosh> 

CALL の以下な分岐を辿るはず。

                /*
                 * We process the common cases first
                 */
                proctype = SCM_PROCEDURE_TYPE(VAL0);
                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;
                }

これって func の呼び出し部分が正にソレ、なのでしょうか ...

SCM_SUBR

ScmSubr 型 (ScmSubrRec 構造体) な定義が以下 (gauche.h)

/* Subr - C defined procedure */
struct ScmSubrRec {
    ScmProcedure common;
    ScmSubrProc *func;
    void *data;
};

ええと、ScmSubrProc 型の定義は gauche.h で以下。

typedef ScmObj ScmSubrProc(ScmObj *, int, void*);

むむ。call/cc だとどうなってるか、ってどうやって調べる?
# それが stdlib.{c, stub} な気もするな ...

あった

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));
}
  }
}

static SCM_DEFINE_SUBR(stdlib_call_with_current_continuation__STUB, 1, 0, SCM_OBJ(&scm__sc.d633[149]), stdlib_call_with_current_continuation, NULL, NULL);

自動生成ちっくなナニですな。引数とか戻りが一緒なのでこれが func 属性にセットされている、という事にしときます。
あ、下の SCM_DEFINE_SUBR で実体作ってるのかな? そゆ事にしときます。(を

一応

紐がついたカンジなんで割り込み受け付け。続きは別途で。