(load "util.scm") (load "misc/ch5-compiler.scm") (display "\nex-5.31 - save-and-restore-for-apply\n") ; 1. save and restore env around operator ; 2. save and restore env around each operand (except last) ; 3. save and restore argl around each operand ; 4. save and restore proc around operand sequence ; (f 'x 'y) ; 1-4 are superfluous ; ((f) 'x 'y) ; 1-4 are superfluous ; no need to save env because compound-apply without args does ; not change env ; (f (g 'x) y) ; 1 is superfluous ; we need 2 because (g 'x) changes the env for y ; we need 3 because (g 'x) changes argl ; we need 4 because (g 'x) changes proc ; (f (g 'x) 'y) ; 1 is superfluous ; 1-2 are superfluous ; (g 'x) changes the env but we don't need it later (better save it anyway) ; 3-4 are still needed (display "[answered]\n") (display "\nex-5.32 - optimize-eceval-application\n") '( ev-application (save continue) (assign unev (op operands) (reg exp)) (assign exp (op operator) (reg exp)) (assign continue (label ev-appl-did-operator-no-restore)) (test (op variable?) (reg exp)) (branch (label ev-variable)) (save env) (save unev) (assign continue (label ev-appl-did-operator)) (goto (label eval-dispatch)) ev-appl-did-operator (restore unev) (restore env) ev-appl-did-operator-no-restore (assign argl (op empty-arglist)) (assign proc (reg val)) (test (op no-operands?) (reg unev)) (branch (label apply-dispatch)) (save proc) ) (display "[ok]\n") ; b. Applying all the optimizations make sense, but the compiled code will ; still run faster because the interpreter has to analyze the code every time ; it executes and the analysis itself adds overhead that the compiler can do ; before runtime (display "[answered]\n") (display "\nex-5.33 - compare-factorial-definitions\n") (define (compile-to-file code target linkage file-name) (set! label-counter 0) (define (write-list-to-port xs port) (if (null? xs) '() (begin (display (car xs) port) (display "\n" port) (write-list-to-port (cdr xs) port)))) (let* ((compile-result (compile code target linkage)) (assembly-insts (statements compile-result)) (port (open-output-file file-name))) (write-list-to-port assembly-insts port) (display "[") (display file-name) (display "]\n") (close-output-port port))) ; Uncomment the following lines to write the assembly code for the to methods ; into files. (compile-to-file '(define (factorial n) (if (= n 1) 1 (* (factorial (- n 1)) n))) 'val 'next "factorial.sicp-asm") ;(define label-counter 0) (compile-to-file '(define (factorial-alt n) (if (= n 1) 1 (* n (factorial-alt (- n 1))))) 'val 'next "factorial-alt.sicp-asm") ;(define label-counter 0) ; $ diff factorial.sicp-asm factorial-alt.sicp-asm ; 33,36c33,34 ; < (assign val (op lookup-variable-value) (const n) (reg env)) ; < (assign argl (op list) (reg val)) ; < (save argl) ; --- ; > (save env) ; > (assign proc (op lookup-variable-value) (const factorial-alt) (reg env)) ; 63c61,63 ; < (restore argl) ; --- ; > (assign argl (op list) (reg val)) ; > (restore env) ; > (assign val (op lookup-variable-value) (const n) (reg env)) ; The regular factorial needs an additional save-and-restore for argl before ; the recursive call. Argl must be saved because it contains the value of n ; before the recursive call. ; The alternative factorial needs an additional save-and-restore for env before ; the recursive call. Env must be saved for other the evaluation of the ; remaining arguments. In this case, the look-up of n which comes second for ; the alternative implementation. ; Neither version executes more efficiently and they have the same number of ; instructions. (display "[answered]\n") (display "\nex-5.34 - factorial-iter\n") (compile-to-file '(define (factorial n) (define (iter product counter) (if (> counter n) product (iter (* counter product) (+ counter 1)))) (iter 1 1)) 'val 'next "factorial-iter.sicp-asm") (display "\nex-5.35\n")