stack の動作を机上で確認
最初の状態が分からん、と言いつつ gdb で動かしてみようとしたりしたのですが微妙。どうしたものやら、と言いつつ vm.c をぱらぱら眺めていたら Constructor なるコメントを発見。これが stack の最初の状態ってコトにして見てみる事に。
初期化は以下?
vm.c の Scm_NewVM() 手続きの以下なあたり。
#ifdef USE_CUSTOM_STACK_MARKER v->stack = (ScmObj*)GC_generic_malloc((SCM_VM_STACK_SIZE+1)*sizeof(ScmObj), vm_stack_kind); *v->stack++ = SCM_OBJ(v); #else /*!USE_CUSTOM_STACK_MARKER*/ v->stack = SCM_NEW_ARRAY(ScmObj, SCM_VM_STACK_SIZE); #endif /*!USE_CUSTOM_STACK_MARKER*/ v->sp = v->stack; v->stackBase = v->stack; v->stackEnd = v->stack + SCM_VM_STACK_SIZE;
あるいは ScmVM 型 (struct ScmVMRec 構造体) の上記な部分の定義が以下。
ScmObj *sp; /* stack pointer */ ScmObj *stack; /* bottom of allocated stack area */ ScmObj *stackBase; /* base of current stack area */ ScmObj *stackEnd; /* end of current stack area */
上記の sp 属性は vm.c にて以下のマクロが定義済み。
#define SP (vm->sp)
それは良いとして、順番としては
- 領域確保
- 先頭アドレスを sp 属性にセット
- 先頭と末端アドレスを属性にセット
そーゆー意味では他のレジスタについても確認?
ScmVM 型のレジスタな属性
以下な形で初期設定。
v->env = NULL; v->argp = v->stack; v->cont = NULL; v->pc = PC_TO_RETURN; v->base = NULL; v->val0 = SCM_UNDEFINED; for (i=0; i<SCM_VM_MAX_VALUES; i++) v->vals[i] = SCM_UNDEFINED; v->numVals = 1;
vm.h における ScmVM 型の属性としての定義は以下。
/* Registers */ ScmCompiledCode *base; /* Current executing closure's code packet. */ SCM_PCTYPE pc; /* Program pointer. Points into the code vector. (base->code) */ ScmEnvFrame *env; /* Current environment. */ ScmContFrame *cont; /* Current continuation. */ ScmObj *argp; /* Current argument pointer. Points to the incomplete environment frame being accumulated. This is a part of continuation. */ ScmObj val0; /* Value register. */ ScmObj vals[SCM_VM_MAX_VALUES]; /* Value register for multiple values */ int numVals; /* # of values */
- 環境 (env) は NULL
- 引数ポインタ (argp) は stack な値が代入
- 継続 (cont) は NULL
- プログラムカウンタ (pc) は RET がナニ (PC_TO_RETURN マクロ)
- base (詳細不明) も NULL
- val0 レジスタは #undefined がセット
- vals も #undefined で numVals に 1 がセット
てーコトは最初の状態は以下?
argp,sp>| | +---------+
ただし、ここから云々ってのはちょっと違う気がしますが ...
進めてみる
以下なソレだとちょっとは現実的?
gosh> (disasm (lambda () ((lambda (x y) (x) (y 1 2)) i j))) main_code (name=#f, code=0x8149f88, size=14, const=2, stack=14): args: #f 0 GREF-PUSH #<identifier user#i>; i 2 GREF-PUSH #<identifier user#j>; j 4 LOCAL-ENV(2) ; ((lambda (x y) (x) (y 1 2)) i j) 5 PRE-CALL(0) 9 7 LREF1 ; x 8 CALL(0) ; (x) 9 CONSTI-PUSH(1) 10 CONSTI-PUSH(2) 11 LREF0 ; y 12 TAIL-CALL(2) ; (y 1 2) 13 RET #<undef> gosh>
i とか j とかって define 済みってコトにしときます。値は適当。微妙に気になるのが GREF-PUSH の行番号が飛んでいるコト。GLOBAL_REF の中で INCR_PC してるんですが、スルーします。とりあえず
- i の値 (手続きオブジェクト) を push
- j の値 (手続きオブジェクト) を push
した時点で以下と思われます。
sp>| | | j | argp>| i | +-------+
で、LOCAL-ENV から FINISH_ENV マクロがナニされて以下?
argp,sp>| | | size | <- 2? | info | <- #f? env>| up | <- NULL? | j | | i | +-------+
で、継続フレームがナニ。しかしこれ、どうやって i とか j とか参照するんだろ、と言いつつ続行。次は継続 push ですか。
実際には以下なカンジになっていると思われる。
5 PRE-CALL(0) 9 6 CONSTI-PUSH(1) 7 LREF1 ; x 8 CALL(0) ; (x) 9 CONSTI-PUSH(1)
で、以下?
argp,sp>| | +-------+ | base | <- スルー | pc | <- next_pc は 9 CONSTI_PUSH(1) | size | <- argp と sp は同じなので 0? | argp | <- *1 | env | <- env *1,cont>| prev | <- 積む時点の CONT は NULL? +-------+ | size | <- 2? | info | <- #f? env>| up | <- NULL? +-------+ | j | | i | +-------+
したら、x を val0 にセットするんですが LREF1 は以下。
CASE(SCM_VM_LREF1) { VAL0 = ENV_DATA(ENV, 1); NEXT1; }
ENV_DATA(ENV, 1) の値を val0 に格納してます。ENV_DATA マクロが以下。
#define ENV_DATA(env, num) (*(((ScmObj*)(env))-(num)-1))
上記によれば env の二つ前なので i が取り出されて val0 にセットされます。env がベースポインタな訳ですかそうですか。なかなか凄い。
で、次が CALL ですが、i とか j とかはユーザ定義な手続きってコトにします。SCM_VM_CALL の以下なあたりがナニ、のはず。
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; }
引数無いんで env はどこか他所をナニ。とりあえず stack の状態は以下?
argp,sp>| | +-------+ | base | <- スルー | pc | <- next_pc は 9 CONSTI_PUSH(1) | size | <- argp と sp は同じなので 0? | argp | <- *1 | env | <- *2 *1,cont>| prev | <- 積む時点の CONT は NULL? +-------+ | size | <- 2? | info | <- #f? *2>| up | <- NULL? +-------+ | j | | i | +-------+
とりあえず継続なナニに積んであるので安心。で、上記には出てきませんが i な手続きの中で RET がナニされて手続きから戻るはず、という事で以下。
CASE(SCM_VM_RET) { RETURN_OP(); NEXT; }
RETURN_OP マクロが以下。
/* return operation. */ #define RETURN_OP() \ do { \ if (CONT == NULL || BOUNDARY_FRAME_P(CONT)) { \ return; /* no more continuations */ \ } \ POP_CONT(); \ } while (0)
cont は NULL では御座いませんので POP_CONT マクロがナニ。ちょい長いんで該当すると思われるソレを以下に。
} else if (IN_STACK_P((ScmObj*)CONT)) { \ SP = CONT->argp + CONT->size; \ ENV = CONT->env; \ ARGP = CONT->argp; \ PC = CONT->pc; \ BASE = CONT->base; \ CONT = CONT->prev; \ } else { \
これは見事に巻き戻ります。面白いなぁ。
備忘録まで、上記のナニが呼ばれる直前の stack を再掲。
argp,sp>| | +-------+ | base | <- スルー | pc | <- next_pc は 9 CONSTI_PUSH(1) | size | <- argp と sp は同じなので 0? | argp | <- *1 | env | <- *2 *1,cont>| prev | <- 積む時点の CONT は NULL? +-------+ | size | <- 2? | info | <- #f? *2>| up | <- NULL? +-------+ | j | | i | +-------+
とりあえず出かけますので、続きは明日。