SICP 読み (261) 5.2 レジスタ計算機での計算
いやはや。久々にハマッた。少い時間で無理矢理ヤッてるからこうなるのでしょうが、まだまだ修行が足らんな。(何
問題 5.12
一応考えた通りに動作するようになっている模様ですが、まだまだアヤしげ。昨晩書き残したエントリの下書きを入れつつログを以下に。
まず、昨晩ですが微妙な誤りに気がつき要件を整理している。曰く
まず _a list of all instructions_ ですが、これはリストな例で言えば
((assign (((restore n) (restore continue) (assign n (op -) (reg n) (const 2)) (以下略)) ((assign n (reg val)) (restore val) (restore continue) (以下略)))) (goto (((test (op <) (reg n) (const 2)) (以下略)) ((reg continue)))) (branch (略)))
みたいな感じ (assemble された手続きは略して書いてますが) になるはず。
次とその次の _a list of the registers used to hold entry points_ と _a list of the registers that are saved or restored_ はそのままで良いのかな??
最後の _for each register, a list of the sources from which it is assigned_ は
((continue (((restore n) (restore continue) (以下略)) ((assign n (reg val)) (restore val) (以下略)))) (n (((op -) (reg n) (const 1)) ((op -) (reg n) (const 2)))) (val (以下略)))
みたいな感じになるんだとしたら、昨晩エントリな手続きはダウト。
で、正しくは以下、と書いているがこれが大間違い。
(define (set-list-of-inst l) (let ((elem (assoc (car l) list-of-inst))) (if elem (set-car! (cadr elem) (cadr l)) (set! list-of-inst (cons l list-of-inst)))) 'done) (define (set-hold-entry-points l) (if (dup-element? l hold-entry-points) (set! hold-entry-points (cons l hold-entry-points))) 'done) (define (set-saved-or-restored l) (if (dup-element? l saved-of-restored) (set! saved-of-restored (cons l saved-of-restored))) 'done) (define (set-list-of-reg l) (let ((elem (assoc (car l) list-of-reg))) (if elem (if (dup-element? (cadr l) list-of-reg) (set-car! (cadr elem) (cadr l))) (set! list-of-reg (cons l list-of-reg)))) 'done)
上記の内、set-list-of-reg と set-list-of-inst がナニ。あとは上記手続きの呼び出しを make-goto、make-branch、make-primitive-exp と make-assign に盛り込んで make-new-machine の dispatch 手続きも修正入れたのは良いのですが、動作不良で死亡、というのが昨晩のざっくりしたストーリーだったりします。
今回ハマッた件については、scheme に関する理解を深めるためにも、経緯の検証はぜったいヤるべきだと思っていますが、現時点ではそのリキはございません。とりあえず、現時点でのソース及び出力をソースから以下に。
(define (make-machine register-names ops controller-text) (let ((machine (make-new-machine))) (for-each (lambda (register-name) ((machine 'allocate-register) register-name)) register-names) ((machine 'install-operations) ops) ((machine 'install-instruction-sequence) (assemble controller-text machine)) machine)) (define (make-register name) (let ((contents '*unassigned*)) (define (dispatch message) (cond ((eq? message 'get) contents) ((eq? message 'set) (lambda (value) (set! contents value))) (else (error "Unknown request -- REGISTER" message)))) dispatch)) (define (get-contents register) (register 'get)) (define (set-contents! register value) ((register 'set) value)) ;;**original (unmonitored) version from section 5.2.1 (define (make-stack) (let ((s '())) (define (push x) (set! s (cons x s))) (define (pop) (if (null? s) (error "Empty stack -- POP") (let ((top (car s))) (set! s (cdr s)) top))) (define (initialize) (set! s '()) 'done) (define (dispatch message) (cond ((eq? message 'push) push) ((eq? message 'pop) (pop)) ((eq? message 'initialize) (initialize)) (else (error "Unknown request -- STACK" message)))) dispatch)) (define (pop stack) (stack 'pop)) (define (push stack value) ((stack 'push) value)) ;;**monitored version from section 5.2.4 (define (make-stack) (let ((s '()) (number-pushes 0) (max-depth 0) (current-depth 0)) (define (push x) (set! s (cons x s)) (set! number-pushes (+ 1 number-pushes)) (set! current-depth (+ 1 current-depth)) (set! max-depth (max current-depth max-depth))) (define (pop) (if (null? s) (error "Empty stack -- POP") (let ((top (car s))) (set! s (cdr s)) (set! current-depth (- current-depth 1)) top))) (define (initialize) (set! s '()) (set! number-pushes 0) (set! max-depth 0) (set! current-depth 0) 'done) (define (print-statistics) (newline) (display (list 'total-pushes '= number-pushes 'maximum-depth '= max-depth))) (define (dispatch message) (cond ((eq? message 'push) push) ((eq? message 'pop) (pop)) ((eq? message 'initialize) (initialize)) ((eq? message 'print-statistics) (print-statistics)) (else (error "Unknown request -- STACK" message)))) dispatch)) (define (make-new-machine) (let ((pc (make-register 'pc)) (flag (make-register 'flag)) (stack (make-stack)) (the-instruction-sequence '()) (list-of-inst '()) (hold-entry-points '()) (saved-or-restored '()) (list-of-reg '())) (let ((the-ops (list (list 'initialize-stack (lambda () (stack 'initialize))) ;;**next for monitored stack (as in section 5.2.4) ;; -- comment out if not wanted (list 'print-stack-statistics (lambda () (stack 'print-statistics))))) (register-table (list (list 'pc pc) (list 'flag flag)))) ;; exersize 5.12 (define (get-list-of-inst) list-of-inst) (define (get-hold-entry-points) hold-entry-points) (define (get-saved-or-restored) saved-or-restored) (define (get-list-of-reg) list-of-reg) (define (dup-element? x y) (define (dup-elem-iter l) (cond ((null? l) #f) ((equal? x (car l)) #t) (else (dup-elem-iter (cdr l))))) (dup-elem-iter y)) (define (set-list-of-inst l) (let ((elem (assoc (car l) list-of-inst))) (if elem (if (not (dup-element? (cadr l) (cdr elem))) (set-cdr! elem (cons (cadr l) (cdr elem)))) (set! list-of-inst (cons l list-of-inst)) )) 'done) (define (set-hold-entry-points l) (if (not (dup-element? l hold-entry-points)) (set! hold-entry-points (cons l hold-entry-points))) 'done) (define (set-saved-or-restored l) (if (not (dup-element? l saved-or-restored)) (set! saved-or-restored (cons l saved-or-restored))) 'done) (define (set-list-of-reg l) (let ((elem (assoc (car l) list-of-reg))) (if elem (let ((elem-cdr (cdr elem))) (if (not (dup-element? (cadr l) elem-cdr)) (set-cdr! elem (cons (cadr l) (cdr elem))))) (set! list-of-reg (cons l list-of-reg)) )) 'done) ;; exersize 5.12 (define (allocate-register name) (if (assoc name register-table) (error "Multiply defined register: " name) (set! register-table (cons (list name (make-register name)) register-table))) 'register-allocated) (define (lookup-register name) (let ((val (assoc name register-table))) (if val (cadr val) (error "Unknown register:" name)))) (define (execute) (let ((insts (get-contents pc))) (if (null? insts) 'done (begin ((instruction-execution-proc (car insts))) (execute))))) (define (dispatch message) (cond ((eq? message 'start) (set-contents! pc the-instruction-sequence) (execute)) ((eq? message 'install-instruction-sequence) (lambda (seq) (set! the-instruction-sequence seq))) ((eq? message 'allocate-register) allocate-register) ((eq? message 'get-register) lookup-register) ((eq? message 'install-operations) (lambda (ops) (set! the-ops (append the-ops ops)))) ((eq? message 'stack) stack) ((eq? message 'operations) the-ops) ;; exersize 5.12 ((eq? message 'get-inst) (get-list-of-inst)) ((eq? message 'get-entry) (get-hold-entry-points)) ((eq? message 'get-stack) (get-saved-or-restored)) ((eq? message 'get-reg) (get-list-of-reg)) ((eq? message 'set-inst) set-list-of-inst) ((eq? message 'set-entry) set-hold-entry-points) ((eq? message 'set-stack) set-saved-or-restored) ((eq? message 'set-reg) set-list-of-reg) ;; exersize 5.12 (else (error "Unknown request -- MACHINE" message)))) dispatch))) (define (start machine) (machine 'start)) (define (get-register-contents machine register-name) (get-contents (get-register machine register-name))) (define (set-register-contents! machine register-name value) (set-contents! (get-register machine register-name) value) 'done) (define (get-register machine reg-name) ((machine 'get-register) reg-name)) (define (assemble controller-text machine) (extract-labels controller-text (lambda (insts labels) (update-insts! insts labels machine) insts))) (define (extract-labels text receive) (if (null? text) (receive '() '()) (extract-labels (cdr text) (lambda (insts labels) (let ((next-inst (car text))) (if (symbol? next-inst) (if (assoc next-inst labels) (error "Dup label entry -- ASSEMBLE" next-inst) (receive insts (cons (make-label-entry next-inst insts) labels))) (receive (cons (make-instruction next-inst) insts) labels))))))) (define (update-insts! insts labels machine) (let ((pc (get-register machine 'pc)) (flag (get-register machine 'flag)) (stack (machine 'stack)) (ops (machine 'operations))) (for-each (lambda (inst) (set-instruction-execution-proc! inst (make-execution-procedure (instruction-text inst) labels machine pc flag stack ops))) insts))) (define (make-instruction text) (cons text '())) (define (instruction-text inst) (car inst)) (define (instruction-execution-proc inst) (cdr inst)) (define (set-instruction-execution-proc! inst proc) (set-cdr! inst proc)) (define (make-label-entry label-name insts) (cons label-name insts)) (define (lookup-label labels label-name) (let ((val (assoc label-name labels))) (if val (cdr val) (error "Undefined label -- ASSEMBLE" label-name)))) (define (make-execution-procedure inst labels machine pc flag stack ops) (cond ((eq? (car inst) 'assign) (make-assign inst machine labels ops pc)) ((eq? (car inst) 'test) (make-test inst machine labels ops flag pc)) ((eq? (car inst) 'branch) (make-branch inst machine labels flag pc)) ((eq? (car inst) 'goto) (make-goto inst machine labels pc)) ((eq? (car inst) 'save) (make-save inst machine stack pc)) ((eq? (car inst) 'restore) (make-restore inst machine stack pc)) ((eq? (car inst) 'perform) (make-perform inst machine labels ops pc)) (else (error "Unknown instruction type -- ASSEMBLE" inst)))) (define (make-assign inst machine labels operations pc) (let ((target (get-register machine (assign-reg-name inst))) (value-exp (assign-value-exp inst))) (let ((value-proc (if (operation-exp? value-exp) (make-operation-exp value-exp machine labels operations) (make-primitive-exp (car value-exp) machine labels)))) ((machine 'set-reg) (list (assign-reg-name inst) value-exp)) ;; exersize 5.12 (lambda () ; execution procedure for assign (set-contents! target (value-proc)) (advance-pc pc))))) (define (assign-reg-name assign-instruction) (cadr assign-instruction)) (define (assign-value-exp assign-instruction) (cddr assign-instruction)) (define (advance-pc pc) (set-contents! pc (cdr (get-contents pc)))) (define (make-test inst machine labels operations flag pc) (let ((condition (test-condition inst))) (if (operation-exp? condition) (let ((condition-proc (make-operation-exp condition machine labels operations))) (lambda () (set-contents! flag (condition-proc)) (advance-pc pc))) (error "Bad TEST instruction -- ASSEMBLE" inst)))) (define (test-condition test-instruction) (cdr test-instruction)) (define (make-branch inst machine labels flag pc) (let ((dest (branch-dest inst))) (if (label-exp? dest) (let ((insts (lookup-label labels (label-exp-label dest)))) ((machine 'set-inst) (list 'branch (map car insts))) ;; exersize 5.12 (lambda () (if (get-contents flag) (set-contents! pc insts) (advance-pc pc)))) (error "Bad BRANCH instruction -- ASSEMBLE" inst)))) (define (branch-dest branch-instruction) (cadr branch-instruction)) (define (make-goto inst machine labels pc) (let ((dest (goto-dest inst))) (cond ((label-exp? dest) (let ((insts (lookup-label labels (label-exp-label dest)))) ((machine 'set-inst) (list 'goto (map car insts))) ;; exersize 5.12 (lambda () (set-contents! pc insts)))) ((register-exp? dest) (let ((reg (get-register machine (register-exp-reg dest)))) ((machine 'set-entry) (register-exp-reg dest)) ;; exersize 5.12 (lambda () (set-contents! pc (get-contents reg))))) (else (error "Bad GOTO instruction -- ASSEMBLE" inst))))) (define (goto-dest goto-instruction) (cadr goto-instruction)) (define (make-save inst machine stack pc) (let ((reg (get-register machine (stack-inst-reg-name inst)))) ((machine 'set-stack) (stack-inst-reg-name inst)) ;; exersize 5.12 (lambda () (push stack (get-contents reg)) (advance-pc pc)))) (define (make-restore inst machine stack pc) (let ((reg (get-register machine (stack-inst-reg-name inst)))) ((machine 'set-stack) (stack-inst-reg-name inst)) ;; exersize 5.12 (lambda () (set-contents! reg (pop stack)) (advance-pc pc)))) (define (stack-inst-reg-name stack-instruction) (cadr stack-instruction)) (define (make-perform inst machine labels operations pc) (let ((action (perform-action inst))) (if (operation-exp? action) (let ((action-proc (make-operation-exp action machine labels operations))) (lambda () (action-proc) (advance-pc pc))) (error "Bad PERFORM instruction -- ASSEMBLE" inst)))) (define (perform-action inst) (cdr inst)) (define (make-primitive-exp exp machine labels) (cond ((constant-exp? exp) (let ((c (constant-exp-value exp))) (lambda () c))) ((label-exp? exp) (let ((insts (lookup-label labels (label-exp-label exp)))) ((machine 'set-inst) (list 'assign (map car insts))) ;; exersize 5.12 (lambda () insts))) ((register-exp? exp) (let ((r (get-register machine (register-exp-reg exp)))) (lambda () (get-contents r)))) (else (error "Unknown expression type -- ASSEMBLE" exp)))) (define (register-exp? exp) (tagged-list? exp 'reg)) (define (register-exp-reg exp) (cadr exp)) (define (constant-exp? exp) (tagged-list? exp 'const)) (define (constant-exp-value exp) (cadr exp)) (define (label-exp? exp) (tagged-list? exp 'label)) (define (label-exp-label exp) (cadr exp)) (define (make-operation-exp exp machine labels operations) (let ((op (lookup-prim (operation-exp-op exp) operations)) (aprocs (map (lambda (e) (if (label-exp? e) (error "cannot place label element -- ASSEMBLE" e) (make-primitive-exp e machine labels))) (operation-exp-operands exp)))) (lambda () (apply op (map (lambda (p) (p)) aprocs))))) (define (operation-exp? exp) (and (pair? exp) (tagged-list? (car exp) 'op))) (define (operation-exp-op operation-exp) (cadr (car operation-exp))) (define (operation-exp-operands operation-exp) (cdr operation-exp)) (define (lookup-prim symbol operations) (let ((val (assoc symbol operations))) (if val (cadr val) (error "Unknown operation -- ASSEMBLE" symbol)))) ;; from 4.1 (define (tagged-list? exp tag) (if (pair? exp) (eq? (car exp) tag) ; false)) #f))
で、この評価器に fib なソレを吸わせてみたのが以下。
gosh> (add-load-path ".") ("." "/usr/share/gauche/site/lib" "/usr/share/gauche/0.8.7/lib") gosh> (load "ch5-regsim.scm") #t gosh> (define m (make-machine '(n continue val) (list (list '< <) (list '- -) (list '+ +)) '(controller (assign continue (label fib-done)) fib-loop (test (op <) (reg n) (const 2)) (branch (label immediate-answer)) ;; set up to compute Fib(n - 1) (save continue) (assign continue (label afterfib-n-1)) (save n) (assign n (op -) (reg n) (const 1)) (goto (label fib-loop)) afterfib-n-1 (restore n) (restore continue) ;; set up to compute Fib(n - 2) (assign n (op -) (reg n) (const 2)) (save continue) (assign continue (label afterfib-n-2)) (save val) (goto (label fib-loop)) afterfib-n-2 (assign n (reg val)) (restore val) (restore continue) (assign val (op +) (reg val) (reg n)) (goto (reg continue)) immediate-answer (assign val (reg n)) (goto (reg continue)) fib-done))) m gosh> (m 'get-inst) ((goto ((test (op <) (reg n) (const 2)) (branch (label immediate-answer)) (save continue) (assign continue (label afterfib-n-1)) (save n) (assign n (op -) (reg n) (const 1)) (goto (label fib-loop)) #0=(restore n) #1=(restore continue) #2=(assign n (op -) (reg n) (const 2)) #3=(save continue) #4=(assign continue (label afterfib-n-2)) #5=(save val) #6=(goto (label fib-loop)) #7=(assign n (reg val)) #8=(restore val) #9=(restore continue) #10=(assign val (op +) (reg val) (reg n)) #11=(goto (reg continue)) #12=(assign val (reg n)) #13=(goto (reg continue)))) (branch (#12# #13#)) (assign (#7# #8# #9# #10# #11# #12# #13#) (#0# #1# #2# #3# #4# #5# #6# #7# #8# #9# #10# #11# #12# #13#) ())) ;; 整形してみたもの ((goto ((test (op <) (reg n) (const 2)) (branch (label immediate-answer)) (save continue) (assign continue (label afterfib-n-1)) (save n) (assign n (op -) (reg n) (const 1)) (goto (label fib-loop)) (restore n) (restore continue) (assign n (op -) (reg n) (const 2)) (save continue) (assign continue (label afterfib-n-2)) (save val) (goto (label fib-loop)) (assign n (reg val)) (restore val) (restore continue) (assign val (op +) (reg val) (reg n)) (goto (reg continue)) (assign val (reg n)) (goto (reg continue)))) (branch ((assign val (reg n)) (goto (reg continue)))) (assign ((assign n (reg val)) (restore val) (restore continue) (assign val (op +) (reg val) (reg n)) (goto (reg continue)) (assign val (reg n)) (goto (reg continue))) ((restore n) (restore continue) (assign n (op -) (reg n) (const 2)) (save continue) (assign continue (label afterfib-n-2)) (save val) (goto (label fib-loop)) (assign n (reg val)) (restore val) (restore continue) (assign val (op +) (reg val) (reg n)) (goto (reg continue)) (assign val (reg n)) (goto (reg continue))) ())) ;; 整形してみたもの gosh> (m 'get-entry) (continue) gosh> (m 'get-stack) (val n continue) gosh> (m 'get-reg) ((val ((reg n)) ((op +) (reg val) (reg n))) (n ((reg val)) ((op -) (reg n) (const 2)) ((op -) (reg n) (const 1))) (continue ((label afterfib-n-2)) ((label afterfib-n-1)) ((label fib-done)))) gosh>
とりあえず思った通りには動作しているんだと思いたいのですが ...
# 検証は別途で。