├── _config.yml ├── R0-interp-example.rkt ├── R0-height.rkt ├── interp-R0.rkt ├── lecture-Aug-25.md ├── lecture-Sep-22.md ├── lecture-Nov-19.md ├── lecture-Aug-27.md ├── lecture-Oct-22.md ├── lecture-Oct-29.md ├── lecture-Nov-10.md ├── lecture-Oct-8.md ├── lecture-Sep-3.md ├── lecture-Oct-1.md ├── lecture-Sep-1.md ├── lecture-Sep-10.md ├── lecture-Oct-15.md ├── lecture-Dec-1.md ├── lecture-Nov-5.md ├── lecture-Sep-17.md ├── lecture-Nov-3.md ├── lecture-Nov-12.md ├── lecture-Oct-13.md ├── lecture-Sep-29.md ├── lecture-Sep-24.md ├── lecture-Nov-17.md ├── lecture-Sep-15.md ├── lecture-Oct-27.md ├── lecture-Sep-8.md ├── lecture-Dec-10.md ├── lecture-Oct-20.md ├── README.md └── lecture-Oct-6.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /R0-interp-example.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require "utilities.rkt") 3 | (require "interp-R0.rkt") 4 | 5 | ;; 42 6 | (define E1 (Int 42)) 7 | 8 | ;; (read) 9 | (define E2 (Prim 'read '())) 10 | 11 | ;; (- 42) 12 | (define E3 (Prim '- (list E1))) 13 | 14 | ;; (+ (- 42) 5) 15 | (define E4 (Prim '+ (list E3 (Int 5)))) 16 | 17 | ;; (+ (read) (- (read))) 18 | (define E5 (Prim '+ (list E2 (Prim '- (list E2))))) 19 | 20 | (interp-R0 (Program '() E1)) 21 | (interp-R0 (Program '() E2)) 22 | (interp-R0 (Program '() E3)) 23 | (interp-R0 (Program '() E4)) 24 | (interp-R0 (Program '() E5)) -------------------------------------------------------------------------------- /R0-height.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (struct Int (value)) 4 | (struct Prim (op arg*)) 5 | (struct Read ()) 6 | (struct Add (left right)) 7 | (struct Neg (value)) 8 | 9 | (define E1 (Int 42)) 10 | (define E2 (Prim 'read '())) 11 | (define E3 (Prim '- (list E1))) 12 | (define E4 (Prim '+ (list E3 (Int 5)))) 13 | (define E5 (Prim '+ (list E2 (Prim '- (list E2))))) 14 | 15 | (define (list-max ls) 16 | (foldl max 0 ls)) 17 | 18 | (define (height e) 19 | (match e 20 | [(Int n) 1] 21 | [(Prim op e*) 22 | (add1 (list-max (map height e*)))] 23 | )) 24 | 25 | (height E1) 26 | (height E2) 27 | (height E3) 28 | (height E4) 29 | (height E5) -------------------------------------------------------------------------------- /interp-R0.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require racket/fixnum) 3 | (require "utilities.rkt") 4 | (provide interp-R0) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | ;; Interpreter for R0: integer arithmetic 8 | 9 | ;; Note to maintainers of this code: 10 | ;; A copy of this interpreter is in the book and should be 11 | ;; kept in sync with this code. This code does not use 12 | ;; the match 'app' feature because the book doesn't introduce 13 | ;; that until a later. 14 | 15 | (define (interp-exp e) 16 | (match e 17 | [(Int n) n] 18 | [(Prim 'read '()) 19 | (define r (read)) 20 | (cond [(fixnum? r) r] 21 | [else (error 'interp-R0 "expected an integer" r)])] 22 | [(Prim '- (list e)) 23 | (define v (interp-exp e)) 24 | (fx- 0 v)] 25 | [(Prim '+ (list e1 e2)) 26 | (define v1 (interp-exp e1)) 27 | (define v2 (interp-exp e2)) 28 | (fx+ v1 v2)] 29 | )) 30 | 31 | (define (interp-R0 p) 32 | (match p 33 | [(Program info e) (interp-exp e)] 34 | )) 35 | -------------------------------------------------------------------------------- /lecture-Aug-25.md: -------------------------------------------------------------------------------- 1 | August 25 2 | --------- 3 | 4 | Welcome to Compilers! (P423, P523, E313, E513) 5 | 6 | * Instructors: Jeremy and Caner 7 | 8 | * Roll call 9 | 10 | * What's a compiler? 11 | 12 | * Table of Contents of Essentials of Compilation 13 | 14 | * Assignments, Quizzes, Exams, Grading, Academic Integrity 15 | 16 | * Technology 17 | 18 | * Canvas FA20: COMPILERS: 10222 19 | Link to real course web page 20 | Grades 21 | 22 | * Web page: 23 | https://iucompilercourse.github.io/IU-P423-P523-E313-E513-Fall-2020/ 24 | 25 | * Chat: Slack http://iu-compiler-course.slack.com/ 26 | 27 | * Email group: Piazza http://piazza.com/iu/fall2020/p423p523e313e513/home 28 | 29 | * Lecture video: 30 | Zoom Meeting ID 950 3713 8921 31 | Google Meet https://meet.google.com/pyt-eqtm-pqw 32 | 33 | * Github repository for assignment submission, starter code 34 | 35 | * What to do when technology fails 36 | 37 | * Zoom fails during lecture: communicate on slack, switch to Google Meet 38 | * Github: communicate on slack, wait 39 | * Technology glitches will not impact grades 40 | 41 | * Concrete Syntax, Abstract Syntax Trees, Racket Structures 42 | 43 | * Programs in concrete syntax and in ASTs 44 | 45 | 42 46 | 47 | (read) 48 | 49 | (- 10) 50 | 51 | (+ (- 10) 5) 52 | 53 | (+ (read) (- (read))) 54 | 55 | * Racket structures 56 | 57 | (struct Int (value)) 58 | (struct Prim (op arg*)) 59 | 60 | * Grammars 61 | * Concrete syntax 62 | 63 | exp ::= int | (read) | (- exp) | (+ exp exp) | (- exp exp) 64 | R0 ::= exp 65 | 66 | * Abstract syntax 67 | 68 | exp ::= (Int int) | (Prim 'read '()) 69 | | (Prim '- (list exp)) 70 | | (Prim '+ (list exp exp)) 71 | R0 ::= (Program '() exp) 72 | 73 | 74 | * Pattern Matching and Structural Recursion `R0-height.rkt` 75 | 76 | -------------------------------------------------------------------------------- /lecture-Sep-22.md: -------------------------------------------------------------------------------- 1 | # Example x86 output from running example 2 | 3 | Input R1 program: 4 | 5 | (let ([v 1]) 6 | (let ([w 42]) 7 | (let ([x (+ v 7)]) 8 | (let ([y x]) 9 | (let ([z (+ x w)]) 10 | (+ z (- y))))))) 11 | 12 | using two registers: rbx, rcx 13 | 14 | | Color | Home | 15 | |-------|-----------| 16 | t | 0 | rbx | 17 | v | 0 | rbx | 18 | w | 2 | -16(%rbp) | 19 | x | 0 | rbx | 20 | y | 0 | rbx | 21 | z | 1 | rcx | 22 | 23 | Output x86: 24 | 25 | start: 26 | movq $1, %rbx 27 | movq $42, -16(%rbp) 28 | addq $7, %rbx 29 | movq %rbx, %rcx 30 | addq -16(%rbp), %rcx 31 | negq %rbx 32 | movq %rcx, %rax 33 | addq %rbx, %rax 34 | jmp conclusion 35 | 36 | .globl main 37 | main: 38 | pushq %rbp 39 | movq %rsp, %rbp 40 | pushq %rbx 41 | subq $8, %rsp 42 | jmp start 43 | 44 | conclusion: 45 | addq $8, %rsp 46 | popq %rbx 47 | popq %rbp 48 | retq 49 | 50 | 51 | (let ([x1 (read)]) 52 | (let ([x2 (read)]) 53 | (+ (+ x1 x2) 54 | 42))) 55 | 56 | start: 57 | callq read_int 58 | movq %rax, %rbx 59 | callq read_int 60 | movq %rax, %rcx 61 | addq %rcx, %rbx 62 | movq %rbx, %rax 63 | addq $42, %rax 64 | jmp conclusion 65 | 66 | .globl main 67 | main: 68 | pushq %rbp 69 | movq %rsp, %rbp 70 | pushq %rbx 71 | subq $8, %rsp 72 | jmp start 73 | conclusion: 74 | addq $8, %rsp 75 | popq %rbx 76 | popq %rbp 77 | retq 78 | 79 | 80 | 81 | # Code Review of Register Allocation 82 | 83 | ## Liveness Analysis 84 | 85 | uncover-live 86 | uncover-line-block 87 | uncover-live-stmts 88 | uncover-live-instr ** 89 | write-vars 90 | read-vars 91 | 92 | ## Build Interference 93 | 94 | build-interference (print-dot) 95 | build-interference-block 96 | build-interference-instr ** 97 | 98 | ## Allocate Registers 99 | 100 | allocate-registers 101 | color-graph ** 102 | choose-color 103 | identify-home 104 | assign-homes-block (no change) 105 | 106 | 107 | -------------------------------------------------------------------------------- /lecture-Nov-19.md: -------------------------------------------------------------------------------- 1 | # Compiling Loops & Liveness Analysis via Dataflow 2 | 3 | Example program: 4 | 5 | (let ([sum (vector 0)]) 6 | (let ([_ (for ([x (vector 1 2 3)]) 7 | (vector-set! sum 0 (+ x (vector-ref sum 0))))]) 8 | (vector-ref sum 0))) 9 | 10 | 11 | ## Explicate Control 12 | 13 | (assign y (for ([x seq]) body)) cont-label 14 | ===> 15 | vec = seq' 16 | i = 0 17 | n = (vector-length vec) 18 | goto loop-label 19 | 20 | loop-label: 21 | if (eq? i n) 22 | goto cont-label 23 | else 24 | goto body-label 25 | 26 | body-label: 27 | x = (vector-ref vec i) 28 | body' 29 | i = i + 1 30 | goto loop-label 31 | 32 | ## Liveness Analysis 33 | 34 | Recall that this is a backwards analysis. 35 | 36 | The rule for an assignment statement: 37 | 38 | (S - {x}) \/ R(e) 39 | x = e 40 | S 41 | 42 | state = set of variables 43 | 44 | transfer function: 45 | 46 | f(x = e, S) = (S - {x}) \/ R(e) 47 | 48 | meet operator: (merge state information) 49 | 50 | meet : set * set -> set 51 | 52 | meet S1 S2 = S1 \/ S2 (set union) 53 | 54 | partial order: 55 | 56 | set containment (reverse of subset-or-equal) 57 | 58 | 1. initialize live-before of each block with empty set 59 | 2. apply the transfer function to all the blocks, 60 | over and over again until the live-before sets 61 | for the blocks stop changing. 62 | 63 | start: 64 | {} 65 | vec = (vector 1 2 3) 66 | {vec} 67 | i = 0 68 | {i,vec} 69 | n = (vector-length vec) 70 | {i,n,vec} 71 | goto loop-label 72 | 73 | loop-label: 74 | {i,n,vec} 75 | if (eq? i n) 76 | {} 77 | goto conclusion 78 | else 79 | {i,n,vec} 80 | goto body-label 81 | 82 | body-label: 83 | {i,n,vec} 84 | x = (vector-ref vec i) 85 | {x,i,n,vec} 86 | body' 87 | {i,n,vec} 88 | i = i + 1 89 | {i,n,vec} 90 | goto loop-label 91 | 92 | 93 | suppose a function has three variables: x,y,z 94 | 95 | lattice of "states" 96 | meet = greatest lower bound 97 | 98 | {}__________ top 99 | | \ \ 100 | {x} {y} {z} 101 | | / \ / \ 102 | {x,y} {y,z} {x,z} 103 | | /_________/ 104 | {x,y,z} bottom 105 | 106 | 107 | -------------------------------------------------------------------------------- /lecture-Aug-27.md: -------------------------------------------------------------------------------- 1 | # August 27 2 | 3 | 4 | ## Teams and Github Repositories 5 | 6 | ## Definitional Interpreters `interp-R0.rkt`, `R0-interp-example.rkt` 7 | 8 | Draw correctness diagram. 9 | 10 | ## The R1 Language: R0 + variables and let 11 | 12 | exp ::= int | (read) | (- exp) | (+ exp exp) 13 | | var | (let ([var exp]) exp) 14 | R1 ::= exp 15 | 16 | Examples: 17 | 18 | (let ([x (+ 12 20)]) 19 | (+ 10 x)) 20 | 21 | (let ([x 32]) 22 | (+ (let ([x 10]) x) 23 | x)) 24 | 25 | Interpreter for R1: `interp-R1.rkt` 26 | 27 | ## x86 Assembly Language 28 | 29 | reg ::= rsp | rbp | rsi | rdi | rax | .. | rdx | r8 | ... | r15 30 | arg ::= $int | %reg | int(%reg) 31 | instr ::= addq arg, arg | 32 | subq arg, arg | 33 | negq arg | 34 | movq arg, arg | 35 | callq label | 36 | pushq arg | 37 | popq arg | 38 | retq 39 | prog ::= .globl main 40 | main: instr^{+} 41 | 42 | 43 | Intel Machine: 44 | * program counter 45 | * registers 46 | * memory (stack and heap) 47 | 48 | Example program: 49 | 50 | (+ 10 32) 51 | 52 | => 53 | 54 | .globl main 55 | main: 56 | movq $10, %rax 57 | addq $32, %rax 58 | movq %rax, %rdi 59 | callq print_int 60 | movq $0, %rax 61 | retq 62 | 63 | 64 | ## What's different? 65 | 66 | 1. 2 args and return value vs. 2 arguments with in-place update 67 | 2. nested expressions vs. atomic expressions 68 | 3. order of evaluation: left-to-right depth-first, vs. sequential 69 | 4. unbounded number of variables vs. registers + memory 70 | 5. variables can overshadow vs. uniquely named registers + memory 71 | 72 | * `select-instructions`: convert each R1 operation into a sequence 73 | of instructions 74 | * `remove-complex-opera*`: ensure that each sub-expression is 75 | atomic by introducing temporary variables 76 | * `explicate-control`: convert from the AST to a control-flow graph 77 | * `assign-homes`: replace variables with stack locations 78 | * `uniquify`: rename variables so they are all unique 79 | 80 | 81 | In what order should we do these passes? 82 | 83 | Gordian Knot: 84 | * instruction selection 85 | * register/stack allocation 86 | 87 | solution: do instruction selection optimistically, assuming all 88 | registers then do register allocation then patch up the 89 | instructions 90 | 91 | 92 | R_1 93 | | uniquify 94 | V 95 | R_1 96 | | remove-complex-opera* 97 | V 98 | R_1 99 | | explicate-control 100 | V 101 | C_0 102 | | select instructions 103 | V 104 | x86* 105 | | assign homes 106 | V 107 | x86* 108 | | patch instructions 109 | V 110 | x86 111 | | print x86 112 | V 113 | x86 string 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /lecture-Oct-22.md: -------------------------------------------------------------------------------- 1 | # Lambda: Lexically Scoped Functions 2 | 3 | ## Example 4 | 5 | (define (f [x : Integer]) : (Integer -> Integer) 6 | (let ([y 4]) 7 | (lambda: ([z : Integer]) : Integer 8 | (+ x (+ y z))))) 9 | 10 | (let ([g (f 5)]) 11 | (let ([h (f 3)]) 12 | (+ (g 11) (h 15)))) 13 | 14 | 15 | ## Syntax 16 | 17 | concrete syntax: 18 | 19 | exp ::= ... | (lambda: ([var : type]...) : type exp) 20 | R5 ::= def* exp 21 | 22 | abstract syntax: 23 | 24 | exp ::= ... | (Lambda ([var : type]...) type exp) 25 | R5 ::= (ProgramDefsExp info def* exp) 26 | 27 | 28 | ## Interpreter for R5 29 | 30 | see `interp-R5.rkt`: 31 | 32 | * case for lambda, 33 | * case for application, 34 | * case for define (mcons), 35 | * case for program (backpatching). 36 | 37 | ## Type Checker for R5 38 | 39 | see `type-check-R5.rkt`: 40 | 41 | The case for lambda. 42 | 43 | ## Free Variables 44 | 45 | Def. A variable is *free with respect to an expression* e if the 46 | variable occurs inside e but does not have an enclosing binding in e. 47 | 48 | Use above example to show examples of free variables. 49 | 50 | ## Closure Representation 51 | 52 | Figure 7.2 in book, diagram of g and h from above example. 53 | 54 | # Closure Conversion Pass (after reveal-functions) 55 | 56 | For lambda: 57 | 58 | (lambda: (ps ...) : rt body) 59 | ==> 60 | (vector (function-ref name) fvs ...) 61 | 62 | and also generate a top-level function 63 | 64 | (define (name [clos : _] ps ...) 65 | (let ([fv_1 (vector-ref clos 1)]) 66 | (let ([fv_2 (vector-ref clos 2)]) 67 | ... 68 | body'))) 69 | 70 | For application: 71 | 72 | (e es ...) 73 | ==> 74 | (let ([tmp e']) 75 | ((vector-ref tmp 0) tmp es' ...)) 76 | 77 | ## Example 78 | 79 | (define (f (x : Integer)) : (Integer -> Integer) 80 | (let ((y 4)) 81 | (lambda: ((z : Integer)) : Integer 82 | (+ x (+ y z))))) 83 | 84 | (let ((g ((fun-ref f) 5))) 85 | (let ((h ((fun-ref f) 3))) 86 | (+ (g 11) (h 15)))) 87 | 88 | ==> 89 | 90 | (define (f (clos.1 : _) (x : Integer)) : (Vector ((Vector _) Integer -> Integer)) 91 | (let ((y 4)) 92 | (vector (fun-ref lam.1) x y))) 93 | 94 | (define (lam.1 (clos.2 : (Vector _ Integer Integer)) (z : Integer)) : Integer 95 | (let ((x (vector-ref clos.2 1))) 96 | (let ((y (vector-ref clos.2 2))) 97 | (+ x (+ y z))))) 98 | 99 | (let ((g (let ((t.1 (vector (fun-ref f)))) 100 | ((vector-ref t.1 0) t.1 5)))) 101 | (let ((h (let ((t.2 (vector (fun-ref f)))) 102 | ((vector-ref t.2 0) t.2 3)))) 103 | (+ (let ((t.3 g)) ((vector-ref t.3 0) t.3 11)) 104 | (let ((t.4 h)) ((vector-ref t.4 0) t.4 15))))) 105 | -------------------------------------------------------------------------------- /lecture-Oct-29.md: -------------------------------------------------------------------------------- 1 | # Closure Convertion: Compilation Pass 2 | 3 | ## Example 4 | 5 | (define (f [x : Integer]) : (Integer -> Integer) 6 | (let ([y 4]) 7 | (lambda: ([z : Integer]) : Integer 8 | (+ x (+ y z))))) 9 | 10 | (let ([g (f 5)]) 11 | (let ([h (f 3)]) 12 | (+ (g 11) (h 15)))) 13 | 14 | From last time: 15 | 16 | 1. lambda's create closures 17 | (a vector with function pointer and values of free variables) 18 | 2. a function call retrieves the function pointer from the closure 19 | and calls it, passing is the closure and the regular arguments. 20 | 3. generate a function definition for each lambda. 21 | It has an extra parameter for the closure and 22 | starts with a sequence of let bindings that 23 | put the values of the free variables (from the closure) 24 | into variables with the same names as the free variables. 25 | 26 | 27 | 28 | ## Closure Conversion Pass (after reveal-functions) 29 | 30 | For lambda: 31 | 32 | (lambda: (ps ...) : rt body) 33 | ==> 34 | (vector (fun-ref name) fv_1 ... fv_n) 35 | 36 | where 37 | 38 | fv_1 ... fv_n = (free-variables (lambda: (ps ...) : rt body)) 39 | 40 | use racket `set` or `hash` 41 | 42 | and also generate a top-level function 43 | 44 | (define (name [clos : (Vector _ fvT_1 ... fvT_n)] ps ...) 45 | (let ([fv_1 (vector-ref clos 1)]) 46 | ... 47 | (let ([fv_n (vector-ref clos n)]) 48 | body'))) 49 | 50 | For function references: 51 | 52 | (fun-ref f) 53 | ==> 54 | (vector (fun-ref f)) 55 | 56 | For application: 57 | 58 | (e es ...) 59 | ==> 60 | (let ([tmp e']) 61 | ((vector-ref tmp 0) tmp es' ...)) 62 | 63 | Types should also be converted: 64 | 65 | (T_1 ... T_n -> T_r) 66 | ==> 67 | (Vector ((Vector _) T_1' ... T_n' -> T_r')) 68 | 69 | where T_1' ... T_n' and T_r' have been recursively 70 | converted. 71 | 72 | Vector types should be recursively converted. 73 | 74 | Integers and Booleans do not change. 75 | 76 | 77 | ## Example 78 | 79 | After `reveal-functions`, the example is transformed into the 80 | following using `fun-ref` to refer to function `f`. 81 | 82 | (define (f74 [x75 : Integer]) : (Integer -> Integer) 83 | (let ([y76 4]) 84 | (lambda: ([z77 : Integer]) : Integer 85 | (+ x75 (+ y76 z77))))) 86 | 87 | (define (main) : Integer 88 | (let ([g78 ((fun-ref f74) 5)]) 89 | (let ([h79 ((fun-ref f74) 3)]) 90 | (+ (g78 11) (h79 15))))) 91 | 92 | 93 | Closure conversion produces the following: 94 | 95 | (define (f74 [fvs82 : _] [x75 : Integer]) 96 | : (Vector ((Vector _) Integer -> Integer)) 97 | (let ([y76 4]) 98 | (vector (fun-ref lambda80) x75 y76))) 99 | 100 | (define (lambda80 [fvs81 : (Vector _ Integer Integer)] 101 | [z77 : Integer]) 102 | : Integer 103 | (let ([x75 (vector-ref fvs81 1)]) 104 | (let ([y76 (vector-ref fvs81 2)]) 105 | (+ x75 (+ y76 z77))))) 106 | 107 | (define (main) : Integer 108 | (let ([g78 (let ([app83 (vector (fun-ref f74))]) 109 | ((vector-ref app83 0) app83 5))]) 110 | (let ([h79 (let ([app84 (vector (fun-ref f74))]) 111 | ((vector-ref app84 0) app84 3))]) 112 | (+ (let ([app85 g78]) 113 | ((vector-ref app85 0) app85 11)) 114 | (let ([app86 h79]) 115 | ((vector-ref app86 0) app86 15)))))) 116 | 117 | 118 | alternative 119 | 120 | (define (main) : Integer 121 | (let ([j (if (eq? (read) 0) (fun-ref f74) (lambda: ...))]) 122 | (let ([g78 (j 5)]) 123 | (let ([h79 ((fun-ref f74) 3)]) 124 | (+ (g78 11) (h79 15)))))) 125 | 126 | 127 | -------------------------------------------------------------------------------- /lecture-Nov-10.md: -------------------------------------------------------------------------------- 1 | # Code Review of Functions 2 | 3 | 4 | ## Shrink 5 | 6 | Changes `ProgramDefsExp` to `ProgramDefs` by adding a definition for 7 | the `main` function. 8 | 9 | 10 | ## Reveal Functions 11 | 12 | Changes `(Var f)` to `(FunRef f)` when `f` is the name of a function. 13 | 14 | 15 | ## Limit Functions 16 | 17 | For functions with more than 6 parameters, reduce the number of 18 | parameters to 6 by packing parameter 6 and higher into a vector. 19 | 20 | * `Def` pass parameter 6 and higher in a vector 21 | * `Var` replace some parameters with vector-refs 22 | 23 | 24 | ## Remove Complex Operands 25 | 26 | `Apply` and `FunRef` are complex. 27 | 28 | New auxiliary function `rco-def`. 29 | 30 | 31 | ## Explicate Control 32 | 33 | Add cases for `Apply` and `FunRef`. 34 | 35 | New auxiliary function `explicate-control-def`. 36 | 37 | 38 | ## Instruction Selection 39 | 40 | * `FunRef` to `leaq` 41 | 42 | * `Call` move arguments into registers, indirect call, move rax to lhs 43 | 44 | * `TailCall` move arguments into registers, `TailJmp` 45 | (see `functions_tests_21.rkt`) 46 | 47 | * `Return` as usual, jump to `conclusion` but need to 48 | add function name to `conclusion` label. 49 | 50 | New auxiliary function `select-instr-def`. 51 | 52 | * `Def` remove parameters, initial them from registers 53 | 54 | * Add function name to `start` label 55 | 56 | 57 | ## Uncover Live 58 | 59 | * Update `free-vars` to handle `FunRef`. 60 | 61 | * Update `read-vars` and `write-vars` to handle 62 | `IndirectCallq`, `TailJmp`, and `leaq`. 63 | 64 | 65 | ## Build Interference 66 | 67 | * `Callq` and `IndirectCallq`, add edges between live vectors and 68 | callee-saved registers 69 | 70 | 71 | ## Allocate Registers 72 | 73 | New auxiliary function `allocate-registers-def`. 74 | 75 | * `Def` 76 | 77 | Perform register allocation separately for each function definition. 78 | Adapt the code for `Program` in the past assignments. 79 | 80 | 81 | ## Patch Instructions 82 | 83 | * `leaq` destination in a register 84 | 85 | * `TailJmp` target in `rax` 86 | 87 | New auxiliary function `patch-instr-def`. 88 | 89 | ## Print x86 90 | 91 | ### `FunRef` to PC-relative address 92 | 93 | ### `IndirectCallq` to `callq *` 94 | 95 | ### `TailJmp` 96 | 97 | insert conclusion 98 | indirect jump 99 | (see `functions_tests_21.s`) 100 | 101 | ### New auxiliary function `print-x86-def`, for `Def` 102 | 103 | prelude: 104 | 105 | 1. Start with `.global` and `.align` directives followed 106 | by the label for the function. 107 | 2. Push `rbp` to the stack and set `rbp` to current stack 108 | pointer. 109 | 3. Push to the stack all of the callee-saved registers that were 110 | used for register allocation. 111 | 4. Move the stack pointer `rsp` down by the size of the stack 112 | frame for this function, which depends on the number of regular 113 | spills. (Aligned to 16 bytes.) 114 | 5. Move the root stack pointer `r15` up by the size of the 115 | root-stack frame for this function, which depends on the number of 116 | spilled vectors. 117 | 6. Initialize to zero all of the entries in the root-stack frame. 118 | 7. Jump to the start block. 119 | 120 | The prelude of the `main` function has one additional task: call the 121 | `initialize` function to set up the garbage collector and move the 122 | value of the global `rootstack_begin` in `r15`. This should happen 123 | before step 5 above, which depends on `r15`. 124 | 125 | conclusion: 126 | 127 | 1. Move the stack pointer back up by the size of the stack frame 128 | for this function. 129 | 2. Restore the callee-saved registers by popping them from the 130 | stack. 131 | 3. Move the root stack pointer back down by the size of the 132 | root-stack frame for this function. 133 | 4. Restore `rbp` by popping it from the stack. 134 | 5. Return to the caller with the `retq` instruction. 135 | -------------------------------------------------------------------------------- /lecture-Oct-8.md: -------------------------------------------------------------------------------- 1 | ## Tuples and Garbage Collection 2 | 3 | ### Review of the previous lectures 4 | 5 | * 2-space copying collector 6 | * copy via Cheney's algorithm (BFS with Queue in the ToSpace) 7 | * data representation (root stack, tuple metadata with pointer mask) 8 | * running example: 9 | 10 | (let ([v (vector 42)]) 11 | (let ([w (vector v)]) 12 | (let ([v^ (vector-ref w 0)]) 13 | (vector-ref v^ 0)))) 14 | 15 | * review passes from last time 16 | * `type-check`: introduce `HasType` 17 | * `shrink`: update to generate `HasType` 18 | * `expose-allocation` (new): 19 | `(vector ...)` becomes collect-allocate-initialize 20 | * `remove-complex-opera*`: 21 | * `collect`, `allocate`, and `global-value` are complex 22 | * careful not to separate `Prim` from its surrounding `HasType` 23 | 24 | ### explicate-control 25 | 26 | minor changes to handle the new forms 27 | 28 | ### uncover-locals 29 | 30 | Collect the local variables and their types by inspecting 31 | assignment statements. Store in the info field of Program. 32 | 33 | ### select-instructions (5.3.3) 34 | 35 | Here is where we implement the new operations needed for tuples. 36 | 37 | example: block9056 38 | 39 | * `vector-set!` turns into movq with a deref in the target 40 | 41 | lhs = (vector-set! vec n arg); 42 | 43 | becomes 44 | 45 | movq vec', %r11 46 | movq arg', 8(n+1)(%r11) 47 | movq $0, lhs' 48 | 49 | what if we use rax instead: 50 | 51 | movq vec', %rax 52 | movq -16(%rbp), 8(n+1)(%rax) 53 | movq $0, lhs' 54 | 55 | movq vec', %rax 56 | movq -16(%rbp), %rax 57 | movq %rax, 8(n+1)(%rax) 58 | movq $0, lhs' 59 | 60 | 61 | We use `r11` for temporary storage, so we remove it from the list 62 | of registers used for register allocation. 63 | 64 | * `vector-ref` turns into a movq with deref in the source 65 | 66 | lhs = (vector-ref vec n); 67 | 68 | becomes 69 | 70 | movq vec', %r11 71 | movq 8(n+1)(%r11), lhs' 72 | 73 | * `allocate` 74 | 75 | 1. put the current free_ptr into lhs 76 | 2. move the free_ptr forward by 8(len+1) (room for tag) 77 | 3. initialize the tag (use bitwise-ior and arithmetic-shift) 78 | using the type information for the pointer mask. 79 | 80 | So 81 | 82 | lhs = (allocate len (Vector type ...)); 83 | 84 | becomes 85 | 86 | movq free_ptr(%rip), lhs' 87 | addq 8(len+1), free_ptr(%rip) 88 | movq lhs', %r11 89 | movq $tag, 0(%r11) 90 | 91 | * `collect` turns into a `callq` to the collect function. 92 | 93 | Pass the top of the root stack (`r15`) in register `rdi` and 94 | the number of bytes in `rsi`. 95 | 96 | (collect bytes) 97 | 98 | becomes 99 | 100 | movq %r15, %rdi 101 | movq $bytes, %rsi 102 | callq collect 103 | 104 | ### allocate-registers 105 | 106 | * Spill vector-typed variables to the root stack. Handle this 107 | in the code for assigning homes (converting colors to 108 | stack locations and registers.) 109 | 110 | Use r15 for the top of the root stack. Remove it from consideration 111 | by the register allocator. 112 | 113 | * If a vector variable is live during a call to collect, 114 | make sure to spill it. Do this by adding interference edges 115 | between the call-live vector variables and the callee-saved 116 | registers. You'll need to pass the variable-type information 117 | as another parameter to build-interference. 118 | 119 | example: block9059: define vecinit9047 120 | -> block9058: collect 121 | -> block9056: use vecinit9047 122 | 123 | 124 | ### print-x89 125 | 126 | * Move the root stack forward to make room for the vector spills. 127 | 128 | * The first call to collect might happen before all the 129 | slots in the root stack have been initialized. 130 | So make sure to zero-initialize the root stack in the prelude! 131 | -------------------------------------------------------------------------------- /lecture-Sep-3.md: -------------------------------------------------------------------------------- 1 | Overview of the Passes 2 | ---------------------- 3 | 4 | R1 5 | | uniquify 6 | V 7 | R1' 8 | | remove-complex-opera* 9 | V 10 | R1'' 11 | | explicate-control 12 | V 13 | C0 14 | | select-instructions 15 | V 16 | x86* 17 | | assign-homes 18 | V 19 | x86* 20 | | patch-instructions 21 | V 22 | x86 23 | | print-x86 24 | V 25 | x86-in-text 26 | 27 | Select Instructions 28 | ------------------- 29 | 30 | Translate statements into x86-style instructions. 31 | 32 | For example 33 | 34 | x = (+ 10 32); 35 | => 36 | movq $10, x 37 | addq $32, x 38 | 39 | Some cases can be handled with a single instruction. 40 | 41 | x = (+ 10 x); 42 | => 43 | addq $10, x 44 | 45 | 46 | The `read` operation must be turned into a 47 | call to the `read_int` function in `runtime.c`. 48 | 49 | x = (read); 50 | => 51 | callq read_int 52 | movq %rax, x 53 | 54 | The return statement is treated like an assignment to `rax` followed 55 | by a jump to the `conclusion` label. 56 | 57 | return e; 58 | => 59 | instr 60 | jmp conclusion 61 | 62 | where 63 | 64 | rax = e; 65 | => 66 | instr 67 | 68 | 69 | The Stack and Procedure Call Frames 70 | ----------------------------------- 71 | 72 | The stack is a conceptually sequence of frames, one for each procedure 73 | call. The stack grows down. 74 | 75 | The *base pointer* `rbp` is used for indexing into the frame. 76 | 77 | The *stack poitner* `rsp` points to the top of the stack. 78 | 79 | | Position | Contents | 80 | | --------- | -------------- | 81 | | 8(%rbp) | return address | 82 | | 0(%rbp) | old rbp | 83 | | -8(%rbp) | variable 1 | 84 | | -16(%rbp) | variable 2 | 85 | | -24(%rbp) | variable 3 | 86 | | ... | ... | 87 | | 0(%rsp) | variable n | 88 | 89 | 90 | Assign Homes 91 | ------------ 92 | 93 | Replace variables with stack locations. 94 | 95 | Consider the program `(+ 52 (- 10))`. 96 | 97 | Suppose we have two variables in the pseudo-x86, `tmp.1` and `tmp.2`. 98 | We places them in the -16 and -8 offsets from the base pointer `rbp` 99 | using the `deref` form. 100 | 101 | movq $10, tmp.1 102 | negq tmp.1 103 | movq tmp.1, tmp.2 104 | addq $52, tmp.2 105 | movq tmp.2, %rax 106 | => 107 | movq $10, -16(%rbp) 108 | negq -16(%rbp) 109 | movq -16(%rbp), -8(%rbp) 110 | addq $52, -8(%rbp) 111 | movq -8(%rbp), %rax 112 | 113 | 114 | Patch Instructions 115 | ------------------ 116 | 117 | Continuing the above example, we need to ensure that 118 | each instruction follows the rules of x86. 119 | 120 | For example, the move from stack location -16 to -8 uses two memory 121 | locations in the same instruction. So we split it up into two 122 | instructions and use rax to hold the value at location -16. 123 | 124 | movq $10 -16(%rbp) 125 | negq -16(%rbp) 126 | movq -16(%rbp) -8(%rbp) * 127 | addq $52 -8(%rbp) 128 | movq -8(%rbp) %rax 129 | => 130 | movq $10 -16(%rbp) 131 | negq -16(%rbp) 132 | movq -16(%rbp), %rax * 133 | movq %rax, -8(%rbp) * 134 | addq $52, -8(%rbp) 135 | movq -8(%rbp), %rax 136 | 137 | 138 | Print x86 139 | --------- 140 | 141 | Translate the x86 AST into a string in the form of the x86 concrete 142 | syntax. 143 | 144 | We also need to include a prelude and conclusion for the main 145 | procedure. 146 | 147 | The return address is saved to the stack by the caller (For the `main` 148 | function, the caller is the operating system.) 149 | 150 | The prelude must 151 | 1. save the old base pointer, 152 | 2. move the base pointer to the top of the stack 153 | 3. move the stack pointer down passed all the local variables. 154 | 4. jump to the start label 155 | 156 | The conclusion must 157 | 1. move the stack pointer up passed all the local variables 158 | 2. pops the old base pointer 159 | 3. returns from the `main` function via `retq` 160 | 161 | Continuing the above example 162 | 163 | start: 164 | movq $10, -16(%rbp) 165 | negq -16(%rbp) 166 | movq -16(%rbp), %rax 167 | movq %rax, -8(%rbp) 168 | addq $52, -8(%rbp) 169 | movq -8(%rbp), %rax 170 | jmp conclusion 171 | 172 | .globl _main 173 | main: 174 | pushq %rbp 175 | movq %rsp, %rbp 176 | subq $16, %rsp 177 | jmp start 178 | 179 | conclusion: 180 | addq $16, %rsp 181 | popq %rbp 182 | retq 183 | 184 | -------------------------------------------------------------------------------- /lecture-Oct-1.md: -------------------------------------------------------------------------------- 1 | ## Garbage Collection 2 | 3 | Def. The *live data* are all of the tuples that might be accessed by 4 | the program in the future. We can overapproximate this as all of the 5 | tuples that are reachable, transitively, from the registers or 6 | procedure call stack. We refer to the registers and stack collectively 7 | as the *root set*. 8 | 9 | The goal of a garbage collector is to reclaim the data that is not 10 | live. 11 | 12 | We shall use a 2-space copying collector, using Cheney's algorithm 13 | (BFS) for the copy. 14 | 15 | Alternative garbage collection techniques: 16 | * generational copy collectors 17 | * mark and sweep 18 | * reference counting + mark and sweep 19 | 20 | Overview of how GC fits into a running program.: 21 | 22 | 0. Ask the OS for 2 big chunks of memory. Call them FromSpace and ToSpace. 23 | 1. Run the program, allocating tuples into the FromSpace. 24 | 2. When the FromSpace is full, copy the *live data* into the ToSpace. 25 | 3. Swap the roles of the ToSpace and FromSpace and go back to step 1. 26 | 27 | Draw Fig. 5.6. (just the FromSpace) 28 | 29 | ### Graph Copy via Cheney's Algorithm 30 | 31 | * breadth-first search (quick reminder what that is) uses a queue 32 | * Cheney: use the ToSpace as the queue, use two pointers to keep 33 | track of the front (scan pointer) and back (free pointer) of the queue. 34 | 1. Copy tuples pointed to by the root set into the ToSpace 35 | to form the initial queue. 36 | 2. While copying a tuple, mark the old one and store the 37 | address of the new tuple inside the old tuple. 38 | This is called a *forwarding pointer*. 39 | 3. Start processing tuples from the front of the queue. For 40 | each tuple, copy the tuples that are directly reachable from 41 | it to the back of the queue in the ToSpace, unless the tuple 42 | has already been copied. Update the pointers in the 43 | processed tuple to the copies or the forwarding pointer. 44 | * Draw Fig. 5.7 45 | 46 | An implementation of a garbage collector is in `runtime.c`. 47 | 48 | 49 | ### Data Representation 50 | 51 | * Problems: 52 | 1. how to differentiate pointers from other things on the 53 | procedure call stack? 54 | 2. how can the GC access the pointers that are in registers? 55 | 3. how to differentiate poitners from other things inside tuples? 56 | * Solutions 57 | 1. Use a root stack (aka. shadow stack), i.e., place all 58 | tuples in a separate stack that works in parallel to the 59 | normal stack. Draw Fig. 5.7. 60 | 2. Spill vector-typed variables to the root stack if they are 61 | live during a call to the collector. 62 | 3. Add a 64-bit header or "tag" to each tuple. (Fig. 5.8) 63 | The header includes 64 | * 1 bit to indicate forwarding (0) or not (1). If 0, then 65 | the header is the forwarding pointer. 66 | * 6 bits to store the length of the tuple (max of 50) 67 | * 50 bits for the pointer mask to indicate which elements 68 | of the tuple are pointers. 69 | 70 | 71 | 72 | ### type-check 73 | 74 | Add cases for the new expressions. (Fig 5.1) 75 | 76 | type ::= ... | (Vector type+) | Void 77 | exp ::= ... | (vector exp+) | (vector-ref exp int) 78 | | (vector-set! exp int exp) | (void) 79 | 80 | To help the GC identify vectors (pointers to the heap), 81 | wrap every sub-expression with its type. This information 82 | will be propagated in the flatten pass to all variables. 83 | 84 | (HasType exp type) 85 | 86 | ### shrink 87 | 88 | Add HasType to the generated code. 89 | 90 | ### expose-allocation (new) 91 | 92 | Lower vector creation into a call to collect, a call to allocate, 93 | and then initialize the memory (see 5.3.1). 94 | 95 | Make sure to place the code for sub-expressions prior to 96 | the sequence collect-allocate-initialize. Sub-expressions 97 | may also call collect, and we can't have partially 98 | constructed vectors during collect! 99 | 100 | New forms in the output language: 101 | 102 | exp ::= 103 | (Collect int) call the GC and you're going to need `int` bytes 104 | | (Allocate int type) allocate `int` bytes 105 | | (GlobalValue name) access global variables 106 | 107 | * `free_ptr`: the next empty spot in the FromSpace 108 | * `fromspace_end`: the end of the FromSpace 109 | 110 | ### remove-complex-opera* 111 | 112 | The new forms Collect, Allocate, GlobalValue should be treated 113 | as complex operands. 114 | 115 | Add case for HasType. 116 | 117 | Adapt case for Prim to make sure the enclosing HasType does 118 | not get separated from it. 119 | 120 | -------------------------------------------------------------------------------- /lecture-Sep-1.md: -------------------------------------------------------------------------------- 1 | Overview of the Passes 2 | ---------------------- 3 | 4 | R1 5 | | uniquify 6 | V 7 | R1' 8 | | remove-complex-opera* 9 | V 10 | R1'' 11 | | explicate-control 12 | V 13 | C0 14 | | select instructions 15 | V 16 | x86* 17 | | assign homes 18 | V 19 | x86* 20 | | patch instructions 21 | V 22 | x86 23 | | print x86 24 | V 25 | x86-in-text 26 | 27 | Uniquify 28 | -------- 29 | 30 | This pass gives a unique name to every variable, so that variable 31 | shadowing and scope are no longer important. 32 | 33 | We recommend using `gensym` to generate a new name for each variable 34 | bound by a `let` expression. 35 | 36 | To update variable occurences to match the new names, use an 37 | association list to map the old names to the new names, extending this 38 | map in the case for `let` and doing a lookup in the case for 39 | variables. 40 | 41 | Examples: 42 | 43 | (let ([x 32]) 44 | (let ([y 10]) 45 | (+ x y))) 46 | => 47 | (let ([x.1 32]) 48 | (let ([y.2 10]) 49 | (+ x.1 y.2))) 50 | 51 | 52 | (let ([x 32]) 53 | (+ (let ([x 10]) x) x)) 54 | => 55 | (let ([x.1 32]) 56 | (+ (let ([x.2 10]) x.2) x.1)) 57 | 58 | 59 | Remove Complex Operators and Operands 60 | ------------------------------------- 61 | 62 | This pass makes sure that the arguments of each operation are atomic 63 | expressions, that is, variables or integer constants. The pass 64 | accomplishes this goal by inserting temporary variables to replace the 65 | non-atomic expressions with variables. 66 | 67 | Examples: 68 | 69 | (+ (+ 42 10) (- 10)) 70 | => 71 | (let ([tmp.1 (+ 42 10)]) 72 | (let ([tmp.2 (- 10)]) 73 | (+ tmp.1 tmp.2))) 74 | 75 | 76 | (let ([a 42]) 77 | (let ([b a]) 78 | b)) 79 | => 80 | (let ([a 42]) 81 | (let ([b a]) 82 | b)) 83 | 84 | and not 85 | 86 | (let ([tmp.1 42]) 87 | (let ([a tmp.1]) 88 | (let ([tmp.2 a]) 89 | (let ([b tmp.2]) 90 | b)))) 91 | 92 | 93 | Grammar of the output: 94 | 95 | atm ::= var | int 96 | exp ::= atm | (read) | (- atm) | (+ atm atm) 97 | | (let ([var exp]) exp) 98 | R1'' ::= exp 99 | 100 | Recommended function organization: 101 | 102 | rco-atom : exp -> atm * (var * exp) list 103 | 104 | rco-exp : exp -> exp 105 | 106 | Inside `rco-atom` and `rco-exp`, for recursive calls, 107 | use `rco-atom` when you need the result to be an atom 108 | and use `rco-exp` when you don't care. 109 | 110 | 111 | Explicate Control 112 | ----------------- 113 | 114 | This pass makes the order of evaluation explicit in the syntax. 115 | For now, this means flattening `let` into a sequence of 116 | assignment statements. 117 | 118 | The target of this pass is the C0 language. 119 | Here is the grammar for C0. 120 | 121 | atm ::= int | var 122 | exp ::= atm | (read) | (- atm) | (+ atm atm) 123 | stmt ::= var = exp; 124 | tail ::= return exp; | stmt tail 125 | C0 ::= (label: tail)^+ 126 | 127 | Example: 128 | 129 | (let ([x (let ([y (- 42)]) 130 | y)]) 131 | (- x)) 132 | => 133 | locals: 134 | '(x y) 135 | start: 136 | y = (- 42); 137 | x = y; 138 | return (- x); 139 | 140 | Aside regarding **tail position**. Here is the grammar for R1'' again 141 | but splitting the exp non-terminal into two, one for `tail` position 142 | and one for not-tail `nt` position. 143 | 144 | atm ::= var | int 145 | nt ::= atm | (read) | (- atm) | (+ atm atm) 146 | | (let ([var nt]) nt) 147 | tail ::= atm | (read) | (- atm) | (+ atm atm) 148 | | (let ([var nt]) tail) 149 | R1'' ::= tail 150 | 151 | Recommended function organization: 152 | 153 | explicate-tail : exp -> tail * var list 154 | 155 | explicate-assign : exp -> var -> tail -> tail * var list 156 | 157 | The `explicate-tail` function takes and R1 expression in tail position 158 | and returns a C0 tail and a list of variables that use to be let-bound 159 | in the expression. This list of variables is then stored in the `info` 160 | field of the `Program` node. 161 | 162 | The `explicate-assign` function takes 1) an R1 expression that is not 163 | in tail position, that is, the right-hand side of a `let`, 2) the 164 | `let`-bound variable, and 3) the C0 tail for the body of the `let`. 165 | The output of `explicate-assign` is a C0 tail and a list of variables 166 | that were let-bound. 167 | 168 | Here's a trace of these two functions on the above example. 169 | 170 | explicate-tail (let ([x (let ([y (- 42)]) y)]) (- x)) 171 | explicate-tail (- x) 172 | => {return (- x);}, () 173 | explicate-assign (let ([y (- 42)]) y) x {return (- x);} 174 | explicate-assign y x {return (- x);} 175 | => {x = y; return (- x)}, () 176 | explicate-assign (- 42) y {x = y; return (- x);} 177 | => {y = (- 42); x = y; return (- x);}, () 178 | => {y = (- 42); x = y; return (- x);}, (y) 179 | => {y = (- 42); x = y; return (- x);}, (x y) 180 | 181 | 182 | -------------------------------------------------------------------------------- /lecture-Sep-10.md: -------------------------------------------------------------------------------- 1 | # Register Allocation 2 | 3 | Main ideas: 4 | 5 | * Put as many variables in registers as possible, and *spill* the rest 6 | to the stack. 7 | 8 | * Variables that are not in use at the same time can be assigned to 9 | the same register. 10 | 11 | ## Registers and Calling Conventions 12 | 13 | * caller-save registers 14 | 15 | rax rdx rcx rsi rdi r8 r9 r10 r11 16 | 17 | 18 | * callee-save registers 19 | 20 | rsp rbp rbx r12 r13 r14 r15 21 | 22 | ## Running Example 23 | 24 | (let ([v 1]) 25 | (let ([w 42]) 26 | (let ([x (+ v 7)]) 27 | (let ([y x]) 28 | (let ([z (+ x w)]) 29 | (+ z (- y))))))) 30 | 31 | 32 | After instruction selection: 33 | 34 | locals: v w x y z t 35 | start: 36 | movq $1, v 37 | movq $42, w 38 | movq v, x 39 | addq $7, x 40 | movq x, y 41 | movq x, z 42 | addq w, z 43 | movq y, t 44 | negq t 45 | movq z, %rax 46 | addq t, %rax 47 | jmp conclusion 48 | 49 | ## Liveness Analysis 50 | 51 | Goal: figure out the program regions where a variable is in use. 52 | 53 | Def. A variable is *live* at a program point if the value in the 54 | variable is used at some later point in the program. 55 | 56 | The following equations compute the live before/after sets 57 | for each instruction. 58 | The instructions of the program are numbered 1 to n. 59 | 60 | L_after(k) = L_before(k + 1) 61 | L_after(n) = {} 62 | 63 | L_before(k) = (L_after(k) - W(k)) U R(k) 64 | 65 | Here's the program with the live-after set next to each instruction. 66 | Compute them from bottom to top. 67 | 68 | {} 69 | movq $1, v 70 | {v} 71 | movq $42, w 72 | {v,w} 73 | movq v, x 74 | {w,x} 75 | addq $7, x 76 | {w,x} 77 | movq x, y 78 | {w,x,y} 79 | movq x, z 80 | {w,y,z} 81 | addq w, z 82 | {y,z} 83 | movq y, t 84 | {t,z} 85 | negq t 86 | {t,z} 87 | movq z, %rax 88 | {t} 89 | addq t, %rax 90 | {} 91 | jmp conclusion 92 | {} 93 | 94 | 95 | ## Build the Interference Graph 96 | 97 | Def. An *interference graph* is an undirected graph whose vertices 98 | represent variables and whose edges represent conflicts, i.e., when 99 | two vertices are live at the same time. 100 | 101 | A naive approach: inspect each live-after set, and 102 | add an edge between every pair of variables. 103 | 104 | Down sides: 105 | * It is O(n^2) per instruction (for n variables) 106 | * If one variable is assigned to another,then they have the same value 107 | and can be stored in the same register, but the naive approach 108 | would mark them as conflicting. 109 | Example: consider the instruction from the above program 110 | 111 | movq x, y {w,x,y} 112 | 113 | Both x and y are live at this point, so the naive approach 114 | would mark them as conflicting. But because of this assignment 115 | they hold the same value, so they could share the same register. 116 | 117 | The better approach focuses on writes: it creates an edge between the 118 | variable being written-to by the current instruction and all the 119 | *other* live variables. (One should not create self edges.) For a call 120 | instruction, all caller-save register must be considered as 121 | written-to. For the move instruction, we skip adding an edge between a 122 | live variable and the destination variable if the live variable 123 | matches the source of the move, as per point 2 above. So we have 124 | the followng three rules. 125 | 126 | 1. For an arithmetic instructions, such as (addq s d) 127 | for each v in L_after, 128 | if v != d then 129 | add edge (d,v) 130 | 131 | 2. For a call instruction (callq label), 132 | for each v in L_after, 133 | for each r in caller-save registers 134 | if r != v then 135 | add edge (r,v) 136 | 137 | 3. For a move instruction (movq s d), 138 | for each v in L_after, 139 | if v != d and v != s then 140 | add edge (d,v) 141 | 142 | Let us walk through the running example, proceeding top to bottom, 143 | apply the three rules to build the interference graph. 144 | 145 | {} 146 | movq $1, v rule 3: no interference (v=v) 147 | {v} 148 | movq $42, w rule 3: edge w-v 149 | {v,w} 150 | movq v, x rule 3: edge x-w 151 | {w,x} 152 | addq $7, x rule 1: edge x-w (dup.) 153 | {w,x} 154 | movq x, y rule 3: edge y-w, no edge y-x 155 | {w,x,y} 156 | movq x, z rule 3: edge z-w, edge z-y 157 | {w,y,z} 158 | addq w, z rule 1: edge z-y (dup.) 159 | {y,z} 160 | movq y, t rule 3: edge t-z 161 | {t,z} 162 | negq t.1 rule 1: edge t-z (dup) 163 | {t,z} 164 | movq z, %rax rule 2: but ignore rax 165 | {t} 166 | addq t, $rax rule 1: but ignore rax 167 | {} 168 | jmp conclusion 169 | {} 170 | 171 | So the interference graph looks as follows: 172 | 173 | t ---- z x 174 | |\___ | 175 | | \ | 176 | | \| 177 | y ---- w ---- v 178 | 179 | 180 | -------------------------------------------------------------------------------- /lecture-Oct-15.md: -------------------------------------------------------------------------------- 1 | # Compiling Functions, the Passes 2 | 3 | ## Type Check 4 | 5 | Go over the figure in the book. 6 | 7 | ## Shrink 8 | 9 | (ProgramDefsExp info defs exp) 10 | ==> 11 | (ProgramDefs info (append defs (list mainDef))) 12 | 13 | where `mainDef` is 14 | 15 | (Def 'main '() 'Integer '() exp') 16 | 17 | ## Reveal Functions (new) 18 | 19 | We'll need to generate `leaq` instructions for references to 20 | functions, so it makes sense to differentiate them from let-bound 21 | variables. 22 | 23 | (Var x) 24 | ==> 25 | (Var x) 26 | 27 | (Var f) 28 | ==> 29 | (FunRef f) 30 | 31 | 32 | (Let x (Bool #t) 33 | (Apply (If (Var x) (Var 'add1) (Var 'sub1)) 34 | (Int 41))) 35 | => 36 | (Let x (Bool #t) 37 | (Apply (If (Var x) (FunRef 'add1) (FunRef 'sub1)) 38 | (Int 41))) 39 | 40 | 41 | ## Limit Functions (new) 42 | 43 | Transform functions so that have at most 6 parameters. 44 | 45 | ### Function definition 46 | 47 | (Def f ([x1 : t1] ... [xn : tn]) rt info body) 48 | ==> 49 | (Def f ([x1 : t1] ... [x5 : t5] [vec : (Vector t6 ... tn)]) rt info 50 | new-body) 51 | 52 | and transform the `body`, replace occurences of parameters `x6` and 53 | higher as follows 54 | 55 | x6 56 | ==> 57 | (vector-ref vec 0) 58 | 59 | x7 60 | ==> 61 | (vector-ref vec 1) 62 | 63 | ... 64 | 65 | ### Function application 66 | 67 | If there are more than 6 arguments, pass arguments 6 and higher in a 68 | vector: 69 | 70 | (Apply e0 (e1 ... en)) 71 | ==> 72 | (Apply e0 (e1 ... e5 (vector e6 ... en))) 73 | 74 | 75 | ## Remove Complex Operands 76 | 77 | Treat `FunRef` and `Apply` as complex operands. 78 | 79 | (Prim '+ (list (Int 5) (FunRef add1))) 80 | => 81 | (Let ([tmp (FunRef add1)]) 82 | (Prim '+ (list (Int 5) (Var tmp)))) 83 | 84 | Arguments of `Apply` need to be atomic expressions. 85 | 86 | 87 | ## Explicate Control 88 | 89 | * assignment 90 | * tail 91 | * predicate 92 | 93 | Add cases for `FunRef` and `Apply` to the three helper functions 94 | for assignment, tail, and predicate contexts. 95 | 96 | In assignment and predicate contexts, `Apply` becomes `Call`. 97 | 98 | In tail contexts, `Apply` becomes `TailCall`. 99 | 100 | You'll need a new helper function for function definitions. 101 | The code will be similar to the previous code for `Program` 102 | 103 | Previous assignment: 104 | 105 | (define/override (explicate-control p) 106 | (match p 107 | [(Program info body) 108 | (set! control-flow-graph '()) 109 | (define-values (body-block vars) (explicate-tail body)) 110 | (define new-info (dict-set info 'locals vars)) 111 | (Program new-info 112 | (CFG (dict-set control-flow-graph 'start body-block)))] 113 | )) 114 | 115 | adapt the above to process every function definition. 116 | 117 | 118 | ## Uncover Locals 119 | 120 | Add a case for `TailCall` to the helper for tail contexts. 121 | 122 | Create a new helper function for function definitions. 123 | Again, it will be similar to the previous code for `Program`. 124 | 125 | 126 | ## Select Instructions 127 | 128 | ### `FunRef` becomes `leaq` 129 | 130 | We'll keep `FunRef` as an instruction argument for now, 131 | placing it in a `leaq` instruction. 132 | 133 | (Assign lhs (FunRef f)) 134 | ==> 135 | (Instr 'leaq (list (FunRef f) lhs')) 136 | 137 | ### `Call` becomes `IndirectCallq` 138 | 139 | (Assign lhs (Call fun (arg1 ... argn))) 140 | ==> 141 | movq arg'1 rdi 142 | movq arg'2 rsi 143 | ... 144 | (IndirectCallq fun') 145 | (Instr 'movq (Reg 'rax) lhs') 146 | 147 | ### `TailCall` becomes `TailJmp` 148 | 149 | We postpone the work of popping the frame until later by inventing an 150 | instruction we'll call `TailJmp`. 151 | 152 | (TailCall fun (arg1 ... argn)) 153 | ==> 154 | movq arg'1 rdi 155 | movq arg'2 rsi 156 | ... 157 | (TailJmp fun') 158 | 159 | ### Function Definitions 160 | 161 | (Def f ([x1 : T1] ... [xn : Tn]) rt info CFG) 162 | 1. CFG => CFG' 163 | 2. prepend to start block from CFG' 164 | movq rdi x1 165 | ... 166 | 4. parameters get added to the list of local variables 167 | => 168 | (Def f '() '() new-info new-CFG) 169 | 170 | alternative: 171 | replace parameters (in the CFG) with argument registers 172 | 173 | 174 | ## Uncover Live 175 | 176 | New helper function for function definitions. 177 | 178 | `leaq` reads from the first argument and writes to the second. 179 | 180 | `IndirectCallq` and `TailJmp` read from their argument and you must 181 | assume they write to all the caller-saved registers. 182 | 183 | ## Build Interference Graph 184 | 185 | New helper function for function definitions. 186 | 187 | Compute one interference graph per function. 188 | 189 | Spill vector-typed variables that are live during a function call. 190 | (Because our functions make trigger `collect`.) So add interference 191 | edges between those variables and the callee-saved registers. 192 | 193 | ## Patch Instructions 194 | 195 | The destination of `leaq` must be a register. 196 | 197 | The destination of `TailJmp` should be `rax`. 198 | 199 | (TailJmp %rbx) 200 | ==> 201 | movq %rbx, %rax 202 | (TailJmp rax) 203 | 204 | ## Print x86 205 | 206 | 207 | (FunRef label) => label(%rip) 208 | 209 | (IndirectCallq arg) => callq *arg 210 | 211 | (TailJmp rax) 212 | => 213 | addq frame-size, %rsp move stack pointer up 214 | popq %rbx callee-saved registers 215 | ... 216 | subq root-frame-size, %r15 move root-stack pointer 217 | popq %rbp restore rbp 218 | jmp *%rax jump to the target function 219 | 220 | -------------------------------------------------------------------------------- /lecture-Dec-1.md: -------------------------------------------------------------------------------- 1 | # Compiling Assignment and Begin 2 | 3 | Example program: 4 | 5 | (let ([sum 0]) 6 | (let ([i 5]) 7 | (begin 8 | (while (> i 0) 9 | (begin 10 | (set! sum (+ sum i)) 11 | (set! i (- i 1)))) 12 | sum))) 13 | 14 | An example that involves `lambda`: 15 | 16 | (let ([x 0]) 17 | (let ([y 0]) 18 | (let ([z 20]) 19 | (let ([f (lambda: ([a : Integer]) : Integer (+ a (+ x z)))]) 20 | (begin 21 | (set! x 10) 22 | (set! y 12) 23 | (f y)))))) 24 | 25 | Yet another example: 26 | 27 | (define (f [x : Integer]) : (Vector ( -> Integer) ( -> Void)) 28 | (vector 29 | (lambda: () : Integer x) 30 | (lambda: () : Void (set! x (+ 1 x))))) 31 | 32 | (let ([counter (f 0)]) 33 | (let ([get (vector-ref counter 0)]) 34 | (let ([inc (vector-ref counter 1)]) 35 | (begin 36 | (inc) 37 | (get))))) 38 | 39 | ## Convert Assignments 40 | 41 | To extend the lifetime of variables, we can "box" them, that is, put 42 | them on the heap. 43 | 44 | If a variable is never on the LHS of a `set!`, then there is no need 45 | to box it. We can copy its value into a closure to extend its life. 46 | 47 | If a variable is not free in any `lambda`, then there is also no need 48 | to box it, even if it is assigned-to. We can translate `set!` into 49 | assignment in C7. 50 | 51 | So for this example, we box `x` but not `y`, `z`, and `a`. 52 | 53 | (let ([x 0]) 54 | (let ([y 0]) 55 | (let ([z 20]) 56 | (let ([f (lambda: ([a : Integer]) : Integer (+ a (+ x z)))]) 57 | (begin 58 | (set! x 10) 59 | (set! y 12) 60 | (f y)))))) 61 | 62 | ==> 63 | 64 | (define (main) : Integer 65 | (let ([x0 (vector 0)]) 66 | (let ([y1 0]) 67 | (let ([z2 20]) 68 | (let ([f4 (lambda: ([a3 : Integer]) : Integer 69 | (+ a3 (+ (vector-ref x0 0) z2)))]) 70 | (begin 71 | (vector-set! x0 0 10) 72 | (set! y1 12) 73 | (f4 y1))))))) 74 | 75 | To determine which variables to box, create a auxiliary function 76 | `assigned&free` that returns the set of variables that are 77 | assigned-to, the set of variables that are free-in-a-lambda, and an 78 | updated expression in which the bound variables that are both 79 | assigned-to and free-in-a-lambda have been marked with the 80 | `AssignedFree` AST node. 81 | 82 | Recipe for boxing a variable: 83 | 84 | 1. initialize the variable with a one-element vector containing the 85 | original initializer. 86 | 87 | (Let (AssignedFree x) e body) 88 | ==> 89 | (Let x (Prim 'vector (list e')) body') 90 | 91 | 2. change uses of the variable to `vector-ref`. 92 | 93 | (Var x) 94 | ==> 95 | (Prim 'vector-ref (list (Var x) (Int 0))) 96 | 97 | 3. change `set!` with the variable on the LHS to a `vector-set!`. 98 | 99 | (SetBang x e) 100 | ==> 101 | (Prim 'vector-set! (list (Var x) (Int 0) e')) 102 | 103 | 104 | ## Remove Complex Operands 105 | 106 | The `while`, `set!`, and `begin` expressions are all complex, 107 | so they should be let-bound to temporary variables. 108 | Their subexpressions are allowed to be complex. 109 | 110 | 111 | (let ([x0 10]) 112 | (let ([y1 0]) 113 | (+ (+ (begin (set! y1 (read)) x0) 114 | (begin (set! x0 (read)) y1)) 115 | x0))) 116 | ==> 117 | (let ([x0 10]) 118 | (let ([y1 0]) 119 | (let ([tmp1 (begin (set! y1 (read)) x0)]) ;; GOOD! 120 | (let ([tmp2 (begin (set! x0 (read)) y1)]) 121 | (+ (+ tmp1 tmp2) 122 | x0))))) 123 | 124 | or?? 125 | 126 | (let ([x0 10]) 127 | (let ([y1 0]) 128 | (begin 129 | (set! y1 (read)) ;;; WRONG! 130 | (set! x0 (read)) 131 | (+ (+ x0 y1) x0)))) 132 | 133 | 134 | (rco-atom (Begin es body)) 135 | = 136 | (values (Var tmp) 137 | (tmp . (Begin es^ body^))) 138 | 139 | where 140 | es^ = (map rco-exp es) 141 | body^ = (rco-exp body) 142 | 143 | 144 | ## Explicate Control 145 | 146 | To handle `begin`, we need a new auxiliary function: 147 | `explicate-effect`. It takes an expression and a continuation block 148 | and produces a block that performs the effects in the expression 149 | followed by the block. 150 | 151 | ### Integers 152 | 153 | (explicate-effect (Int n) cont) 154 | ==> 155 | cont 156 | 157 | ### Assignment 158 | 159 | (explicate-effect (SetBang x e) cont) 160 | ==> 161 | (explicate-assign x e cont) 162 | 163 | ### Begin 164 | 165 | (explicate-effect (Begin es body) cont) 166 | ==> 167 | es' 168 | 169 | where 170 | 171 | body' = (explicate-effect body cont) 172 | 173 | and `es'` is the reslult of applying `explicate-effect` to each 174 | expression in `es`, back-to-front, using `body'` as the initial 175 | continuation. (Hint: use `for/foldr`.) 176 | 177 | 178 | (explicate-tail (Begin es body)) 179 | ===> 180 | body^ = (explicate-tail body) 181 | (for/foldr ([cont body^]) ([e es]) 182 | (explicate-effect e cont)) 183 | 184 | 185 | ### Apply 186 | 187 | (explicate-effect (Call e es) cont) 188 | ==> 189 | (Seq (Call e es) (force cont)) 190 | 191 | 192 | There are three new kinds of statements in C7: 193 | 1. `Call` 194 | 2. `read` 195 | 3. `vector-set!` 196 | 197 | ### Plus (and other primitives besides read and vector-set!) 198 | 199 | (explicate-effect (Prim '+ args) cont) 200 | ===> 201 | cont 202 | 203 | 204 | ## Select Instructions 205 | 206 | Add cases for the three new statements: `Call`, `read`, and 207 | `vector-set!` by adapting the code generation for those forms in 208 | assignment position. 209 | 210 | 211 | ## Register Allocation 212 | 213 | Use dataflow analysis in `uncover-live`. 214 | 215 | 216 | -------------------------------------------------------------------------------- /lecture-Nov-5.md: -------------------------------------------------------------------------------- 1 | ## Compiling R6, Instruction Selection, continued 2 | 3 | * `(Exit)` 4 | 5 | (Assign lhs (Exit)) 6 | ===> 7 | movq $-1, %rdi 8 | callq exit 9 | 10 | * `(Assign lhs (AllocateClosure len ty arity))` 11 | 12 | Treat this just like `Allocate` except that you'll put 13 | the `arity` into the tag at the front of the vector. 14 | Use bits 57 and higher for the arity. 15 | 16 | [(Assign lhs (AllocateClosure len `(Vector ,ts ...) arity)) 17 | (define lhs^ (select-instr-arg lhs)) 18 | ;; Add one quad word for the meta info tag 19 | (define size (* (add1 len) 8)) 20 | ;;highest 7 bits are unused 21 | ;;lowest 1 bit is 1 saying this is not a forwarding pointer 22 | (define is-not-forward-tag 1) 23 | ;;next 6 lowest bits are the length 24 | (define length-tag (arithmetic-shift len 1)) 25 | ;;bits [6,56] are a bitmask indicating if [0,50] are pointers 26 | (define ptr-tag 27 | (for/fold ([tag 0]) ([t (in-list ts)] [i (in-naturals 7)]) 28 | (bitwise-ior tag (arithmetic-shift (b2i (root-type? t)) i)))) 29 | (define arity-tag ...) 30 | ;; Combine the tags into a single quad word 31 | (define tag (bitwise-ior arity-tag ptr-tag length-tag is-not-forward-tag)) 32 | (list (Instr 'movq (list (Global 'free_ptr) (Reg tmp-reg))) 33 | (Instr 'addq (list (Imm size) (Global 'free_ptr))) 34 | (Instr 'movq (list (Imm tag) (Deref tmp-reg 0))) 35 | (Instr 'movq (list (Reg tmp-reg) lhs^)) 36 | ) 37 | ] 38 | 39 | * `(Assign lhs (Prim 'procedure-arity (list e)))` 40 | 41 | Extract the arity from the tag of the vector. 42 | 43 | (Assign lhs (Prim 'procedure-arity (list e))) 44 | ===> 45 | movq e', %r11 46 | movq 0(%r11), %r11 47 | sarq $57, %r11 48 | movq %r11, lhs' 49 | 50 | * `(Assign lhs (Prim 'vector-length (list e)))` 51 | 52 | Extract the length from the tag of the vector. 53 | 54 | (Assign lhs (Prim 'vector-length (list e))) 55 | ===> 56 | movq e', %r11 57 | movq 0(%r11), %r11 58 | andq $126, %r11 // 1111110 59 | sarq $1, %r11 60 | movq %r11, lhs' 61 | 62 | 63 | ## `Vectorof`, `vector-ref`, and `vector-set!` 64 | 65 | The type checker for R6 treats vector operations differently 66 | if the vector is of type `(Vectorof T)`. 67 | The index can be an arbitrary expression, e.g. 68 | suppose `vec` has type `(Vectorof T)`. Then 69 | the index could be `(read)` 70 | 71 | (let ([vec1 (vector (inject 1 Integer) (inject 2 Integer))]) ;; vec1 : (Vector Any Any) 72 | (let ([vec2 (inject vec1 (Vector Any Any))]) ;; vec2 : Any 73 | (let ([vec3 (project vec2 (Vectorof Any))]) ;; vec3 : (Vectorof Any) 74 | (vector-ref vec3 (read))))) 75 | 76 | and the type of `(vector-ref vec (read))` is `T`. 77 | 78 | Recall instruction selection for `vector-ref`: 79 | 80 | (Assign lhs (Prim 'vector-ref (list evec (Int n)))) 81 | ===> 82 | movq evec', %r11 83 | movq offset(%r11), lhs' 84 | 85 | where offset is 8(n+1) 86 | 87 | If the index is not of the form `(Int i)`, but an arbitrary 88 | expression, then instead of computing the offset `8(n+1)` at compile 89 | time, you can generate the following instructions. Note the use of the 90 | new instruction `imulq`. 91 | 92 | (Assign lhs (Prim 'vector-ref (list evec en))) 93 | ===> 94 | movq en', %r11 95 | addq $1, %r11 96 | imulq $8, %r11 97 | addq evec', %r11 98 | movq 0(%r11) lhs' 99 | 100 | The same idea applies to `vector-set!`. 101 | 102 | 103 | # The R7 Language: Mini Racket (Dynamically Typed) 104 | 105 | exp ::= int | (read) | ... | (lambda (var ...) exp) 106 | | (vector-ref exp exp) | (vector-set! exp exp exp) 107 | def ::= (define (var var ...) exp) 108 | R7 ::= def... exp 109 | 110 | # Compiling R7 to R6 by cast insertion 111 | 112 | The main invariant is that every subexpression that we generate should 113 | have type `Any`, which we accomplish by using `inject`. 114 | 115 | To perform an operation on a value of type `Any`, we `project` it to 116 | the appropriate type for the operation. 117 | 118 | Example: 119 | R7: 120 | 121 | (+ #t 42) 122 | 123 | R6: 124 | 125 | (inject 126 | (+ (project (inject #t Boolean) Integer) 127 | (project (inject 42 Integer) Integer)) 128 | Integer) 129 | ===> 130 | x86 code 131 | 132 | 133 | Booleans: 134 | 135 | #t 136 | ===> 137 | (inject #t Boolean) 138 | 139 | Integer: 140 | 141 | 42 142 | ===> 143 | (inject 42 Integer) 144 | 145 | Arithmetic: 146 | 147 | (+ e_1 e_2) 148 | ==> 149 | (inject 150 | (+ (project e'_1 Integer) 151 | (project e'_2 Integer)) 152 | Integer) 153 | 154 | Variables: 155 | 156 | x 157 | ===> 158 | x 159 | 160 | Lambda: 161 | 162 | (lambda (x_1 ... x_n) e) 163 | ===> 164 | (inject (lambda: ([x_1 : Any] ... [x_n : Any]) : Any e') 165 | (Any ... Any -> Any)) 166 | 167 | example: 168 | 169 | (lambda (x y) (+ x y)) 170 | ===> 171 | (inject (lambda: ([x : Any] [y : Any]) : Any 172 | (inject (+ (project x Integer) (project y Integer)) Integer)) 173 | (Any Any -> Any)) 174 | 175 | Application: 176 | 177 | (e_0 e_1 ... e_n) 178 | ===> 179 | ((project e'_0 (Any ... Any -> Any)) e'_1 ... e'_n) 180 | 181 | Vector Reference: 182 | 183 | (vector-ref e_1 e_2) 184 | ===> 185 | (vector-ref (project e'_1 (Vectorof Any)) 186 | (project e'_2 Integer)) 187 | 188 | 189 | Vector: 190 | 191 | (vector e1 ... en) 192 | ===> 193 | (inject 194 | (vector e1' ... en') 195 | (Vector Any .... Any)) 196 | 197 | R7: 198 | (vector 1 #t) heterogeneous 199 | 200 | (inject (vector (inject 1 Integer) (inject #t Boolean)) 201 | (Vector Any Any)) : Any 202 | 203 | R6: (Vector Int Bool) heterogeneous 204 | (Vectorof Int) homogeneous 205 | 206 | actually see: 207 | 208 | (Vector Any Any) 209 | (Vectorof Any) 210 | -------------------------------------------------------------------------------- /lecture-Sep-17.md: -------------------------------------------------------------------------------- 1 | # Concrete Syntax of R2 2 | 3 | bool ::= #t | #f 4 | cmp ::= eq? | < | <= | > | >= 5 | exp ::= int | (read) | (- exp) | (+ exp exp) | (- exp exp) 6 | | var | (let ([var exp]) exp) 7 | | bool | (and exp exp) | (or exp exp) | (not exp) 8 | | (cmp exp exp) | (if exp exp exp) 9 | R2 ::= exp 10 | 11 | New things: 12 | * Boolean literals: `#t` and `#f`. 13 | * Logical operators on Booleans: `and`, `or`, `not`. 14 | * Comparison operators: `eq?`, `<`, etc. 15 | * The `if` conditional expression. Branching! 16 | * Subtraction on integers. 17 | 18 | 19 | # Semantics of R2 20 | 21 | (define (interp-op op) 22 | (match op 23 | ... 24 | ['not (lambda (v) (match v [#t #f] [#f #t]))] 25 | ['eq? (lambda (v1 v2) 26 | (cond [(or (and (fixnum? v1) (fixnum? v2)) 27 | (and (boolean? v1) (boolean? v2))) 28 | (eq? v1 v2)]))] 29 | ['< (lambda (v1 v2) 30 | (cond [(and (fixnum? v1) (fixnum? v2)) (< v1 v2)]))] 31 | ...)) 32 | 33 | (define (interp-exp env) 34 | (lambda (e) 35 | (define recur (interp-exp env)) 36 | (match e 37 | ... 38 | [(Bool b) b] 39 | [(If cnd thn els) 40 | (define b (recur cnd)) 41 | (match b 42 | [#t (recur thn)] 43 | [#f (recur els)])] 44 | [(Prim 'and (list e1 e2)) 45 | (define v1 (recur e1)) 46 | (match v1 47 | [#t (match (recur e2) [#t #t] [#f #f])] 48 | [#f #f])] 49 | [(Prim op args) 50 | (apply (interp-op op) (for/list ([e args]) (recur e)))] 51 | ))) 52 | 53 | (define (interp-R2 p) 54 | (match p 55 | [(Program info e) 56 | ((interp-exp '()) e)] 57 | )) 58 | 59 | Things to note: 60 | * Our treatment of Booleans and operations on them is strict in the 61 | sense that we don't allow other kinds of values (such as integers) 62 | to be treated as if they are Booleans. 63 | * `and` is short-circuiting. 64 | * The handling of primitive operators has been factored out 65 | into an auxilliary function named `interp-op`. 66 | 67 | 68 | # Type errors and static type checking 69 | 70 | In Racket: 71 | 72 | > (not 1) 73 | #f 74 | 75 | > (car 1) 76 | car: contract violation 77 | expected: pair? 78 | given: 1 79 | 80 | In Typed Racket: 81 | 82 | > (not 1) 83 | #f 84 | 85 | > (car 1) 86 | Type Checker: Polymorphic function `car' could not be applied to arguments: 87 | Domains: (Listof a) 88 | (Pairof a b) 89 | Arguments: One 90 | in: (car 1) 91 | 92 | 93 | A type checker, aka. type system, enforces at compile-time that only 94 | the appropriate operations are applied to values of a given type. 95 | 96 | To accomplish this, a type checker must predict what kind of value 97 | will be produced by an expression at runtime. 98 | 99 | (not 1) ;; not an R2 program! 100 | 101 | Type checker: 102 | 103 | (define/public (unary-op-types) 104 | '((- . ((Integer) . Integer)) 105 | (not . ((Boolean) . Boolean)) 106 | )) 107 | 108 | (define/public (binary-op-types) 109 | '((+ . ((Integer Integer) . Integer)) 110 | (- . ((Integer Integer) . Integer)) 111 | (and . ((Boolean Boolean) . Boolean)) 112 | (or . ((Boolean Boolean) . Boolean)) 113 | (< . ((Integer Integer) . Boolean)) 114 | (<= . ((Integer Integer) . Boolean)) 115 | (> . ((Integer Integer) . Boolean)) 116 | (>= . ((Integer Integer) . Boolean)) 117 | )) 118 | 119 | (define/public (nullary-op-types) 120 | '((read . (() . Integer)))) 121 | 122 | (define (type-check-exp env) ;; return a type: Integer, Boolean 123 | (lambda (e) 124 | (match e 125 | [(Var x) (dict-ref env x)] 126 | [(Int n) 'Integer] 127 | [(Bool b) 'Boolean] 128 | [(Let x e body) 129 | (define Te ((type-check-exp env) e)) 130 | (define Tb ((type-check-exp (dict-set env x Te)) body)) 131 | Tb] 132 | ... 133 | [(If e1 e2 e3) 134 | (define T1 ((type-check-exp env) e1)) 135 | (unless (equal? T1 'Boolean) (error ...)) 136 | (define T2 ((type-check-exp env) e2)) 137 | (define T3 ((type-check-exp env) e3)) 138 | (unless (equal? T2 T3) (error ...)) 139 | T2] 140 | [(Prim op es) 141 | (define-values (new-es ts) 142 | (for/lists (exprs types) ([e es]) ((type-check-exp env) e))) 143 | (define t-ret (type-check-op op ts)) 144 | (values (Prim op new-es) t-ret)] 145 | [else 146 | (error "type-check-exp couldn't match" e)]))) 147 | 148 | (define (type-check env) 149 | (lambda (e) 150 | (match e 151 | [(Program info body) 152 | (define Tb ((type-check-exp '()) body)) 153 | (unless (equal? Tb 'Integer) 154 | (error "result of the program must be an integer, not " Tb)) 155 | (Program info body)] 156 | ))) 157 | 158 | How should the type checker handle the `if` expression? 159 | 160 | 161 | # Shrinking R2 162 | 163 | Several of the language forms in R2 are redundant and can be easily 164 | expressed using other language forms. They are present in R2 to make 165 | programming more convenient, but they do not fundamentally increase 166 | the expressiveness of the language. 167 | 168 | To reduce the number of language forms that later compiler passes have 169 | to deal with, we shrink R2 by translating away some of the forms. 170 | For example, subtraction is expressible as addition and negation. 171 | 172 | (- e1 e2) => (+ e1 (- e2)) 173 | 174 | The less-than-or-equal operation on integers is expressible using 175 | less-than and not. Here's an incorrect first attempt: 176 | 177 | (<= e1 e2) => (not (< e2 e1)) wrong! 178 | 179 | When compiling, one must always keep in mind that expressions 180 | may contain side effects, such as `(read)`. Flipping the order 181 | of `e1` and `e2` can change a program's behavior, which means 182 | that the above transformation is incorrect. 183 | 184 | 185 | -------------------------------------------------------------------------------- /lecture-Nov-3.md: -------------------------------------------------------------------------------- 1 | 2 | We'll implement a dynamically-typed language called R7, a subset of 3 | Racket. 4 | 5 | Example R7 program 6 | 7 | (not (if (eq? (read) 1) #f 0)) 8 | 9 | We'll implement the compiler for R7 in two stages. 10 | 11 | 1. Extend our typed language with a new type `Any` that is equiped 12 | with the operations `inject` and `project` that convert a value 13 | of any other type to `Any` and back again. This language is R6. 14 | 15 | (let ([x (inject (Int 42) Integer)]) 16 | (project x Integer)) ;; result is 42 17 | 18 | (let ([x (inject (Bool #t) Boolean)]) 19 | (project x Integer)) ;; error! 20 | 21 | 2. Create a new pass (at the beginning) that translates from R7 to R6 22 | that uses `Any` as the type for just about everying and that 23 | inserts `inject` and `project` in lots of places. 24 | 25 | 26 | 27 | # The R6 Language: Any 28 | 29 | type ::= ... | Any 30 | ftype ::= Integer | Boolean | (Vector Any ...) | (Vectorof Any) 31 | | (Any ... -> Any) 32 | exp ::= ... | (inject exp ftype) | (project exp ftype) | 33 | | (boolean? exp) | (integer? exp) | (vector? exp) 34 | | (procedure? exp) | (void? exp) 35 | 36 | The `Vectorof` type is for homogeneous vectors of arbitrary length. 37 | That is, their elements are all of the same type and the length is 38 | determined at runtime. 39 | 40 | * type checking R6 41 | 42 | * interpreting R6 43 | 44 | Another example: 45 | 46 | (let ([v (inject (vector (inject 42 Integer)) 47 | (Vector Any))]) 48 | (let ([w (project v (Vector Any))]) 49 | (let ([x (vector-ref w 0)]) 50 | (project x Integer)))) 51 | 52 | 53 | 54 | # Compiling R6 55 | 56 | The runtime representation of a value of type `Any` is a 64 bit value 57 | whose 3 least-significant bits (right-most) encode the runtime type, 58 | which we call a *tag*. 59 | 60 | tagof(Integer) = 001 61 | tagof(Boolean) = 100 62 | tagof((Vector ...)) = 010 63 | tagof((Vectorof ...)) = 010 64 | tagof((... -> ...)) = 011 65 | tagof(Void) = 101 66 | 67 | If the value is an integer or Boolean, then the other 61 bits store 68 | that value. (Shifted by 3.) 69 | 70 | If the value is a vector or function, then the 64 bits is an 71 | address. All our values are 8-byte aligned, so we don't need the 72 | bottom 3 bits. To obtain the address from an `Any` value, just write 73 | 000 to the rightmost 3 bits. 74 | 75 | ## Shrink 76 | 77 | * Compiling `Project` to `tag-of-any`, `value-of-any`, and `exit`. 78 | 79 | If `ty` is `Boolean` or `Integer`: 80 | 81 | (project e ty) 82 | ===> 83 | (let ([tmp e]) 84 | (if (eq? (tag-of-any tmp) tag) 85 | (value-of-any tmp ty) 86 | (exit)))) 87 | 88 | where tag is tagof(ty) 89 | 90 | If `ty` is a function or vector (e.g. `(Vector Integer Boolean)`), 91 | you also need to check the vector 92 | length or procedure arity. Those two operations be added as two new 93 | primitives. Use the primitives: `vector-length`, `procedure-arity`. 94 | 95 | 96 | * Compile `Inject` to `make-any` 97 | 98 | (inject e ty) 99 | ===> 100 | (make-any e tag) 101 | 102 | where tag is the result of tagof(ty) 103 | 104 | * Abstract syntax for the new forms: 105 | 106 | exp ::= ... | (Prim 'tag-of-any (list exp)) 107 | | (Prim 'make-any (list exp (Int tag))) 108 | | (ValueOf exp type) 109 | | (Exit) 110 | 111 | 112 | ## Check Bounds (missing from book) 113 | 114 | Adapt `type-check-R6` by changing the cases for `vector-ref` and 115 | `vector-set!` when the vector argument has type `Vectorof T`. 116 | 117 | (vector-ref e1 e2) 118 | ===> 119 | (let ([v e1']) 120 | (let ([i e2']) 121 | (if (< i (vector-length v)) 122 | (vector-ref v i) 123 | (exit)))) 124 | 125 | (vector-set! e1 e2 e3) 126 | ===> 127 | (let ([v e1']) 128 | (let ([i e2']) 129 | (if (< i (vector-length v)) 130 | (vector-set! v i e3') 131 | (exit)))) 132 | 133 | ## Reveal Functions 134 | 135 | Old way: 136 | 137 | (Var f) 138 | ===> 139 | (FunRef f) 140 | 141 | To support `procedure-arity`, we'll need to record the arity of a 142 | function in `FunRefArity`. 143 | 144 | (Var f) 145 | ===> 146 | (FunRefArity f n) 147 | 148 | Which means when processing the `ProgramDefs` form, we need to build 149 | an alist mapping function names to their arity. 150 | 151 | ## Closure Convertion 152 | 153 | To support `procedure-arity`, we use a special purpose 154 | `Closure` form instead of the primitive `vector`, 155 | both in the case for `Lambda` and `FunRefArity`. 156 | 157 | ## Expose Allocation 158 | 159 | Add a case for `Closure` that is similar to the one for `vector` 160 | except that it uses `AllocateClosure` instead of `Allocate`, so that 161 | it can pass along the arity. 162 | 163 | ## Remove Complex Operands 164 | 165 | Add case for `AllocateClosure`. 166 | 167 | ## Explicate Control 168 | 169 | Add case for `AllocateClosure`. 170 | 171 | ## Instruction Selection 172 | 173 | * `(Prim 'make-any (list e (Int tag)))` 174 | 175 | For tag of an Integer or Boolean: (Void too?) 176 | 177 | (Assign lhs (Prim 'make-any (list e (Int tag))) 178 | ===> 179 | movq e', lhs' 180 | salq $3, lhs' 181 | orq tag, lhs' 182 | 183 | where `3` is the length of the tag. 184 | 185 | For other types (vectors and functions): 186 | 187 | (Assign lhs (Prim 'make-any (list e (Int tag)))) 188 | ===> 189 | movq e', lhs' 190 | orq tag, lhs' 191 | 192 | * `(Prim 'tag-of-any (list e))` 193 | 194 | (Assign lhs (Prim 'tag-of-any (list e))) 195 | ===> 196 | movq e', lhs 197 | andq $7, lhs 198 | 199 | where `7` is the binary number `111`. 200 | 201 | * `(ValueOf e ty)` 202 | 203 | If `ty` is an Integer, Boolean, Void: 204 | 205 | (Assign lhs (ValueOf e ty)) 206 | ==> 207 | movq e', lhs' 208 | sarq $3, lhs 209 | 210 | where `3` is the length of the tag. 211 | 212 | If `ty` is a vector or procedure (a pointer): 213 | 214 | (Assign lhs (ValueOf e ty)) 215 | ==> 216 | movq $-8, lhs 217 | andq e', lhs 218 | 219 | where -8 is `(bitwise-not (string->number "#b111"))` 220 | 221 | 222 | To be continued next lecture... 223 | 224 | -------------------------------------------------------------------------------- /lecture-Nov-12.md: -------------------------------------------------------------------------------- 1 | # Challenge: Optimizing Closures 2 | 3 | While closure conversion enables lexical scoping, which is quite 4 | useful, it has the disadvantage of introducing some runtime overhead. 5 | The goal of this challenge assignment is to reduce the overhead in 6 | places where the full generality of closures is not needed. 7 | 8 | We'll be going over an algorithm described in *Optimizing Closures in 9 | O(0) time*, by Andrew W. Keep, Alex Hearn, and R. Kent Dybvig (Scheme 10 | and Functional Programming, 2012). The algorithm performs 3 types of 11 | transformations: optimizing direct calls, optimizing known calls, and 12 | eliminating closures. 13 | 14 | ## Optimize Direct Calls 15 | 16 | Replace an application that has a `lambda` in the operator position 17 | with a `let` expression. (This is a particularly simple form of 18 | inlining.) 19 | 20 | ((lambda: ([x : Integer]) : Integer (+ x 1)) 3) 21 | 22 | ==> 23 | 24 | (let ([x 3]) (+ x 1)) 25 | 26 | 27 | ## Optimize Known Calls 28 | 29 | Def. A function is *known at a call site* if it is provable that the 30 | operator expression of the call will alway evaluate to that function. 31 | 32 | In this pass we handle the obvious case of a known call in which the 33 | operator of the call is a variable bound to a lambda. We replace the 34 | `vector-ref` with the function's label, so we end up with a direct 35 | call instead of an indirect call. 36 | 37 | `lambda_test_9.rkt` 38 | 39 | (let ([y (read)]) 40 | (let ([f (lambda: ([x : Integer]) : Integer 41 | (+ x y))]) 42 | (f 21))) 43 | 44 | ==> closure conversion 45 | 46 | (define (lambda1 [fvs2 : (Vector _ Integer)] [x9 : Integer]) : Integer 47 | (let ([y8 (vector-ref fvs2 1)]) 48 | (+ x9 y8))) 49 | 50 | (define (main) : Integer 51 | (let ([y8 (read)]) 52 | (let ([f0 (Closure 1 (list (fun-ref lambda1) y8))]) 53 | ((vector-ref f0 0) f0 21)))) 54 | 55 | ==> optimize known calls 56 | 57 | (define (lambda1 [fvs2 : (Vector _ Integer)] [x9 : Integer]) : Integer 58 | (let ([y8 (vector-ref fvs2 1)]) 59 | (+ x9 y8))) 60 | 61 | (define (main) : Integer 62 | (let ([y8 (read)]) 63 | (let ([f0 (Closure 1 (list (fun-ref lambda1) y8))]) 64 | ((fun-ref lambda1) f0 21)))) 65 | 66 | 67 | When this optimization doesn't apply: 68 | 69 | Don't know which lambda flows into the application. 70 | 71 | (let ([f (if (eq? (read) 0) 72 | (lambda (x) (+ x 1)) 73 | (lambda (y) (+ y 2)))]) 74 | (f 2)) 75 | 76 | (let ([f (lambda (x) (+ x 1))] 77 | [g (lambda (y) (+ y 2))]) 78 | (let ([vec (vector f g)]) 79 | ((vector-ref vec (read)) 2))) 80 | 81 | If you update your solution to the partial evaluation challenge 82 | assignment, then the known-call optimization will also work through 83 | multiple `let` bindings. 84 | 85 | 86 | (let ([f (lambda: ([x : Integer]) : Integer (+ x 1))]) 87 | (let ([g f]) 88 | (g 41))) 89 | 90 | ==> partial evaluation 91 | 92 | (define (main) : Integer 93 | (let ([f9 (lambda: ( [x8 : Integer]) : Integer 94 | (+ x8 1))]) 95 | (f9 41))) 96 | 97 | ==> closure conversion 98 | 99 | (define (lambda1 [fvs2 : (Vector _)] [x8 : Integer]) : Integer 100 | (+ x8 1)) 101 | 102 | (define (main ) : Integer 103 | (let ([f9 (Closure 1 (list (fun-ref lambda1)))]) 104 | ((vector-ref f9 0) f9 41))) 105 | 106 | ==> optimize known calls 107 | 108 | (define (lambda1 [fvs2 : (Vector _)] [x8 : Integer]) : Integer 109 | (+ x8 1)) 110 | 111 | (define (main) : Integer 112 | (let ([f9 (Closure 1 (list (fun-ref lambda1)))]) 113 | ((fun-ref lambda1) f9 41))) 114 | 115 | 116 | 117 | ## Eliminating Closures 118 | 119 | If a function has no free variables and is only used in calls in which 120 | the function is known (not used in other ways such as passed as a 121 | parameter or stored in a vector), then eliminate the closure. 122 | 123 | Def. A function is *well-known* if it is only used at call sites for 124 | which it is known. 125 | 126 | Eliminate closures of well-known functions with no free variables. e.g. 127 | 128 | (define (add [x : Integer] [y : Integer]) : Integer (+ x y)) 129 | (add 40 2) 130 | 131 | ==> closure conversion 132 | 133 | (define (add8 [fvs1 : _] [x9 : Integer] [y0 : Integer]) : Integer 134 | (+ x9 y0)) 135 | 136 | (define (main) : Integer 137 | (let ([clos2 (Closure 2 (list (fun-ref add8)))]) 138 | ((vector-ref clos2 0) clos2 40 2))) 139 | 140 | ==> optimize known calls 141 | 142 | (define (add8 [fvs1 : _] [x9 : Integer] [y0 : Integer]) : Integer 143 | (+ x9 y0)) 144 | 145 | (define (main) : Integer 146 | (let ([clos2 (Closure 2 (list (fun-ref add8)))]) 147 | ((fun-ref add8) clos2 40 2))) 148 | 149 | ==> eliminate closures 150 | 151 | (define (add8 [x9 : Integer] [y0 : Integer]) : Integer 152 | (+ x9 y0)) 153 | 154 | (define (main) : Integer 155 | ((fun-ref add8) 40 2)) 156 | 157 | An example in which the optimization does not apply: 158 | 159 | (define (add [x : Integer] [y : Integer]) : Integer (+ x y)) 160 | (define (sub [x : Integer] [y : Integer]) : Integer (- x y)) 161 | 162 | (+ (add 20 1) 163 | (let ([f (if (eq? (read) 0) add sub)]) 164 | (f 20 1))) 165 | 166 | 167 | The act of eliminating a closure can affect the number of free 168 | variables in another closure, possibly enabling the elimination of 169 | that closure as well. 170 | 171 | (let ([f (lambda: ([y : Integer]) : Integer y)]) 172 | (let ([g (lambda: ([x : Integer]) : Integer (f x))]) 173 | (g 42))) 174 | 175 | ==> closure conversion and elimination on (lambda: ([y : Integer]) ...) 176 | 177 | (define (lambda2 [y8 : Integer]) : Integer 178 | y8) 179 | 180 | (let ([g (lambda: ([x : Integer]) : Integer ((fun-ref lambda2) x))]) 181 | (g 42)) 182 | 183 | ==> closure conversion and elimination on (lambda: ([x : Integer]) : Integer ...) 184 | 185 | (define (lambda2 [y8 : Integer]) : Integer 186 | y8) 187 | 188 | (define (lambda4 [x0 : Integer]) : Integer 189 | ((fun-ref lambda2) x0)) 190 | 191 | ((fun-ref lambda4) 42) 192 | 193 | 194 | ## Algorithm for Closure Conversion and Closure Optimization 195 | 196 | 1. Mark each call site as either known or unknown. 197 | 198 | 2. Mark each function (`define` or `lambda`) as well-known or not. 199 | 200 | 3. Closure and free variable elimination. 201 | 202 | 3.1. Compute the free variables for each unprocessed function. 203 | 204 | 3.2. Eliminate each well-known function with no free variables. 205 | 206 | 3.3. Goto step 3.1. 207 | 208 | 4. Mop-up: apply closure conversion to the remaining functions. 209 | 210 | 211 | -------------------------------------------------------------------------------- /lecture-Oct-13.md: -------------------------------------------------------------------------------- 1 | # Compiling Functions 2 | 3 | ## The R4 Language 4 | 5 | Concrete Syntax: 6 | 7 | type ::= ... | (type... -> type) 8 | exp ::= ... | (exp exp...) 9 | def ::= (define (var [var : type]...) : type exp) 10 | R4 ::= def... exp 11 | 12 | Abstract Syntax: 13 | 14 | exp ::= ... | (Apply exp exp...) 15 | def ::= (Def var ([var : type] ...) type '() exp) 16 | R4 ::= (ProgramDefsExp '() (def ...) exp) 17 | 18 | * Because of the function type `(type... -> type)`, functions are 19 | first-class in that they can be passed as arguments to other functions 20 | and returned from them. They can also be stored inside tuples. 21 | 22 | * Functions may be recursive and even mutually recursive. That is, 23 | each function name is in scope for the entire program. 24 | 25 | Example program: 26 | 27 | (define (map-vec [f : (Integer -> Integer)] 28 | [v : (Vector Integer Integer)]) : (Vector Integer Integer) 29 | (vector (f (vector-ref v 0)) (f (vector-ref v 1)))) 30 | 31 | (define (add1 [x : Integer]) : Integer 32 | (+ x 1)) 33 | 34 | (vector-ref (map-vec add1 (vector 0 41)) 1) 35 | 36 | Go over the interpreter (Fig. 6.4) 37 | 38 | ## Functions in x86 39 | 40 | Labels can be used to mark the beginning of a function 41 | 42 | The address of a label can be obtained using the `leaq` instruction 43 | and PC-relative addressing: 44 | 45 | leaq add1(%rip), %rbx 46 | 47 | Calling a function whose address is in a register, i.e., indirect 48 | function call. 49 | 50 | callq *%rbx 51 | 52 | ### Abstract Syntax: 53 | 54 | arg ::= ... | (FunRef label) 55 | instr ::= ... | (IndirectCallq arg) | (TailJmp arg) 56 | | (Instr 'leaq (list arg arg)) 57 | def ::= (Def label '() '() info ((label . block) ...)) 58 | x86_3 ::= (ProgramDefs info (def...)) 59 | 60 | ### Calling Conventions 61 | 62 | The `callq` instruction 63 | 1. pushes the return address onto the stack 64 | 2. jumps to the target label or address (for indirect call) 65 | 66 | But there is more to do to make a function call: 67 | 1. parameter passing 68 | 2. pushing and popping frames on the procedure call stack 69 | 3. coordinating the use of registers for local variables 70 | 71 | 72 | #### Parameter Passing 73 | 74 | The C calling convention uses the following six registers (in that order) 75 | for argument passing: 76 | 77 | rdi, rsi, rdx, rcx, r8, r9 78 | 79 | The calling convention says that the stack may be used for argument 80 | passing if there are more than six arguments, but we shall take an 81 | alternate approach that makes it easier to implement efficient tail 82 | calls. If there are more than six arguments, then `r9` will store a 83 | tuple containing the sixth argument and the rest of the arguments. 84 | 85 | #### Pushing and Popping Frames 86 | 87 | The instructions for each function will have a prelude and conclusion 88 | similar to the one we've been generating for `main`. 89 | 90 | The most important aspect of the prelude is moving the stack pointer 91 | down by the size needed the function's frame. Similarly, the 92 | conclusion needs to move the stack pointer back up. 93 | 94 | Recall that we are storing variables of vector type on the root stack. 95 | So the prelude needs to move the root stack pointer `r15` up and the 96 | conclusion needs to move the root stack pointer back down. Also, in 97 | the prelude, this frame's slots in the root stack must be initialized 98 | to `0` to signal to the garbage collector that those slots do not yet 99 | contain a pointer to a vector. 100 | 101 | As we did for `main`, the prelude must also save the contents of the 102 | old base pointer `rbp` and set it to the top of the frame, so that we 103 | can use it for accessing local variables that have been spilled to the 104 | stack. 105 | 106 | |Caller View | Callee View | Contents | Frame 107 | |---------------|---------------|----------------|--------- 108 | | 8(%rbp) | | return address | 109 | | 0(%rbp) | | old rbp | 110 | | -8(%rbp) | | callee-saved | Caller (e.g. map-vec) 111 | | ... | | ... | 112 | | -8(j+1)(%rbp) | | spill | 113 | | ... | | ... | 114 | | | 8(%rbp) | return address | 115 | | | 0(%rbp) | old rbp | 116 | | | -8(%rbp) | callee-saved | Callee (e.g. add1 as f) 117 | | | ... | ... | 118 | | | -8(j+1)(%rbp) | spill | 119 | | | ... | ... | 120 | 121 | 122 | #### Coordinating Registers 123 | 124 | Recall that the registers are categorized as either caller-saved or 125 | callee-saved. 126 | 127 | If the function uses any of the callee-saved registers, then the 128 | previous contents of those registers needs to be saved and restored in 129 | the prelude and conclusion of the function. 130 | 131 | Regarding caller-saved registers, nothing new needs to be done. 132 | Recall that we make sure not to assign call-live variables to 133 | caller-saved registers. 134 | 135 | #### Efficient Tail Calls 136 | 137 | Normally the amount of stack space used by a program is O(d) where d 138 | is the depth of nested function calls. 139 | 140 | This means that recursive functions almost always use at least O(n) 141 | space. 142 | 143 | However, we can sometimes use much less space. 144 | 145 | A *tail call* is a function call that is the last thing to happen 146 | inside another function. 147 | 148 | Example: the recursive call to `tail-sum` is a tail call. 149 | 150 | (define (tail-sum [n : Integer] [r : Integer]) : Integer 151 | (if (eq? n 0) 152 | r 153 | (tail-sum (- n 1) (+ n r)))) 154 | 155 | (+ (tail-sum 5 0) 27) 156 | 157 | 158 | (define (sum [n : Integer]) : Integer 159 | (if (eq? n 0) 160 | 0 161 | (+ n (sum (- n 1))))) ;; not a tail call 162 | 163 | Because a tail call is the last thing to happen, we no longer need the 164 | caller's frame and can reuse that stack space for the callee's frame. 165 | So we can clean up the current frame and then jump to the callee. 166 | However, some care must be taken regarding argument passing. 167 | 168 | The standard convention for passing more than 6 arguments is to use 169 | slots in the caller's frame. But we're deleting the caller's frame. 170 | We could use the callee's frame, but its difficult to move all the 171 | variables without stomping on eachother because the caller and callee 172 | frames overlap in memory. This could be solved by using auxilliary 173 | memory somewhere else, but that increases the amount of memory 174 | traffic. 175 | 176 | We instead recommend using the heap to pass the arguments that don't 177 | fit in the 6 registers. 178 | 179 | Instead of `callq`, use `jmp` for the tail call because the return 180 | address that is already on the stack is the correct one. 181 | 182 | Use `rax` to hold the target address for an indirect jump. 183 | 184 | -------------------------------------------------------------------------------- /lecture-Sep-29.md: -------------------------------------------------------------------------------- 1 | # Booleans and Conditional (R2) Continued 2 | 3 | ## Select Instructions 4 | 5 | Thanks to `explicate-control` and the grammar of C1, compiling `if` 6 | statements to x86 is straightforward. Let `arg1` and `arg2` be the 7 | results of translating `atm1` and `atm2` to x86, respectively. 8 | 9 | if (eq? atm1 atm2) => cmpq arg2, arg1 10 | goto l1; je l1 11 | else jmp l2 12 | goto l2; 13 | 14 | and similarly for the other comparison operators. 15 | 16 | We only use the `set` and `movzbq` dance for comparisons in an 17 | assignment statement. Let `arg1` and `arg2` be the results of 18 | translating `atm1` and `atm2` to x86, respectively. 19 | 20 | var = (eq? atm1 atm2); => cmpq arg2, arg1 21 | sete %al 22 | mozbq %al, var 23 | 24 | 25 | ## Register Allocation 26 | 27 | ### Liveness Analysis 28 | 29 | We know how to perform liveness on a basic block. 30 | But now we have a whole graph full of basic blocks. 31 | Example: 32 | 33 | start 34 | / \ 35 | / \ 36 | inner_then inner_else 37 | | \____/ | 38 | | / \ | 39 | outer_then outer_else 40 | 41 | locals: (x y) 42 | start: 43 | callq 'read_int 44 | movq %rax, x 45 | callq 'read_int 46 | movq %rax, y 47 | cmpq $1, x 48 | jl inner_then 49 | jmp inner_else 50 | inner_then: 51 | cmpq $0, x 52 | je outer_then 53 | jmp outer_else 54 | inner_else: 55 | cmpq $2, x 56 | live after ?? {y} (union) 57 | je outer_then 58 | jmp outer_else 59 | outer_then: {y} 60 | movq y, %rax 61 | addq $2, %rax 62 | jmp conclusion 63 | outer_else: {y} 64 | movq y, %rax 65 | addq $10, %rax 66 | jmp conclusion 67 | 68 | Q: In what *order* should we process the blocks? 69 | A: Reverse topological order. 70 | In other words, first process the blocks with no out-edges, 71 | because the live-after set for the last instruction in each 72 | of those blocks is the empty set. In this example, first 73 | process `outer_then` and `outer_else`. Then imagine that those 74 | blocks are removed from the graph. Again select a block with 75 | no out-edges and repeat the process, continuing until all the 76 | blocks are gone. 77 | 78 | Q: How do we compute the live-after set for the instruction at the end 79 | of each block? After all, we don't know which way the conditional 80 | jumps will go. 81 | A: Take the *union* of the live-before set of the first instruction of 82 | every *successor* block. Thus we compute a conservative 83 | approximation of the real live-before set. 84 | 85 | 86 | ### Build Interference 87 | 88 | Nothing surprising. Need to give `movzbq` special treatment similar to 89 | the `movq` instruction. Also, the register `al` should be considered 90 | the same as `rax`, because it is a part of `rax`. 91 | 92 | 93 | ## Patch Instructions 94 | 95 | * `cmpq` the second argument must not be an immediate. 96 | 97 | * `movzbq` the target argument must be a register. 98 | 99 | 100 | ## Challenge: Optimize and Remove Jumps 101 | 102 | The output of `explicate-control` for our running example is not quite 103 | as nice as we advertised above. It generates lots of trivial blocks 104 | that just goto another block. 105 | 106 | block8482: 107 | if (eq? x8473 2) 108 | goto block8479; 109 | else 110 | goto block8480; // block8476 111 | block8481: 112 | if (eq? x8473 0) 113 | goto block8477; 114 | else 115 | goto block8478; 116 | block8480: 117 | goto block8476; 118 | block8479: 119 | goto block8475; 120 | block8478: 121 | goto block8476; 122 | block8477: 123 | goto block8475; 124 | block8476: 125 | return (+ y8474 10); 126 | block8475: 127 | return (+ y8474 2); 128 | start: 129 | x8473 = (read); 130 | y8474 = (read); 131 | if (< x8473 1) 132 | goto block8481; 133 | else 134 | goto block8482; 135 | 136 | Two passes: 137 | 1. `optimize-jumps` collapses sequences of jumps through trivial 138 | blocks into a single jump. Remove the trivial blocks. 139 | 140 | B1 -> B2* -> B3* -> B4 -> B5* -> B6 141 | => 142 | B1 -> B4 -> B6 143 | 144 | 145 | 2. `remove-jumps` merges a block with one that comes after 146 | if there is only one in-edge to the later one. 147 | 148 | 149 | B1 B2 B3 B1 B2 B3 150 | | \ / B4 \ / 151 | | \ / \ \ / 152 | B4 B5 => \ B5 153 | \ / \ / 154 | \ / \ / 155 | B6 B6 156 | 157 | B1 B2 B3 B1 B2 B3 158 | | \ / B4 \ / 159 | | \ / \ \ / 160 | B4 B5* => \ \/ 161 | \ / \ / 162 | \ / \ / 163 | B6 B6 164 | 165 | 166 | # R3 Language (Tuples) 167 | 168 | (define v (vector 1 2 #t)) ;; [1,2,#t] : (Vector Integer Integer Boolean) 169 | (vector-ref v 0) ;; 1 170 | (vector-ref v 1) ;; 2 171 | (vector-set! v 0 5) ;; [5,2,#t] 172 | (vector-ref v 0) ;; 5 173 | 174 | type ::= Integer | Boolean | (Vector type+) | Void 175 | exp ::= ... 176 | | (vector exp+) 177 | | (vector-ref exp int) 178 | | (vector-set! exp int exp) 179 | | (void) 180 | R3 ::= (program exp) 181 | 182 | 183 | ## Example Programs 184 | 185 | * Tuples are first class 186 | 187 | (let ([t (vector 40 #t (vector 2))]) 188 | (if (vector-ref t 1) 189 | (+ (vector-ref t 0) 190 | (vector-ref (vector-ref t 2) 0)) 191 | 44)) 192 | 193 | * Tuples may be aliased 194 | 195 | (let ([t1 (vector 3 7)]) ;; t1 -> [42,7] <- t2 *** 196 | (let ([t2 t1]) ;; t2 -> [3,7] XXX 197 | (let ([_ (vector-set! t2 0 42)]) 198 | (vector-ref t1 0)))) 199 | 200 | * Tuples live forever (from the programmer's view) 201 | 202 | (vector-ref 203 | (let ([t (vector 3 7)]) 204 | t) 205 | 0) ;; 3 206 | 207 | ;; Question: How is that different from integers? 208 | 209 | (+ 210 | (let ([t 5]) 211 | t) 212 | 0) 213 | 214 | ;; Answer: Ok, consider this example in which (vector 3 7) 215 | ;; is not returned from the `let`, but we can nevertheless 216 | ;; refer to it through the vector bound to `x`. 217 | 218 | (let ([x (vector (vector))]) ;; x -> [ * ] 219 | (+ ;; | 220 | (let ([t (vector 3 7)]) ;; t -> [3,7] 221 | (vector-set! x 0 t) 222 | 5) ;; 5 223 | (vector-ref (vector-ref x 0) 0)) ;; 3 224 | ;; 8 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /lecture-Sep-24.md: -------------------------------------------------------------------------------- 1 | # More x86 with an eye toward instruction selection for R2 2 | 3 | cc ::= e | l | le | g | ge 4 | instr ::= ... | xorq arg, arg | cmpq arg, arg | set arg 5 | | movzbq arg, arg | j label 6 | 7 | x86 assembly does not have Boolean values (false and true), 8 | but the integers 0 and 1 will serve. 9 | 10 | x86 assembly does not have direct support for logical `not`. 11 | But `xorq` can do the job: 12 | 13 | | 0 | 1 | 14 | |---|---| 15 | 0 | 0 | 1 | 16 | 1 | 1 | 0 | 17 | 18 | var = (not arg); => movq arg, var 19 | xorq $1, var 20 | 21 | The `cmpq` instruction can be used to implement `eq?`, `<`, etc. 22 | But it's a bit strange. It puts the result in a mysterious 23 | EFLAGS register. 24 | 25 | var = (< arg1 arg2) => cmpq arg2, arg1 26 | setl %al 27 | movzbq %al, var 28 | 29 | The `cmpq` instruction can also be used for conditional branching. 30 | The conditional jump instructions `je`, `jl`, etc. also read 31 | from the EFLAGS register. 32 | 33 | 34 | if (eq? arg1 arg2) => cmpq arg2, arg1 35 | goto l1; je l1 36 | else jmp l2 37 | goto l2; 38 | 39 | 40 | # The C1 intermediate language 41 | 42 | Syntax of C1 43 | 44 | bool ::= #t | #f 45 | atm ::= int | var | bool 46 | cmp ::= eq? | < | <= | > | >= 47 | exp ::= ... | (not atm) | (cmp atm atm) 48 | stmt ::= ... 49 | tail ::= ... 50 | | goto label; 51 | | if (cmp atm atm) 52 | goto label; 53 | else 54 | goto label; 55 | C1 ::= label1: 56 | tail1 57 | label2: 58 | tail2 59 | ... 60 | 61 | 62 | # Explicate Control 63 | 64 | Consider the following program 65 | 66 | (let ([x (read)]) 67 | (let ([y (read)]) 68 | (if (if (< x 1) (eq? x 0) (eq? x 2)) 69 | (+ y 2) 70 | (+ y 10)))) 71 | 72 | A straightforward way to compile an `if` expression is to recursively 73 | compile the condition, and then use the `cmpq` and `je` instructions 74 | to branch on its Boolean result. Let's first focus in the `(if (< x 1) ...)`. 75 | 76 | ... 77 | cmpq $1, x ;; (< x 1) 78 | setl %al 79 | movzbq %al, tmp 80 | cmpq $1, tmp ;; (if (< x 1) ...) 81 | je then_branch1 82 | jmp else_branch1 83 | ... 84 | 85 | But notice that we used two `cmpq`, a `setl`, and a `movzbq` 86 | when it would have been better to use a single `cmpq` with 87 | a `jl`. 88 | 89 | callq read_int 90 | movq %rax, x 91 | callq read_int 92 | movq %rax, y 93 | cmpq $1, x ;; (if (< x 1) ...) 94 | jl then_branch1 95 | jmp else_branch1 96 | ... 97 | 98 | Ok, so we should recognize when the condition of an `if` is a 99 | comparison, and specialize our code generation. But can we do even 100 | better? Consider the outer `if` in the example program, whose 101 | condition is not a comparison, but another `if`. Can we rearrange the 102 | program so that the condition of the `if` is a comparison? How about 103 | pushing the outer `if` inside the inner `if`: 104 | 105 | (let ([x (read)]) 106 | (let ([y (read)]) 107 | (if (< x 1) 108 | (if (eq? x 0) 109 | (+ y 2) 110 | (+ y 10)) 111 | (if (eq? x 2) 112 | (+ y 2) 113 | (+ y 10))))) 114 | 115 | Unfortunately, now we've duplicated the two branches of the outer `if`. 116 | A compiler must *never* duplicate code! 117 | 118 | Now we come to the reason that our Cn programs take the forms of a 119 | control flow *graph* instead of a *tree*. A graph allows multiple 120 | edges to point to the save vertex, thereby enabling sharing instead of 121 | duplication. Recall that the nodes of the control flow graph are 122 | labeled `tail` statements and the edges are expressed with `goto`. 123 | 124 | Using these insights, we can compile the example to the following C1 125 | program. 126 | 127 | (let ([x (read)]) 128 | (let ([y (read)]) 129 | (if (if (< x 1) (eq? x 0) (eq? x 2)) 130 | (+ y 2) 131 | (+ y 10)))) 132 | => 133 | start: 134 | x = (read); 135 | y = (read); 136 | if (< x 1) 137 | goto inner_then; 138 | else 139 | goto inner_else; 140 | inner_then: 141 | if (eq? x 0) 142 | goto outer_then; 143 | else 144 | goto outer_else; 145 | inner_else: 146 | if (eq? x 2) 147 | goto outer_then; 148 | else 149 | goto outer_else; 150 | outer_then: 151 | return (+ y 2); 152 | outer_else: 153 | return (+ y 10); 154 | 155 | Notice that we've acheived both objectives. 156 | 1. The condition of each `if` is a comparison. 157 | 2. We have not duplicated the two branches of the outer `if`. 158 | 159 | 160 | A new function for compiling the condition expression of an `if`: 161 | 162 | explicate-pred : R2_exp x C1_tail x C1_tail -> C1_tail x var list 163 | 164 | (explicate-pred #t B1 B2) => B1 165 | 166 | (explicate-pred #f B1 B2) => B2 167 | 168 | (explicate-pred (< atm1 atm2) B1 B2) => if (< atm1 atm2) 169 | goto l1; 170 | else 171 | goto l2; 172 | where B1 and B2 are added to the CFG with labels l1 and l2. 173 | 174 | (explicate-pred (if e1 e2 e3) B1 B2) => B5 175 | where we add B1 and B2 to the CFG with labels l1 and l2. 176 | (explicate-pred e2 (goto l1) (goto l2)) => B3 177 | (explicate-pred e3 (goto l1) (goto l2)) => B4 178 | (explicate-pred e1 B3 B4) => B5 179 | 180 | explicate-tail : R2_exp -> C1_tail x var list 181 | 182 | (explicate-tail (if e1 e2 e3)) => B3 183 | where (explicate-tail e2) => B1 184 | (explicate-tail e3) => B2 185 | (explicate-pred e1 B1 B2) => B3 186 | 187 | 188 | explicate-assign : R2_exp -> var -> C1_tail -> C1_tail x var list 189 | 190 | (explicate-assign (Int n) x B1) => (Seq (Assign (Var x) (Int n)) B1) 191 | 192 | (explicate-assign (if e1 e2 e3) x B1) => B4 193 | where we add B1 to the CFG with label l1 194 | (explicate-assign e2 x (goto l1)) => B2 195 | (explicate-assign e3 x (goto l1)) => B3 196 | (explicate-pred e1 B2 B3) => B4 197 | 198 | 199 | example: 200 | 201 | (explicate-tail 202 | (let ([x (read)]) 203 | (let ([y (read)]) 204 | (if (if (< x 1) (eq? x 0) (eq? x 2)) 205 | (+ y 2) 206 | (+ y 10))))) 207 | (explicate-tail (let ([y (read)]) ...)) => B1 208 | (explicate-tail (if ... (+ y 2) (+ y 10))) => B2 209 | (explicate-tail (+ y 2)) => B3 = return (+ y 2); 210 | (explicate-tail (+ y 10)) => B4 = return (+ y 10); 211 | (explicate-pred (if (< x 1) (eq? x 0) (eq? x 2) B3 B4)) => B2 212 | put B3 and B4 in the CFG, get labels l3 l4. 213 | (explicate-pred (eq? x 0) (goto l3) (goto l4)) => B6 214 | B6 = if (eq? x 0) 215 | goto l3; 216 | else 217 | goto l4; 218 | (explicate-pred (eq? x 2) (goto l3) (goto l4)) => B7 219 | B7 = if (eq? x 2) 220 | goto l3; 221 | else 222 | goto l4; 223 | (explicate-pred (< x 1) B6 B7) => B2 224 | put B6 and B7 into CFG, get labels l6, l7. 225 | B2 = if (< x 1) 226 | goto l6; 227 | else 228 | goto l7; 229 | (explicate-assign (read) y B2) => (y = (read); B2) = B1 230 | (explicate-assign (read) x B1) => x = (read); B1 231 | 232 | -------------------------------------------------------------------------------- /lecture-Nov-17.md: -------------------------------------------------------------------------------- 1 | # Dataflow Analysis 2 | 3 | References: 4 | 5 | * A Unified Approach to Global Program Optimization 6 | by Gary A. Kildall, POPL 1973. 7 | Gary's Ph.D. thesis at U.W. 8 | He is also known for the CP/M operating system that was an 9 | early competitor with MS-DOS (Microsoft). 10 | 11 | Analyses and Optimizations: 12 | 13 | * constant propagation and folding 14 | * common subexpression elimination 15 | * live expression analysis 16 | 17 | ## Constant Propagation and Folding 18 | 19 | A: a = 1 20 | B: c = 0 21 | for i = 1...10 { 22 | C: b = 2 23 | D: d = a + b 24 | E: e = b + c 25 | F: c = 4 26 | } 27 | 28 | Hasse Diagram, Finite Descending Chains 29 | 30 | {(a,...), (b,...), (c,...), (d,...), (e, ...) } (5 pairs) 31 | | 32 | ... 33 | | 34 | {(a,1),(b,2)} 35 | | 36 | {(a,1)} 37 | \ 38 | {} 39 | 40 | Peel one iteration: 41 | 42 | A: a = 1 43 | B: c = 0 44 | i = 1 45 | C: b = 2 46 | D: d = 3 47 | E: e = 2 48 | F: c = 4 49 | for i = 2...10 { 50 | E: e = b + c 51 | } 52 | 53 | Peel two iterations: 54 | 55 | A: a = 1 56 | B: c = 0 57 | i = 1 58 | C: b = 2 59 | D: d = 3 60 | E: e = 2 61 | F: c = 4 62 | i = 2 63 | E: e = 6 64 | 65 | {} 66 | a = 3 67 | {(a,3)} 68 | if (eq? (read) 0) { 69 | {(a,3)} 70 | b = 5 71 | {(a,3),(b,5)} 72 | } else { 73 | {(a,3)} 74 | b = 10 75 | {(a,3),(b,10)} 76 | } 77 | {(a,3),(b,5)} /\ {(a,3),(b,10)} = {(a,3)} 78 | c = b 79 | {(a,3)} 80 | 81 | Consider every possible execution path, propagating constants along 82 | each path. Then the analysis information P_N for a node N is the 83 | *intersection* of the information from all paths to that node. 84 | 85 | Let P^i_N be the information for the ith path from the entry node 86 | up to but not including node N. 87 | 88 | P^3(C) = {(a,1),(c,0)} 89 | P^7(C) = {(a,1),(b,2),(c,4),(d,3),(e,2)} 90 | 91 | A {} 92 | A->B {(a,1)} 93 | A->B->C {(a,1),(c,0)} 94 | A->B->C->D *{(a,1),(b,2),(c,0)} 95 | A->B->C->D->E {(a,1),(b,2),(c,0),(d,3)} 96 | A->B->C->D->E->F {(a,1),(b,2),(c,0),(d,3),(e,2)} 97 | A->B->C->D->E->F->C {(a,1),(b,2),(c,4),(d,3),(e,2)} 98 | A->B->C->D->E->F->C->D *{(a,1),(b,2),(c,4),(d,3),(e,2)} 99 | A->B->C->D->E->F->C->D->E {(a,1),(b,2),(c,4),(d,3),(e,2)} 100 | A->B->C->D->E->F->C->D->E->F {(a,1),(b,2),(c,4),(d,3),(e,6)} 101 | ... 102 | ... 103 | ->D *{...} 104 | 105 | Then P_N = Inter_i P^i_N. 106 | 107 | P_D = {(a,1),(b,2)} 108 | 109 | This approach is known as meet-over-all-paths (MOP). 110 | 111 | This approach is not practical because the number of paths are often 112 | infinite. 113 | 114 | Instead of waiting to the end to take the intersection, we could do it 115 | eagerly, intersecting along the way with prior results. 116 | 117 | A {} 118 | A->B {(a,1)} 119 | A->B->C {(a,1),(c,0)} 120 | A->B->C->D {(a,1),(b,2),(c,0)} 121 | A->B->C->D->E {(a,1),(b,2),(c,0),(d,3)} 122 | A->B->C->D->E->F {(a,1),(b,2),(c,0),(d,3),(e,2)} 123 | A->B->C->D->E->F->C {(a,1)} 124 | = {(a,1),(b,2),(c,4),(d,3),(e,2)} /\ {(a,1),(c,0)} 125 | A->B->C->D->E->F->C->D {(a,1),(b,2)} 126 | = {(a,1),(b,2)} /\ {(a,1),(b,2),(c,0)} 127 | ...C->D->E->F->C->D->E {(a,1),(b,2),(d,3))} 128 | = {(a,1),(b,2),(d,3)} /\ {(a,1),(b,2),(c,0),(d,3)} 129 | ...D->E->F->C->D->E->F {(a,1),(b,2),(d,3))} 130 | = {(a,1),(b,2),(c,4)(d,3))} /\ {(a,1),(b,2),(c,0),(d,3),(e,2)} 131 | ...E->F->C->D->E->F->C {(a,1)} 132 | = {(a,1),(b,2),(c,4)(d,3))} /\ {(a,1)} 133 | ...E->F->C->D->E->F->C->D {(a,1),(b,2)} 134 | = {(a,1),(b,2)} /\ {(a,1),(b,2)} 135 | 136 | We got the same answer for P_C, so going around the loop again isn't 137 | going to change the results. 138 | 139 | Informal Global Analysis Algorithm For Constant Propagation & Folding 140 | 141 | 1. For entry node E, set P_E = {} 142 | 2. Process node E, send info to the successor nodes. 143 | 3. Intersect the incoming pools for each node with the prior pool 144 | for the node, if there is one. If not, take the incoming pool. 145 | 4. If the result for a node changed, then send updated info to the 146 | successor nodes. 147 | 148 | ## Generalization to other Static Analysis Problems 149 | 150 | * An "transfer" function f that maps a node and an input pool to an 151 | output pool. Recall the example program: 152 | 153 | A: a = 1 154 | B: c = 0 155 | for i = 1...10 { 156 | C: b = 2 157 | D: d = a + b 158 | E: e = b + c 159 | F: c = 4 160 | } 161 | 162 | The transfer function for constant propagation for this program is: 163 | 164 | f(A,S) = {(a,1)} U (S - a) 165 | f(B,S) = {(c,0)} U (S - c) 166 | f(C,S) = {(b,2)} U (S - b) 167 | f(D,S) = if (a,n1) in S and (b,n2) in S then 168 | {(d,n1+n2)} U (S - d) 169 | else 170 | S - d 171 | 172 | * Initial value for the entry node. 173 | 174 | * The information must form a meet-semilattice. 175 | 176 | - The meet operation /\ is commutative and associative. 177 | - The semilattice has a "bottom" element (e.g. empty set). 178 | (It may also have a "top" element, in which case it is a lattice.) 179 | - The elements are partially ordered in a way that 180 | jives with the meet operation. 181 | 182 | A <= B iff A /\ B = A 183 | 184 | A subset or equal B 185 | iff 186 | A /\ B = A 187 | 188 | * The transfer functions must be monotonic. 189 | 190 | X <= Y --> f(N,X) <= f(N,Y) 191 | 192 | which implies that taking the meet "early" gives you correct 193 | but possibly less precise results. 194 | 195 | f(N, X /\ Y) <= f(N, X) /\ f(N, Y) 196 | 197 | If the transfer function is distributive 198 | 199 | f(N, X /\ Y) = f(N, X) /\ (N, Y) 200 | 201 | then applying the meet "early" doesn't lose precision. 202 | 203 | * For termination, the lattice needs to have finite descending chains. 204 | 205 | ## Common Subexpression Elimination 206 | 207 | Example: 208 | 209 | T: r = a + b 210 | U: s = r + x 211 | V: t = (a + b) + x 212 | W: return t 213 | 214 | ==> 215 | 216 | r = a + b 217 | s = r + x 218 | return s 219 | 220 | Need to keep track of which expressions are equivalent, 221 | i.e., we need to track equivalence classes of expressions. 222 | 223 | P_T = { a | b | x } 224 | P_U = { a | b | x | a+b, r } 225 | P_V = { a | b | x | a+b, r | r+x, s } 226 | P_W = { a | b | x | a+b, r | r+x, s, (a + b) + x, t } 227 | 228 | The transfer function for common subexpression elimination: 229 | 230 | For each subexpression e in the node N: 231 | 232 | * If e is already in an equiv. class, then it is redundant. 233 | * If not, create a new class in P with singleton e. 234 | * If node N is an assignment d = e, remove from P 235 | all expressions containing d. For each expression e' in P 236 | that contains e, create e'' by replacing e with d, and place e'' 237 | in the class of e'. 238 | 239 | The meet operation P1 /\ P2 forms a partition over the expressions 240 | common to both P1 and P2, with 241 | 242 | (P1 /\ P2)(e) = P1(e) /\ P2(e) 243 | 244 | ## Live Expression Analysis 245 | 246 | Generalizes liveness analysis from variables to expressions. 247 | An *expression is live* if it may be used in the future. 248 | 249 | This analysis works backwards, from the end of the program to the 250 | beginning. Flip edges to have it work forwards. 251 | 252 | The transfer function is: 253 | 254 | If N is x := e, 255 | P <- (P - {e'| e' in P, x in e' }) U {e' | e' in e} 256 | 257 | The meet operation is set union. 258 | 259 | Initial pools are {}. 260 | 261 | ## Implementation 262 | 263 | Worklist Algorithm: 264 | 265 | Place entry node in the worklist. 266 | While the worklist is not empty: 267 | Pop a node from the worklist 268 | Apply the transfer function for that node. 269 | Place its successors who changed in the worklist. 270 | 271 | To speed up the ordering: 272 | 273 | 1. Form the condensation graph by collapsing strongly-connected components. 274 | 275 | 2. Process the condensation graph in topological order. For each 276 | component, run the worklist algorithm to a fixed point. 277 | 278 | 279 | -------------------------------------------------------------------------------- /lecture-Sep-15.md: -------------------------------------------------------------------------------- 1 | # Register Allocation: Graph Coloring via Sudoku 2 | 3 | * Goal: map each variable to a register such that no two interfering 4 | variables get mapped to the same register. 5 | 6 | * Secondary goal: minimize the number of stack locations that need 7 | to be used. 8 | 9 | * In the interference graph, this means that adjacent vertices must be 10 | mapped to different registers. 11 | 12 | * If we think of registers and stack locations as colors, then this 13 | becomes an instance of the *graph coloring problem*. 14 | 15 | If you aren't familiar with graph coloring, you might instead be 16 | familiar with another instance of it, *Sudoku*. 17 | 18 | Review Sudoku and relate it to graph coloring. 19 | 20 | ------------------- 21 | | 9 |5 3 7| 1 4| 22 | | 3 8| 4 6| 9 | 23 | |4 |1 |2 | 24 | ------------------- 25 | | | | 2| 26 | |7 9|8 2|1 5| 27 | |6 | | | 28 | ------------------- 29 | | 4| 8| 6| 30 | | 6 |4 9 |7 2 | 31 | |8 7 |6 2 3| 4 | 32 | ------------------- 33 | 34 | * Squares on the board corresponds to vertices in the graph. 35 | * The vertices for squares in the same *row* are connected by edges. 36 | * The vertices for squares in the same *column* are connected by edges. 37 | * The vertices for squares in the same *3x3 region* are connected by edges. 38 | * The numbers 1-9 are corresponds to 9 different colors. 39 | 40 | What strategies do you use to play Sudoku? 41 | 42 | * Pencil Marks? (most-constrained-first) 43 | We'll record the colors that cannot be used, 44 | i.e. the *saturation* of the vertex. 45 | * Backtracking? 46 | * Register allocation is easier than Sudoku in 47 | that we can spill to the stack, i.e., we can always add more colors. 48 | * Also, it is important for a register allocator to be 49 | efficient, and backtracking is exponential time. 50 | * So it's better to *not* use backtracking. 51 | 52 | We'll use the DSATUR algorithm of Brelaz (1979). 53 | Use natural numbers for colors. 54 | The set W is our worklist, that is, the vertices that still need to be 55 | colored. 56 | 57 | W <- vertices(G) 58 | while W /= {} do 59 | pick a vertex u from W with maximal saturation 60 | find the lowest color c not in { color[v] | v in adjacent(u) }. 61 | color[u] <- c 62 | W <- W - {u} 63 | 64 | Initial state: 65 | 66 | {} {} {} 67 | t ---- z x 68 | |\_ | 69 | | \__ | 70 | | \| 71 | y ---- w ---- v 72 | {} {} {} 73 | 74 | There's a tie amogst all vertices. Color t 0. Update saturation of adjacent. 75 | 76 | {} {0} {} 77 | t:0----z x 78 | |\_ | 79 | | \__ | 80 | | \| 81 | y ---- w ---- v 82 | {} {} {} 83 | 84 | Vertex z is the most saturated. Color z 1. Update saturation of adjacent. 85 | 86 | {1} {0} {} 87 | t:0----z:1 x 88 | |\_ | 89 | | \__ | 90 | | \| 91 | y ---- w ---- v 92 | {1} {1} {} 93 | 94 | 95 | There is a tie between y and w. Color w 0. 96 | 97 | {1} {0} {0} 98 | t:0----z:1 x 99 | |\_ | 100 | | \__ | 101 | | \| 102 | y ----w:0---- v 103 | {0,1} {1} {0} 104 | 105 | Vertex y is the most saturated. Color y 2. 106 | 107 | {1} {0,2} {0} 108 | t:0----z:1 x 109 | |\_ | 110 | | \__ | 111 | | \| 112 | y:2----w:0---- v 113 | {0,1} {1,2} {0} 114 | 115 | Vertex x and v are the most saturated. Color v 1. 116 | 117 | {1} {0,2} {0} 118 | t:0----z:1 x 119 | |\_ | 120 | | \__ | 121 | | \| 122 | y:2----w:0----v:1 123 | {0,1} {1,2} {0} 124 | 125 | Vertex x is the most saturated. Color x 1. 126 | 127 | {1} {0,2} {0} 128 | t:0----z:1 x:1 129 | |\_ | 130 | | \__ | 131 | | \| 132 | y:2----w:0----v:1 133 | {0,1} {1,2} {0} 134 | 135 | * Create variable to register/stack location mapping: 136 | We're going to reserve `rax` and `r15` for other purposes, 137 | and we use `rsp` and `rbp` for maintaining the stack, 138 | so we have 12 remaining registers to use: 139 | 140 | rbx rcx rdx rsi rdi r8 r9 r10 r11 r12 r13 r14 141 | 142 | Map the first 12 colors to the above registers, and map the rest of 143 | the colors to stack locations (starting with a -8 offset from `rbp` 144 | and going down in increments of 8 bytes. 145 | 146 | 0 -> rbx 147 | 1 -> rcx 148 | 2 -> rdx 149 | 3 -> rsi 150 | ... 151 | 12 -> -8(%rbp) 152 | 13 -> -16(%rbp) 153 | 14 -> -24(%rbp) 154 | ... 155 | 156 | So we have the following variable-to-home mapping 157 | 158 | v -> rcx 159 | w -> rbx 160 | x -> rcx 161 | y -> rdx 162 | z -> rcx 163 | t -> rbx 164 | 165 | 166 | * Update the program, replacing variables according to the variable-to-home 167 | mapping. We also record the number of bytes needed of stack space 168 | for the local variables, which in this case is 0. 169 | 170 | Recall the example program after instruction selection: 171 | 172 | locals: v w x y z t 173 | start: 174 | movq $1, v 175 | movq $42, w 176 | movq v, x 177 | addq $7, x 178 | movq x, y 179 | movq x, z 180 | addq w, z 181 | movq y, t 182 | negq t 183 | movq z, %rax 184 | addq t, %rax 185 | jmp conclusion 186 | 187 | Here's the output of register allocation, after applying 188 | the variable-to-home mapping. 189 | 190 | stack-space: 0 191 | start: 192 | movq $1, %rcx 193 | movq $42, %rbx 194 | movq %rcx, %rcx 195 | addq $7, %rcx 196 | movq %rcx, %rdx 197 | movq %rcx, %rcx 198 | addq %rbx, %rcx 199 | movq %rdx, %rbx 200 | negq %rbx 201 | movq %rcx, %rax 202 | addq %rbx, %rax 203 | jmp conclusion 204 | 205 | # Challenge: Move Biasing 206 | 207 | In the above output code there are two movq instructions that can be 208 | removed because their source and target are the same. A better 209 | register allocation would have put t, v, x, and y into the same 210 | register, thereby removing the need for three of the movq 211 | instructions. 212 | 213 | Here is the *move-graph* for the example program. 214 | 215 | 216 | t z --- x 217 | \_ _/ \_ 218 | \_ _/ \_ 219 | \ / \ 220 | y w v 221 | 222 | Let's redo the coloring, fast-forwarding past the coloring of t and z. 223 | 224 | {1} {0} {} 225 | t:0----z:1 x 226 | |\_ | 227 | | \__ | 228 | | \| 229 | y ---- w ---- v 230 | {1} {1} {} 231 | 232 | There is a tie between y and w regarding saturation, but 233 | this time we note that y is move related to t, which has color 0, 234 | whereas w is not move related. So we color y to 0. 235 | 236 | {1} {0} {} 237 | t:0----z:1 x 238 | |\_ | 239 | | \__ | 240 | | \| 241 | y:0---- w ---- v 242 | {1} {0,1} {} 243 | 244 | Next we color w to 2. 245 | 246 | {1} {0,2} {2} 247 | t:0----z:1 x 248 | |\_ | 249 | | \__ | 250 | | \| 251 | y:0----w:2---- v 252 | {1,2} {0,1} {2} 253 | 254 | Now x and v are equally saturated, but x is move related 255 | to y and z whereas v is not move related to any vertex 256 | that has already been colored. So we color x to 0. 257 | 258 | {1} {0,2} {2} 259 | t:0----z:1 x:0 260 | |\_ | 261 | | \__ | 262 | | \| 263 | y:0----w:2----v:0 264 | {1,2} {0,1} {2} 265 | 266 | Finally, we color v to 0. 267 | 268 | So we have the assignment: 269 | 270 | v -> rbx 271 | w -> rdx 272 | x -> rbx 273 | y -> rbx 274 | z -> rcx 275 | t -> rbx 276 | 277 | and generate the following code. 278 | 279 | movq $1, %rbx 280 | movq $42, %rdx 281 | movq %rbx, %rbx 282 | addq $7, %rbx 283 | movq %rbx, %rbx 284 | movq %rbx, %rcx 285 | addq %rdx, %rcx 286 | movq %rbx, %rbx 287 | negq %rbx 288 | movq %rcx, %rax 289 | addq %rbx, %rax 290 | jmp conclusion 291 | 292 | As promised, there are three trivial movq's that can 293 | be removed in patch-instructions. 294 | 295 | movq $1, %rbx 296 | movq $42, %rdx 297 | addq $7, %rbx 298 | movq %rbx, %rcx 299 | addq %rdx, %rcx 300 | negq %rbx 301 | movq %rcx, %rax 302 | addq %rbx, %rax 303 | jmp conclusion 304 | -------------------------------------------------------------------------------- /lecture-Oct-27.md: -------------------------------------------------------------------------------- 1 | # Code Review of Tuple & Garbage Collection 2 | 3 | Announcements: 4 | * Midterm exam on Friday, available 7am to 11:59pm, 90 minutes. 5 | 6 | ## `type-check` 7 | 8 | * add `HasType` 9 | * type checking of `vector-set!` 10 | 11 | ``` 12 | (define (type-check-exp env e) 13 | (match e 14 | [(Var x) 15 | (define type (match-alist x env)) 16 | (values (HasType (Var x) type) type)] 17 | ... 18 | [(Prim 'vector-set! (list vect (Int i) val)) 19 | (define-values (vect-exp vect-type) (type-check-exp env vect)) 20 | (define-values (i-exp i-type) (type-check-exp env (Int i))) 21 | (define-values (val-exp val-type) (type-check-exp env val)) 22 | (if (not (eq? i-type 'Integer)) 23 | (error "The type of index for vector-set! must be an Integer") 24 | (if (not (eq? (car vect-type) 'Vector)) 25 | (error "Vector set got a non vector") 26 | (if (not (equal? (list-ref vect-type (add1 i)) val-type)) 27 | (error (format "Changing vector types is not supported got ~a ~a" 28 | (list-ref vect-type (add1 i)) val-type)) 29 | (values (HasType (Prim 'vector-set! (list vect-exp i-exp val-exp)) 30 | 'Void) 'Void))))] 31 | ...) 32 | ``` 33 | 34 | ## `expose-allocation` 35 | 36 | lower `(vector e1 ... en)` creation into: 37 | 1. evaluate the initializing expressions `e1` ... `en` 38 | 2. call `collect` if there is not enough room in the FromSpace 39 | 3. allocate the vector using the `allocate` form 40 | 4. initialize the vector's elements using `vector-set!` 41 | 42 | ``` 43 | (define (expose-exp e) 44 | (match e 45 | [(HasType (Prim 'vector es) type) 46 | (let* ([len (length es)] 47 | [bytes (* 8 len)] 48 | [vect (gensym 'vec)] 49 | [vars (generate-n-vars len)]) 50 | (expand-into-lets vars (for/list ([e es]) (expose-exp e)) 51 | (do-allocate vect len bytes 52 | (bulk-vector-set (HasType (Var vect) type) vars type) 53 | type) 54 | type))] 55 | ...)) 56 | 57 | ;; for/list, range 58 | (define (generate-n-vars n) 59 | (if (zero? n) '() 60 | (cons (gensym 'tmp) (generate-n-vars (sub1 n))))) 61 | 62 | (define (expand-into-lets vars exps base base-type) 63 | (if (empty? exps) base 64 | (HasType 65 | (Let (car vars) (car exps) 66 | (expand-into-lets (cdr vars) (cdr exps) base base-type)) 67 | base-type))) 68 | 69 | ;; ommitting the HasType's for readability 70 | (define (do-allocate vect len bytes base type) 71 | (Let '_ (If (Prim '< (list (Prim '+ (list (GlobalValue 'free_ptr) (Int bytes))) 72 | (GlobalValue 'fromspace_end))) 73 | (Void) 74 | (Collect bytes)) 75 | (Let vect (Allocate len type) base))) 76 | 77 | (define (bulk-vector-set vect vars types) 78 | (expand-into-lets (duplicate '_ (length vars)) 79 | (make-vector-set-exps vect 0 vars (cdr types)) vect types)) 80 | 81 | ;; use Racket's make-list instead, for/list 82 | (define (duplicate x n) 83 | (if (zero? n) '() 84 | (cons x (duplicate x (sub1 n))))) 85 | 86 | ;; for/list 87 | (define (make-vector-set-exps vect accum vars types) 88 | (if (empty? vars) '() 89 | (cons (Prim 'vector-set! (list vect (Int accum) (Var (car vars)))) 90 | (make-vector-set-exps vect (add1 accum) (cdr vars) (cdr types))))) 91 | ``` 92 | 93 | ## `uncover-locals` 94 | 95 | Collect up all the variables and their types into an association list 96 | in the `Program` info. 97 | 98 | ``` 99 | (define (uncover-block tail) 100 | (match tail 101 | [(Seq (Assign var (HasType x type)) t) 102 | (cons `(,var . ,type) (uncover-block t))] 103 | [x '()])) 104 | 105 | (define (uncover-locals p) 106 | (match p 107 | [(Program info (CFG B-list)) 108 | (let ([locals (remove-duplicates 109 | (append-map (lambda (x) 110 | (uncover-block (cdr x))) B-list))]) 111 | (Program `((locals . ,locals)) (CFG B-list)))])) 112 | ``` 113 | 114 | 115 | ## `select-instructions` 116 | 117 | Lower each of the following forms to x86: 118 | 119 | * `vector-ref` 120 | * `vector-set!` 121 | * `allocate` 122 | * `collect` 123 | 124 | ``` 125 | (define (slct-stmt tail) 126 | (match tail 127 | [(Assign (Var x) (HasType exp t)) 128 | (match exp 129 | ... 130 | [(Prim 'vector-ref (list (HasType vect t1) (HasType (Int n) t2))) 131 | (list (Instr 'movq (list (slct-atom vect) (Reg 'r11))) 132 | (Instr 'movq (list (Deref 'r11 (* 8 (add1 n))) (Var x))))] 133 | [(Prim 'vector-set! (list (HasType vect t1) (HasType (Int n) t2) (HasType arg t3))) 134 | (list (Instr 'movq (list (slct-atom vect) (Reg 'r11))) 135 | (Instr 'movq (list (slct-atom arg) (Deref 'r11 (* 8 (add1 n))))) 136 | (Instr 'movq (list (Imm 0) (Var x))))] 137 | [(Allocate len types) 138 | (let ([tag (calculate-tag (reverse (cdr types)) (length (cdr types)))]) 139 | (list (Instr 'movq (list (Global 'free_ptr) (Var x))) 140 | (Instr 'addq (list (Imm (* 8 (add1 len))) (Global 'free_ptr))) 141 | (Instr 'movq (list (Var x) (Reg 'r11))) 142 | (Instr 'movq (list (Imm tag) (Deref 'r11 0)))))] 143 | [(Collect bytes) 144 | (list (Instr 'movq (list (Reg 'r15) (Reg 'rdi))) 145 | (Instr 'movq (list (Imm bytes) (Reg 'rsi))) 146 | (Callq 'collect))] 147 | ...)])) 148 | ``` 149 | 150 | 151 | ## `build-interference` 152 | 153 | Variables of vector type that are live during a call to `collect` must 154 | be spilled. To ensure that, create interference edges with 155 | callee-saved registers. (We already add edges to the caller-saved 156 | registers.) 157 | 158 | ``` 159 | (define (add-from-instr graph instr live-after types) 160 | (match instr 161 | [(Callq 'collect) 162 | (for ([x live-after]) 163 | (if (list? (match-alist (Var x) types)) ;; is variable x a vector?, vector-type? 164 | (for ([y (append caller-registers callee-registers)]) 165 | (add-edge! graph x y)) 166 | (for ([y caller-registers]) 167 | (add-edge! graph x y))))] 168 | ...)) 169 | ``` 170 | 171 | ## `allocate-registers` 172 | 173 | Spill vector-typed variables to the root stack. 174 | 175 | 2 root stack spills 176 | 3 regular spills 177 | 178 | How much space on root stack? 5 slots 179 | On the regular stack? 5 slots 180 | 181 | Two registers: 182 | 0 int 183 | 1 int 184 | ------ 185 | 2 vector 0 186 | 3 int 1 187 | 4 int 2 188 | 5 vector 3 189 | 6 int 4 190 | ``` 191 | (define (assign-nat n type) 192 | (let [(last-reg (sub1 (length reg-colors)))] 193 | (cond [(<= n last-reg) 194 | (Reg (rev-match-alist n reg-colors))] 195 | [(list? type) ;; vector-type? 196 | (Deref 'r15 (* 8 (add1 (- n last-reg))))] 197 | [else 198 | (Deref 'rbp (* (add1 (- n last-reg)) (- 8)))] 199 | ))) 200 | 201 | (define (generate-assignments locals colors) 202 | (cond [(empty? locals) '()] 203 | [else (match (car locals) 204 | [`(,(Var v) . ,type) 205 | (cons `(,v . ,(assign-nat (match-alist v colors) type)) 206 | (generate-assignments (cdr locals) colors))])])) 207 | ``` 208 | 209 | ## `print-x86` 210 | 211 | In the prelude, call the `initialize` function to set up the garbage 212 | collector. 213 | 214 | In the prelude and conclusion, add code for pushing and popping a 215 | frame for `main` to the root stack. Initialize all the slots in the 216 | frame to zero. 217 | 218 | ``` 219 | (define (make-main stack-size used-regs root-spills) 220 | (let* ([extra-pushes (filter (lambda (reg) 221 | (match reg 222 | [(Reg x) (index-of callee-registers x)] 223 | [x false])) 224 | used-regs)] 225 | [push-bytes (* 8 (length extra-pushes))] 226 | [stack-adjust (- (round-stack-to-16 (+ push-bytes stack-size)) push-bytes)]) 227 | (Block '() 228 | (append (list (Instr 'pushq (list (Reg 'rbp))) 229 | (Instr 'movq (list (Reg 'rsp) (Reg 'rbp)))) 230 | (map (lambda (x) (Instr 'pushq (list x))) extra-pushes) 231 | (list (Instr 'subq (list (Imm stack-adjust) (Reg 'rsp)))) 232 | (initialize-garbage-collector root-spills) 233 | (list (Jmp 'start)))))) 234 | 235 | (define (initialize-garbage-collector root-spills) 236 | (list (Instr 'movq (list (Imm root-stack-size) (Reg 'rdi))) 237 | (Instr 'movq (list (Imm heap-size) (Reg 'rsi))) 238 | (Callq 'initialize) 239 | (Instr 'movq (list (Global 'rootstack_begin) (Reg 'r15))) 240 | (Instr 'movq (list (Imm 0) (Deref (Reg 'r15) 0)) 241 | ... 242 | (Instr 'movq (list (Imm 0) (Deref (Reg 'r15) k)) 243 | (Instr 'addq (list (Imm root-spills) (Reg 'r15))))) 244 | ``` 245 | -------------------------------------------------------------------------------- /lecture-Sep-8.md: -------------------------------------------------------------------------------- 1 | # Code Review 2 | 3 | ## `uniquify` 4 | 5 | (define (uniquify-exp symtab) 6 | (lambda (e) 7 | (match e 8 | [(Var x) (Var (get-sym-rep symtab x))] ;; (dict-ref symtab x) 9 | [(Int n) (Int n)] 10 | [(Let x e body) 11 | (let* ([new-sym (gensym x)] 12 | [new-symtab (add-to-symtab symtab x new-sym)]) 13 | ;; (dict-set symtab x new-sym) 14 | (Let new-sym ((uniquify-exp symtab) e) 15 | ((uniquify-exp new-symtab) body)))] 16 | [(Prim op es) 17 | (Prim op (for/list ([e es]) ((uniquify-exp symtab) e)))] 18 | ))) 19 | 20 | Example: 21 | 22 | (let ([x 10]) 23 | (let ([x (+ x 1)]) 24 | x)) 25 | 26 | wrong: 27 | (let ([x.1 10]) 28 | (let ([x.2 (+ x.2 1)]) 29 | x.2)) 30 | 31 | correct: 32 | (let ([x.1 10]) 33 | (let ([x.2 (+ x.1 1)]) 34 | x.2)) 35 | 36 | 37 | ## `remove-complex-opera*` 38 | 39 | ;; rco-atom : exp -> exp * (var * exp) list 40 | (define (rco-atom e) 41 | (match e 42 | [(Var x) (values (Var x) '())] 43 | [(Int n) (values (Int n) '())] 44 | [(Let x rhs body) 45 | (define new-rhs (rco-exp rhs)) 46 | (define-values (new-body body-ss) (rco-atom body)) 47 | (values new-body (append `((,x . ,new-rhs)) body-ss))] 48 | [(Prim op es) 49 | (define-values (new-es sss) 50 | (for/lists (l1 l2) ([e es]) (rco-atom e))) 51 | (define ss (append* sss)) 52 | (define tmp (gensym 'tmp)) 53 | (values (Var tmp) 54 | (append ss `((,tmp . ,(Prim op new-es)))))] 55 | )) 56 | 57 | (define (make-lets bs e) 58 | (match bs 59 | [`() e] 60 | [`((,x . ,e^) . ,bs^) 61 | (Let x e^ (make-lets bs^ e))])) 62 | 63 | ;; rco-exp : exp -> exp 64 | (define (rco-exp e) 65 | (match e 66 | [(Var x) (Var x)] 67 | [(Int n) (Int n)] 68 | [(Let x rhs body) 69 | (Let x (rco-exp rhs) (rco-exp body))] 70 | [(Prim op es) 71 | (define-values (new-es sss) 72 | (for/lists (l1 l2) ([e es]) (rco-atom e))) 73 | (make-lets (append* sss) (Prim op new-es))] 74 | )) 75 | 76 | ## `explicate-control` 77 | 78 | (define (explicate-tail exp) 79 | (match exp 80 | [(Var x) (values (Return (Var x)) '())] 81 | [(Int n) (values (Return (Int n)) '())] 82 | [(Let lhs rhs body) 83 | (let*-values 84 | ([(body-c0 body-vars) (explicate-tail body)] 85 | [(new-tail new-rhs-vars) (explicate-assign lhs rhs body-c0)]) 86 | (values new-tail (append new-rhs-vars body-vars)))] 87 | [(Prim op es) 88 | (values (Return (Prim op es)) '())])) 89 | 90 | (define (explicate-assign r1exp v c) 91 | (match r1exp 92 | [(Let x e body) 93 | (define-values (tail let-binds) (explicate-assign body v c)) 94 | (define-values (tail^ let-binds^) (explicate-assign e (Var x) tail)) 95 | (values tail^ (cons x (append let-binds let-binds^)))] 96 | [else 97 | (values (Seq (Assign v r1exp) c) '())] 98 | )) 99 | 100 | ## `select-instructions` 101 | 102 | (define (select-instr-atm a) 103 | (match a 104 | [(Int i) (Imm i)] 105 | [(Var _) a])) 106 | 107 | (define (select-instr-assign v e) 108 | (match e 109 | [(Int i) 110 | (list (Instr 'movq (list (select-instr-atm e) v)))] 111 | [(Var _) 112 | (list (Instr 'movq (list (select-instr-atm e) v)))] 113 | [(Prim 'read '()) 114 | (list (Callq 'read_int) 115 | (Instr 'movq (list (Reg 'rax) v)))] 116 | [(Prim '- (list a)) 117 | (list (Instr 'movq (list (select-instr-atm a) v)) 118 | (Instr 'negq (list v)))] 119 | [(Prim '+ (list a1 a2)) 120 | (list (Instr 'movq (list (select-instr-atm a1) v)) 121 | (Instr 'addq (list (select-instr-atm a2) v)))])) 122 | 123 | (define (select-instr-stmt stmt) 124 | (match stmt 125 | [(Assign (Var v) (Prim '+ (list (Var v1) a2))) #:when (equal? v v1) 126 | (list (Instr 'addq (list (select-instr-atm a2) (Var v))))] 127 | [(Assign (Var v) (Prim '+ (list a1 (Var v2)))) #:when (equal? v v2) 128 | (list (Instr 'addq (list (select-instr-atm a1) (Var v))))] 129 | [(Assign v e) 130 | (select-instr-assign v e)])) 131 | 132 | (define (select-instr-tail t) 133 | (match t 134 | [(Seq stmt t*) 135 | (append (select-instr-stmt stmt) (select-instr-tail t*))] 136 | [(Return (Prim 'read '())) 137 | (list (Callq 'read_int) (Jmp 'conclusion))] 138 | [(Return e) (append 139 | (select-instr-assign (Reg 'rax) e) 140 | (list (Jmp 'conclusion)))])) 141 | 142 | (define (select-instructions p) 143 | (match p 144 | [(Program info (CFG (list (cons 'start t)))) 145 | (Program info 146 | (CFG (list (cons 'start (Block '() (select-instr-tail t))))))])) 147 | 148 | ## `assign-homes` 149 | 150 | (define (calc-stack-space ls) (* 8 (length ls)) 151 | 152 | (define (find-index v ls) 153 | (cond 154 | ;;[(eq? v (Var-name (car ls))) 1] 155 | [(eq? v (car ls)) 1] 156 | [else (add1 (find-index v (cdr ls)))] 157 | )) 158 | 159 | (define (assign-homes-imm i ls) 160 | (match i 161 | [(Reg reg) (Reg reg)] 162 | [(Imm int) (Imm int)] 163 | [(Var v) (Deref 'rbp (* -8 (find-index v (cdr ls))))] 164 | )) 165 | 166 | (define (assign-homes-instr i ls) 167 | (match i 168 | [(Instr op (list e1)) 169 | (Instr op (list (assign-homes-imm e1 ls)))] 170 | [(Instr op (list e1 e2)) 171 | (Instr op (list (assign-homes-imm e1 ls) (assign-homes-imm e2 ls)))] 172 | [else i] 173 | )) 174 | 175 | (define (assign-homes-block b ls) 176 | (match b 177 | [(Block info es) 178 | (Block info (for/list ([e es]) (assign-homes-instr e ls)))] 179 | )) 180 | 181 | (define (assign-homes p) 182 | (match p 183 | [(Program info (CFG es)) 184 | (Program (list (cons 'stack-space (calc-stack-space (cdr (car info))))) 185 | (CFG (for/list ([ls es]) 186 | (cons (car ls) (assign-homes-block (cdr ls) (car info))))))] 187 | )) 188 | 189 | ## `patch-instructions` 190 | 191 | (define (patch-instr instruction) 192 | (match instruction 193 | [(Instr op (list (Deref reg off) (Deref reg2 off2))) 194 | (list (Instr 'movq (list (Deref reg off) (Reg 'rax))) 195 | (Instr op (list (Reg 'rax) (Deref reg2 off2))))] 196 | [else (list instruction)])) 197 | 198 | (define (patch-block b) 199 | (match b 200 | [(Block '() instrs) (Block '() (append-map patch-instr instrs))] 201 | )) 202 | 203 | (define (patch-instructions p) 204 | (match p 205 | [(Program info (CFG B-list)) 206 | (Program info 207 | (CFG 208 | (map 209 | (lambda (x) `(,(car x) . ,(patch-block (cdr x)))) 210 | B-list)))])) 211 | 212 | ## `print-x86` 213 | 214 | (define (print-x86-imm e) 215 | (match e 216 | [(Deref reg i) 217 | (format "~a(%~a)" i reg)] 218 | [(Imm n) (format "$~a" n)] 219 | [(Reg r) (format "%~a" r)] 220 | )) 221 | 222 | (define (print-x86-instr e) 223 | (verbose "R1/print-x86-instr" e) 224 | (match e 225 | [(Callq f) 226 | (format "\tcallq\t~a\n" (label-name (symbol->string f)))] 227 | [(Jmp label) (format "\tjmp ~a\n" (label-name label))] 228 | [(Instr instr-name (list s d)) 229 | (format "\t~a\t~a, ~a\n" instr-name 230 | (print-x86-imm s) 231 | (print-x86-imm d))] 232 | [(Instr instr-name (list d)) 233 | (format "\t~a\t~a\n" instr-name (print-x86-imm d))] 234 | [else (error "R1/print-x86-instr, unmatched" e)] 235 | )) 236 | 237 | (define (print-x86-block e) 238 | (match e 239 | [(Block info ss) 240 | (string-append* (for/list ([s ss]) (print-x86-instr s)))] 241 | [else 242 | (error "R1/print-x86-block unhandled " e)])) 243 | 244 | (define (print-x86 e) 245 | (match e 246 | [(Program info (CFG G)) 247 | (define stack-space (dict-ref info 'stack-space)) 248 | (string-append 249 | (string-append* 250 | (for/list ([(label block) (in-dict G)]) 251 | (string-append (format "~a:\n" (label-name label)) 252 | (print-x86-block block)))) 253 | "\n" 254 | (format "\t.globl ~a\n" (label-name "main")) 255 | (format "~a:\n" (label-name "main")) 256 | (format "\tpushq\t%rbp\n") 257 | (format "\tmovq\t%rsp, %rbp\n") 258 | (format "\tsubq\t$~a, %rsp\n" (align stack-space 16)) 259 | (format "\tjmp ~a\n" (label-name 'start)) 260 | (format "~a:\n" (label-name 'conclusion)) 261 | (format "\taddq\t$~a, %rsp\n" (align stack-space 16)) 262 | (format "\tpopq\t%rbp\n") 263 | (format "\tretq\n") 264 | )] 265 | [else (error "print-x86, unmatched" e)] 266 | )) 267 | 268 | -------------------------------------------------------------------------------- /lecture-Dec-10.md: -------------------------------------------------------------------------------- 1 | # Review for Final Exam: Functions & Lambda 2 | 3 | We reviewed lambda last time, so today we'll review functions. 4 | 5 | 6 | Hypothetical alternative language with 2nd-class functions 7 | 8 | exp ::= ... | (Apply var exp...) 9 | def ::= (Def var ([var : type] ...) type '() exp) 10 | R4-2nd ::= (ProgramDefsExp '() (def ...) exp) 11 | 12 | ## Functions (R4) 13 | 14 | Syntax: 15 | 16 | type ::= ... | (type... -> type) 17 | exp ::= ... | (Apply exp exp...) 18 | def ::= (Def var ([var : type] ...) type '() exp) 19 | R4 ::= (ProgramDefsExp '() (def ...) exp) 20 | 21 | In R4, functions are *first class* values which means they can be 22 | stored in vectors, passed as arguments to other functions, etc. 23 | 24 | Functions in R4 are *not lexically scoped*, that is, the body of a 25 | function cannot refer to a (non-global) variable defined outside the 26 | function. To reinforce this point, the syntax of R4 only allows 27 | function definitions at the top level, and not nested inside 28 | expressions. 29 | 30 | Example: 31 | 32 | (define (add1 [y : Integer]) : Integer 33 | (+ 1 y)) 34 | 35 | (define (twice [f : (Integer -> Integer)] [x : Integer]) : Integer 36 | (+ (f x) (f x))) 37 | 38 | (twice add1 (read)) 39 | 40 | ## Functions (x86) 41 | 42 | A function is an address, i.e., the address of the first instruction 43 | in the function's code. 44 | 45 | We obtain the address of a function by using PC-relative addressing 46 | on the label for the function. 47 | 48 | leaq _twice65(%rip), %rcx 49 | 50 | We call a function using the `callq` instruction with a `*` 51 | followed by a register that is holding the address of the function. 52 | 53 | callq *%rcx 54 | 55 | Calling conventions: 56 | 57 | * Parameters are passed in these six registers, in order: 58 | 59 | rdi rsi rdx rcx r8 r9 60 | 61 | If there are more than six parameters, we pass parameters 62 | 6 and higher in a vector that is passed in `r9`. 63 | 64 | * return value is placed in `rax`. 65 | 66 | * If a function uses callee-saved registers, it must 67 | save and restore them in the prelude and conclusion. 68 | 69 | rsp rbp r15 (rbx r12 r13 r14) 70 | 71 | * We make sure not to use caller-saved registers for variables 72 | that are live during a call. 73 | 74 | rax r11 (rcx rdx rsi rdi r8 r9 r10) 75 | 76 | Example: 77 | 78 | 79 | .globl _add64 80 | .align 16 81 | _add164: 82 | pushq %rbp 83 | movq %rsp, %rbp 84 | jmp _add64start 85 | _add164start: 86 | movq $1, %rax 87 | addq %rdi, %rax 88 | jmp _add164conclusion 89 | _add164conclusion: 90 | popq %rbp 91 | retq 92 | 93 | .globl _twice65 94 | .align 16 95 | _twice65: 96 | pushq %rbp 97 | movq %rsp, %rbp 98 | pushq %r13 *** callee saved registers 99 | pushq %r12 100 | pushq %rbx 101 | subq $8, %rsp *** why this? 102 | *** odd # of callee-saved 103 | jmp _twice65start 104 | _twice65start: 105 | movq %rdi, %rbx 106 | movq %rsi, %r12 107 | movq %r12, %rdi 108 | callq *%rbx *** call to f 109 | movq %rax, %r13 110 | movq %r12, %rdi 111 | callq *%rbx *** call to f 112 | movq %rax, %rcx 113 | movq %r13, %rax 114 | addq %rcx, %rax 115 | jmp _twice65conclusion 116 | _twice65conclusion: 117 | addq $8, %rsp 118 | popq %rbx *** callee saved registers 119 | popq %r12 120 | popq %r13 121 | popq %rbp 122 | retq 123 | 124 | .globl _main 125 | .align 16 126 | _main: 127 | pushq %rbp 128 | movq %rsp, %rbp 129 | pushq %r12 130 | pushq %rbx 131 | movq $65536, %rdi 132 | movq $65536, %rsi 133 | callq _initialize 134 | movq _rootstack_begin(%rip), %r15 135 | jmp _mainstart 136 | _mainstart: 137 | leaq _twice65(%rip), %rbx 138 | leaq _add164(%rip), %r12 139 | callq _read_int 140 | movq %rax, %rsi *** tail call to twice 141 | movq %r12, %rdi 142 | movq %rbx, %rax 143 | popq %rbx 144 | popq %r12 145 | popq %rbp 146 | jmp *%rax 147 | _mainconclusion: 148 | popq %rbx 149 | popq %r12 150 | popq %rbp 151 | retq 152 | 153 | 154 | ## Reveal Functions 155 | 156 | Differentiate between function names and let-bound variables. 157 | 158 | (define (add147 [y49 : Integer]) : Integer 159 | (+ 1 y49) 160 | ) 161 | 162 | (define (twice48 [f50 : (Integer -> Integer)] 163 | [x51 : Integer]) : Integer 164 | (+ (f50 x51) (f50 x51)) 165 | ) 166 | 167 | (define (main) : Integer 168 | ((fun-ref twice48) (fun-ref add147) (read)) 169 | ) 170 | 171 | 172 | ## Limit Functions 173 | 174 | Limit functions to 6 parameters. Use vectors for the extra ones. 175 | 176 | ## Remove Complex Operands 177 | 178 | `FunRef` and `Apply` are complex. 179 | Arguments of `Apply` are atomic. 180 | 181 | (define (add147 [y49 : Integer]) : Integer 182 | (+ 1 y49)) 183 | 184 | (define (twice48 [f50 : (Integer -> Integer)] 185 | [x51 : Integer]) : Integer 186 | (let ([tmp52 (f50 x51)]) 187 | (let ([tmp53 (f50 x51)]) 188 | (+ tmp52 tmp53)))) 189 | 190 | (define (main) : Integer 191 | (let ([tmp54 (fun-ref twice48)]) 192 | (let ([tmp55 (fun-ref add147)]) 193 | (let ([tmp56 (read)]) 194 | (tmp54 tmp55 tmp56))))) 195 | 196 | 197 | ## Explicate Control 198 | 199 | Differentiate between calls and tail calls. 200 | 201 | (define (add147 [y49 : Integer]) : Integer 202 | add147start: 203 | return (+ 1 y49); 204 | ) 205 | 206 | (define (twice48 [f50 : (Integer -> Integer)] 207 | [x51 : Integer]) : Integer 208 | twice48start: 209 | tmp52 = (call f50 x51); 210 | tmp53 = (call f50 x51); 211 | return (+ tmp52 tmp53); 212 | ) 213 | 214 | (define (main) : Integer 215 | mainstart: 216 | tmp54 = (fun-ref twice48); 217 | tmp55 = (fun-ref add147); 218 | tmp56 = (read); 219 | (tail-call tmp54 tmp55 tmp56) 220 | ) 221 | 222 | 223 | ## Select Instructions 224 | 225 | 1. FunRef: translate to PC-relative addressing of the function 226 | label 227 | 2. Function calls: pass arguments in registers, get result 228 | in `rax`, translate to `callq *`. 229 | 3. Function definitions: retrieve arguments from registers 230 | 4. Tail calls: use fake `tail-jmp` instruction to postpone 231 | generated code for popping the frame. 232 | 233 | Output: 234 | 235 | (define (add147) : Integer 236 | add147start: 237 | movq %rdi, y49 *** 238 | movq $1, %rax 239 | addq y49, %rax 240 | jmp add147conclusion 241 | ) 242 | 243 | (define (twice48) : Integer 244 | twice48start: 245 | movq %rdi, f50 *** 246 | movq %rsi, x51 *** 247 | movq x51, %rdi *** 248 | callq *f50 *** 249 | movq %rax, tmp52 250 | movq x51, %rdi *** 251 | callq *f50 *** 252 | movq %rax, tmp53 253 | movq tmp52, %rax 254 | addq tmp53, %rax 255 | jmp twice48conclusion 256 | ) 257 | 258 | (define (main) : Integer 259 | mainstart: 260 | leaq (fun-ref twice48), tmp54 *** 261 | leaq (fun-ref add147), tmp55 *** 262 | callq read_int 263 | movq %rax, tmp56 264 | movq tmp55, %rdi *** 265 | movq tmp56, %rsi *** 266 | tail-jmp tmp54 *** 267 | ) 268 | 269 | 270 | ## Register Allocation 271 | 272 | 273 | An example with functions and vectors (example in book): 274 | 275 | (define (map [f : (Integer -> Integer)] 276 | [v : (Vector Integer Integer)]) 277 | : (Vector Integer Integer) 278 | (vector (f (vector-ref v 0)) (f (vector-ref v 1)))) 279 | 280 | (define (add1 [x : Integer]) : Integer (+ x 1)) 281 | 282 | (vector-ref (map add1 (vector 0 41)) 1) 283 | 284 | 285 | After instruction selection: 286 | 287 | (define (map47) : Integer 288 | block75: 289 | movq %r15, %rdi 290 | movq $24, %rsi 291 | callq collect 292 | jmp block73 293 | 294 | block74: 295 | movq $0, _57 296 | jmp block73 297 | 298 | block73: 299 | movq free_ptr(%rip), %r11 300 | addq $24, free_ptr(%rip) 301 | movq $5, 0(%r11) 302 | movq %r11, alloc52 303 | movq alloc52, %r11 304 | movq vecinit53, 8(%r11) 305 | movq $0, _56 306 | movq alloc52, %r11 307 | movq vecinit54, 16(%r11) 308 | movq $0, _55 309 | movq alloc52, %rax 310 | jmp map47conclusion 311 | 312 | map47start: 313 | movq %rdi, f49 314 | movq %rsi, v50 315 | movq v50, %r11 316 | movq 8(%r11), tmp62 317 | movq tmp62, %rdi 318 | callq *f49 319 | movq %rax, vecinit53 320 | movq v50, %r11 321 | movq 16(%r11), tmp63 322 | movq tmp63, %rdi 323 | callq *f49 324 | movq %rax, vecinit54 325 | movq free_ptr(%rip), tmp64 326 | movq tmp64, tmp65 327 | addq $24, tmp65 328 | movq fromspace_end(%rip), tmp66 329 | cmpq tmp66, tmp65 330 | jl block74 331 | jmp block75 332 | 333 | 334 | ) 335 | 336 | (define (add48) : Integer 337 | add48start: 338 | movq %rdi, x51 339 | movq x51, %rax 340 | addq $1, %rax 341 | jmp add48conclusion 342 | ) 343 | 344 | (define (main) : Integer 345 | block78: 346 | movq %r15, %rdi 347 | movq $24, %rsi 348 | callq collect 349 | jmp block76 350 | 351 | block77: 352 | movq $0, _61 353 | jmp block76 354 | 355 | block76: 356 | movq free_ptr(%rip), %r11 357 | addq $24, free_ptr(%rip) 358 | movq $5, 0(%r11) 359 | movq %r11, alloc58 360 | movq alloc58, %r11 361 | movq $0, 8(%r11) 362 | movq $0, _60 363 | movq alloc58, %r11 364 | movq $41, 16(%r11) 365 | movq $0, _59 366 | movq tmp68, %rdi 367 | movq alloc58, %rsi 368 | callq *tmp67 369 | movq %rax, tmp72 370 | movq tmp72, %r11 371 | movq 16(%r11), %rax 372 | jmp mainconclusion 373 | 374 | mainstart: 375 | leaq (fun-ref map47), tmp67 376 | leaq (fun-ref add48), tmp68 377 | movq free_ptr(%rip), tmp69 378 | movq tmp69, tmp70 379 | addq $24, tmp70 380 | movq fromspace_end(%rip), tmp71 381 | cmpq tmp71, tmp70 382 | jl block77 383 | jmp block78 384 | ) 385 | 386 | After register allocation: 387 | 388 | (define (map47) : Integer 389 | map47start: 390 | movq %rdi, %r12 391 | movq %rsi, -8(%r15) ***** 392 | movq -8(%r15), %r11 393 | movq 8(%r11), %rdi 394 | movq %rdi, %rdi 395 | callq *%r12 396 | movq %rax, %rbx 397 | movq -8(%r15), %r11 398 | movq 16(%r11), %rdi 399 | movq %rdi, %rdi 400 | callq *%r12 401 | movq %rax, %r12 402 | movq free_ptr(%rip), %rcx 403 | movq %rcx, %rcx 404 | addq $24, %rcx 405 | movq fromspace_end(%rip), %rdx 406 | cmpq %rdx, %rcx 407 | jl block74 408 | jmp block75 409 | 410 | block75: 411 | movq %r15, %rdi 412 | movq $24, %rsi 413 | callq collect 414 | jmp block73 415 | 416 | block74: 417 | movq $0, %rcx 418 | jmp block73 419 | 420 | block73: 421 | movq free_ptr(%rip), %r11 422 | addq $24, free_ptr(%rip) 423 | movq $5, 0(%r11) 424 | movq %r11, %rcx 425 | movq %rcx, %r11 426 | movq %rbx, 8(%r11) 427 | movq $0, %rdx 428 | movq %rcx, %r11 429 | movq %r12, 16(%r11) 430 | movq $0, %rdx 431 | movq %rcx, %rax 432 | jmp map47conclusion 433 | ) 434 | 435 | (define (add48) : Integer 436 | add48start: 437 | movq %rdi, %rdi 438 | movq %rdi, %rax 439 | addq $1, %rax 440 | jmp add48conclusion 441 | ) 442 | 443 | (define (main) : Integer 444 | block78: 445 | movq %r15, %rdi 446 | movq $24, %rsi 447 | callq collect 448 | jmp block76 449 | 450 | block77: 451 | movq $0, %rcx 452 | jmp block76 453 | 454 | block76: 455 | movq free_ptr(%rip), %r11 456 | addq $24, free_ptr(%rip) 457 | movq $5, 0(%r11) 458 | movq %r11, %rsi 459 | movq %rsi, %r11 460 | movq $0, 8(%r11) 461 | movq $0, %rcx 462 | movq %rsi, %r11 463 | movq $41, 16(%r11) 464 | movq $0, %rcx 465 | movq %r12, %rdi 466 | movq %rsi, %rsi 467 | callq *%rbx 468 | movq %rax, %rcx 469 | movq %rcx, %r11 470 | movq 16(%r11), %rax 471 | jmp mainconclusion 472 | 473 | mainstart: 474 | leaq (fun-ref map47), %rbx 475 | leaq (fun-ref add48), %r12 476 | movq free_ptr(%rip), %rcx 477 | movq %rcx, %rcx 478 | addq $24, %rcx 479 | movq fromspace_end(%rip), %rdx 480 | cmpq %rdx, %rcx 481 | jl block77 482 | jmp block78 483 | ) 484 | 485 | -------------------------------------------------------------------------------- /lecture-Oct-20.md: -------------------------------------------------------------------------------- 1 | # Compiling Functions, Example Translations 2 | 3 | ## Example 1 4 | 5 | source program: 6 | 7 | (define (add [x : Integer] [y : Integer]) : Integer 8 | (+ x y)) 9 | 10 | (add 40 2) 11 | 12 | after shrink: 13 | 14 | (define (add [x : Integer] [y : Integer]) : Integer 15 | (+ x y)) 16 | 17 | (define (main) : Integer 18 | (add 40 2)) 19 | 20 | after uniquify: 21 | 22 | (define (add10705 [x10706 : Integer] [y10707 : Integer]) : Integer 23 | (+ x10706 y10707)) 24 | (define (main) : Integer 25 | (add10705 40 2)) 26 | 27 | 28 | after reveal-functions: 29 | 30 | (define (add10705 [x10706 : Integer] [y10707 : Integer]) : Integer 31 | (+ x10706 y10707)) 32 | 33 | (define (main ) : Integer 34 | ((fun-ref add10705) 40 2)) 35 | 36 | after limit-functions 37 | 38 | (define (add10705 [x10706 : Integer] [y10707 : Integer]) : Integer 39 | (+ x10706 y10707)) 40 | 41 | (define (main) : Integer 42 | ((fun-ref add10705) 40 2)) 43 | 44 | skipping expose allocation 45 | 46 | after remove-complex-opera* 47 | 48 | (define (add10705 [x10706 : Integer] [y10707 : Integer]) : Integer 49 | (+ x10706 y10707)) 50 | 51 | (define (main) : Integer 52 | (let ([tmp10708 (fun-ref add10705)]) 53 | (tmp10708 40 2))) 54 | 55 | after explicate-control 56 | 57 | (define (add10705 [x10706 : Integer] [y10707 : Integer]) : Integer 58 | add10705start: 59 | return (+ x10706 y10707); 60 | ) 61 | (define (main) : Integer 62 | mainstart: 63 | tmp10708 = (fun-ref add10705); 64 | (tmp10708 40 2) 65 | ) 66 | 67 | skipping optimize-jumps 68 | 69 | skipping uncover-locals 70 | 71 | after instruction selection 72 | 73 | (define (add10705) : _ 74 | add10705start: 75 | movq %rcx, x10706 76 | movq %rdx, y10707 77 | movq x10706, %rax 78 | addq y10707, %rax 79 | jmp add10705conclusion 80 | ) 81 | (define (main ) : _ 82 | mainstart: 83 | leaq (fun-ref add10705), tmp10708 84 | movq $40, %rcx 85 | movq $2, %rdx 86 | tailjmp tmp10708 87 | ) 88 | 89 | skipping remove-jumps 90 | 91 | skipping uncover-live, build-interference 92 | 93 | after allocate-registers 94 | 95 | (define (add10705 ) : _ 96 | add10705start: 97 | movq %rcx, %rsi 98 | movq %rdx, %rcx 99 | movq %rsi, %rax 100 | addq %rcx, %rax 101 | jmp add10705conclusion 102 | ) 103 | (define (main) : _ 104 | mainstart: 105 | leaq (fun-ref add10705), %rsi 106 | movq $40, %rcx 107 | movq $2, %rdx 108 | tailjmp %rsi 109 | ) 110 | 111 | after patch instructions 112 | 113 | (define (add10705 ) : _ 114 | add10705start: 115 | movq %rcx, %rsi 116 | movq %rdx, %rcx 117 | movq %rsi, %rax 118 | addq %rcx, %rax 119 | jmp add10705conclusion 120 | ) 121 | (define (main ) : _ 122 | mainstart: 123 | leaq (fun-ref add10705), %rsi 124 | movq $40, %rcx 125 | movq $2, %rdx 126 | movq %rsi, %rax 127 | tailjmp %rax 128 | ) 129 | 130 | after print-x86 131 | 132 | .globl _add10709 133 | .align 16 134 | _add10709: 135 | pushq %rbp 136 | movq %rsp, %rbp 137 | jmp _add10709start 138 | _add10709start: 139 | movq %rcx, %rsi 140 | movq %rdx, %rcx 141 | movq %rsi, %rax 142 | addq %rcx, %rax 143 | jmp _add10709conclusion 144 | _add10709conclusion: 145 | popq %rbp 146 | retq 147 | 148 | .globl _main 149 | .align 16 150 | _main: 151 | pushq %rbp 152 | movq %rsp, %rbp 153 | movq $16384, %rdi 154 | movq $16, %rsi 155 | callq _initialize 156 | movq _rootstack_begin(%rip), %r15 157 | jmp _mainstart 158 | _mainstart: 159 | leaq _add10709(%rip), %rsi 160 | movq $40, %rcx 161 | movq $2, %rdx 162 | movq %rsi, %rax 163 | popq %rbp 164 | jmp *%rax 165 | _mainconclusion: 166 | popq %rbp 167 | retq 168 | 169 | ## Example 2 170 | 171 | source program: 172 | 173 | (define (m [a : Integer] [b : Integer] [c : Integer] [d : Integer] 174 | [e : Integer] [f : Integer] [g : Integer] [h : Integer] 175 | [i : Integer]) : Integer 176 | i) 177 | 178 | (m 777 776 775 774 773 772 771 770 42) 179 | 180 | skipping shrink 181 | 182 | after uniquify: 183 | 184 | (define (m4 [a5 : Integer] [b6 : Integer] [c7 : Integer] 185 | [d8 : Integer] 186 | [e9 : Integer] [f10 : Integer] [g11 : Integer] 187 | [h12 : Integer] [i13 : Integer]) : Integer 188 | i13) 189 | 190 | (define (main) : Integer 191 | (m4 777 776 775 774 773 772 771 770 42)) 192 | 193 | after reveal-functions: 194 | 195 | (define (m4 [a5 : Integer] [b6 : Integer] [c7 : Integer] 196 | [d8 : Integer] 197 | [e9 : Integer] 198 | [f10 : Integer] [g11 : Integer] 199 | [h12 : Integer] [i13 : Integer]) : Integer 200 | i13) 201 | 202 | (define (main) : Integer 203 | ((fun-ref m4) ***** 204 | 777 776 775 774 773 772 771 770 42)) 205 | 206 | after limit-functions: 207 | 208 | (define (m4 [a5 : Integer] [b6 : Integer] [c7 : Integer] 209 | [d8 : Integer] 210 | [e9 : Integer] 211 | [vec14 : (Vector Integer Integer Integer Integer)] ***** 212 | ) : Integer 213 | (vector-ref vec14 3)) ***** 214 | (define (main) : Integer 215 | ((fun-ref m4) 777 776 775 774 773 216 | (vector 772 771 770 42))) ***** 217 | 218 | after expose allocation: 219 | 220 | (define (m4 [a5 : Integer] [b6 : Integer] [c7 : Integer] [d8 : Integer] 221 | [e9 : Integer] 222 | [vec14 : (Vector Integer Integer Integer Integer)]) : Integer 223 | (vector-ref vec14 3)) 224 | 225 | (define (main) : Integer 226 | ((fun-ref m4) 777 776 775 774 773 227 | (let ([vecinit16 772]) ***** 228 | (let ([vecinit17 771]) 229 | (let ([vecinit18 770]) 230 | (let ([vecinit19 42]) 231 | (let ([collectret24 (if (< (+ (global-value free_ptr) 40) 232 | (global-value fromspace_end)) 233 | (void) 234 | (collect 40))]) 235 | (let ([alloc15 (allocate 4 (Vector Integer Integer Integer Integer))]) 236 | (let ([initret23 (vector-set! alloc15 0 vecinit16)]) 237 | (let ([initret22 (vector-set! alloc15 1 vecinit17)]) 238 | (let ([initret21 (vector-set! alloc15 2 vecinit18)]) 239 | (let ([initret20 (vector-set! alloc15 3 vecinit19)]) 240 | alloc15)))))))))))) 241 | 242 | skipping remove-complex-opera* 243 | 244 | after explicate-control: 245 | 246 | (define (m4 [a5 : Integer] [b6 : Integer] [c7 : Integer] [d8 : Integer] 247 | [e9 : Integer] 248 | [vec14 : (Vector Integer Integer Integer Integer)]) : Integer 249 | m4start: 250 | return (vector-ref vec14 3); 251 | ) 252 | 253 | (define (main) : Integer 254 | block31: 255 | (collect 40) 256 | goto block29; 257 | block30: 258 | collectret24 = (void); 259 | goto block29; 260 | block29: 261 | alloc15 = (allocate 4 (Vector Integer Integer Integer Integer)); 262 | initret23 = (vector-set! alloc15 0 vecinit16); 263 | initret22 = (vector-set! alloc15 1 vecinit17); 264 | initret21 = (vector-set! alloc15 2 vecinit18); 265 | initret20 = (vector-set! alloc15 3 vecinit19); 266 | (tail-call tmp25 777 776 775 774 773 alloc15) ***** 267 | mainstart: 268 | tmp25 = (fun-ref m4); ***** (thanks to RCO) 269 | vecinit16 = 772; 270 | vecinit17 = 771; 271 | vecinit18 = 770; 272 | vecinit19 = 42; 273 | tmp26 = (global-value free_ptr); 274 | tmp27 = (+ tmp26 40); 275 | tmp28 = (global-value fromspace_end); 276 | if (< tmp27 tmp28) 277 | goto block30; 278 | else 279 | goto block31; 280 | ) 281 | 282 | skipping uncover-locals 283 | 284 | after instruction selection: 285 | 286 | (define (m4) : _ 287 | m4start: 288 | movq %rcx, a5 ***** 289 | movq %rdx, b6 ***** 290 | movq %rdi, c7 ***** 291 | movq %rsi, d8 ***** 292 | movq %r8, e9 ***** 293 | movq %r9, vec14 ***** 294 | movq vec14, %r11 295 | movq 32(%r11), %rax 296 | jmp m4conclusion 297 | ) 298 | (define (main) : _ 299 | block31: 300 | movq %r15, %rdi 301 | movq $40, %rsi 302 | callq collect 303 | jmp block29 304 | 305 | block30: 306 | movq $0, collectret24 307 | jmp block29 308 | 309 | block29: 310 | movq free_ptr(%rip), alloc15 311 | addq $40, free_ptr(%rip) 312 | movq alloc15, %r11 313 | movq $9, 0(%r11) 314 | movq alloc15, %r11 315 | movq vecinit16, 8(%r11) 316 | movq $0, initret23 317 | movq alloc15, %r11 318 | movq vecinit17, 16(%r11) 319 | movq $0, initret22 320 | movq alloc15, %r11 321 | movq vecinit18, 24(%r11) 322 | movq $0, initret21 323 | movq alloc15, %r11 324 | movq vecinit19, 32(%r11) 325 | movq $0, initret20 326 | movq $777, %rcx ***** 327 | movq $776, %rdx ***** 328 | movq $775, %rdi ***** 329 | movq $774, %rsi ***** 330 | movq $773, %r8 ***** 331 | movq alloc15, %r9 ***** 332 | tailjmp tmp25 ***** 333 | 334 | mainstart: 335 | leaq (fun-ref m4), tmp25 ***** 336 | movq $772, vecinit16 337 | movq $771, vecinit17 338 | movq $770, vecinit18 339 | movq $42, vecinit19 340 | movq free_ptr(%rip), tmp26 341 | movq tmp26, tmp27 342 | addq $40, tmp27 343 | movq fromspace_end(%rip), tmp28 344 | cmpq tmp28, tmp27 345 | jl block30 346 | jmp block31 347 | ) 348 | 349 | skipping allocate-registers 350 | 351 | after patch instructions: 352 | 353 | (define (m4) : _ 354 | m4start: 355 | movq %rcx, %r10 356 | movq %rdx, %rcx 357 | movq %rdi, %rcx 358 | movq %rsi, %rcx 359 | movq %r8, %rcx 360 | movq %r9, %rcx 361 | movq %rcx, %r11 362 | movq 32(%r11), %rax 363 | jmp m4conclusion 364 | ) 365 | (define (main) : _ 366 | block30: 367 | movq $0, %rcx 368 | jmp block29 369 | 370 | block29: 371 | movq free_ptr(%rip), %r9 372 | addq $40, free_ptr(%rip) 373 | movq %r9, %r11 374 | movq $9, 0(%r11) 375 | movq %r9, %r11 376 | movq %r13, 8(%r11) 377 | movq $0, %rcx 378 | movq %r9, %r11 379 | movq %r14, 16(%r11) 380 | movq $0, %rcx 381 | movq %r9, %r11 382 | movq -40(%rbp), %rax 383 | movq %rax, 24(%r11) 384 | movq $0, %rcx 385 | movq %r9, %r11 386 | movq %rbx, 32(%r11) 387 | movq $0, %rcx 388 | movq $777, %rcx 389 | movq $776, %rdx 390 | movq $775, %rdi 391 | movq $774, %rsi 392 | movq $773, %r8 393 | movq %r12, %rax ***** 394 | tailjmp %rax ***** 395 | 396 | mainstart: 397 | leaq (fun-ref m4), %r12 398 | movq $772, %r13 399 | movq $771, %r14 400 | movq $770, -40(%rbp) 401 | movq $42, %rbx 402 | movq free_ptr(%rip), %rdx 403 | addq $40, %rdx 404 | movq fromspace_end(%rip), %rcx 405 | cmpq %rcx, %rdx 406 | jl block30 407 | movq %r15, %rdi 408 | movq $40, %rsi 409 | callq collect 410 | jmp block29 411 | ) 412 | 413 | after print x86 414 | 415 | _m4start: 416 | movq %rcx, %r10 417 | movq %rdx, %rcx 418 | movq %rdi, %rcx 419 | movq %rsi, %rcx 420 | movq %r8, %rcx 421 | movq %r9, %rcx 422 | movq %rcx, %r11 423 | movq 32(%r11), %rax 424 | jmp _m4conclusion 425 | 426 | .globl _m4 427 | .align 16 428 | _m4: 429 | pushq %rbp 430 | movq %rsp, %rbp 431 | jmp _m4start 432 | 433 | _m4conclusion: 434 | popq %rbp 435 | retq 436 | _block30: 437 | movq $0, %rcx 438 | jmp _block29 439 | _block29: 440 | movq _free_ptr(%rip), %r9 441 | addq $40, _free_ptr(%rip) 442 | movq %r9, %r11 443 | movq $9, 0(%r11) 444 | movq %r9, %r11 445 | movq %rbx, 8(%r11) 446 | movq $0, %rcx 447 | movq %r9, %r11 448 | movq %r13, 16(%r11) 449 | movq $0, %rcx 450 | movq %r9, %r11 451 | movq %r14, 24(%r11) 452 | movq $0, %rcx 453 | movq %r9, %r11 454 | movq -40(%rbp), %rax 455 | movq %rax, 32(%r11) 456 | movq $0, %rcx 457 | movq $777, %rcx 458 | movq $776, %rdx 459 | movq $775, %rdi 460 | movq $774, %rsi 461 | movq $773, %r8 462 | movq %r12, %rax 463 | addq $16, %rsp ***** conclusion of main 464 | popq %r14 ***** 465 | popq %rbx ***** 466 | popq %r12 ***** 467 | popq %r13 ***** 468 | popq %rbp ***** 469 | jmp *%rax ***** 470 | _mainstart: 471 | leaq _m4(%rip), %r12 ***** 472 | movq $772, %rbx 473 | movq $771, %r13 474 | movq $770, %r14 475 | movq $42, -40(%rbp) 476 | movq _free_ptr(%rip), %rdx 477 | addq $40, %rdx 478 | movq _fromspace_end(%rip), %rcx 479 | cmpq %rcx, %rdx 480 | jl _block30 481 | movq %r15, %rdi 482 | movq $40, %rsi 483 | callq _collect 484 | jmp _block29 485 | 486 | .globl _main 487 | .align 16 488 | _main: 489 | pushq %rbp 490 | movq %rsp, %rbp 491 | pushq %r13 492 | pushq %r12 493 | pushq %rbx 494 | pushq %r14 495 | subq $16, %rsp 496 | movq $16384, %rdi 497 | movq $16, %rsi 498 | callq _initialize 499 | movq _rootstack_begin(%rip), %r15 500 | jmp _mainstart 501 | 502 | _mainconclusion: 503 | addq $16, %rsp 504 | popq %r14 505 | popq %rbx 506 | popq %r12 507 | popq %r13 508 | popq %rbp 509 | retq 510 | 511 | # Lambda: Lexically Scoped Functions 512 | 513 | 514 | ## Example 515 | 516 | (define (f [x : Integer]) : (Integer -> Integer) 517 | (let ([y 4]) 518 | (lambda: ([z : Integer]) : Integer 519 | (+ x (+ y z))))) 520 | 521 | (let ([g (f 5)]) 522 | (let ([h (f 3)]) 523 | (+ (g 11) (h 15)))) 524 | 525 | 526 | ## Syntax 527 | 528 | concrete syntax: 529 | 530 | exp ::= ... | (lambda: ([var : type]...) : type exp) 531 | R5 ::= def* exp 532 | 533 | abstract syntax: 534 | 535 | exp ::= ... | (Lambda ([var : type]...) type exp) 536 | R5 ::= (ProgramDefsExp info def* exp) 537 | 538 | (Let var exp exp) 539 | 540 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Course Webpage for Compilers (P423, P523, E313, and E513) 2 | 3 | Indiana University, Fall 2020 4 | 5 | 6 | High-level programming languages like Racket make it easier to program 7 | relative to low-level languages such as x86 assembly code. But how do 8 | high-level languages work? There's a big gap between Racket and 9 | machine instructions for modern computers. In this class you learn how 10 | to translate Racket programs (a dialect of Scheme) all the way to x86 11 | assembly language. 12 | 13 | Traditionally, compiler courses teach one phase of the compiler at a 14 | time, such as parsing, semantic analysis, and register allocation. The 15 | problem with that approach is it is difficult to understand how the 16 | whole compiler fits together and why each phase is designed the way it 17 | is. Instead, each week we implement a successively larger subset of 18 | the Racket language. The very first subset is a tiny language of 19 | integer arithmetic, and by the time we are done the language includes 20 | first-class functions. 21 | 22 | **Prerequisites:** B521 or C311. Fluency in Racket is highly recommended 23 | as students will do a lot of programming in Racket. Prior knowledge of 24 | an assembly language helps, but is not required. 25 | 26 | **Textbook:** The notes for the course are available 27 | [here](https://www.dropbox.com/s/ktdw8j0adcc44r0/book.pdf?dl=1). If 28 | you have suggestions for improvement, please either send an email to 29 | Jeremy or, even better, make edits to a branch of the book and perform 30 | a pull request. The book is at the following location on github: 31 | 32 | https://github.com/IUCompilerCourse/Essentials-of-Compilation 33 | 34 | **Lecture:** Tuesday and Thursday, 3:15pm to 4:30pm, on Zoom Meeting ID 35 | 950 3713 8921. (See the Piazza announcement for the password.) 36 | 37 | 38 | **Lecture Notes and Recordings:** 39 | 40 | * August 25 [Notes](./lecture-Aug-25.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course/1_hwlujpzd): Introduction, Concrete and Abstract Syntax, Racket Structures, Grammars, 41 | 42 | * August 27 [Notes](./lecture-Aug-27.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+August+27%2C+2020/0_pmzfbou3): Interpreters, Compiler Correctness, R1 Language, x86 43 | 44 | * September 1 [Notes](./lecture-Sep-1.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+1%2C+2020/1_7o6702no): Uniquify, Remove Complex Operands, Explicate Control 45 | 46 | * September 3 [Notes](./lecture-Sep-3.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+3%2C+2020/1_sqpe15y2): Select Instructions, Assign Homes, Path Instructions, Print x86 47 | 48 | * September 8 [Notes](./lecture-Sep-8.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course/1_vizyqbn0): Code review of compiling integers and variables. 49 | 50 | * September 10 [Notes](./lecture-Sep-10.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+10%2C+2020/1_gk7ace03): Register Allocation (Liveness Analysis, Build Interference Graph) 51 | 52 | * September 15 [Notes](./lecture-Sep-15.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course/1_bhbvoxal): Register Allocation (Graph Coloring) 53 | 54 | * September 17 [Notes](./lecture-Sep-17.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+17%2C+2020/1_ana9y0v2): Booleans and Control Flow 55 | 56 | * September 22 [Notes](./lecture-Sep-22.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+22%2C+2020/1_edqiv033): Code review of register allocation. 57 | 58 | * September 24 [Notes](./lecture-Sep-24.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+24%2C+2020/1_a3nbfe77): More x86, Explicate Control with Branching. 59 | 60 | * September 29 [Notes](./lecture-Sep-29.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+September+29%2C+2020/1_n9c7bzm4): Impact of branching on instruction selection and register allocation. Challenge: optmizing and removing jumps. 61 | 62 | * October 1 [Notes](./lecture-Oct-1.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+1%2C+2020/1_j9g6xli5): Garbage Collection: 2-space Copy Collector 63 | 64 | * October 6 [Notes](./lecture-Oct-6.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+6%2C+2020/1_1yjdbvrg): Code review of booleans and control flow 65 | 66 | * October 8 [Notes](./lecture-Oct-8.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+8%2C+2020/1_r8jzdnu3): Tuples and Garbage Collection: the Compiler Passes 67 | 68 | * October 13 [Notes](./lecture-Oct-13.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+13%2C+2020/1_8nm19wcy): Functions and Efficient Tail Calls 69 | 70 | * October 15 [Notes](./lecture-Oct-15.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+15%2C+2020/1_hy383s9a): Compiling Functions, the Passes 71 | 72 | * October 20 [Notes](./lecture-Oct-20.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+20%2C+2020/1_k0t1wmat): Compiling Functions, Examples, Start of Lambda 73 | 74 | * October 22 [Notes](./lecture-Oct-22.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+22%2C+2020/1_vlnmv3sj): Lambdas and Closure Conversion 75 | 76 | * October 27 [Notes](./lecture-Oct-27.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+27%2C+2020/1_q6dmk6st): Code Review of Tuple & Garbage Collection 77 | 78 | * October 29 [Notes](./lecture-Oct-29.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+October+29%2C+2020/1_hm4ono61): Closure Conversion, The Compiler Pass 79 | 80 | * November 3 [Notes](./lecture-Nov-3.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+November+3%2C+2020/1_pw8wgk8w): Dynamic Typing 81 | 82 | * November 5 [Notes](./lecture-Nov-5.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+November+5%2C+2020/1_4jkvtqka): Dynamic Typing, continued 83 | 84 | * November 10 [Notes](./lecture-Nov-10.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+November+10%2C+2020/1_zt4xgnmm): Code Review of Functions 85 | 86 | * November 12 [Notes](./lecture-Nov-12.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+November+12%2C+2020/1_nj17t942): Optimizing Closures 87 | 88 | * November 17 [Notes](./lecture-Nov-17.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+November+17%2C+2020/1_h0iqmju7): Dataflow Analysis 89 | 90 | * November 19 [Notes](./lecture-Nov-19.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+November+19%2C+2020/1_42fqjvwz): Compiling Loops and Liveness Analysis via Dataflow 91 | 92 | * December 1 [Notes](./lecture-Dec-1.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+December+1%2C+2020/1_n2dmgkw1): Assignment and Begin 93 | 94 | * December 3: Review of Dynamic Typing (see notes for Nov. 3 and 5) 95 | 96 | * December 8 [Notes](./lecture-Dec-8.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+December+8%2C+2020/1_71h0sbk8): Code Review of Compiling Lambda 97 | 98 | * December 10 [Notes](./lecture-Dec-10.md), [Video](https://iu.mediaspace.kaltura.com/media/Compiler+Course%2C+December+10%2C+2020/1_b08zavdn): Review of Compiling Functions 99 | 100 | **Office hours** 101 | 102 | * Jeremy Siek (jsiek): Tuesdays and Thursdays 4:30-5:30pm. 103 | Zoom Meeting ID: 949 1594 8290. 104 | 105 | * Caner Derici (cderici): Mondays 11am-12pm, Wednesdays 11am-12pm. 106 | Zoom Meeting ID: 774 5516 2736. 107 | 108 | **Topics:** 109 | 110 | * Instruction Selection 111 | 112 | * Register Allocation 113 | 114 | * Static type checking 115 | 116 | * Conditional control flow 117 | 118 | * Mutable data 119 | 120 | * Garbage collection 121 | 122 | * Procedures and calling conventions 123 | 124 | * First-class functions and closure conversion 125 | 126 | * Dynamic typing 127 | 128 | * Generics 129 | 130 | * High-level optimization (inlining, constant folding, copy 131 | propagation, etc.) 132 | 133 | **Grading:** 134 | 135 | Course grades are based on the following items. For the weighting, see 136 | the Canvas panel on the right-hand side of this web page. Grading 137 | will take into account any technology problems that arrise, i.e., you 138 | won't fail the class because your internet went out. 139 | 140 | * Assignments 141 | * Quizzes 142 | * Midterm Exam (October 23, Online as a Canvas Quiz) 143 | * Final Exam (December 15, Online as a Canvas Quiz) 144 | 145 | **Assignments:** 146 | 147 | Organize into teams of 2-4 students. Assignments will be due bi-weekly 148 | on Mondays at 11:59pm. Teams that include one or more graduate 149 | students are required to complete the challenge exercises. 150 | 151 | Assignment descriptions are posted on Canvas. 152 | Turn in your assignments by creating a github repository and giving 153 | access to Jeremy (jsiek) and Caner (cderici). 154 | 155 | Assignments will be graded based on how many test cases they succeed on. 156 | Partial credit will be given for each "pass" of the compiler. 157 | Some of the tests are in the public support code (see Resources below) 158 | and the rest of the tests will be made available on Sunday night, one 159 | day prior to the due date. The testing will be done on the linux 160 | machine kj.luddy.indiana.edu named 161 | after [Katherine 162 | Johnson](https://en.wikipedia.org/wiki/Katherine_Johnson) of NASA 163 | fame. The testing will include both new tests and all of the tests 164 | from prior assignments. 165 | 166 | You may request feedback on your assignments prior to the due date. 167 | Just commit your work to github and send us email. 168 | 169 | Students are responsible for understanding the entire assignment and 170 | all of the code that their team produces. The midterm and final exam 171 | are designed to test a student's understanding of the assignments. 172 | 173 | Students are free to discuss and get help on the assignments from 174 | anyone or anywhere. When posting questions on Piazza, it is OK to post 175 | your code. 176 | 177 | In contrast, for quizzes and exams, students are asked to work 178 | alone. The quizzes and exams are closed book. We will be using 179 | Respondus Monitor for online proctoring. 180 | 181 | The Final Project is due Dec. 4 and may be turned in late up to 182 | Dec. 11. 183 | 184 | **Late assignment policy:** Assignments may be turned in up to one 185 | week late with a penalty of 10%. 186 | 187 | **Email Discussion Group:** on [Piazza](http://piazza.com/iu/fall2020/p423p523e313e513) 188 | 189 | **Slack Chat/Messaging:** 190 | [Workspace](http://iu-compiler-course.slack.com/) (see invitation 191 | link on Piazza or 192 | [signup](https://join.slack.com/t/iu-compiler-course/signup?x=x-p1325281886868-1312364974614-1331891515409) 193 | using your iu email address). 194 | 195 | **Resources:** 196 | 197 | * [Github repository for support code and test suites is here](https://github.com/IUCompilerCourse/public-student-support-code) 198 | * [Racket](https://download.racket-lang.org/) 199 | * [Racket Documentation](https://docs.racket-lang.org/) 200 | * [Notes on x86-64 programming](http://web.cecs.pdx.edu/~apt/cs491/x86-64.pdf) 201 | * [x86-64 Machine-Level Programming](https://www.cs.cmu.edu/~fp/courses/15411-f13/misc/asm64-handout.pdf) 202 | * [Intel x86 Manual](http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf?_ga=1.200286509.2020252148.1452195021) 203 | * [System V Application Binary Interface](https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf) 204 | * [Uniprocessor Garbage Collection Techniques](https://iu.instructure.com/courses/1735985/files/82131907/download?wrap=1) by Wilson. 205 | * [Fast and Effective Procedure Inlining](https://www.cs.indiana.edu/~dyb/pubs/inlining.pdf) by Waddell and Dybvig. 206 | 207 | **Bias-Based Incident Reporting.** 208 | 209 | Bias-based incident reports can be made by students, faculty and 210 | staff. Any act of discrimination or harassment based on race, 211 | ethnicity, religious affiliation, gender, gender identity, sexual 212 | orientation or disability can be reported through any of the options: 213 | 214 | 1) email biasincident@indiana.edu or incident@indiana.edu; 215 | 216 | 2) call the Dean of Students Office at (812) 855-8188 or 217 | 218 | 3) use the IU mobile App (m.iu.edu). Reports can be made anonymously. 219 | 220 | **Dean on Call.** 221 | 222 | The Dean of Students office provides support for students dealing with 223 | serious or emergency situations after 5 p.m. in which an immediate 224 | response is needed and which cannot wait until the next business 225 | day. Faculty or staff who are concerned about a student’s welfare 226 | should feel free to call the Dean on Call at (812) 856-7774. This 227 | number is not to be given to students or families but is for internal 228 | campus use only. If someone is in immediate danger or experiencing an 229 | emergency, call 911. 230 | 231 | **Boost.** 232 | 233 | Indiana University has developed an award-winning smartphone app to 234 | help students stay on top of their schoolwork in Canvas. The app is 235 | called “Boost,” it is available for free to all IU students, and it 236 | integrates with Canvas to provide reminders about deadlines and other 237 | helpful notifications. For more information, see 238 | https://kb.iu.edu/d/atud. 239 | 240 | **Counseling and Psychological Services.** 241 | 242 | CAPS has expanded their services. For information about the variety of 243 | services offered to students by CAPS visit: 244 | http://healthcenter.indiana.edu/counseling/index.shtml. 245 | 246 | 247 | **Disability Services for Students (DSS).** 248 | 249 | The process to establish accommodations for a student with a 250 | disability is a responsibility shared by the student and the DSS 251 | Office. Only DSS approved accommodations should be utilized in the 252 | classroom. After the student has met with DSS, it is the student’s 253 | responsibility to share their accommodations with the faculty 254 | member. For information about support services or accommodations 255 | available to students with disabilities and for the procedures to be 256 | followed by students and instructors, please visit: 257 | https://studentaffairs.indiana.edu/disability-services-students/. 258 | 259 | **Reporting Conduct and Student Wellness Concerns.** 260 | 261 | All members of the IU community including faculty and staff may report 262 | student conduct and wellness concerns to the Division of Student 263 | Affairs using an online form located at 264 | https://studentaffairs.indiana.edu/dean-students/student-concern/index.shtml. 265 | 266 | **Students needing additional financial or other assistance.** 267 | 268 | The Student Advocates Office (SAO) can help students work through 269 | personal and academic problems as well as financial difficulties and 270 | concerns. SAO also assists students working through grade appeals and 271 | withdrawals from all classes. SAO also has emergency funds for IU 272 | students experiencing emergency financial crisis 273 | https://studentaffairs.indiana.edu/student- advocates/. 274 | 275 | **Disruptive Students.** 276 | 277 | If instructors are confronted by threatening behaviors from students 278 | their first obligation is to insure the immediate safety of the 279 | classroom. When in doubt, call IU Police at 9-911 from any campus 280 | phone or call (812) 855-4111 from off-campus for immediate or 281 | emergency situations. You may also contact the Dean of Students Office 282 | at (812) 855-8188. For additional guidance in dealing with difficult 283 | student situations: 284 | https://ufc.iu.edu/doc/policies/disruptive-students.pdf. 285 | 286 | **Academic Misconduct.** 287 | 288 | If you suspect that a student has cheated, plagiarized or otherwise committed academic misconduct, refer to the Code of Student Rights, Responsibilities and Conduct: 289 | http://studentcode.iu.edu/. 290 | 291 | **Sexual Misconduct.** 292 | 293 | As your instructor, one of my responsibilities is to create a positive 294 | learning environment for all students. Title IX and IU’s Sexual 295 | Misconduct Policy prohibit sexual misconduct in any form, including 296 | sexual harassment, sexual assault, stalking, and dating and domestic 297 | violence. If you have experienced sexual misconduct, or know someone 298 | who has, the University can help. 299 | 300 | If you are seeking help and would like to speak to someone 301 | confidentially, you can make an appointment with: 302 | 303 | * The Sexual Assault Crisis Services (SACS) at (812) 855-8900 304 | (counseling services) 305 | 306 | * Confidential Victim Advocates (CVA) at (812) 856-2469 (advocacy and 307 | advice services) 308 | 309 | * IU Health Center at (812) 855-4011 (health and medical services) 310 | 311 | It is also important that you know that Title IX and University policy 312 | require me to share any information brought to my attention about 313 | potential sexual misconduct, with the campus Deputy Title IX 314 | Coordinator or IU’s Title IX Coordinator. In that event, those 315 | individuals will work to ensure that appropriate measures are taken 316 | and resources are made available. Protecting student privacy is of 317 | utmost concern, and information will only be shared with those that 318 | need to know to ensure the University can respond and assist. I 319 | encourage you to visit 320 | stopsexualviolence.iu.edu to learn more. 321 | -------------------------------------------------------------------------------- /lecture-Oct-6.md: -------------------------------------------------------------------------------- 1 | # Code Review: Booleans and Control Flow 2 | 3 | 4 | ## Type Checking 5 | 6 | ### Example 1 7 | 8 | ``` 9 | (define (type-check-exp env) 10 | (lambda (e) 11 | (match e 12 | [(Var x) (dict-ref env x)] 13 | [(Int n) 'Integer] 14 | [(Bool b) 'Boolean] 15 | [(Prim op args) ((type-check-prim env) e)] 16 | [(Let x e body) 17 | (define Te ((type-check-exp env) e)) 18 | (define Tb ((type-check-exp (dict-set env x Te)) body)) 19 | Tb] 20 | [(If cnd cnsq alt) 21 | (unless (eqv? 'Boolean ((type-check-exp env) cnd)) 22 | (error "condition given to if should be bool, given " cnd)) 23 | (define Tc ((type-check-exp env) cnsq)) 24 | (define Ta ((type-check-exp env) alt)) 25 | (unless (equal? Tc Ta) 26 | (error (string-append "consequent and alternative in if should " 27 | "have same type, given") 28 | (list Tc Ta))) 29 | Tc] 30 | [else 31 | (error "type-check-exp couldn't match" e)]))) 32 | 33 | (define (type-check-prim env) 34 | (lambda (prim) 35 | (let ([recur (type-check-exp env)]) 36 | (match prim 37 | [(Prim 'read (list)) 'Integer] 38 | [(Prim 'eq? (list e1 e2)) 39 | (define Te1 (recur e1)) 40 | (define Te2 (recur e2)) 41 | (if (eqv? Te1 Te2) 42 | (and (eqv? Te1 Te1) 43 | (or (eqv? Te1 'Integer) 44 | (eqv? Te1 'Boolean))) 45 | 'Boolean 46 | (error "eq? should take two ints or two bools, given " (list e1 e2)))] 47 | [(Prim '< (list e1 e2)) 48 | (define Te1 (recur e1)) 49 | (define Te2 (recur e2)) 50 | (if (and (eqv? Te1 'Integer) 51 | (eqv? Te2 'Integer)) 52 | 'Boolean 53 | (error "< should take two ints, given " (list e1 e2)))] 54 | [(Prim '<= (list e1 e2)) 55 | (define Te1 (recur e1)) 56 | (define Te2 (recur e2)) 57 | (if (and (eqv? Te1 'Integer) 58 | (eqv? Te2 'Integer)) 59 | 'Boolean 60 | (error "<= should take two ints, given " (list e1 e2)))] 61 | [(Prim '> (list e1 e2)) 62 | (define Te1 (recur e1)) 63 | (define Te2 (recur e2)) 64 | (if (and (eqv? Te1 'Integer) 65 | (eqv? Te2 'Integer)) 66 | 'Boolean 67 | (error "> should take two ints, given " (list e1 e2)))] 68 | [(Prim '>= (list e1 e2)) 69 | (define Te1 (recur e1)) 70 | (define Te2 (recur e2)) 71 | (if (and (eqv? Te1 'Integer) 72 | (eqv? Te2 'Integer)) 73 | 'Boolean 74 | (error ">= should take two ints, given " (list e1 e2)))] 75 | [(Prim '+ (list e1 e2)) 76 | (define Te1 (recur e1)) 77 | (define Te2 (recur e2)) 78 | (if (and (eqv? Te1 'Integer) 79 | (eqv? Te2 'Integer)) 80 | 'Integer 81 | (error "+ should take two ints, given " (list e1 e2)))] 82 | [(Prim '- (list e)) 83 | (define Te (recur e)) 84 | (if (eqv? Te 'Integer) 85 | 'Integer 86 | (error "- should take one int, given " (list e)))] 87 | [(Prim '- (list e1 e2)) 88 | (define Te1 (recur e1)) 89 | (define Te2 (recur e2)) 90 | (if (and (eqv? Te1 'Integer) 91 | (eqv? Te2 'Integer)) 92 | 'Integer 93 | (error "- should take two ints, given " (list e1 e2)))] 94 | [(Prim 'and (list e1 e2)) 95 | (define Te1 (recur e1)) 96 | (define Te2 (recur e2)) 97 | (if (and (eqv? Te1 'Boolean) 98 | (eqv? Te2 'Boolean)) 99 | 'Boolean 100 | (error "and should take two bools, given " (list e1 e2)))] 101 | [(Prim 'or (list e1 e2)) 102 | (define Te1 (recur e1)) 103 | (define Te2 (recur e2)) 104 | (if (and (eqv? Te1 'Boolean) 105 | (eqv? Te2 'Boolean)) 106 | 'Boolean 107 | (error "or should take two bools, given " (list e1 e2)))] 108 | [(Prim 'not (list e)) 109 | (define Te (recur e)) 110 | (if (eqv? Te 'Boolean) 111 | 'Boolean 112 | (error "not should take one bool, given " (list e)))])))) 113 | 114 | (define (type-check p) 115 | (match p 116 | [(Program info body) 117 | (define Tb ((type-check-exp '()) body)) 118 | (unless (equal? Tb 'Integer) 119 | (error "result of the program must be an integer, not " Tb)) 120 | (Program info body)])) 121 | ``` 122 | 123 | ### Example 2 124 | 125 | ``` 126 | (define (check-bool e) 127 | (match e 128 | ['Boolean 'Boolean] 129 | [else (error "expected Boolean but got" e)] 130 | )) 131 | 132 | (define (check-int e) 133 | (match e 134 | ['Integer 'Integer] 135 | [else (error "expected Integer but got" e)] 136 | )) 137 | 138 | (define (check-eq ts) 139 | (if (equal? (first ts) (last ts)) 140 | (void) 141 | (error "Cannot compare items of different types" ts))) 142 | 143 | (define (type-check-op op ts) 144 | (match op 145 | ['read 'Integer] 146 | ['+ (for ([t ts]) (check-int t)) 'Integer] 147 | ['- (for ([t ts]) (check-int t)) 'Integer] 148 | ['not (for ([t ts]) (check-bool t)) 'Boolean] 149 | ['and (for ([t ts]) (check-bool t)) 'Boolean] 150 | ['or (for ([t ts]) (check-bool t)) 'Boolean] 151 | ['eq? (check-eq ts) 'Boolean] 152 | ['cmp (check-eq ts) 'Boolean] 153 | ['< (for ([t ts]) (check-int t)) 'Boolean] 154 | ['<= (for ([t ts]) (check-int t)) 'Boolean] 155 | ['> (for ([t ts]) (check-int t)) 'Boolean] 156 | ['>= (for ([t ts]) (check-int t)) 'Boolean] 157 | [else (error "unknown operator" op)] 158 | )) 159 | 160 | (define (type-check-exp env) 161 | (lambda (e) 162 | (match e 163 | [(Var x) (dict-ref env x)] 164 | [(Int n) 'Integer] 165 | [(Bool b) 'Boolean] 166 | [(Let x e body) 167 | (define Te ((type-check-exp env) e)) 168 | (define Tb ((type-check-exp (dict-set env x Te)) body)) 169 | Tb] 170 | 171 | [(If e1 e2 e3) 172 | (define T1 ((type-check-exp env) e1)) 173 | (unless (equal? T1 'Boolean) 174 | (error "Conditional of if statement must resolve to a boolean. Was " T1)) 175 | (define T2 ((type-check-exp env) e2)) 176 | (define T3 ((type-check-exp env) e3)) 177 | (unless (equal? T2 T3) 178 | (error "Return types of both branches of If must match. Got" T2 " and " T3)) 179 | T2] 180 | [(Prim op es) 181 | (define ts 182 | (for/list ([e es]) ((type-check-exp env) e))) 183 | (define t-ret (type-check-op op ts)) 184 | t-ret] 185 | [else 186 | (error "type-check-exp couldn't match" e)]))) 187 | 188 | (define (type-checker e) 189 | (match e 190 | [(Program info body) 191 | (define Tb ((type-check-exp '()) body)) 192 | (unless (equal? Tb 'Integer) 193 | (error "result of the program must be an integer, not " Tb)) 194 | (Program info body)] 195 | )) 196 | ``` 197 | 198 | ## Remove Complex Operands 199 | 200 | ### Example 1 201 | 202 | ``` 203 | (define (rco-atom e) 204 | (match e 205 | [(Var x) (values (Var x) '())] 206 | [(Int n) (values (Int n) '())] 207 | [(Bool bool) (values (Bool bool) '())] 208 | [(Let x e body) 209 | (define tmp (gensym "tmp")) 210 | (define-values (e-val e-alist) (rco-atom e)) 211 | (values (Var tmp) (append e-alist `((,tmp . ,(Let x e-val (rco-exp body))))))] 212 | [(Prim op es) 213 | (define tmp (gensym "tmp")) 214 | (define-values (new-es bs) 215 | (for/lists (l1 l2) ([e es]) 216 | (rco-atom e))) 217 | (values (Var tmp) (append bs `((,tmp . ,(Prim op new-es))))))] 218 | [(If cond exp else) 219 | (define tmp (gensym "tmp")) 220 | (define cond-val (rco-exp cond)) 221 | (define exp-val (rco-exp exp)) 222 | (define else-val (rco-exp else)) 223 | (values (Var tmp) `((,tmp . ,(If cond-val exp-val else-val))))] 224 | )) 225 | 226 | (define (rco-exp e) 227 | (match e 228 | [(Var x) (Var x)] 229 | [(Int n) (Int n)] 230 | [(Bool bool) (Bool bool)] 231 | [(Let x e body) 232 | (begin (define e-val (rco-exp e)) 233 | (Let x e-val (rco-exp body)))] 234 | [(Prim op es) 235 | (let [(exps (split-pairs (for/list ([e es]) 236 | (begin (define-values (var alist) (rco-atom e)) 237 | `(,var . ,alist)))))] 238 | (expand-alist (cdr exps) (Prim op (car exps))))] 239 | [(If cond exp else) 240 | (define exp-var (rco-exp exp)) 241 | (define else-var (rco-exp else)) 242 | (define cond-var (rco-exp cond)) 243 | (If cond-var exp-var else-var)] 244 | )) 245 | 246 | (define (remove-complex-opera* p) 247 | (match p 248 | [(Program info e) 249 | (Program info (rco-exp e))] 250 | )) 251 | ``` 252 | 253 | ### Example 2 254 | 255 | ``` 256 | (define (remove-complex-opera* p) 257 | (match p 258 | [(Program info e) 259 | (Program info (rco-exp e))])) 260 | 261 | (define (rco-atom e) 262 | (match e 263 | [(Var x) (values (Var x) '())] 264 | [(Int n) (values (Int n) '())] 265 | [(Bool b) (values (Bool b) '())] 266 | [(Let x rhs body) 267 | (define new-rhs (rco-exp rhs)) 268 | (define-values (new-body body-ss) (rco-atom body)) 269 | (values new-body (append `((,x . ,new-rhs)) body-ss))] 270 | [(Prim op es) 271 | (define-values (new-es sss) 272 | (for/lists (l1 l2) ([e es]) (rco-atom e))) 273 | (define ss (append* sss)) 274 | (define tmp (gensym 'tmp)) 275 | (values (Var tmp) 276 | (append ss `((,tmp . ,(Prim op new-es)))))] 277 | [(If e1 e2 e3) 278 | (define-values (new-es sss) 279 | (for/lists (l1 l2) ([e (list e1 e2 e3)]) (rco-atom e))) 280 | (define ss (append* sss)) 281 | (define tmp (gensym 'tmp)) 282 | (match new-es 283 | [(list e1 e2 e3) 284 | (values (Var tmp) 285 | (append ss `((,tmp . ,(If e1 e2 e3)))))])] 286 | )) 287 | 288 | (define (make-lets^ bs e) 289 | (match bs 290 | [`() e] 291 | [`((,x . ,e^) . ,bs^) 292 | (Let x e^ (make-lets^ bs^ e))])) 293 | 294 | (define (rco-exp e) 295 | (match e 296 | [(Var x) (Var x)] 297 | [(Int n) (Int n)] 298 | [(Bool b) (Bool b)] 299 | [(Let x rhs body) 300 | (Let x (rco-exp rhs) (rco-exp body))] 301 | [(Prim op es) 302 | (define-values (new-es sss) 303 | (for/lists (l1 l2) ([e es]) (rco-atom e))) 304 | (make-lets^ (append* sss) (Prim op new-es))] 305 | [(If e1 e2 e3) 306 | (define-values (new-es sss) 307 | (for/lists (l1 l2) ([e (list e1 e2 e3)]) (rco-atom e))) 308 | (match new-es 309 | [(list e1 e2 e3) 310 | (make-lets^ (append* sss) (If e1 e2 e3))])] 311 | )) 312 | ``` 313 | 314 | ## Explicate Control 315 | 316 | ### Example 1 317 | 318 | ``` 319 | (define (do-assignment exp var tail) 320 | (match exp 321 | [(Return (Int n)) (Seq (Assign var (Int n)) tail)] 322 | [(Return (Var x)) (Seq (Assign var (Var x)) tail)] 323 | [(Return (Bool bool)) (Seq (Assign var (Bool bool)) tail)] 324 | [(Return (Prim op es)) (Seq (Assign var (Prim op es)) tail)] 325 | [(Seq stmt seq-tail) (Seq stmt (do-assignment seq-tail var tail))])) 326 | 327 | (define (explicate-assign exp var tail cgraph) 328 | (match exp 329 | [(If pred then else) 330 | (define tail-block (gensym "block")) 331 | (define-values (then-block then-vars then-graph) (explicate-assign then var (Goto tail-block) cgraph)) 332 | (define-values (else-block else-vars else-graph) (explicate-assign else var (Goto tail-block) then-graph)) 333 | (define-values (pred-exp pred-vars pred-cgraph) (explicate-pred pred then-block else-block else-graph)) 334 | (values pred-exp (remove-duplicates (append then-vars else-vars pred-vars)) 335 | (cons `(,tail-block . ,tail) pred-cgraph))] 336 | [(Let x exp body) 337 | (begin (define-values (exp-body body-vars body-graph) (explicate-assign body var tail cgraph)) 338 | (define-values (body-tail vars newgraph) (explicate-assign exp (Var x) exp-body body-graph)) 339 | (values body-tail (cons (Var x) (remove-duplicates (append body-vars vars))) newgraph))] 340 | [x (begin (define-values (exp-tail exp-vars exp-graph) (explicate-tail exp cgraph)) 341 | (values (do-assignment exp-tail var tail) exp-vars exp-graph)) 342 | ])) 343 | 344 | (define (explicate-pred e true-block false-block cgraph) 345 | (match e 346 | [(Bool b) (values (if b true-block false-block) '() cgraph))] 347 | 348 | ;;[(Bool bool) 349 | ;; (values (IfStmt (Prim 'eq? (list (Bool bool) (Bool #t))) (Goto true-lbl) (Goto false-lbl)) '() cgraph)] 350 | 351 | [(Var x) 352 | (let ([true-lbl (gensym "block")] 353 | [false-blb (gensym "block")]) 354 | (values (IfStmt (Prim 'eq? (list (Var x) (Bool #t))) (Goto true-lbl) (Goto false-lbl)) '() 355 | ... cgraph ...)] 356 | [(Prim 'not (list var)) (values (IfStmt (Prim 'eq? (list var (Bool #f))) (Goto true-lbl) (Goto false-lbl)) '() cgraph)] 357 | [(Prim cmp es) (values (IfStmt (Prim cmp es) (Goto true-lbl) (Goto false-lbl)) '() cgraph)] 358 | [(Let x exp body) 359 | (begin (define-values (exp-body body-vars body-graph) (explicate-pred body true-lbl false-lbl cgraph)) 360 | (define-values (tail vars tail-graph) (explicate-assign exp (Var x) exp-body body-graph)) 361 | (values tail (cons (Var x) (remove-duplicates (append body-vars vars))) tail-graph))] 362 | [(If pred then else) 363 | (let ([true-lbl (gensym "block")] 364 | [false-lbl (gensym "block")]) 365 | (begin (define-values (then-exp then-vars then-cgraph) (explicate-pred then (Goto true-lbl) (Goto false-lbl) cgraph)) 366 | (define-values (else-exp else-vars else-cgraph) (explicate-pred else (Goto true-lbl) (Goto false-lbl) then-cgraph)) 367 | (define-values (pred-exp pred-vars pred-cgraph) (explicate-pred pred then-exp else-exp else-cgraph)) 368 | (values pred-exp (remove-duplicates (append then-vars else-vars pred-vars)) 369 | ... pred-cgraph))))] 370 | )) 371 | 372 | (define (explicate-tail e cgraph) 373 | (match e 374 | [(Var x) (values (Return (Var x)) '() cgraph)] 375 | [(Int n) (values (Return (Int n)) '() cgraph)] 376 | [(Bool bool) (values (Return (Bool bool)) '() cgraph)] 377 | [(Let x e body) 378 | (begin (define-values (exp-body body-vars body-graph) (explicate-tail body cgraph)) 379 | (define-values (tail vars newgraph) (explicate-assign e (Var x) exp-body body-graph)) 380 | (values tail (cons (Var x) (remove-duplicates (append body-vars vars))) newgraph))] 381 | [(Prim op es) 382 | (values (Return (Prim op es)) '() cgraph)] 383 | [(If pred then else) 384 | (let ([then-block (gensym "block")] [else-block (gensym "block")]) 385 | (begin (define-values (then-exp then-vars then-cgraph) (explicate-tail then cgraph)) 386 | (define-values (else-exp else-vars else-cgraph) (explicate-tail else then-cgraph)) 387 | (define-values (pred-exp pred-vars pred-cgraph) (explicate-pred pred then-block else-block else-cgraph)) 388 | (values pred-exp (remove-duplicates (append then-vars else-vars pred-vars)) 389 | (cons `(,then-block . ,then-exp) (cons `(,else-block . ,else-exp) pred-cgraph)))))] 390 | )) 391 | 392 | (define (explicate-control p) 393 | (match p 394 | [(Program info e) 395 | (begin (define-values (tail vars graph) (explicate-tail e '())) 396 | (Program `((locals . ,vars)) (CFG (cons `(start . ,tail) graph))))] 397 | )) 398 | ``` 399 | 400 | 401 | ### Example 2 402 | 403 | ``` 404 | (define Explicate-CFG '()) 405 | 406 | (define (add-to-cfg t) 407 | (define new-label (gensym "l")) 408 | (set! Explicate-CFG (cons (cons new-label t) Explicate-CFG)) 409 | new-label) 410 | 411 | (define (explicate-tail exp) 412 | (match exp 413 | [(Int n) (values (Return (Int n)) '())] 414 | [(Var v) (values (Return (Var v)) '())] 415 | [(Bool bool) (values (Return (Bool bool)) '())] 416 | [(Prim rator rand) (values (Return (Prim rator rand)) '())] 417 | [(Let var exp body) 418 | (let*-values ([(let-body variables1) (explicate-tail body)] 419 | [(assigned-tail variables2) (explicate-assign exp var let-body)]) 420 | (values assigned-tail (cons var (append variables1 variables2))))] 421 | [(If cnd thn els) 422 | (let*-values ([(thn-tail vars1) (explicate-tail thn)] 423 | [(els-tail vars2) (explicate-tail els)]) 424 | (let-values ([(cnd-tail vars3) (explicate-pred cnd thn-tail els-tail)]) 425 | (values cnd-tail (append vars1 vars2 vars3))))])) 426 | 427 | (define (explicate-assign exp var tail) 428 | (match exp 429 | [(Int n) (values (Seq (Assign (Var var) (Int n)) tail) '())] 430 | [(Var v) (values (Seq (Assign (Var var) (Var v)) tail) '())] 431 | [(Bool bool) (values (Seq (Assign (Var var) (Bool bool)) tail) '())] 432 | [(Prim rator rand) (values (Seq (Assign (Var var) (Prim rator rand)) tail) '())] 433 | [(Let var* exp body) 434 | (let*-values ([(body-tail vars1) (explicate-assign body var tail)] 435 | [(exp-tail vars2) (explicate-assign exp var* body-tail)]) 436 | (values exp-tail (cons var* (append vars1 vars2))))] 437 | [(If cnd thn els) 438 | (define label (add-to-cfg tail)) 439 | (let*-values ([(thn-tail vars1) (explicate-assign thn var (Goto label))] 440 | [(els-tail vars2) (explicate-assign els var (Goto label))] 441 | [(cnd-tail vars3) (explicate-pred cnd thn-tail els-tail)]) 442 | (values cnd-tail (append vars3 vars1 vars2)))])) 443 | 444 | (define (explicate-pred e tail1 tail2) 445 | (match e 446 | [(Bool bool) (if bool (values tail1 '()) (values tail2 '()))] 447 | [(Var v) 448 | (define label1 (add-to-cfg tail1)) 449 | (define label2 (add-to-cfg tail2)) 450 | (values (IfStmt (Prim 'eq? (list (Var v) (Bool #t))) 451 | (Goto label1) (Goto label2)) 452 | '())] 453 | [(Prim rator (list exp1 exp2)) 454 | (define label1 (add-to-cfg tail1)) 455 | (define label2 (add-to-cfg tail2)) 456 | (define atm1 (gensym "rator-1-")) 457 | (define atm2 (gensym "rator-2-")) 458 | (let*-values ([(atm2-tail vars2) (explicate-assign exp2 atm2 (IfStmt (Prim rator (list (Var atm1) (Var atm2))) (Goto label1) (Goto label2)))] 459 | [(atm1-tail vars1) (explicate-assign exp1 atm1 atm2-tail)]) 460 | (values atm1-tail (cons atm1 (cons atm2 (append vars1 vars2)))))] 461 | [(Prim 'not (list exp)) 462 | (define label1 (add-to-cfg tail1)) 463 | (define label2 (add-to-cfg tail2)) 464 | (values (IfStmt (Prim 'eq? (list exp (Bool #t))) (Goto label2) (Goto label1)) '())] 465 | [(Let var exp body) 466 | (define label1 (add-to-cfg tail1)) 467 | (define label2 (add-to-cfg tail2)) 468 | (define t (gensym "let-ec-")) 469 | (let*-values ([(body-tail vars1) (explicate-assign body t (IfStmt (Prim 'eq? (list (Var t) (Bool #t))) (Goto label1) (Goto label2)))] 470 | [(exp-tail vars2) (explicate-assign exp var body-tail)]) 471 | (values exp-tail (cons t (cons var (append vars1 vars2)))))] 472 | [(If cnd thn els) 473 | (define label1 (add-to-cfg tail1)) 474 | (define label2 (add-to-cfg tail2)) 475 | (let*-values ([(thn-block vars2) (explicate-pred thn (Goto label1) (Goto label2))] 476 | [(els-block vars3) (explicate-pred els (Goto label1) (Goto label2))] 477 | [(thn-label) (add-to-cfg thn-block)] 478 | [(els-label) (add-to-cfg els-block)] 479 | [(result vars) (explicate-pred cnd (Goto thn-label) (Goto els-label))] 480 | ) 481 | (values result (append vars vars2 vars3)))] 482 | )) 483 | 484 | (define (explicate-control p) 485 | (set! Explicate-CFG '()) 486 | (match p 487 | [(Program info e) 488 | (let-values ([(tail vars) (explicate-tail e)]) 489 | (Program 490 | (list (cons 'locals vars)) 491 | (CFG (cons (cons 'start tail) Explicate-CFG))))] 492 | )) 493 | 494 | ``` 495 | 496 | ## Optimize Jumps 497 | 498 | ``` 499 | #lang racket 500 | (require "utilities.rkt") 501 | (require graph) 502 | (provide (all-defined-out)) 503 | 504 | (define (is-trivial? block) 505 | (match block 506 | [(Goto label) #t] 507 | [else #f])) 508 | 509 | (define (get-label block) 510 | (match block 511 | [(Goto label) label])) 512 | 513 | (define (add-to-hash hash src-label goto-label) 514 | (hash-set! hash src-label goto-label) 515 | (hash-map hash 516 | (lambda (k v) (if (equal? v src-label) 517 | (hash-set! hash k goto-label) 518 | (void)))) 519 | hash) 520 | 521 | (define (short-cut blocks) 522 | (define ret (make-hash)) 523 | (for ([(label block) (in-dict blocks)]) 524 | (if (is-trivial? block) 525 | (add-to-hash ret label (get-label block)) 526 | (hash-set! ret label label))) 527 | ret) 528 | 529 | (define (patch-tail hash tl) 530 | (match tl 531 | [(IfStmt cnd thn els) (IfStmt cnd (patch-tail hash thn) (patch-tail hash els))] 532 | [(Return exp) tl] 533 | [(Seq stmt tail) (Seq stmt (patch-tail hash tail))] 534 | [(Goto label) (Goto (hash-ref hash label))] 535 | )) 536 | 537 | (define (patch-gotos short-cuts blocks) 538 | (for/list ([(label block) (in-dict blocks)]) 539 | (cons label (patch-tail short-cuts block)))) 540 | 541 | (define (optimize-jumps p) 542 | (match p 543 | [(Program info (CFG blocks)) 544 | (define short-cuts (short-cut blocks)) 545 | (define not-short-cut (filter (lambda (b) (or (not (is-trivial? (cdr b))) (equal? (car b) 'start))) blocks)) 546 | (define patched (patch-gotos short-cuts not-short-cut)) 547 | (define ref-graph (block-list->racketgraph patched)) 548 | (define has-neighbors (filter (lambda (b) (or (has-vertex? ref-graph (car b)) (equal? (car b) 'start))) patched)) 549 | (Program info (CFG (patch-gotos short-cuts has-neighbors)))])) 550 | 551 | (define (build-graph-optimize label tail racket-cfg) 552 | (match tail 553 | [(Goto target) (add-directed-edge! racket-cfg target label)] 554 | [(IfStmt cnd thn els) (begin 555 | (build-graph-optimize label thn racket-cfg) 556 | (build-graph-optimize label els racket-cfg))] 557 | [(Seq stmt tl) (build-graph-optimize label tl racket-cfg)] 558 | [_ (void)])) 559 | 560 | (define (block-list->racketgraph blocks) 561 | (define racket-cfg (directed-graph '())) 562 | (for ([(label block) (in-dict blocks)]) 563 | (build-graph-optimize label block racket-cfg)) 564 | racket-cfg) 565 | ``` 566 | 567 | ## Select Instructions 568 | 569 | ``` 570 | (define (sel-ins-atm c0a) 571 | (match c0a 572 | [(Int n) (Imm n)] 573 | [(Var x) (Var x)] 574 | [(Bool b) 575 | (match b 576 | [#t (Imm 1)] 577 | [#f (Imm 0)])])) 578 | 579 | (define (sel-ins-stmt c0stmt) 580 | (match c0stmt 581 | [(Assign v e) 582 | (if (atm? e) 583 | (list (Instr 'movq (list (sel-ins-atm e) v))) 584 | (match e 585 | [(Prim 'read '()) 586 | (list (Callq 'read_int) 587 | (Instr 'movq (list (Reg 'rax) v)))] 588 | [(Prim '- (list atm)) 589 | (define x86atm (sel-ins-atm atm)) 590 | (if (equal? x86atm v) 591 | (list (Instr 'negq (list v))) 592 | (list (Instr 'movq (list x86atm v)) 593 | (Instr 'negq (list v))))] 594 | [(Prim '+ (list atm1 atm2)) 595 | (define x86atm1 (sel-ins-atm atm1)) 596 | (define x86atm2 (sel-ins-atm atm2)) 597 | (cond [(equal? x86atm1 v) (list (Instr 'addq (list x86atm2 v)))] 598 | [(equal? x86atm2 v) (list (Instr 'addq (list x86atm1 v)))] 599 | [else (list (Instr 'movq (list x86atm1 v)) 600 | (Instr 'addq (list x86atm2 v)))])] 601 | [(Prim 'not (list atm)) 602 | (if (eqv? v atm) 603 | (list (Instr 'xorq (list (Imm 1) v))) 604 | (list (let ([atm_ (sel-ins-atm atm)]) 605 | (Instr 'movq (list atm_ v))) 606 | (Instr 'xorq (list (Imm 1) v))))] 607 | [(Prim 'eq? (list atm1 atm2)) 608 | (let ([atm1_ (sel-ins-atm atm1)] 609 | [atm2_ (sel-ins-atm atm2)] 610 | [v_ (sel-ins-atm v)]) 611 | (list 612 | (Instr 'cmpq (list atm2_ atm1_)) 613 | (Instr 'set (list 'e (Reg 'al))) 614 | (Instr 'movzbq (list (Reg 'al) v_))))] 615 | [(Prim '< (list atm1 atm2)) 616 | (let ([atm1_ (sel-ins-atm atm1)] 617 | [atm2_ (sel-ins-atm atm2)] 618 | [v_ (sel-ins-atm v)]) 619 | (list 620 | (Instr 'cmpq (list atm2_ atm1_)) 621 | (Instr 'set (list 'l (Reg 'al))) 622 | (Instr 'movzbq (list (Reg 'al) v_))))]))])) 623 | 624 | (define (sel-ins-tail c0t) 625 | (match c0t 626 | [(Return e) 627 | (append (sel-ins-stmt (Assign (Reg 'rax) e)) 628 | (list (Jmp 'conclusion)))] 629 | [(Seq stmt tail) 630 | (define x86stmt (sel-ins-stmt stmt)) 631 | (define x86tail (sel-ins-tail tail)) 632 | (append x86stmt x86tail)] 633 | [(Goto label) 634 | (list (Jmp label)) ] 635 | [(IfStmt (Prim 'eq? (list arg1 arg2)) (Goto label1) (Goto label2)) 636 | (let ([arg1_ (sel-ins-atm arg1)] 637 | [arg2_ (sel-ins-atm arg2)]) 638 | (list 639 | (Instr 'cmpq (list arg2_ arg1_)) 640 | (JmpIf 'e label1) 641 | (Jmp label2)))] 642 | [(IfStmt (Prim '< (list arg1 arg2)) (Goto label1) (Goto label2)) 643 | (let ([arg1_ (sel-ins-atm arg1)] 644 | [arg2_ (sel-ins-atm arg2)]) 645 | (list 646 | (Instr 'cmpq (list arg2_ arg1_)) 647 | (JmpIf 'l label1) 648 | (Jmp label2)))])) 649 | 650 | (define (select-instructions p) 651 | (match p 652 | [(Program info (CFG es)) 653 | (Program info (CFG (for/list ([ls es]) (cons (car ls) (Block '() (sel-ins-tail (cdr ls)))))))])) 654 | ``` 655 | 656 | ## Remove Jumps 657 | 658 | ``` 659 | (define (fix-block instrs cfg removed-blocks all-blocks curr-block) 660 | (cond 661 | [(null? instrs) '()] 662 | [else (let ([instr (car instrs)]) 663 | (match instr 664 | ;; check if the target has only this edge 665 | [(Jmp target) #:when (and (not (equal? target 'conclusion)) 666 | (equal? (length (get-neighbors cfg target)) 1) 667 | (< (edge-weight cfg target curr-block) 2)) 668 | (begin 669 | (set-add! removed-blocks target) 670 | (append 671 | (fix-block (Block-instr* (dict-ref all-blocks target)) cfg removed-blocks all-blocks curr-block) 672 | (fix-block (cdr instrs) cfg removed-blocks all-blocks curr-block)))] 673 | [_ (cons instr (fix-block (cdr instrs) cfg removed-blocks all-blocks curr-block))]))])) 674 | 675 | (define (remove-jumps p) 676 | (match p 677 | [(Program info (CFG blocks)) 678 | ;; Get cfg 679 | (define r-cfg (dict-ref info 'r-cfg)) 680 | ;; tsorted vertices 681 | (define vertices-order (tsort (transpose r-cfg))) 682 | ;;keep track of new blocks 683 | (define new-blocks '()) 684 | ;;keep track of removed blocks 685 | (define removed-blocks (mutable-set)) 686 | ;;remove jumps 687 | (for ([vert vertices-order]) 688 | (if (not (set-member? removed-blocks vert)) 689 | (let* ([instrs (Block-instr* (dict-ref blocks vert))] 690 | [block-info (Block-info (dict-ref blocks vert))] 691 | [new-instrs (fix-block instrs r-cfg removed-blocks blocks vert)] 692 | [new-block (Block block-info new-instrs)]) 693 | (set! new-blocks (cons (cons vert new-block) new-blocks))) 694 | (void))) 695 | ;;(display new-blocks) 696 | (Program info (CFG new-blocks))])) 697 | ``` 698 | 699 | ## Uncover Live 700 | 701 | ### Example 1 702 | 703 | ``` 704 | (define (uncover-live p) 705 | (match p 706 | [(Program info (CFG e)) 707 | (define cfg-with-edges 708 | (isomorph e)) 709 | (define cfg-we-tp (transpose cfg-with-edges)) 710 | (define reverse-top-order 711 | (tsort cfg-we-tp)) 712 | (Program 713 | info 714 | (CFG 715 | (foldl 716 | (lambda (label cfg) 717 | (begin 718 | (define block (cdr (assv label e))) 719 | (define-values (instr+ bl-info) 720 | (match block 721 | [(Block bl-info instr+) (values instr+ bl-info)])) 722 | (define neighbors (get-neighbors cfg-with-edges label)) 723 | (define live-after 724 | (foldr 725 | (lambda (nbr lv-after) 726 | (set-union 727 | lv-after 728 | ; the lv-before of its neighbor 729 | ; TODO this assv is failing? or see above 730 | (begin 731 | (match (cdr (assv nbr cfg)) 732 | [(Block bl-info instr+) 733 | (car bl-info)])))) 734 | '() 735 | (filter (lambda (vtx) (not (eqv? vtx 'conclusion))) 736 | neighbors))) 737 | (define liveness-blk (liveness instr+ live-after)) 738 | (define blonk (Block liveness-blk instr+)) 739 | (cons `(,label . ,blonk) cfg))) 740 | '() 741 | ; remove conclusion from liveness analysis since we have not 742 | ; created it yet 743 | (filter (lambda (vtx) (not (eqv? vtx 'conclusion))) 744 | reverse-top-order))))])) 745 | ``` 746 | 747 | ### Example 2 748 | 749 | ``` 750 | (define/public (adjacent-instr s) 751 | (match s 752 | [(Jmp label) 753 | (cond [(string-suffix? (symbol->string label) "conclusion") (set)] 754 | [else (set label)])] 755 | [(JmpIf cc label) (set label)] 756 | [else (set)])) 757 | 758 | (define (adjacent-instrs b) 759 | (match b 760 | [(Block info ss) 761 | (for/fold ([outs (set)]) ([s ss]) 762 | (set-union outs (adjacent-instr s)))] 763 | )) 764 | 765 | (define (CFG->graph cfg) 766 | (define G (directed-graph '())) 767 | (for ([label (in-dict-keys cfg)]) 768 | (add-vertex! G label)) 769 | (for ([(s b) (in-dict cfg)]) 770 | (for ([t (adjacent-instrs b)]) 771 | (add-directed-edge! G s t))) 772 | G) 773 | 774 | (define (live-before label CFG-hash) 775 | (match (hash-ref CFG-hash label) 776 | [(Block info ss) 777 | (car (dict-ref info 'lives))])) 778 | 779 | (define/public (uncover-live-CFG cfg) 780 | (define G (CFG->graph cfg)) 781 | (define CFG-hash (make-hash)) 782 | (for ([label (tsort (transpose G))]) 783 | (define live-after 784 | (for/fold ([lives (set)]) 785 | ([lbl (in-neighbors G label)]) 786 | (set-union lives (live-before lbl CFG-hash)))) 787 | (define new-block 788 | (uncover-live-block (dict-ref cfg label) live-after)) 789 | (hash-set! CFG-hash label new-block) 790 | ) 791 | (hash->list CFG-hash)) 792 | 793 | (define/override (uncover-live ast) 794 | (verbose "uncover-live " ast) 795 | (match ast 796 | [(Program info (CFG G)) 797 | (Program info (CFG (uncover-live-CFG G)))] 798 | )) 799 | ``` 800 | 801 | ## Patch Instructions 802 | 803 | ``` 804 | (define (patch-instructions-instrs instr) 805 | (match instr 806 | [(Instr op (list (Deref r1 n1) (Deref r2 n2))) 807 | (list (Instr 'movq (list (Deref r1 n1) (Reg 'rax))) 808 | (Instr op (list (Reg 'rax) (Deref r2 n2))))] 809 | [(Instr 'movq (list (Reg r1) (Reg r2))) 810 | (cond 811 | [(equal? r1 r2) '()] 812 | [else (list instr)])] 813 | [(Instr 'cmpq (list arg1 (Imm n))) 814 | (list (Instr 'movq (list (Imm n) (Reg 'rax))) 815 | (Instr 'cmpq (list arg1 (Reg 'rax))))] 816 | [(Instr 'movzbq (list arg1 (Imm n))) 817 | (list (Instr 'movq (list (Imm n) (Reg 'rax))) 818 | (Instr 'mvzbq (list arg1 (Reg 'rax))))] 819 | [_ (list instr)])) 820 | 821 | (define (patch-instructions-block block) 822 | (match block 823 | [(Block info instrs) 824 | (Block info (flatten (for/list ([instr instrs]) 825 | (patch-instructions-instrs instr))))])) 826 | 827 | (define (patch-instructions p) 828 | (match p 829 | [(Program info (CFG blocks)) 830 | (Program info (CFG (for/list ([block blocks]) 831 | (cons (car block) (patch-instructions-block (cdr block)))))) ])) 832 | ``` 833 | --------------------------------------------------------------------------------