色々確認

PC とか NEXT とか RET とかその他。

PC

vm.c にて以下の定義。

#define PC    (vm->pc)

ええと vm は ScmVMRec 構造体のオブジェクトを指すポインタと勝手に決め。メンバの定義は以下なカンジ。

    SCM_PCTYPE pc;              /* Program pointer.  Points into the code
                                   vector. (base->code) */

何を指しているのかは若干微妙。

#define SCM_PCTYPE ScmWord*

でも、INCR_PC マクロが以下な定義になってて

#define INCR_PC                 (PC++)

ええと ++ してるってコトは pc が指してるのは配列なのでしょうか。

むむ

PC ってどこでセットされとるんかな、と言いつつ Scm_VMApply 手続きを見てみると以下な記述を発見。

    if (numargs <= 4) {
        PC = apply_calls[numargs];
    } else {
        PC = SCM_NEW_ARRAY(ScmWord, 2);
        PC[0] = SCM_VM_INSN1(SCM_VM_TAIL_CALL, numargs);
        PC[1] = SCM_VM_INSN(SCM_VM_RET);
    }
    return proc;

あらら。apply_calls は配列で vm.c にて定義。

/* Static VM instruction arrays.
   Scm_VMApplyN modifies VM's pc to point it. */

static ScmWord apply_calls[][2] = {
    { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 0),
      SCM_VM_INSN(SCM_VM_RET) },
    { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 1),
      SCM_VM_INSN(SCM_VM_RET) },
    { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 2),
      SCM_VM_INSN(SCM_VM_RET) },
    { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 3),
      SCM_VM_INSN(SCM_VM_RET) },
    { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 4),
      SCM_VM_INSN(SCM_VM_RET) },
};

4 以下は static で用意されているのか。ヤッテる事自体は numargs の値に関わらず同じと言っても過言ではないですな。ええと 配列要素への代入に用いられているマクロの定義が以下。

#define SCM_VM_INSN(code)           SCM_WORD(code)
#define SCM_VM_INSN1(code, arg)     SCM_WORD((long)((arg)<<8) | (code))
#define SCM_VM_INSN2(code, arg0, arg1)  \
    SCM_WORD((long)((arg1) << 18) | ((arg0) << 8) | (code))

これ、取り出すマクロを見てみた方が良いな。

#define SCM_VM_INSN_CODE(obj)       (SCM_WORD(obj)&0x0ff)
#define SCM_VM_INSN_ARG(obj)        ((signed long)SCM_WORD(obj) >> 8)
#define SCM_VM_INSN_ARG0(obj)       ((SCM_WORD(obj) >>  8) & 0x03ff)
#define SCM_VM_INSN_ARG1(obj)       ((SCM_WORD(obj) >> 18) & 0x03ff)

しかし結局 Scm_VMApply 手続きは一体何なのか。proc 渡してるのは良いのですが、何もせずに戻してるだけなんですが。ちなみに引数はスタックに push している模様。あ、手続きは val0 に格納されているな。
しかしこの SCM_VM_TAIL_CALL はキツいな。あるいは TAIL_CALL_INSTRUCTION マクロも以下な定義。

#define TAIL_CALL_INSTRUCTION()                 \
    do {                                        \
        if (!TAIL_POS()) {                      \
            CHECK_STACK(CONT_FRAME_SIZE);       \
            PUSH_CONT(PC);                      \
            PC = PC_TO_RETURN;                  \
        }                                       \
    } while (0)

ここでも PC リセットしてます。PC_TO_RETURN は何か、というと

/* A stub VM code to make VM return immediately */
static ScmWord return_code[] = { SCM_VM_INSN(SCM_VM_RET) };
#define PC_TO_RETURN  return_code

との事。TAIL_POS じゃなかったら、とありますが TAIL_POS マクロは以下

/* check if *pc is an return instruction.  if so, some
   shortcuts are taken. */
#define TAIL_POS()         (*PC == SCM_VM_INSN(SCM_VM_RET))

ふむ。*pc が RET でなかったらスタックに PC なソレを継続フレームに push してから PC を RET でリセットしてる模様。TAIL_CALL_INSTRUCTION の概要にも同様な記述があるな。成程。
インライン化、というあたりが微妙なカンジ。

うーん

まとめに踏み込めそうな気もするんですが、どうなんでしょ。

メモ

VM instruction は PC が指す配列を順にめくっていって RET が出てきたら継続フレームから直前の環境に戻る、という形なのか。
で、そのループで APPLY がどうか、というのが今回のお題のキモ。
APPLY された時に PC なソレを継続フレイムに退避して APPLY なナニを、というあたりが微妙っちゃ微妙です。もう少し確認が必要らしい。