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