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 の違い、というのも要チェキ、という事ッスか。