Exercise 5.37. One way to understand the compiler's preserving mechanism for optimizing stack usage is to see what extra operations would be generated if we did not use this idea. Modify preserving so that it always generates the save and restore operations. Compile some simple expressions and identify the unnecessary stack operations that are generated. Compare the code to that generated with the preserving mechanism intact. ———————————————————————————————————————————————————————————————————————— Here is the updated procedure: (define (preserving regs seq1 seq2) (if (null? regs) (append-instruction-sequences seq1 seq2) (let ((first-reg (car regs))) ; (if (and (needs-register? seq2 first-reg) ; (modifies-register? seq1 first-reg)) (preserving (cdr regs) (make-instruction-sequence (list-union (list first-reg) (registers-needed seq1)) (list-difference (registers-modified seq1) (list first-reg)) (append `((save ,first-reg)) (statements seq1) `((restore ,first-reg)))) seq2)))) ; (preserving (cdr regs) seq1 seq2))))) With the optimization: 1 ]=> (pp (compile '(if (> n 1) 0 1) 'val 'next)) ((env) (env proc argl continue val) ((assign proc (op lookup-variable-value) (const >) (reg env)) (assign val (const 1)) (assign argl (op list) (reg val)) (assign val (op lookup-variable-value) (const n) (reg env)) (assign argl (op cons) (reg val) (reg argl)) (test (op primitive-procedure?) (reg proc)) (branch (label primitive-branch28)) compiled-branch27 (assign continue (label after-call26)) (assign val (op compiled-procedure-entry) (reg proc)) (goto (reg val)) primitive-branch28 (assign val (op apply-primitive-procedure) (reg proc) (reg argl)) after-call26 (test (op false?) (reg val)) (branch (label false-branch24)) true-branch25 (assign val (const 0)) (goto (label after-if23)) false-branch24 (assign val (const 1)) after-if23)) Without: 1 ]=> (pp (compile '(if (> n 1) 0 1) 'val 'next)) ((env continue) (proc argl val) ((save continue) (save env) (save continue) (save env) (save continue) (assign proc (op lookup-variable-value) (const >) (reg env)) (restore continue) (restore env) (restore continue) (save continue) (save proc) (save env) (save continue) (assign val (const 1)) (restore continue) (assign argl (op list) (reg val)) (restore env) (save argl) (save continue) (assign val (op lookup-variable-value) (const n) (reg env)) (restore continue) (restore argl) (assign argl (op cons) (reg val) (reg argl)) (restore proc) (restore continue) (test (op primitive-procedure?) (reg proc)) (branch (label primitive-branch6)) compiled-branch5 (assign continue (label after-call4)) (assign val (op compiled-procedure-entry) (reg proc)) (goto (reg val)) primitive-branch6 (save continue) (assign val (op apply-primitive-procedure) (reg proc) (reg argl)) (restore continue) after-call4 (restore env) (restore continue) (test (op false?) (reg val)) (branch (label false-branch2)) true-branch3 (save continue) (assign val (const 0)) (restore continue) (goto (label after-if1)) false-branch2 (save continue) (assign val (const 1)) (restore continue) after-if1)) So, just looking at the first few operations: ((assign proc (op lookup-variable-value) (const >) (reg env)) (assign val (const 1)) (assign argl (op list) (reg val)) (assign val (op lookup-variable-value) (const n) (reg env)) (assign argl (op cons) (reg val) (reg argl)) (test (op primitive-procedure?) (reg proc)) These become: ((save continue) (save env) (save continue) (save env) (save continue) (assign proc (op lookup-variable-value) (const >) (reg env)) (restore continue) (restore env) (restore continue) (save continue) (save proc) (save env) (save continue) (assign val (const 1)) (restore continue) (assign argl (op list) (reg val)) (restore env) (save argl) (save continue) (assign val (op lookup-variable-value) (const n) (reg env)) (restore continue) (restore argl) (assign argl (op cons) (reg val) (reg argl)) (restore proc) (restore continue) (test (op primitive-procedure?) (reg proc)) The issue is that when the compiler generates these sequences, it must preserve certain registers which may be modified by the generated code in some cases but not others. For example the first line which assigns the proc, in this case is a direct lookup of > in the environment, and does not modify any register other than proc itself. However, since the procedure to be applied could equally well have been a combination itself, the code to evaluate the procedure is wrapped with save and restore operations for the env and continue registers.