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 |
+-------+とりあえず出かけますので、続きは明日。