色々確認
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 なナニを、というあたりが微妙っちゃ微妙です。もう少し確認が必要らしい。