Toy VM(2)
Gauche の命令列の一部だけが動くおもちゃの vm を作ってスタックマシンを理解しよう、の続き。時間が空いてしまったので何もかも忘れている。以下、何もかも途中だけど自分のために記録。
SECD Machine をある程度理解した今の頭で、やりたいことの骨格を改めて書いてみた。
- 手続き vm は、手続き vm-code が生成する gauche の命令列のリスト版、を実行する。
- 未だちゃんと扱っていないが gauche はプログラムカウンタ pc を使う(SECD では明示的な pc を使っていなかった)。
- val0 レジスタを使う。
- stack 操作を gauche のまねをする。
命令について。
- CONST; 値を val0 にセット。
- PUSH; スタックに val0 を push
以下のセットが分かれば今回の目的は達成されたと言って良い。
- CONT レジスタの扱い。
- RET; 継続の扱いが必要。
- PRE-CALL; 今のところ何もしていない。
- CALL, TAIL-CALL; 継続の扱いが必須
TODO;
- identifier の理解はかなり怪しい。Common Lisp とは違う、とだけ頭に入れておく。
gosh> (vm-code '(* (+ 6 2) 3)) ((PRE-CALL 2) 11 (CONST) 6 (PUSH) (CONST) 2 (PUSH) (GREF) #<identifier tgvm#+> (CALL 2) (PUSH) (CONST) 3 (PUSH) (GREF) #<identifier tgvm#*> (TAIL-CALL 2) (RET)) gosh> (vm 's 'e 0 'd (vm-code '(* (+ 6 2) 3)) ()) 24
(define (vm s e pc d base val0) (define (identifier->proc id) (cond ((equal? id '+) +) ((equal? id '-) -) ((equal? id '*) *) ((equal? id '>) >) ((equal? id '<) <) (else (errorf "Unknown id:~a" id)))) ;; (let ((c (list-tail base pc))) ;;#?=(list :s s :e e :pc pc :d d :val0 val0) (match c ((('CONST) val . c) (vm s e (+ pc 2) d base val)) ((('PUSH) . c) (vm (cons val0 s) e (+ pc 1) d base val0)) ((('GREF) var . c) (vm s e (+ pc 2) d base (identifier->proc var))) ((('RET) . c) ;; TODO val0) ((('PRE-CALL proc-id) location . c) ;; TODO (vm s e (+ pc 2) d base val0)) ;; CALL ((('CALL nargs) . c) (unless (procedure? val0) (errorf "val0 is not PROC: ~a" val0)) (let ((args ())) (for-each (lambda (n) (set! args (cons (pop! s) args))) (iota nargs)) (vm s e (+ pc 1) d base (apply val0 args)))) ((('TAIL-CALL nargs) . c) (unless (procedure? val0) (errorf "val0 is not PROC: ~a" val0)) (let ((args ())) (for-each (lambda (n) (set! args (cons (pop! s) args))) (iota nargs)) (vm s e (+ pc 1) d base (apply val0 args)))) ;; base condition (else (values (list :s s :e e :pc pc :d d :val0 val0) 'stop)))))
メモ
- http://practical-scheme.net/gauche/memo-stack-j.html VM のスタック操作 は、未完と書かれているように、現在の (0.8.14) 版とは異なる。例えば PUSH_ENV_HDR はもはや存在していない。
マクロだとか定義の場所。すぐ忘れる。
Reading Gauche
- http://wiki.monaos.org/pukiwiki.php?Reading%20Gauche%2Fvm%2Finsn インストラクション一覧
- http://wiki.monaos.org/pukiwiki.php?cmd=read&page=Reading%20Gauche%2Fvm%2Finsn%2FTAIL_CALL
プログラム断片。http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3aYAGHG も改めて参考にしつつ。とにかく最小の例 -- 正しい TAIL-CALLあるいは CALL の例 -- が欲しい。
gosh> (vm-code '(begin (+ 1 2) 1)) ((PRE-CALL 2) 11 (CONST) 1 (PUSH) (CONST) 2 (PUSH) (GREF) #<identifier tgvm#+> (CALL 2) (CONST) 1 (RET))
これかな。PRE-CALL で次に実行するPC (= 11) を保存しておいて、CALL の後でそれを復帰させる。
今の自作のコードは根本的に間違っていて、かつ、たまたま動いているように見えてるだけだ。うーん。