6 001 SICP Compilation Context special purpose vs

  • Slides: 34
Download presentation
6. 001 SICP Compilation • Context: special purpose vs. universal machines • Compilation vs.

6. 001 SICP Compilation • Context: special purpose vs. universal machines • Compilation vs. Interpretation • Evolution from ec-eval to compiler • Why is compiled code more efficient than interpreter? • Some ideas in compiler implementation • Target, linkages, & register management 1

Special-Purpose Machines Register Machine Input Output Example: gcd register machine a a, b hold

Special-Purpose Machines Register Machine Input Output Example: gcd register machine a a, b hold integers as input b rem a holds gcd(a, b) t 2

Scheme Evaluator as Universal Machine )gcd 12 8( MIT Scheme 4 )define (gcd a

Scheme Evaluator as Universal Machine )gcd 12 8( MIT Scheme 4 )define (gcd a b( ) if (= b 0( a ) gcd b (remainder a b(((( • Able to reconfigure (or program) our universal machine to perform the desired computation 3

MC-Eval is a Universal Machine )gcd 12 8( mc-eval 4 MIT Scheme )define (gcd

MC-Eval is a Universal Machine )gcd 12 8( mc-eval 4 MIT Scheme )define (gcd a b( ) if (= b 0( a ) gcd b (remainder a b(((( 4

Alternative Language versions possible Universal Machine MIT Scheme is a C program! )gcd 12

Alternative Language versions possible Universal Machine MIT Scheme is a C program! )gcd 12 8( C Language version of Scheme Evaluator 4 )define (gcd a b( ) if (= b 0( a ) gcd b (remainder a b(((( 5

EC-Eval – Register Machine Code Universal Machine )gcd 12 8( ec-eval: Stored Machine Instructions

EC-Eval – Register Machine Code Universal Machine )gcd 12 8( ec-eval: Stored Machine Instructions Universal Register Machine 4 )define (gcd a b( ) if (= b 0( a ) gcd b (remainder a b(((( 6

Compiler 8 12 gcd: Stored Machine Instructions Universal Register Machine 4 compiler Not necessarily

Compiler 8 12 gcd: Stored Machine Instructions Universal Register Machine 4 compiler Not necessarily a universal machine )define (gcd a b( ) if (= b 0( a ) gcd b (remainder a b(((( 7

Compilation – A Plan • Use same register machine as ec-eval. . . except

Compilation – A Plan • Use same register machine as ec-eval. . . except we don't hard-wire in the ec-eval controller • Look at the machine instructions that ec-eval used • save these instructions for later execution • Compiler implementation: • ev-xxx serve as initial template for compile-xxx • We'll see that this is grossly inefficient • many machine instructions are executed during interpretation that are not necessary during compilation • so we will examine compiler ideas that lead us toward more efficient generated machine code 8

Register Machine for Compiler • 7 registers • exp • env • continue •

Register Machine for Compiler • 7 registers • exp • env • continue • val • unev • proc • argl temporary register (rarely used) current environment return point resulting value temporary register (rarely used) operator value argument values • Abstract operations • environment model, primitive procedures • syntax operations now needed by compiler but not in output compiled code 9

Watching ec-eval on (f x) – pg. 1 1. )save continue( prepare to eval

Watching ec-eval on (f x) – pg. 1 1. )save continue( prepare to eval operator 2. )save env( (save env, operands, 3. )assign unev (op operands) (reg exp(( continue) 4. )save unev( 5. )assign exp (op operator) (reg exp(( 6. )assign continue (label ev-appl-did-operator(( 7. )goto (label eval-dispatch(( figure out what operator is. . . 8. )test (op self-evaluating? ) (reg exp(( 9. )branch (label ev-self-eval(( ah, it's a variable so look up 10. )test (op variable? ) (reg exp(( 11. )branch (label ev-variable(( 12. )assign val (op lookup-variable-value) (reg exp) (reg env(( 13. )goto (reg continue(( ; ev-appl-did-operator 14. )restore unev( 15. )restore env( 16. (assign argl (op empty-arglist)) 17. (assign proc (reg val)) store operator in proc 18. (test (op no-operands? ) (reg unev)) 10

Watching ec-eval on (f x) – pg. 2 19. 20. 21. 22. 23. 24.

Watching ec-eval on (f x) – pg. 2 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. )branch (label apply-dispatch(( branch never taken (save proc) (save argl) eval each of the operands. . . (assign exp (op first-operand) (reg unev)) (test (op last-operand? ) (reg unev)) (branch (label ev-appl-last-arg)) figure out how to eval (assign continue (label ev-appl-accum-last-arg)) first operand (goto (label eval-dispatch)) (test (op self-evaluating? ) (reg exp)) (branch (label ev-self-eval)) (test (op variable? ) (reg exp)) (branch (label ev-variable)) (assign val (op lookup-variable-value) (reg exp) (reg env)) (goto (reg continue)) goto accum-last-arg at line 33 (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) add to arg list (restore proc) have proc and argl; ; computation proceeds at apply-dispatch ready to apply 11

Key difference: compiling vs. interpreting • Interpreter/evaluator • See expression for first time at

Key difference: compiling vs. interpreting • Interpreter/evaluator • See expression for first time at run-time • Have to test to determine what kind of expression • Compiler: • We see expression at compile-time • Can pre-determine what registers needed or changed – can be much more efficient in saves/restores! 12

Register Usage – (f x) exp env val continue unev proc argl • eliminated

Register Usage – (f x) exp env val continue unev proc argl • eliminated syntax instructions used in ec-eval which can be pre-computed by the compiler 1. 2. 3. 4. 5. 6. 12. 14. 15. 16. 17. 20. 21. 22. 25. 31. 33. 34. 35. save assign restore reference )save continue( )save env( )assign unev (op operands) (reg exp(( )save unev( )assign exp (op operator) (reg exp(( )assign continue (label ev-appl-did-operator(( (assign val (op lookup-variable-value) (reg exp) (reg env)) (restore unev) (restore env) (assign argl (op empty-arglist)) (assign proc (reg val)) (save proc) (save argl) (assign exp (op first-operand) (reg unev)) (assign continue (label ev-appl-accum-last-arg)) (assign val (op lookup-variable-value) (reg exp) (reg env)) (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) (restore proc) 13

Don't need continuations: exp env val continue unev proc argl • no internal branches;

Don't need continuations: exp env val continue unev proc argl • no internal branches; don't need to save or assign continue register 1. 2. 3. 4. 5. 6. 12. 14. 15. 16. 17. 20. 21. 22. 25. 31. 33. 34. 35. save assign restore reference )save continue( )save env( )assign unev (op operands) (reg exp(( )save unev( )assign exp (op operator) (reg exp(( )assign continue (label ev-appl-did-operator(( (assign val (op lookup-variable-value) (reg exp) (reg env)) (restore unev) (restore env) (assign argl (op empty-arglist)) (assign proc (reg val)) (save proc) (save argl) (assign exp (op first-operand) (reg unev)) (assign continue (label ev-appl-accum-last-arg)) (assign val (op lookup-variable-value) (reg exp) (reg env)) (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) (restore proc) 14

Recognize form of (f x) exp env val continue unev proc argl • compiler

Recognize form of (f x) exp env val continue unev proc argl • compiler knows operator & operands are variables (ec-eval had to discover this at runtime) save assign restore reference 2. 3. 4. 5. )save env( )assign unev (op operands) (reg exp(( )save unev( )assign exp (op operator) (reg exp(( 12. 14. 15. 16. 17. 20. 21. 22. (assign val (op lookup-variable-value) (reg exp) )const f( (restore unev) (restore env) (assign argl (op empty-arglist)) (assign proc (reg val)) (save proc) (save argl) (assign exp (op first-operand) (reg unev)) (reg env)) 31. 33. 34. 35. (assign val (op lookup-variable-value) (reg exp) )const x( (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) (restore proc) (reg env)) 15

Remove extras saves and restores exp env val continue unev proc argl • detect

Remove extras saves and restores exp env val continue unev proc argl • detect when no change to reg between save and restore 2. save assign restore reference )save env( 12. (assign val (op lookup-variable-value) (const f) (reg env)) 15. 16. 17. 20. 21. (restore env) (assign argl (op empty-arglist)) (assign proc (reg val)) (save proc) (save argl) 31. 33. 34. 35. (assign val (op lookup-variable-value) (const x) (reg env)) (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) (restore proc) 16

Optimize Register Target exp env val continue unev proc argl • assign to proc

Optimize Register Target exp env val continue unev proc argl • assign to proc directly (no need to assign to val, then from val to proc) 12. )assign proc val save assign restore reference (op lookup-variable-value) (const f) (reg env(( 16. (assign argl (op empty-arglist)) 17. (assign proc (reg val)) 21. (save argl) 31. (assign val (op lookup-variable-value) (const x) (reg env)) 33. (restore argl) 34. (assign argl (op adjoin-arg) (reg val) (reg argl)) 17

Optimize argument list exp env val continue unev proc argl • recognize that we

Optimize argument list exp env val continue unev proc argl • recognize that we have only one argument (adjoin directly onto empty list argl) save assign restore reference 12. )assign proc (op lookup-variable-value) (const f) (reg env(( 16. (assign argl (op empty-arglist)) 21. (save argl) 31. (assign val (op lookup-variable-value) (const x) (reg env)) 33. (restore argl) 34. (assign argl (op list) (reg val)) 18

Our compiler: (compile '(f x) 'val 'next) results ; Same three lines from our

Our compiler: (compile '(f x) 'val 'next) results ; Same three lines from our register usage exercise: (assign proc (op lookup-variable-value) (const f) (reg env)) (assign val (op lookup-variable-value) (const x) (reg env)) (assign argl (op list) (reg val)) ; standard pattern for <apply-dispatch> (test (op primitive-procedure? ) (reg proc)) (branch (label primitive-branch 9)) compiled-branch 8 (assign continue (label after-call 7)) (assign val (op compiled-procedure-entry) (reg proc)) (goto (reg val)) primitive-branch 9 (assign val (op apply-primitive-procedure) (reg proc) (reg argl)) after-call 7 ; falls through to "next" code 19

Compiler Implementation Concepts Machine Instruction Sequence )compile exp target linkage( )f x( • Instruction

Compiler Implementation Concepts Machine Instruction Sequence )compile exp target linkage( )f x( • Instruction Sequences • compile a given expression • produce and append instruction sequences together as needed • Target • the register desired to hold value at end of instruction sequence • Linkages • how to connect instruction sequence to other instruction sequences (next, return, named label) 20

Examples – Targets & Linkages ) • compile <exp> <target> <linkage>) Different Targets: (compile

Examples – Targets & Linkages ) • compile <exp> <target> <linkage>) Different Targets: (compile 'x 'val 'next) (assign val (op lookup-variable-value) (const x) (reg env)) (compile 'x 'proc 'next) (assign proc (op lookup-variable-value) (const x) (reg env)) • Linkage 'return: (compile 'x 'val 'return) (assign val (op lookup-variable-value) (const x) (reg env)) (goto (reg continue)) • Linkage '<named-label>: (compile 'x 'val 'after-x-lookup) (assign val (op lookup-variable-value) (const x) (reg env)) (goto (label after-x-lookup)) 21

compiler – dispatch on expression type )define (compile exp target linkage( ) cond ((self-evaluating?

compiler – dispatch on expression type )define (compile exp target linkage( ) cond ((self-evaluating? exp( ) compile-self-evaluating exp target linkage(( )) quoted? exp) (compile-quoted exp target linkage(( )) variable? exp ( ) compile-variable exp target linkage(( )) assignment? exp ( ) compile-assignment exp target linkage(( )) definition? exp( ) compile-definition exp target linkage(( )) if? exp) (compile-if exp target linkage(( )) lambda? exp) (compile-lambda exp target linkage(( )) begin? exp( ) compile-sequence ) begin-actions exp) target linkage(( )) application? exp( ) compile-application exp target linkage(( ) else (error "Unknown exp type -- COMPILE” exp(((( 22

Look at a basic case: • Back in ec-eval, we had: ev-self-eval (assign val

Look at a basic case: • Back in ec-eval, we had: ev-self-eval (assign val (reg exp)) (goto (reg continue)) • Compiled code template: (assign <target> (const <expression>)) <linkage> • Compiler procedure (partial) that handles linkage: (define (compile-self-evaluating exp target linkage) (end-with-linkage `((assign , target (const , exp))))) 23

Compiling Linkages )define (end-with-linkage instruction-sequence) (preserving '(continue) ; ignore this for now instruction-sequence (compile-linkage)))

Compiling Linkages )define (end-with-linkage instruction-sequence) (preserving '(continue) ; ignore this for now instruction-sequence (compile-linkage))) (define (compile-linkage) (cond ((eq? linkage 'return) (make-instruction-sequence '(continue) '((goto (reg continue))))) ((eq? linkage 'next) (empty-instruction-sequence)) (else (make-instruction-sequence '() `((goto (label , linkage))))))) 24

Compiling Application – More General Case • In general, may need to save/restore registers;

Compiling Application – More General Case • In general, may need to save/restore registers; e. g. eval of operator may itself modify env, continue [(save continue)] [(save env)] <evaluate operator; result in proc> [(restore env)] [(save proc)] <evaluate operands; result in argl> [(restore proc)] [(restore continue)] <apply procedure in proc to args in argl, and link> • Paranoid/simple approach: save & restore anyway • More efficient approach: manage register usage carefully 25

Managing Registers – Encode the Contract! ; needs: expression to evaluate ; environment ;

Managing Registers – Encode the Contract! ; needs: expression to evaluate ; environment ; continue return point ; output: value of expression ; modifies: unev ; stack: unchanged (make-instruction-sequence needs modifies statements) instruction sequence registers needed (used by sequence) registers modified by sequence machine statements (machine code) • "instruction sequence" data structure: • remember registers needed and modified in addition to code 26

Appending Machine Instructions • If we know code-1 and code-2 don't interfere, just append

Appending Machine Instructions • If we know code-1 and code-2 don't interfere, just append code • Update merged "needs" and "modifies" data for combined sequence )append-instruction-sequences seq-1 seq-2( needs-1 needs-2 modifies-1 modifies-2 code-1 needs-1 modifies-1 UNION (needs-2 – modifies-2 modifies-1) code-1 code-2 27

Preserving Needed Registers • A "smart" append of two code sequences which "preserves" registers

Preserving Needed Registers • A "smart" append of two code sequences which "preserves" registers • save any of specified registers if (and only if) seq-1 clobbers them )preserving regs seq-1 seq-2) needs-1 needs-2 modifies-1 modifies-2 code-1 code-2 needs-1 modifies-1 UNION (needs-2 – modifies-2 modifies-1) save regs (needs-2 AND mod-1) code-1 restore code-2 28

Now Understand Compile of Application )]save continue)] [(save env)] <evaluate operator; result in proc>

Now Understand Compile of Application )]save continue)] [(save env)] <evaluate operator; result in proc> Same template [(restore env)] as before [(save proc)] <evaluate operands; result in argl> [(restore proc)] [(restore continue)] <apply procedure in proc to args in argl, and link> (define (compile-application exp target linkage) (let ((proc-code (compile (operator exp) 'proc 'next)) (arg-codes (map (lambda (arg) (compile arg 'val 'next)) (operands exp)))) (preserving '(env continue) proc-code (preserving '(proc continue) (construct-arglist arg-codes) (compile-procedure-call target linkage))))) 29

De-compilation & Optimization Machine Instruction Sequence decompile (10 +) Scheme expression ) ) assign

De-compilation & Optimization Machine Instruction Sequence decompile (10 +) Scheme expression ) ) assign proc (op lookup-variable-value ( const +) (reg env (( (assign val (const 10)) (assign argl (op list) (reg val)) (test (op primitive-procedure? ) (reg proc)) (branch (label primitive-branch 9)) compiled-branch 8 (assign continue (label after-call 7)) (assign val (op compiled-procedure-entry) (reg proc)) (goto (reg val)) primitive-branch 9 (assign val (op apply-primitive-procedure) (reg proc) (reg argl)) after-call 7 • Optimization: constant '(10) directly into argl • Optimization? : replace whole thing with value 10 30

Summary • Compilation vs. Interpretation • Interpretation: evaluate expressions at run-time • Compilation: analyze

Summary • Compilation vs. Interpretation • Interpretation: evaluate expressions at run-time • Compilation: analyze program code and generate register machine instructions for later execution • Some ideas in compilation – towards efficient code • Templates, targets & linkages • Strategies for register management • Many further optimizations possible! 31

May 3, 2000 Recitation Problem 6. 001 • Decompile the following register code (what

May 3, 2000 Recitation Problem 6. 001 • Decompile the following register code (what expression was compiled to produce these machine instructions)? (assign proc (op lookup-variable-value) (const list) (reg env)) (assign val (op lookup-variable-value) (const y) (reg env)) (assign argl (op list) (reg val)) (assign val (op lookup-variable-value) (const x) (reg env)) (assign argl (op cons) (reg val) (reg argl)) (assign val (op lookup-variable-value) (const +) (reg env)) (assign argl (op cons) (reg val) (reg argl)) ; ; proceed with apply-dispatch pattern 32

Watching ec-eval on (f x) – pg. 1 1. )save continue( prepare to eval

Watching ec-eval on (f x) – pg. 1 1. )save continue( prepare to eval operator 2. )save env( (save env, operands, 3. )assign unev (op operands) (reg exp(( continue) 4. )save unev( 5. )assign exp (op operator) (reg exp(( 6. )assign continue (label ev-appl-did-operator(( 7. )goto (label eval-dispatch(( figure out what operator is. . . 8. )test (op self-evaluating? ) (reg exp(( 9. )branch (label ev-self-eval(( 10. )test (op variable? ) (reg exp(( ah, it's a variable so look up 11. )branch (label ev-variable(( 12. )assign val (op lookup-variable-value) (reg exp) (reg env(( 13. )goto (reg continue(( 14. )restore unev( 15. )restore env( 16. (assign argl (op empty-arglist)) 17. (assign proc (reg val)) store operand in proc 18. (test (op no-operands? ) (reg unev)) 33

Watching ec-eval on (f x) – pg. 2 19. 20. 21. 22. 23. 24.

Watching ec-eval on (f x) – pg. 2 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. )branch (label apply-dispatch(( branch never taken (save proc) (save argl) eval each of the operands. . . (assign exp (op first-operand) (reg unev)) (test (op last-operand? ) (reg unev)) (branch (label ev-appl-last-arg)) (assign continue (label ev-appl-accum-last-arg)) (goto (label eval-dispatch)) figure out how to eval (test (op self-evaluating? ) (reg exp)) first operand (branch (label ev-self-eval)) (test (op variable? ) (reg exp)) (branch (label ev-variable)) (assign val (op lookup-variable-value) (reg exp) (reg env)) (goto (reg continue)) goto accum-last-arg at line 33 (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) add to arg list (restore proc) have proc and argl; ; computation proceeds at apply-dispatch ready to apply 34