LOCAL_ENV_CLOSURES
昨晩のエントリの標題、微妙に違うな。色々試し中。
例えば
gosh> (disasm (lambda () (lambda (x) x)))
main_code (name=#f, code=0x81f4a20, size=3, const=1, stack=0):
args: #f
0 CLOSURE #<lambda 0> ; (lambda (x) x)
2 RET
internal_closure_0 (name=#f, code=0x81f00e0, size=2, const=0 stack=0):
args: #f
0 LREF0 ; x
1 RET
#<undef>
gosh> とか (これは CLOSURE なインストラクション確認のため)。
あるいは
gosh> (disasm (lambda () (if (null? x) x ((lambda (x) x) x))))
main_code (name=#f, code=0x80f9de0, size=12, const=3, stack=4):
args: #f
0 GREF #<identifier user#x>; x
2 BNNULL 7 ; (null? x)
4 GREF #<identifier user#x>; x
6 RET
7 GREF-PUSH #<identifier user#x>; x
9 LOCAL-ENV(1) ; ((lambda (x) x) x)
10 LREF0 ; x
11 RET
#<undef>
gosh> とか (これは LOCAL-ENV なインストラクション確認のため)。
で、compile.scm 開いて中身を見てみたら pass3/$LET という手続きで LOCAL-ENV-CLOSURES というインストラクションを云々してそげな事が分かる。
試してみたのが以下。
gosh> (disasm (lambda () (let f ((ret '()) (l '(1 2 3 4 5))) (if (null? l) ret (f (append (list (car l)) ret) (cdr l))))))
main_code (name=#f, code=0x814bf00, size=22, const=1, stack=16):
args: #f
0 CONSTN-PUSH
1 CONST-PUSH (1 2 3 4 5)
3 LOCAL-ENV(2)
4 LREF0 ; l
5 BNNULL 9 ; (null? l)
7 LREF1 ; ret
8 RET
9 LREF0 ; l
10 CAR ; (car l)
11 LIST(1) ; (list (car l))
12 PUSH
13 LREF1 ; ret
14 APPEND(2) ; (append (list (car l)) ret)
15 PUSH
16 LREF0 ; l
17 CDR-PUSH ; (cdr l)
18 LOCAL-ENV-JUMP(1) 4 ; (f (append (list (car l)) ret) (cdr l))
20 RET
21 RET
#<undef>
gosh>弱い。もう少しざっくり中身を見てみたら、どうやら letrec なの? という当たりがついたので今から試してみます。
ええと、プログラミング Scheme の p.53 から以下。
gosh> (disasm (lambda () (letrec ((sum (lambda (ls) (if (null? ls) 0 (+ (car ls) (sum (cdr ls)))))) (sum '(1 2 3 4 5)))))
)
main_code (name=#f, code=0x81f4960, size=3, const=1, stack=4):
args: #f
0 LOCAL-ENV-CLOSURES(1) (#<lambda 0>); (letrec ((sum (lambda (ls) (if (null? ls ...
2 CONSTU-RET
internal_closure_0 (name=sum, code=0x8144f40, size=15, const=0 stack=11):
args: #f
0 LREF0 ; ls
1 BNNULL 5 ; (null? ls)
3 CONSTI(0)
4 RET
5 LREF0 ; ls
6 CAR-PUSH ; (car ls)
7 PRE-CALL(1) 13
9 LREF0 ; ls
10 CDR-PUSH ; (cdr ls)
11 LREF10 ; sum
12 LOCAL-ENV-CALL(1) ; (sum (cdr ls))
13 NUMADD2 ; (+ (car ls) (sum (cdr ls)))
14 RET
#<undef>
gosh> 駄目か、と思ったらビンゴ。わははは。しかし意味が全然分からんぞ。
ええと
0 LOCAL-ENV-CLOSURES(1)
引数はリスト一発だな。で、この次に internal_closure_0 な手続きオブジェクトへのポインタが格納されている、と。
で、以下なあたりを実行して
SP += nlocals;
FINISH_ENV(SCM_FALSE, ENV);
e = get_env(vm);
z = (ScmObj*)e - nlocals;
SCM_FOR_EACH(cp, cp) {
if (SCM_COMPILED_CODE_P(SCM_CAR(cp))) {
*z++ = clo = Scm_MakeClosure(SCM_CAR(cp), e);
} else {
*z++ = SCM_CAR(cp);
}
}
VAL0 = clo;
NEXT1;次のインストラクションが
2 CONSTU-RET
って何だろ。#undef 戻す?
ちなみに CONSTU-RET のナニが以下。
CASE(SCM_VM_CONSTU_RET) {
VAL0 = SCM_UNDEFINED;
vm->numVals = 1;
RETURN_OP();
NEXT;
}RETURN_OP は RET ですわな。
/* return operation. */
#define RETURN_OP() \
do { \
if (CONT == NULL || BOUNDARY_FRAME_P(CONT)) { \
return; /* no more continuations */ \
} \
POP_CONT(); \
} while (0)RET??
なんとなく POP_CONT がアヤシげに見えるのですが、LOCAL-ENV-CLOSURES で云々って gosh に手続き吸わせてみたら
gosh> (letrec ((sum (lambda (ls) (if (null? ls) 0 (+ (car ls) (sum (cdr ls)))))) (sum '(1 2 3 4 5))) ) #<undef>
とほほほ。リトライ。
gosh> (letrec ((sum (lambda (ls) (if (null? ls) 0 (+ (car ls) (sum (cdr ls))))))) (sum '(1 2 3 4 5))) 15
これを disasm に吸わせると
gosh> (disasm (lambda () (letrec ((sum (lambda (ls) (if (null? ls) 0 (+ (car ls) (sum (cdr ls))))))) (sum '(1 2 3 4 5)))))
main_code (name=#f, code=0x80f8bc0, size=7, const=2, stack=8):
args: #f
0 LOCAL-ENV-CLOSURES(1) (#<lambda 0>); (letrec ((sum (lambda (ls) (if (null? ls ...
2 CONST-PUSH (1 2 3 4 5)
4 LREF0 ; sum
5 LOCAL-ENV-TAIL-CALL(1) ; (sum '(1 2 3 4 5))
6 RET
internal_closure_0 (name=sum, code=0x8144e80, size=15, const=0 stack=11):
args: #f
0 LREF0 ; ls
1 BNNULL 5 ; (null? ls)
3 CONSTI(0)
4 RET
5 LREF0 ; ls
6 CAR-PUSH ; (car ls)
7 PRE-CALL(1) 13
9 LREF0 ; ls
10 CDR-PUSH ; (cdr ls)
11 LREF10 ; sum
12 LOCAL-ENV-CALL(1) ; (sum (cdr ls))
13 NUMADD2 ; (+ (car ls) (sum (cdr ls)))
14 RET
#<undef>
gosh> いやはや。ナチュラル全開。これだと意図が類推可能ですな。あと、TAIL-CALL と LOCAL-ENV-TAIL-CALL の違い、というのも要チェキ、という事ッスか。