ボトムアップ

compile.scm を読むための準備として、少しずつ compile - disassemble を繰り返して、 Gauche VM の挙動を外から把握しよう。最終的に GaucheVM への命令列が手で作れて、実行できると嬉しい。
http://www.practical-scheme.net/wiliki/wiliki.cgi?Gauche%3aYAGHG%3aIntroduction も参考に、gauche.internal.test を作った。
また compile.scm に関数を追加して、make しなおした。これは命令列を眺めて遊ぶ目的としては、どちらかは不要と思うけどとりあえず。

(define (compile-pass1 program)
  (pass1 program (make-bottom-cenv)))

(define (compile-pass2 iform)
  (pass2 iform))

(define (compile-pass3 iform)
  (pass3 iform
         (make-compiled-code-builder 0 0 '%toplevel #f #f)
         '() 'tail))

まずは即値を試そう。コンパイラをインクリメンタルに作るときと同じように。

gosh> (compile-p1 42)
($const 42)
#<undef>
gosh> (compile-pass1 42)
#(5 42)

なるほど、$const って唯の数値、 enum か。

gosh> (compile-pass3 (compile-pass1 42))
#<compiled-code %toplevel@0x12f4690>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 42)))
main_code (name=%toplevel, code=0x12c64e0, size=2, const=0, stack=0):
args: #f
     0 CONSTI(42) 
     1 RET 
#<undef>

#t と #f だと命令が違っている。まだその違いの理由はまだ分からない。

gosh> (vm-dump-code (compile-pass3 (compile-pass1 #t)))
main_code (name=%toplevel, code=0x12c64a0, size=2, const=0, stack=0):
args: #f
     0 CONST-RET #t
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 #f)))
main_code (name=%toplevel, code=0x12c6470, size=1, const=0, stack=0):
args: #f
     0 CONSTF-RET 

fixnum の最大値も試す。const=0 と const=1 の違いがある。

gosh> (vm-dump-code (compile-pass3 (compile-pass1 (greatest-fixnum))))
main_code (name=%toplevel, code=0x130be88, size=2, const=0, stack=0):
args: #f
     0 CONST-RET 536870911
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 (+ 1 (greatest-fixnum)))))
main_code (name=%toplevel, code=0x130be48, size=2, const=1, stack=0):
args: #f
     0 CONST-RET 536870912
#<undef>

次は unary primitive(一引数の組み込み手続き)。予想に反して、null?, integer?, zero? は微妙に命令列として違うようだ。Gauche の目的に即した理由があるに違いない。インタプリタであるので、いろいろな制約が掛かっているはず。

gosh> (vm-dump-code (compile-pass3 (compile-pass1 '(null? 42))))
main_code (name=%toplevel, code=0xa2c10, size=3, const=0, stack=0):
args: #f
     0 CONSTI(42) 
     1 NULLP                    ; (null? 42)
     2 RET 
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 '(integer? #t))))
main_code (name=%toplevel, code=0x1220390, size=5, const=1, stack=4):
args: #f
     0 CONST-PUSH #t
     2 GREF-TAIL-CALL(1) #<identifier gauche.internal#integer?>; (integer? #t)
     4 RET 
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 '(zero? 32))))
main_code (name=%toplevel, code=0xa2980, size=4, const=0, stack=1):
args: #f
     0 CONSTI-PUSH(32) 
     1 CONSTI(0) 
     2 NUMEQ2                   ; (zero? 32)
     3 RET 
#<undef>

次は binary primitive。=, eq? は期待するシンプルな命令、スタックにプッシュして命令実行。
だけど、+ は簡単になりすぎている。調べたところ pass1 の時点で足し算が実行されている。

gosh> (vm-dump-code (compile-pass3 (compile-pass2 (compile-pass1 '(eq? #\a 1)))))
main_code (name=%toplevel, code=0x12200c0, size=5, const=0, stack=1):
args: #f
     0 CONST-PUSH #\a
     2 CONSTI(1) 
     3 EQ                       ; (eq? #\a 1)
     4 RET 
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass2 (compile-pass1 '(= 7 7)))))
main_code (name=%toplevel, code=0x5cdde0, size=4, const=0, stack=1):
args: #f
     0 CONSTI-PUSH(7) 
     1 CONSTI(7) 
     2 NUMEQ2                   ; (= 7 7)
     3 RET 
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass2 (compile-pass1 '(+ 7 7)))))
main_code (name=%toplevel, code=0x130b9b8, size=2, const=0, stack=0):
args: #f
     0 CONSTI(14) 
     1 RET 
#<undef>

local variables, let はもう少し調べる必要がある。LOCAL-ENV, CONST-RET, LREF0 がまだイメージが分からない。

gosh> (vm-dump-code (compile-pass3 (compile-pass1 '(let ((x 3)) #t))))
main_code (name=%toplevel, code=0x5cdac0, size=4, const=0, stack=4):
args: #f
     0 CONSTI-PUSH(3) 
     1 LOCAL-ENV(1)             ; (let ((x 3)) #t)
     2 CONST-RET #t
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 '(let ((x 3)) x))))
main_code (name=%toplevel, code=0x5cda60, size=4, const=0, stack=4):
args: #f
     0 CONSTI-PUSH(3) 
     1 LOCAL-ENV(1)             ; (let ((x 3)) x)
     2 LREF0                    ; x
     3 RET 
#<undef>
gosh> (vm-dump-code (compile-pass3 (compile-pass1 '(let ((x 3) (y 7)) #f))))
main_code (name=%toplevel, code=0x5cd9d0, size=4, const=0, stack=5):
args: #f
     0 CONSTI-PUSH(3) 
     1 CONSTI-PUSH(7) 
     2 LOCAL-ENV(2)             ; (let ((x 3) (y 7)) #f)
     3 CONSTF-RET 
#<undef>