├── README.md ├── _config.yml ├── index.md ├── index.orig ├── lectures ├── Aug-24.md ├── Aug-26.md ├── Aug-31.md ├── Dec-2.md ├── Dec-7.md ├── Dec-9.md ├── L_Int_height.py ├── L_Int_height.rkt ├── Nov-11.md ├── Nov-16.md ├── Nov-18.md ├── Nov-2.md ├── Nov-4.md ├── Nov-9.md ├── Oct-12.md ├── Oct-14.md ├── Oct-26.md ├── Oct-28.md ├── Oct-5.md ├── Rint-interp-example.rkt ├── Sep-14.md ├── Sep-16.md ├── Sep-2.md ├── Sep-21.md ├── Sep-23.md ├── Sep-28.md ├── Sep-30.md ├── Sep-7.md ├── interp-Rint.rkt ├── interp-Rvar.rkt ├── interp_Pint.py ├── interp_Pvar.py └── utilities.rkt ├── python ├── Dockerfile └── setup.sh └── racket ├── Dockerfile └── setup.sh /README.md: -------------------------------------------------------------------------------- 1 | # IU-Fall-2021 2 | Course web page for Fall 2021. 3 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | ## Course Webpage for Compilers (P423, P523, E313, and E513) 2 | 3 | Indiana University, Fall 2021 4 | 5 | 6 | High-level programming languages like Racket and Python make it easier 7 | to program compared to low-level languages such as x86 assembly 8 | code. But how do high-level languages work? There's a big gap between 9 | high-level languages and machine instructions for modern computers. In 10 | this class you learn how to translate Racket or Python programs (your 11 | choice!) all the way to x86 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 input 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:** Fluency in Racket or Python is highly recommended 23 | as students will do a lot of programming in one of those 24 | languages. Prior knowledge of an assembly language helps, but is not 25 | required. 26 | 27 | **Textbook:** *Essentials of Compilation* 28 | 29 | * The Racket version of the textbook for the course available 30 | [here](https://www.dropbox.com/s/ktdw8j0adcc44r0/book.pdf?dl=1). 31 | * The Python version of the textbook for the course available 32 | [here](https://www.dropbox.com/s/mfxtojk4yif3toj/python-book.pdf?dl=1). 33 | 34 | If you have suggestions for improvement, please either send an email 35 | to Jeremy or, even better, make edits to a branch of the book and 36 | perform a pull request. The book is at the following location on 37 | github: 38 | 39 | https://github.com/IUCompilerCourse/Essentials-of-Compilation 40 | 41 | **Lecture:** Tuesdays and Thursdays 3:15-4:30pm, Informatics Building 42 | (Myles Brand Hall), Room E130. 43 | 44 | **Office hours** 45 | 46 | * Jeremy Siek (jsiek): Mon 11am-noon, Tue 2-3pm, in Luddy 3014 (or nearby). 47 | 48 | **Topics:** 49 | 50 | * Instruction Selection 51 | 52 | * Register Allocation 53 | 54 | * Static type checking 55 | 56 | * Conditional control flow 57 | 58 | * Mutable data 59 | 60 | * Garbage collection 61 | 62 | * Procedures and calling conventions 63 | 64 | * First-class functions and closure conversion 65 | 66 | * Dynamic typing 67 | 68 | * Generics 69 | 70 | * High-level optimization (inlining, constant folding, copy 71 | propagation, etc.) 72 | 73 | **Grading:** 74 | 75 | Course grades are based on the following items. For the weighting, see 76 | the Canvas panel on the right-hand side of this web page. Grading 77 | will take into account any technology problems that arrise, i.e., you 78 | won't fail the class because your internet went out. 79 | 80 | * Assignments (40%) 81 | * Midterm Exam (October 21, in class) (25%) 82 | * Final Exam (December 14, 12:35-2:35 PM, in class) (35%) 83 | 84 | **Assignments:** 85 | 86 | Organize into teams of 2-4 students. Assignments will be due bi-weekly 87 | on Mondays at 11:59pm. Teams that include one or more graduate 88 | students are required to complete the challenge exercises. 89 | 90 | Assignment descriptions are posted on Canvas. Turn in your 91 | assignments by submitting your code to the 92 | [autograder](https://autograder.sice.indiana.edu/web/course/28). 93 | There is a Racket and Python version of each assignment. Submit your 94 | `compiler` file, either `compiler.rkt` or `compiler.py` depending on 95 | the language you are using. 96 | 97 | Assignments will be graded based on how many test cases they succeed 98 | on. Partial credit will be given for each "pass" of the compiler. 99 | Some of the tests are in the public support code (see Resources 100 | below). The testing will be done on a linux (ubuntu) machine. The 101 | testing will include both new tests and all of the tests from prior 102 | assignments. 103 | 104 | You may request feedback on your assignments prior to the due date. 105 | Just submit your work to the autograder and send us email. 106 | 107 | Students are responsible for understanding the entire assignment and 108 | all of the code that their team produces. The midterm and final exam 109 | are designed to test a student's understanding of the assignments. 110 | 111 | Students are free to discuss and get help on the assignments from 112 | anyone or anywhere. When posting questions on Slack, it is OK to post 113 | your code. 114 | 115 | In contrast, for quizzes and exams, students are asked to work 116 | alone. The quizzes and exams are closed book. 117 | 118 | The Final Project is due Dec. 10 and may be turned in late up to 119 | Dec. 15. 120 | 121 | **Late assignment policy:** Assignments may be turned in up to one 122 | week late with a penalty of 10%. 123 | 124 | **Slack Chat/Messaging:** 125 | [Workspace](https://iucompilercou-vm16750.slack.com) ( 126 | [signup](https://join.slack.com/t/slack-55j8169/shared_invite/zt-ulktwirl-CoKBrKGdKanD_R50e9Q46g) 127 | using your iu email address). 128 | 129 | **Schedule** 130 | 131 | Day | Lecture Topic | Reading Due | Assignment Due 132 | Aug. 24 | [Introduction](./lectures/Aug-24.md) | | 133 | Aug. 26 | [Compiling from LVar to x86](./lectures/Aug-26.md) | Ch. 1 | 134 | Aug. 31 | [Uniquify, Remove Complex Operands, Explicate Control](./lectures/Aug-31.md) | Ch. 2 | 135 | Sep. 2 | [Select Instructions, Assign Homes, Patch Instructions, Prelude & Conclusion](./lectures/Sep-2.md) | | 136 | Sep. 6 | | | [Integers and Variables](https://iu.instructure.com/courses/1996310/assignments/12582634), submit in [Racket](https://autograder.sice.indiana.edu/web/project/319) or [Python](https://autograder.sice.indiana.edu/web/project/320) 137 | Sep. 7 | [Register Allocation: liveness analysis, interference graph](./lectures/Sep-7.md) | | 138 | Sep. 9 | Code Review: Integers and Variables | Ch. 3 | 139 | Sep. 14 | [Register Allocation: graph coloring](./lectures/Sep-14.md) | | 140 | Sep. 16 | [The LIf language & type checking](./lectures/Sep-16.md) | | 141 | Sep. 20 | | | [Register Allocation](https://iu.instructure.com/courses/1996310/assignments/12689957), submit in [Racket](https://autograder.sice.indiana.edu/web/project/326) or [Python](https://autograder.sice.indiana.edu/web/project/327) 142 | Sep. 21 | Code Review: Register Allocation | Ch. 4 | 143 | Sep. 23 | [Compiling LIf to x86](./lectures/Sep-23.md) 144 | Sep. 28 | [Compiling LIf to x86, cont'd](./lectures/Sep-28.md) | 145 | Sep. 30 | [Loops and Dataflow Analysis](./lectures/Sep-30.md) 146 | Oct. 4 | | | [Booleans and Conditionals](https://iu.instructure.com/courses/1996310/assignments/12714002), submit in [Racket](https://autograder.sice.indiana.edu/web/project/330) and [Python](https://autograder.sice.indiana.edu/web/project/331) 147 | Oct. 5 | [More Dataflow Analysis](./lectures/Oct-5.md) | | 148 | Oct. 7 | Code Review: Conditionals 149 | Oct. 12 | [Loops: RCO, Explicate, Remove Jumps](./lectures/Oct-12.md) 150 | Oct. 14 | [Tuples and Garbage Collection](./lectures/Oct-14.md) 151 | Oct. 18 | | | [Loops and Dataflow Analysis](), submit in [Racket](https://autograder.sice.indiana.edu/web/project/335) or [Python](https://autograder.sice.indiana.edu/web/project/336) 152 | Oct. 19 | Review for Midterm 153 | Oct. 21 | **Midterm Exam** 154 | Oct. 26 | [Tuples and GC, cont'd](./lectures/Oct-26.md) 155 | Oct. 28 | [Tuples and GC, cont'd](./lectures/Oct-28.md) 156 | Nov. 2 | [Compiling Functions to x86](./lectures/Nov-2.md) 157 | Nov. 4 | [Compiling Functions, cont'd](./lectures/Nov-4.md) 158 | Nov. 8 | | | [Tuples and Garbage Collection](https://iu.instructure.com/courses/1996310/assignments/12803441), submit in [Racket](https://autograder.sice.indiana.edu/web/project/343) or [Python](https://autograder.sice.indiana.edu/web/project/344) 159 | Nov. 9 | [Example: Simple Call](./lectures/Nov-9.md) 160 | Nov. 11 | [Examples: Tail Calls, Many Parameters](./lectures/Nov-11.md) 161 | Nov. 16 | [Compiling Lambda](./lectures/Nov-16.md) 162 | Nov. 18 | [Examples of Closure Conversion](./lectures/Nov-18.md) 163 | Nov. 29 | | | [Functions](https://iu.instructure.com/courses/1996310/assignments/12844466), submit in [Racket](https://autograder.sice.indiana.edu/web/project/349) or [Python](https://autograder.sice.indiana.edu/web/project/350) 164 | Nov. 30 | Code Review: Functions 165 | Dec. 2 | [Dynamic Typing](./lectures/Dec-2.md) 166 | Dec. 7 | [Dynamic Typing, continued](./lectures/Dec-7.md) 167 | Dec. 9 | [Review for Final Exam](./lectures/Dec-9.md) 168 | Dec. 14 | **Final Exam** 12:35-2:35 p.m. in class 169 | 170 | 171 | **Resources:** 172 | 173 | * Lecture videos recorded from last year's [class](https://iucompilercourse.github.io/IU-P423-P523-E313-E513-Fall-2020/). 174 | * Github repository for the support code and test suites 175 | - for [Racket](https://github.com/IUCompilerCourse/public-student-support-code) 176 | - for [Python](https://github.com/IUCompilerCourse/python-student-support-code) 177 | * Web server for running the reference compiler on a program: [Racket version](https://homes.luddy.indiana.edu/classes/fall2021/engr/e513-jsiek/compiler.php) 178 | * [Racket Download](https://download.racket-lang.org/) 179 | * [Racket Documentation](https://docs.racket-lang.org/) 180 | * [Python 3.10 Download](https://www.python.org/downloads/release/python-3100rc1/) 181 | * [Python 3.10 Documentation](https://docs.python.org/3.10/) 182 | * [Python ast module declarations](https://github.com/python/typeshed/blob/master/stdlib/_ast.pyi) 183 | * [Notes on x86-64 programming](http://web.cecs.pdx.edu/~apt/cs491/x86-64.pdf) 184 | * [x86-64 Machine-Level Programming](https://www.cs.cmu.edu/~fp/courses/15411-f13/misc/asm64-handout.pdf) 185 | * [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) 186 | * [System V Application Binary Interface](https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf) 187 | * [Uniprocessor Garbage Collection Techniques](https://iu.instructure.com/courses/1735985/files/82131907/download?wrap=1) by Wilson. 188 | * [Fast and Effective Procedure Inlining](https://www.cs.indiana.edu/~dyb/pubs/inlining.pdf) by Waddell and Dybvig. 189 | 190 | 191 | **COVID Policies and Precautions** 192 | 193 | All students signed the Community Responsibility Acknowledgement 194 | (CRA). Your agreement to the public health measures in the CRA is a 195 | condition of physical presence on the campus this fall. Included in 196 | that commitment were the requirements to be vaccinated, for wearing 197 | masks in all IU buildings, and maintaining social distancing in all IU 198 | buildings. These are classroom requirements. 199 | 200 | These requirements are necessary for us to protect each other. 201 | 202 | Therefore, if a student is present in a class without a mask, the 203 | student will be asked to put on a mask and I will report the student 204 | to the Division of Student Affairs: Office of Student Conduct. 205 | 206 | * If a student refuses to put a mask on after being instructed to do 207 | so, the instructor may end the class immediately, and contact the 208 | Office of Student Conduct. Violation of the mask rule is a threat 209 | to public safety within the meaning of the Summary Suspension Policy. 210 | * If a student comes to class without a mask twice, the student’s 211 | final grade will be reduced by one letter (e.g., from an A to a B, 212 | for instance). 213 | * If the student comes to class without a mask three times, the 214 | student will be withdrawn from the class without refund of tuition 215 | and reported to the Office of Student Conduct. 216 | * If Student Conduct receives three cumulative reports from any 217 | combination of instructors or staff members that a student is not 218 | complying with the requirements of masking and physical distancing, 219 | the student will be summarily suspended from the university for the 220 | semester. 221 | 222 | 223 | **Bias-Based Incident Reporting.** 224 | 225 | Bias-based incident reports can be made by students, faculty and 226 | staff. Any act of discrimination or harassment based on race, 227 | ethnicity, religious affiliation, gender, gender identity, sexual 228 | orientation or disability can be reported through any of the options: 229 | 230 | 1) email biasincident@indiana.edu or incident@indiana.edu; 231 | 232 | 2) call the Dean of Students Office at (812) 855-8188 or 233 | 234 | 3) use the IU mobile App (m.iu.edu). Reports can be made anonymously. 235 | 236 | **Dean on Call.** 237 | 238 | The Dean of Students office provides support for students dealing with 239 | serious or emergency situations after 5 p.m. in which an immediate 240 | response is needed and which cannot wait until the next business 241 | day. Faculty or staff who are concerned about a student’s welfare 242 | should feel free to call the Dean on Call at (812) 856-7774. This 243 | number is not to be given to students or families but is for internal 244 | campus use only. If someone is in immediate danger or experiencing an 245 | emergency, call 911. 246 | 247 | **Boost.** 248 | 249 | Indiana University has developed an award-winning smartphone app to 250 | help students stay on top of their schoolwork in Canvas. The app is 251 | called “Boost,” it is available for free to all IU students, and it 252 | integrates with Canvas to provide reminders about deadlines and other 253 | helpful notifications. For more information, see 254 | https://kb.iu.edu/d/atud. 255 | 256 | **Counseling and Psychological Services.** 257 | 258 | CAPS has expanded their services. For information about the variety of 259 | services offered to students by CAPS visit: 260 | http://healthcenter.indiana.edu/counseling/index.shtml. 261 | 262 | 263 | **Disability Services for Students (DSS).** 264 | 265 | The process to establish accommodations for a student with a 266 | disability is a responsibility shared by the student and the DSS 267 | Office. Only DSS approved accommodations should be utilized in the 268 | classroom. After the student has met with DSS, it is the student’s 269 | responsibility to share their accommodations with the faculty 270 | member. For information about support services or accommodations 271 | available to students with disabilities and for the procedures to be 272 | followed by students and instructors, please visit: 273 | https://studentaffairs.indiana.edu/disability-services-students/. 274 | 275 | **Reporting Conduct and Student Wellness Concerns.** 276 | 277 | All members of the IU community including faculty and staff may report 278 | student conduct and wellness concerns to the Division of Student 279 | Affairs using an online form located at 280 | https://studentaffairs.indiana.edu/dean-students/student-concern/index.shtml. 281 | 282 | **Students needing additional financial or other assistance.** 283 | 284 | The Student Advocates Office (SAO) can help students work through 285 | personal and academic problems as well as financial difficulties and 286 | concerns. SAO also assists students working through grade appeals and 287 | withdrawals from all classes. SAO also has emergency funds for IU 288 | students experiencing emergency financial crisis 289 | https://studentaffairs.indiana.edu/student- advocates/. 290 | 291 | **Disruptive Students.** 292 | 293 | If instructors are confronted by threatening behaviors from students 294 | their first obligation is to insure the immediate safety of the 295 | classroom. When in doubt, call IU Police at 9-911 from any campus 296 | phone or call (812) 855-4111 from off-campus for immediate or 297 | emergency situations. You may also contact the Dean of Students Office 298 | at (812) 855-8188. For additional guidance in dealing with difficult 299 | student situations: 300 | https://ufc.iu.edu/doc/policies/disruptive-students.pdf. 301 | 302 | **Academic Misconduct.** 303 | 304 | If you suspect that a student has cheated, plagiarized or otherwise committed academic misconduct, refer to the Code of Student Rights, Responsibilities and Conduct: 305 | http://studentcode.iu.edu/. 306 | 307 | **Sexual Misconduct.** 308 | 309 | As your instructor, one of my responsibilities is to create a positive 310 | learning environment for all students. Title IX and IU’s Sexual 311 | Misconduct Policy prohibit sexual misconduct in any form, including 312 | sexual harassment, sexual assault, stalking, and dating and domestic 313 | violence. If you have experienced sexual misconduct, or know someone 314 | who has, the University can help. 315 | 316 | If you are seeking help and would like to speak to someone 317 | confidentially, you can make an appointment with: 318 | 319 | * The Sexual Assault Crisis Services (SACS) at (812) 855-8900 320 | (counseling services) 321 | 322 | * Confidential Victim Advocates (CVA) at (812) 856-2469 (advocacy and 323 | advice services) 324 | 325 | * IU Health Center at (812) 855-4011 (health and medical services) 326 | 327 | It is also important that you know that Title IX and University policy 328 | require me to share any information brought to my attention about 329 | potential sexual misconduct, with the campus Deputy Title IX 330 | Coordinator or IU’s Title IX Coordinator. In that event, those 331 | individuals will work to ensure that appropriate measures are taken 332 | and resources are made available. Protecting student privacy is of 333 | utmost concern, and information will only be shared with those that 334 | need to know to ensure the University can respond and assist. I 335 | encourage you to visit 336 | stopsexualviolence.iu.edu to learn more. 337 | -------------------------------------------------------------------------------- /index.orig: -------------------------------------------------------------------------------- 1 | ## Welcome to GitHub Pages 2 | 3 | You can use the [editor on GitHub](https://github.com/IUCompilerCourse/IU-Fall-2021/edit/gh-pages/index.md) to maintain and preview the content for your website in Markdown files. 4 | 5 | Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. 6 | 7 | ### Markdown 8 | 9 | Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for 10 | 11 | ```markdown 12 | Syntax highlighted code block 13 | 14 | # Header 1 15 | ## Header 2 16 | ### Header 3 17 | 18 | - Bulleted 19 | - List 20 | 21 | 1. Numbered 22 | 2. List 23 | 24 | **Bold** and _Italic_ and `Code` text 25 | 26 | [Link](url) and ![Image](src) 27 | ``` 28 | 29 | For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/). 30 | 31 | ### Jekyll Themes 32 | 33 | Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/IUCompilerCourse/IU-Fall-2021/settings/pages). The name of this theme is saved in the Jekyll `_config.yml` configuration file. 34 | 35 | ### Support or Contact 36 | 37 | Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out. 38 | -------------------------------------------------------------------------------- /lectures/Aug-24.md: -------------------------------------------------------------------------------- 1 | # August 24 2 | 3 | Welcome to Compilers! (P423, P523, E313, E513) 4 | 5 | ## Instructor: Jeremy 6 | 7 | ## What's a compiler? 8 | 9 | ## Table of Contents of Essentials of Compilation 10 | 11 | ## Assignments, Quizzes, Exams, Grading, Academic Integrity 12 | 13 | ## Technology 14 | 15 | * Canvas FA21: COMPILERS: 18429 16 | Link to real course web page 17 | Grades 18 | 19 | * Web page: https://iucompilercourse.github.io/IU-Fall-2021/ 20 | 21 | * Communication: Slack http://iu-compiler-course.slack.com/ 22 | 23 | * Autograder: https://autograder.sice.indiana.edu/web/course/28 24 | 25 | ## Concrete Syntax, Abstract Syntax Trees (AST) 26 | 27 | * Programs in concrete syntax (Racket/Python) 28 | 29 | 42 42 30 | 31 | (read) input_int() 32 | 33 | (- 10) -10 34 | 35 | (+ (- 10) 5) -10 + 5 36 | 37 | (+ (read) (- (read))) input_int() + -input_int() 38 | 39 | * Racket structures for AST 40 | 41 | (struct Int (value)) 42 | (struct Prim (op arg*)) 43 | 44 | * Python classes for AST 45 | 46 | [Python ast module](https://docs.python.org/3.10/library/ast.html) 47 | 48 | [ast module type declarations](https://github.com/python/typeshed/blob/master/stdlib/_ast.pyi) 49 | 50 | class Constant(expr): 51 | value: Any 52 | 53 | class BinOp(expr): 54 | left: expr 55 | op: operator 56 | right: expr 57 | 58 | class UnaryOp(expr): 59 | op: unaryop 60 | operand: expr 61 | 62 | * Grammars 63 | * Concrete syntax 64 | 65 | Racket style: 66 | 67 | exp ::= int | "(read)" | "(-" exp ")" | "(+" exp exp ")" 68 | exp ::= int | (read) | (- ) | (+ ) 69 | L_Int ::= exp 70 | 71 | Python style: 72 | 73 | exp ::= int | input_int() | - exp | exp + exp 74 | stmt ::= print(exp) | exp 75 | L_Int ::= stmt* 76 | 77 | * Abstract syntax 78 | 79 | Racket: 80 | 81 | exp ::= (Int int) | (Prim 'read '()) 82 | | (Prim '- (list exp)) 83 | | (Prim '+ (list exp exp)) 84 | L_Int ::= (Program '() exp) 85 | 86 | Python: 87 | 88 | exp ::= Constant(int) | Call(Name('input_int'), []) 89 | | UnaryOp(USub(), exp) 90 | | BinOp(exp, Add(), exp) 91 | stmt ::= Expr(Call(Name('print'), [exp])) | Expr(exp) 92 | L_Int ::= Module(stmt*) 93 | 94 | -------------------------------------------------------------------------------- /lectures/Aug-26.md: -------------------------------------------------------------------------------- 1 | # August 26 2 | 3 | ## Teams 4 | 5 | ## Pattern Matching and Structural Recursion 6 | 7 | Examples: 8 | 9 | * [`L_Int_height.rkt`](./L_Int_height.rkt) 10 | 11 | * [`L_Int_height.py`](./L_Int_height.py) 12 | 13 | ## Definitional Interpreters for L_Int 14 | 15 | [`interp-Rint.rkt`](./interp-Rint.rkt) 16 | 17 | [`Rint-interp-example.rkt`](./Rint-interp-example.rkt) 18 | 19 | [`interp_Pint.py`](./interp_Pint.py) 20 | 21 | 22 | ## The L_Var Language: L_Int + variables and let 23 | 24 | Racket version: 25 | 26 | exp ::= int | (read) | (- exp) | (+ exp exp) 27 | | var | (let ([var exp]) exp) 28 | L_Var ::= exp 29 | 30 | Python version: 31 | 32 | exp ::= int | input_int() | - exp | exp + exp | var 33 | stmt ::= print(exp) | exp | var = exp 34 | L_Var ::= stmt* 35 | 36 | Racket examples: 37 | 38 | (let ([x (+ 12 20)]) 39 | (+ 10 x)) 40 | 41 | (let ([x 32]) 42 | (+ (let ([x 10]) x) 43 | x)) 44 | 45 | Python examples: 46 | 47 | x = 12 + 20 48 | print(10 + x) 49 | 50 | x = 33 51 | x = 10 52 | print(x + x) 53 | 54 | Racket Interpreter for L_Var: [`interp-Rvar.rkt`](./interp-Rvar.rkt) 55 | 56 | Python Interpreter for L_Var: [`interp_Pvar.rkt`](./interp_Pvar.rkt) 57 | 58 | ## x86 Assembly Language 59 | 60 | reg ::= rsp | rbp | rsi | rdi | rax | .. | rdx | r8 | ... | r15 61 | arg ::= $int | %reg | int(%reg) 62 | instr ::= addq arg, arg | 63 | subq arg, arg | 64 | negq arg | 65 | movq arg, arg | 66 | callq label | 67 | pushq arg | 68 | popq arg | 69 | retq 70 | prog ::= .globl main 71 | main: instr^{+} 72 | 73 | 74 | Intel Machine: 75 | * program counter 76 | * registers 77 | * memory (stack and heap) 78 | 79 | -------------------------------------------------------------------------------- /lectures/Aug-31.md: -------------------------------------------------------------------------------- 1 | # Lecture: Compiling L_Var, Uniquify, Remove Complex, Explicate Control 2 | 3 | ## x86 Assembly Language 4 | 5 | reg ::= rsp | rbp | rsi | rdi | rax | .. | rdx | r8 | ... | r15 6 | arg ::= $int | %reg | int(%reg) 7 | instr ::= addq arg, arg | 8 | subq arg, arg | 9 | negq arg | 10 | movq arg, arg | 11 | callq label | 12 | pushq arg | 13 | popq arg | 14 | retq 15 | prog ::= .globl main 16 | main: instr^{+} 17 | 18 | 19 | Intel Machine: 20 | * program counter 21 | * registers 22 | * memory (stack and heap) 23 | 24 | Example compilation of a Racket/Python program: 25 | 26 | (+ 10 32) 10 + 32 27 | 28 | => 29 | 30 | .globl main 31 | main: 32 | movq $10, %rax 33 | addq $32, %rax 34 | movq %rax, %rdi 35 | callq print_int 36 | movq $0, %rax 37 | retq 38 | 39 | 40 | ## What's different? 41 | 42 | 1. 2 args and return value vs. 2 arguments with in-place update 43 | 2. nested expressions vs. atomic expressions 44 | 3. order of evaluation: left-to-right depth-first, vs. sequential 45 | 4. unbounded number of variables vs. registers + memory 46 | 5. variables can overshadow vs. uniquely named registers + memory 47 | 48 | * `select_instructions`: convert each L_Var operation into 49 | one or more instructions 50 | * `remove_complex_opera*`: ensure that each sub-expression is 51 | atomic by introducing temporary variables 52 | * `explicate_control`: convert from the AST to basic blocks with jumps 53 | * `assign_homes`: replace variables with stack locations 54 | * `uniquify`: rename variables so they are all unique 55 | 56 | 57 | In what order should we do these passes? 58 | 59 | Gordian Knot: 60 | * instruction selection 61 | * register/stack allocation 62 | 63 | Doing instruction selection first enables improved register 64 | allocation. For example, instruction selection reveals which function 65 | parameters live in which registers. 66 | 67 | On the other hand, we should do instruction selection after register 68 | allocation because register allocation may fail to put some variables 69 | in registers, and x86 instructions may each only access one memory 70 | location (non-register), so the compiler must choose different 71 | instruction sequences depending on the register allocation. 72 | 73 | solution: 74 | 1. do instruction selection optimistically, assuming all 75 | variables are assigned to registers 76 | 2. then do register allocation 77 | 3. then patch up the instructions using a reserved register (rax) 78 | 79 | ## Overview of the Passes for Racket and Python L_Var compilers 80 | 81 | L_Var L_Var 82 | | uniquify | remove complex operands 83 | V V 84 | L_Var L_Var 85 | | remove complex operands | select instructions 86 | V V 87 | L_Var x86_Var 88 | | explicate control | assign homes 89 | V V 90 | C_Var x86* 91 | | select instructions | patch instructions 92 | V V 93 | x86_Var x86* 94 | | assign homes | prelude & conclusion 95 | V V 96 | x86* x86 97 | | patch instructions 98 | V 99 | x86* 100 | | prelude & conclusion 101 | V 102 | x86 103 | 104 | 105 | Uniquify (Racket only) 106 | ---------------------- 107 | 108 | This pass gives a unique name to every variable, so that variable 109 | shadowing and scope are no longer important. 110 | 111 | We recommend using `gensym` to generate a new name for each variable 112 | bound by a `let` expression. 113 | 114 | To update variable occurences to match the new names, use an 115 | association list to map the old names to the new names, extending this 116 | map in the case for `let` and doing a lookup in the case for 117 | variables. 118 | 119 | Examples: 120 | 121 | (let ([x 32]) 122 | (let ([y 10]) 123 | (+ x y))) 124 | => 125 | (let ([x.1 32]) 126 | (let ([y.2 10]) 127 | (+ x.1 y.2))) 128 | 129 | 130 | (let ([x 32]) 131 | (+ (let ([x 10]) x) x)) 132 | => 133 | (let ([x.1 32]) 134 | (+ (let ([x.2 10]) x.2) x.1)) 135 | 136 | 137 | Remove Complex Operators and Operands 138 | ------------------------------------- 139 | 140 | This pass makes sure that the arguments of each operation are atomic 141 | expressions, that is, variables or integer constants. The pass 142 | accomplishes this goal by inserting temporary variables to replace the 143 | non-atomic expressions with variables. 144 | 145 | Racket examples: 146 | 147 | (+ (+ 42 10) (- 10)) 148 | => 149 | (let ([tmp.1 (+ 42 10)]) 150 | (let ([tmp.2 (- 10)]) 151 | (+ tmp.1 tmp.2))) 152 | 153 | 154 | (let ([a 42]) 155 | (let ([b a]) 156 | b)) 157 | => 158 | (let ([a 42]) 159 | (let ([b a]) 160 | b)) 161 | 162 | and not 163 | 164 | (let ([tmp.1 42]) 165 | (let ([a tmp.1]) 166 | (let ([tmp.2 a]) 167 | (let ([b tmp.2]) 168 | b)))) 169 | 170 | Python example: 171 | 172 | y = 10 173 | x = 42 + -y 174 | print(x + 10) 175 | => 176 | y = 10 177 | tmp_0 = -y 178 | x = 42 + tmp_0 179 | tmp_1 = x + 10 180 | print(tmp_1) 181 | 182 | 183 | Grammar of the Racket output: 184 | 185 | atm ::= var | int 186 | exp ::= atm | (read) | (- atm) | (+ atm atm) 187 | | (let ([var exp]) exp) 188 | L^ANF_Var ::= exp 189 | 190 | Grammar of the Python output 191 | 192 | atm ::= Constant(int) | Name(var) 193 | exp ::= atm | Call(Name('input_int'), []) 194 | | UnaryOp(USub(), atm) | BinOp(atm, Add(), atm) 195 | stmt ::= Expr(Call(Name('print'),[atm])) | Expr(exp) | Assign([Name(var)], exp) 196 | L^ANF_Var ::= Module(stmt*) 197 | 198 | Recommended function organization: 199 | 200 | rco_atom : exp -> (Pair atm (Listof (Pair var exp))) 201 | rco_exp : exp -> exp 202 | remove-complex-opera* : L_Var -> L^ANF_Var 203 | 204 | Inside `rco_atom` and `rco_exp`, for recursive calls, use `rco_atom` 205 | when you need the result to be an atom and use `rco_exp` when you 206 | don't care. 207 | 208 | Alternatively, omit `rco_atom` but add a `need_atomic` parameter to `rco_exp`. 209 | Here's the Python version 210 | 211 | Binding = Tuple[Name, expr] 212 | Temporaries = List[Binding] 213 | 214 | def rco_exp(self, e: expr, need_atomic: bool) -> Tuple[expr, Temporaries] 215 | def rco_stmt(self, s: stmt) -> List[stmt] 216 | def remove_complex_operands(self, p: Module) -> Module 217 | 218 | 219 | ## Explicate Control (Racket only) 220 | 221 | This pass makes the order of evaluation simple and explicit in the 222 | syntax. For now, this means flattening `let` into a sequence of 223 | assignment statements. 224 | 225 | The target of this pass is the C_Var language. 226 | Here is the grammar for C_Var. 227 | 228 | atm ::= int | var 229 | exp ::= atm | (read) | (- atm) | (+ atm atm) 230 | stmt ::= var = exp; 231 | tail ::= return exp; | stmt tail 232 | C_Var ::= (label: tail)^+ 233 | 234 | Example: 235 | 236 | (let ([x (let ([y (- 42)]) 237 | y)]) 238 | (- x)) 239 | => 240 | locals: 241 | '(x y) 242 | start: 243 | y = (- 42); 244 | x = y; 245 | return (- x); 246 | 247 | Aside regarding **tail position**. Here is the grammar for L^ANF_Var again 248 | but splitting the exp non-terminal into two, one for `tail` position 249 | and one for not-tail `nt` position. 250 | 251 | atm ::= var | int 252 | nt ::= atm | (read) | (- atm) | (+ atm atm) 253 | | (let ([var nt]) nt) 254 | tail ::= atm | (read) | (- atm) | (+ atm atm) 255 | | (let ([var nt]) tail) 256 | L^ANF_Var' ::= tail 257 | 258 | Recommended function organization: 259 | 260 | explicate-tail : exp -> (Pair tail (Listof var)) 261 | 262 | explicate-assign : exp -> var -> tail -> (Pair tail (Listof var)) 263 | 264 | The `explicate-tail` function takes and L^ANF_Var expression in tail position 265 | and returns a C_Var tail and a list of variables that use to be let-bound 266 | in the expression. This list of variables is then stored in the `info` 267 | field of the `Program` node. 268 | 269 | The `explicate-assign` function takes 1) an R1 expression that is not 270 | in tail position, that is, the right-hand side of a `let`, 2) the 271 | `let`-bound variable, and 3) the C0 tail for the body of the `let`. 272 | The output of `explicate-assign` is a C0 tail and a list of variables 273 | that were let-bound. 274 | 275 | Here's a trace of these two functions on the above example. 276 | 277 | explicate-tail (let ([x (let ([y (- 42)]) y)]) (- x)) 278 | explicate-tail (- x) 279 | => {return (- x);}, () 280 | explicate-assign (let ([y (- 42)]) y) x {return (- x);} 281 | explicate-assign y x {return (- x);} 282 | => {x = y; return (- x)}, () 283 | explicate-assign (- 42) y {x = y; return (- x);} 284 | => {y = (- 42); x = y; return (- x);}, () 285 | => {y = (- 42); x = y; return (- x);}, (y) 286 | => {y = (- 42); x = y; return (- x);}, (x y) 287 | -------------------------------------------------------------------------------- /lectures/Dec-2.md: -------------------------------------------------------------------------------- 1 | # Lecture: Dynamic Typing 2 | 3 | We'll implement a dynamically-typed language Ldyn, a subset of 4 | Racket/Python. 5 | 6 | Example Ldyn program 7 | 8 | (not (if (eq? (read) 1) #f 0)) 9 | 10 | not (False if input_int() == 1 else 0) 11 | 12 | We'll implement the compiler for Ldyn in two stages. 13 | 14 | 1. Extend our typed language with a new type `Any` that is equiped 15 | with the operations `inject` and `project` that convert a value 16 | of any other type to `Any` and back again. This language is Lany. 17 | 18 | (let ([x (inject (Int 42) Integer)]) 19 | (project x Integer)) ;; result is 42 20 | 21 | (let ([x (inject (Bool #t) Boolean)]) 22 | (project x Integer)) ;; error! 23 | 24 | 2. Create a new pass (at the beginning) that translates from Ldyn to Lany 25 | that uses `Any` as the type for just about everying and that 26 | inserts `inject` and `project` in lots of places. 27 | 28 | 29 | 30 | ## The Lany Language 31 | 32 | type ::= ... | Any 33 | ftype ::= Integer | Boolean | (Vector Any ...) | (Vectorof Any) 34 | | (Any ... -> Any) 35 | exp ::= ... | (inject exp ftype) | (project exp ftype) | 36 | | (boolean? exp) | (integer? exp) | (vector? exp) 37 | | (procedure? exp) | (void? exp) 38 | 39 | The `Vectorof` type is for homogeneous vectors of arbitrary length. 40 | That is, their elements are all of the same type and the length is 41 | determined at runtime. 42 | 43 | * type checking Lany 44 | 45 | * interpreting Lany 46 | 47 | Another example: 48 | 49 | (let ([v (inject (vector (inject 42 Integer)) 50 | (Vector Any))]) 51 | (let ([w (project v (Vector Any))]) 52 | (let ([x (vector-ref w 0)]) 53 | (project x Integer)))) 54 | 55 | 56 | 57 | ## Compiling Lany 58 | 59 | The runtime representation of a value of type `Any` is a 64 bit value 60 | whose 3 least-significant bits (right-most) encode the runtime type, 61 | which we call a *tag*. 62 | 63 | tagof(Integer) = 001 64 | tagof(Boolean) = 100 65 | tagof((Vector ...)) = 010 66 | tagof((Vectorof ...)) = 010 67 | tagof((... -> ...)) = 011 68 | tagof(Void) = 101 69 | 70 | If the value is an integer or Boolean, then the other 61 bits store 71 | that value. (Shifted by 3.) 72 | 73 | If the value is a vector or function, then the 64 bits is an 74 | address. All our values are 8-byte aligned, so we don't need the 75 | bottom 3 bits. To obtain the address from an `Any` value, just write 76 | 000 to the rightmost 3 bits. 77 | 78 | ## Shrink 79 | 80 | * Compiling `Project` to `tag-of-any`, `value-of-any`, and `exit`. 81 | 82 | If `ty` is `Boolean` or `Integer`: 83 | 84 | (project e ty) 85 | ===> 86 | (let ([tmp e]) 87 | (if (eq? (tag-of-any tmp) tag) 88 | (value-of-any tmp ty) 89 | (exit)))) 90 | 91 | where tag is tagof(ty) 92 | 93 | If `ty` is a function or vector (e.g. `(Vector Integer Boolean)`), 94 | you also need to check the vector 95 | length or procedure arity. Those two operations be added as two new 96 | primitives. Use the primitives: `vector-length`, `procedure-arity`. 97 | 98 | 99 | * Compile `Inject` to `make-any` 100 | 101 | (inject e ty) 102 | ===> 103 | (make-any e tag) 104 | 105 | where tag is the result of tagof(ty) 106 | 107 | * Abstract syntax for the new forms: 108 | 109 | exp ::= ... | (Prim 'tag-of-any (list exp)) 110 | | (Prim 'make-any (list exp (Int tag))) 111 | | (ValueOf exp type) 112 | | (Exit) 113 | 114 | 115 | ## Check Bounds (missing from book) 116 | 117 | Adapt `type-check-Lany` by changing the cases for `vector-ref` and 118 | `vector-set!` when the vector argument has type `Vectorof T`. 119 | 120 | (vector-ref e1 e2) 121 | ===> 122 | (let ([v e1']) 123 | (let ([i e2']) 124 | (if (< i (vector-length v)) 125 | (vector-ref v i) 126 | (exit)))) 127 | 128 | (vector-set! e1 e2 e3) 129 | ===> 130 | (let ([v e1']) 131 | (let ([i e2']) 132 | (if (< i (vector-length v)) 133 | (vector-set! v i e3') 134 | (exit)))) 135 | 136 | ## Reveal Functions 137 | 138 | Old way: 139 | 140 | (Var f) 141 | ===> 142 | (FunRef f) 143 | 144 | To support `procedure-arity`, we'll need to record the arity of a 145 | function in `FunRefArity`. 146 | 147 | (Var f) 148 | ===> 149 | (FunRefArity f n) 150 | 151 | Which means when processing the `ProgramDefs` form, we need to build 152 | an alist mapping function names to their arity. 153 | 154 | ## Closure Convertion 155 | 156 | To support `procedure-arity`, we use a special purpose 157 | `Closure` form instead of the primitive `vector`, 158 | both in the case for `Lambda` and `FunRefArity`. 159 | 160 | ## Expose Allocation 161 | 162 | Add a case for `Closure` that is similar to the one for `vector` 163 | except that it uses `AllocateClosure` instead of `Allocate`, so that 164 | it can pass along the arity. 165 | 166 | ## Remove Complex Operands 167 | 168 | Add case for `AllocateClosure`. 169 | 170 | ## Explicate Control 171 | 172 | Add case for `AllocateClosure`. 173 | 174 | ## Instruction Selection 175 | 176 | * `(Prim 'make-any (list e (Int tag)))` 177 | 178 | For tag of an Integer or Boolean: (Void too?) 179 | 180 | (Assign lhs (Prim 'make-any (list e (Int tag))) 181 | ===> 182 | movq e', lhs' 183 | salq $3, lhs' 184 | orq tag, lhs' 185 | 186 | where `3` is the length of the tag. 187 | 188 | For other types (vectors and functions): 189 | 190 | (Assign lhs (Prim 'make-any (list e (Int tag)))) 191 | ===> 192 | movq e', lhs' 193 | orq tag, lhs' 194 | 195 | * `(Prim 'tag-of-any (list e))` 196 | 197 | (Assign lhs (Prim 'tag-of-any (list e))) 198 | ===> 199 | movq e', lhs 200 | andq $7, lhs 201 | 202 | where `7` is the binary number `111`. 203 | 204 | * `(ValueOf e ty)` 205 | 206 | If `ty` is an Integer, Boolean, Void: 207 | 208 | (Assign lhs (ValueOf e ty)) 209 | ==> 210 | movq e', lhs' 211 | sarq $3, lhs 212 | 213 | where `3` is the length of the tag. 214 | 215 | If `ty` is a vector or procedure (a pointer): 216 | 217 | (Assign lhs (ValueOf e ty)) 218 | ==> 219 | movq $-8, lhs 220 | andq e', lhs 221 | 222 | where -8 is `(bitwise-not (string->number "#b111"))` 223 | 224 | 225 | ## Compiling Lany, Instruction Selection, continued 226 | 227 | * `(Exit)` 228 | 229 | (Assign lhs (Exit)) 230 | ===> 231 | movq $-1, %rdi 232 | callq exit 233 | 234 | * `(Assign lhs (AllocateClosure len ty arity))` 235 | 236 | Treat this just like `Allocate` except that you'll put 237 | the `arity` into the tag at the front of the vector. 238 | Use bits 57 and higher for the arity. 239 | 240 | [(Assign lhs (AllocateClosure len `(Vector ,ts ...) arity)) 241 | (define lhs^ (select-instr-arg lhs)) 242 | ;; Add one quad word for the meta info tag 243 | (define size (* (add1 len) 8)) 244 | ;;highest 7 bits are unused 245 | ;;lowest 1 bit is 1 saying this is not a forwarding pointer 246 | (define is-not-forward-tag 1) 247 | ;;next 6 lowest bits are the length 248 | (define length-tag (arithmetic-shift len 1)) 249 | ;;bits [6,56] are a bitmask indicating if [0,50] are pointers 250 | (define ptr-tag 251 | (for/fold ([tag 0]) ([t (in-list ts)] [i (in-naturals 7)]) 252 | (bitwise-ior tag (arithmetic-shift (b2i (root-type? t)) i)))) 253 | (define arity-tag ...) 254 | ;; Combine the tags into a single quad word 255 | (define tag (bitwise-ior arity-tag ptr-tag length-tag is-not-forward-tag)) 256 | (list (Instr 'movq (list (Global 'free_ptr) (Reg tmp-reg))) 257 | (Instr 'addq (list (Imm size) (Global 'free_ptr))) 258 | (Instr 'movq (list (Imm tag) (Deref tmp-reg 0))) 259 | (Instr 'movq (list (Reg tmp-reg) lhs^)) 260 | ) 261 | ] 262 | 263 | * `(Assign lhs (Prim 'procedure-arity (list e)))` 264 | 265 | Extract the arity from the tag of the vector. 266 | 267 | (Assign lhs (Prim 'procedure-arity (list e))) 268 | ===> 269 | movq e', %r11 270 | movq 0(%r11), %r11 271 | sarq $57, %r11 272 | movq %r11, lhs' 273 | 274 | * `(Assign lhs (Prim 'vector-length (list e)))` 275 | 276 | Extract the length from the tag of the vector. 277 | 278 | (Assign lhs (Prim 'vector-length (list e))) 279 | ===> 280 | movq e', %r11 281 | movq 0(%r11), %r11 282 | andq $126, %r11 // 1111110 283 | sarq $1, %r11 284 | movq %r11, lhs' 285 | 286 | 287 | ## `Vectorof`, `vector-ref`, and `vector-set!` 288 | 289 | The type checker for Lany treats vector operations differently 290 | if the vector is of type `(Vectorof T)`. 291 | The index can be an arbitrary expression, e.g. 292 | suppose `vec` has type `(Vectorof T)`. Then 293 | the index could be `(read)` 294 | 295 | ;; vec1 : (Vector Any Any) 296 | (let ([vec1 (vector (inject 1 Integer) (inject 2 Integer))]) 297 | (let ([vec2 (inject vec1 (Vector Any Any))]) ;; vec2 : Any 298 | (let ([vec3 (project vec2 (Vectorof Any))]) ;; vec3 : (Vectorof Any) 299 | (vector-ref vec3 (read))))) 300 | 301 | and the type of `(vector-ref vec (read))` is `T`. 302 | 303 | Recall instruction selection for `vector-ref`: 304 | 305 | (Assign lhs (Prim 'vector-ref (list evec (Int n)))) 306 | ===> 307 | movq evec', %r11 308 | movq offset(%r11), lhs' 309 | 310 | where offset is 8(n+1) 311 | 312 | If the index is not of the form `(Int i)`, but an arbitrary 313 | expression, then instead of computing the offset `8(n+1)` at compile 314 | time, you can generate the following instructions. Note the use of the 315 | new instruction `imulq`. 316 | 317 | (Assign lhs (Prim 'vector-ref (list evec en))) 318 | ===> 319 | movq en', %r11 320 | addq $1, %r11 321 | imulq $8, %r11 322 | addq evec', %r11 323 | movq 0(%r11) lhs' 324 | 325 | The same idea applies to `vector-set!`. 326 | 327 | 328 | # The Ldyn Language: Mini Racket (Dynamically Typed) 329 | 330 | exp ::= int | (read) | ... | (lambda (var ...) exp) 331 | | (vector-ref exp exp) | (vector-set! exp exp exp) 332 | def ::= (define (var var ...) exp) 333 | Ldyn ::= def... exp 334 | 335 | # Compiling Ldyn to Lany by cast insertion 336 | 337 | The main invariant is that every subexpression that we generate should 338 | have type `Any`, which we accomplish by using `inject`. 339 | 340 | To perform an operation on a value of type `Any`, we `project` it to 341 | the appropriate type for the operation. 342 | 343 | Example: 344 | Ldyn: 345 | 346 | (+ #t 42) 347 | 348 | Lany: 349 | 350 | (inject 351 | (+ (project (inject #t Boolean) Integer) 352 | (project (inject 42 Integer) Integer)) 353 | Integer) 354 | ===> 355 | x86 code 356 | 357 | 358 | Booleans: 359 | 360 | #t 361 | ===> 362 | (inject #t Boolean) 363 | 364 | Integer: 365 | 366 | 42 367 | ===> 368 | (inject 42 Integer) 369 | 370 | Arithmetic: 371 | 372 | (+ e_1 e_2) 373 | ==> 374 | (inject 375 | (+ (project e'_1 Integer) 376 | (project e'_2 Integer)) 377 | Integer) 378 | 379 | Variables: 380 | 381 | x 382 | ===> 383 | x 384 | 385 | Lambda: 386 | 387 | (lambda (x_1 ... x_n) e) 388 | ===> 389 | (inject (lambda: ([x_1 : Any] ... [x_n : Any]) : Any e') 390 | (Any ... Any -> Any)) 391 | 392 | example: 393 | 394 | (lambda (x y) (+ x y)) 395 | ===> 396 | (inject (lambda: ([x : Any] [y : Any]) : Any 397 | (inject (+ (project x Integer) (project y Integer)) Integer)) 398 | (Any Any -> Any)) 399 | 400 | Application: 401 | 402 | (e_0 e_1 ... e_n) 403 | ===> 404 | ((project e'_0 (Any ... Any -> Any)) e'_1 ... e'_n) 405 | 406 | Vector Reference: 407 | 408 | (vector-ref e_1 e_2) 409 | ===> 410 | (vector-ref (project e'_1 (Vectorof Any)) 411 | (project e'_2 Integer)) 412 | 413 | 414 | Vector: 415 | 416 | (vector e1 ... en) 417 | ===> 418 | (inject 419 | (vector e1' ... en') 420 | (Vector Any .... Any)) 421 | 422 | Ldyn: 423 | (vector 1 #t) heterogeneous 424 | 425 | (inject (vector (inject 1 Integer) (inject #t Boolean)) 426 | (Vector Any Any)) : Any 427 | 428 | Lany: (Vector Int Bool) heterogeneous 429 | (Vectorof Int) homogeneous 430 | 431 | actually see: 432 | 433 | (Vector Any Any) 434 | (Vectorof Any) 435 | 436 | 437 | -------------------------------------------------------------------------------- /lectures/Dec-7.md: -------------------------------------------------------------------------------- 1 | # Lecture: Dynamic Typing Continued 2 | 3 | # The Ldyn Language: Mini Racket (Dynamically Typed) 4 | 5 | exp ::= int | (read) | ... | (lambda (var ...) exp) 6 | | (vector-ref exp exp) | (vector-set! exp exp exp) 7 | def ::= (define (var var ...) exp) 8 | Ldyn ::= def... exp 9 | 10 | # Compiling Ldyn to Lany by cast insertion 11 | 12 | The main invariant is that every subexpression that we generate should 13 | have type `Any`, which we accomplish by using `inject`. 14 | 15 | To perform an operation on a value of type `Any`, we `project` it to 16 | the appropriate type for the operation. 17 | 18 | Example: 19 | Ldyn: 20 | 21 | (+ #t 42) 22 | 23 | Lany: 24 | 25 | (inject 26 | (+ (project (inject #t Boolean) Integer) 27 | (project (inject 42 Integer) Integer)) 28 | Integer) 29 | ===> 30 | x86 code 31 | 32 | 33 | Booleans: 34 | 35 | #t 36 | ===> 37 | (inject #t Boolean) 38 | 39 | Integer: 40 | 41 | 42 42 | ===> 43 | (inject 42 Integer) 44 | 45 | Arithmetic: 46 | 47 | (+ e_1 e_2) 48 | ==> 49 | (inject 50 | (+ (project e'_1 Integer) 51 | (project e'_2 Integer)) 52 | Integer) 53 | 54 | Variables: 55 | 56 | x 57 | ===> 58 | x 59 | 60 | Lambda: 61 | 62 | (lambda (x_1 ... x_n) e) 63 | ===> 64 | (inject (lambda: ([x_1 : Any] ... [x_n : Any]) : Any e') 65 | (Any ... Any -> Any)) 66 | 67 | example: 68 | 69 | (lambda (x y) (+ x y)) 70 | ===> 71 | (inject (lambda: ([x : Any] [y : Any]) : Any 72 | (inject (+ (project x Integer) (project y Integer)) Integer)) 73 | (Any Any -> Any)) 74 | 75 | Application: 76 | 77 | (e_0 e_1 ... e_n) 78 | ===> 79 | ((project e'_0 (Any ... Any -> Any)) e'_1 ... e'_n) 80 | 81 | Vector Reference: 82 | 83 | (vector-ref e_1 e_2) 84 | ===> 85 | (vector-ref (project e'_1 (Vectorof Any)) 86 | (project e'_2 Integer)) 87 | 88 | 89 | Vector: 90 | 91 | (vector e1 ... en) 92 | ===> 93 | (inject 94 | (vector e1' ... en') 95 | (Vector Any .... Any)) 96 | 97 | Ldyn: 98 | (vector 1 #t) heterogeneous 99 | 100 | (inject (vector (inject 1 Integer) (inject #t Boolean)) 101 | (Vector Any Any)) : Any 102 | 103 | Lany: (Vector Int Bool) heterogeneous 104 | (Vectorof Int) homogeneous 105 | 106 | actually see: 107 | 108 | (Vector Any Any) 109 | (Vectorof Any) 110 | 111 | 112 | 113 | ## Instruction Selection 114 | 115 | * `(Prim 'make-any (list e (Int tag)))` 116 | 117 | For tag of an Integer or Boolean 118 | 119 | (Assign lhs (Prim 'make-any (list e (Int tag))) 120 | ===> 121 | movq e', lhs' 122 | salq $3, lhs' 123 | orq tag, lhs' 124 | 125 | where `3` is the length of the tag. 126 | 127 | For other types (vectors and functions): 128 | 129 | (Assign lhs (Prim 'make-any (list e (Int tag)))) 130 | ===> 131 | movq e', lhs' 132 | orq tag, lhs' 133 | 134 | * `(Prim 'tag-of-any (list e))` 135 | 136 | (Assign lhs (Prim 'tag-of-any (list e))) 137 | ===> 138 | movq e', lhs 139 | andq $7, lhs 140 | 141 | where `7` is the binary number `111`. 142 | 143 | * `(ValueOf e ty)` 144 | 145 | If `ty` is an Integer, Boolean, Void: 146 | 147 | (Assign lhs (ValueOf e ty)) 148 | ==> 149 | movq e', lhs' 150 | sarq $3, lhs 151 | 152 | where `3` is the length of the tag. 153 | 154 | If `ty` is a vector or procedure (a pointer): 155 | 156 | (Assign lhs (ValueOf e ty)) 157 | ==> 158 | movq $-8, lhs 159 | andq e', lhs 160 | 161 | where -8 is `(bitwise-not (string->number "#b111"))` 162 | 163 | * `(Exit)` 164 | 165 | (Assign lhs (Exit)) 166 | ===> 167 | movq $-1, %rdi 168 | callq exit 169 | 170 | * `(Assign lhs (AllocateClosure len ty arity))` 171 | 172 | Treat this just like `Allocate` except that you'll put 173 | the `arity` into the tag at the front of the vector. 174 | Use bits 57 and higher for the arity. 175 | 176 | [(Assign lhs (AllocateClosure len `(Vector ,ts ...) arity)) 177 | (define lhs^ (select-instr-arg lhs)) 178 | ;; Add one quad word for the meta info tag 179 | (define size (* (add1 len) 8)) 180 | ;;highest 7 bits are unused 181 | ;;lowest 1 bit is 1 saying this is not a forwarding pointer 182 | (define is-not-forward-tag 1) 183 | ;;next 6 lowest bits are the length 184 | (define length-tag (arithmetic-shift len 1)) 185 | ;;bits [6,56] are a bitmask indicating if [0,50] are pointers 186 | (define ptr-tag 187 | (for/fold ([tag 0]) ([t (in-list ts)] [i (in-naturals 7)]) 188 | (bitwise-ior tag (arithmetic-shift (b2i (root-type? t)) i)))) 189 | (define arity-tag ...) 190 | ;; Combine the tags into a single quad word 191 | (define tag (bitwise-ior arity-tag ptr-tag length-tag is-not-forward-tag)) 192 | (list (Instr 'movq (list (Global 'free_ptr) (Reg tmp-reg))) 193 | (Instr 'addq (list (Imm size) (Global 'free_ptr))) 194 | (Instr 'movq (list (Imm tag) (Deref tmp-reg 0))) 195 | (Instr 'movq (list (Reg tmp-reg) lhs^)) 196 | ) 197 | ] 198 | 199 | * `(Assign lhs (Prim 'procedure-arity (list e)))` 200 | 201 | Extract the arity from the tag of the vector. 202 | 203 | (Assign lhs (Prim 'procedure-arity (list e))) 204 | ===> 205 | movq e', %r11 206 | movq 0(%r11), %r11 207 | sarq $57, %r11 208 | movq %r11, lhs' 209 | 210 | * `(Assign lhs (Prim 'vector-length (list e)))` 211 | 212 | Extract the length from the tag of the vector. 213 | 214 | (Assign lhs (Prim 'vector-length (list e))) 215 | ===> 216 | movq e', %r11 217 | movq 0(%r11), %r11 218 | andq $126, %r11 // 1111110 219 | sarq $1, %r11 220 | movq %r11, lhs' 221 | 222 | 223 | ## `Vectorof`, `vector-ref`, and `vector-set!` 224 | 225 | The type checker for Lany treats vector operations differently 226 | if the vector is of type `(Vectorof T)`. 227 | The index can be an arbitrary expression, e.g. 228 | suppose `vec` has type `(Vectorof T)`. Then 229 | the index could be `(read)` 230 | 231 | ;; vec1 : (Vector Any Any) 232 | (let ([vec1 (vector (inject 1 Integer) (inject 2 Integer))]) 233 | (let ([vec2 (inject vec1 (Vector Any Any))]) ;; vec2 : Any 234 | (let ([vec3 (project vec2 (Vectorof Any))]) ;; vec3 : (Vectorof Any) 235 | (vector-ref vec3 (read))))) 236 | 237 | and the type of `(vector-ref vec (read))` is `T`. 238 | 239 | Recall instruction selection for `vector-ref`: 240 | 241 | (Assign lhs (Prim 'vector-ref (list evec (Int n)))) 242 | ===> 243 | movq evec', %r11 244 | movq offset(%r11), lhs' 245 | 246 | where offset is 8(n+1) 247 | 248 | If the index is not of the form `(Int i)`, but an arbitrary 249 | expression, then instead of computing the offset `8(n+1)` at compile 250 | time, you can generate the following instructions. Note the use of the 251 | new instruction `imulq`. 252 | 253 | (Assign lhs (Prim 'vector-ref (list evec en))) 254 | ===> 255 | movq en', %r11 256 | addq $1, %r11 257 | imulq $8, %r11 258 | addq evec', %r11 259 | movq 0(%r11) lhs' 260 | 261 | The same idea applies to `vector-set!`. 262 | 263 | 264 | -------------------------------------------------------------------------------- /lectures/Dec-9.md: -------------------------------------------------------------------------------- 1 | # Review for Final Exam 2 | 3 | ## source program (map-tuple) 4 | 5 | def map(f : Callable[[int],int], 6 | v : tuple[(int, int,)]) -> tuple[(int, int,)]: 7 | return (f(v[0]), f(v[1]),) 8 | 9 | def inc(x:int) -> int: 10 | return x + 1 11 | 12 | n = input_int() 13 | print(map(inc, (0, n,))[1]) 14 | 15 | 16 | 17 | ## shrink 18 | 19 | def map(f:Callable[(, int,)], v:tuple[(int, int,)]) -> tuple[(int, int,)]: 20 | return (f(v[0]), f(v[1]),) 21 | 22 | def inc(x:int) -> int: 23 | return x + 1 24 | 25 | def main() -> int: 26 | n = input_int() 27 | print(map(inc, (0, n,))[1]) 28 | return 0 29 | 30 | 31 | ## reveal_functions 32 | 33 | def map(f:Callable[[int], int], v:tuple[int,int]) -> tuple[int,int]: 34 | return (f(v[0]), f(v[1]),) 35 | 36 | def inc(x:int) -> int: 37 | return x + 1 38 | 39 | def main() -> int: 40 | n = input_int() 41 | print({map}({inc}, (0, n,))[1]) 42 | return 0 43 | 44 | 45 | ## limit_functions 46 | 47 | def map(f:Callable[[int], int], v:tuple[int,int]) -> tuple[int,int]: 48 | return (f(v[0]), f(v[1]),) 49 | 50 | def inc(x:int) -> int: 51 | return x + 1 52 | 53 | def main() -> int: 54 | n = input_int() 55 | print({map}({inc}, (0, n,))[1]) 56 | return 0 57 | 58 | 59 | ## expose_allocation 60 | 61 | def map(f:Callable[[int], int], v:tuple[int,int]) -> tuple[int,int]: 62 | return { 63 | init.156 = f(v[0]) 64 | init.157 = f(v[1]) 65 | if free_ptr + 24 < fromspace_end: 66 | else: 67 | collect(24) 68 | alloc.155 = allocate(2,tuple[int,int]) 69 | alloc.155[0] = init.156 70 | alloc.155[1] = init.157 71 | alloc.155} 72 | 73 | def inc(x:int) -> int: 74 | return x + 1 75 | 76 | def main() -> int: 77 | n = input_int() 78 | print({map}({inc}, { 79 | init.159 = 0 80 | init.160 = n 81 | if free_ptr + 24 < fromspace_end: 82 | else: 83 | collect(24) 84 | alloc.158 = allocate(2,tuple[int,int]) 85 | alloc.158[0] = init.159 86 | alloc.158[1] = init.160 87 | alloc.158})[1]) 88 | return 0 89 | 90 | 91 | ## remove_complex_operands 92 | 93 | def map(f:Callable[[int], int], v:tuple[int,int]) -> tuple[int,int]: 94 | return { 95 | tmp.161 = v[0] 96 | init.156 = f(tmp.161) 97 | tmp.162 = v[1] 98 | init.157 = f(tmp.162) 99 | tmp.163 = free_ptr 100 | tmp.164 = tmp.163 + 24 101 | tmp.165 = fromspace_end 102 | if tmp.164 < tmp.165: 103 | else: 104 | collect(24) 105 | alloc.155 = allocate(2,tuple[int,int]) 106 | alloc.155[0] = init.156 107 | alloc.155[1] = init.157 108 | alloc.155} 109 | 110 | def inc(x:int) -> int: 111 | return x + 1 112 | 113 | def main() -> int: 114 | n = input_int() 115 | fun.166 = {map} 116 | fun.167 = {inc} 117 | tmp.171 = { 118 | init.159 = 0 119 | init.160 = n 120 | tmp.168 = free_ptr 121 | tmp.169 = tmp.168 + 24 122 | tmp.170 = fromspace_end 123 | if tmp.169 < tmp.170: 124 | else: 125 | collect(24) 126 | alloc.158 = allocate(2,tuple[int,int]) 127 | alloc.158[0] = init.159 128 | alloc.158[1] = init.160 129 | alloc.158} 130 | tmp.172 = fun.166(fun.167, tmp.171) 131 | tmp.173 = tmp.172[1] 132 | print(tmp.173) 133 | return 0 134 | 135 | 136 | ## explicate_control 137 | 138 | def map(f:Callable[[int], int], v:tuple[int,int]) -> tuple[int,int]: 139 | _block.174: 140 | alloc.155 = allocate(2,tuple[int,int]) 141 | alloc.155[0] = init.156 142 | alloc.155[1] = init.157 143 | return alloc.155 144 | _block.175: 145 | goto _block.174 146 | _block.176: 147 | collect(24) 148 | goto _block.174 149 | _mapstart: 150 | tmp.161 = v[0] 151 | init.156 = f(tmp.161) 152 | tmp.162 = v[1] 153 | init.157 = f(tmp.162) 154 | tmp.163 = free_ptr 155 | tmp.164 = tmp.163 + 24 156 | tmp.165 = fromspace_end 157 | if tmp.164 < tmp.165: 158 | goto _block.175 159 | else: 160 | goto _block.176 161 | 162 | 163 | def inc(x:int) -> int: 164 | _incstart: 165 | return x + 1 166 | 167 | 168 | def main() -> int: 169 | _block.177: 170 | alloc.158 = allocate(2,tuple[int,int]) 171 | alloc.158[0] = init.159 172 | alloc.158[1] = init.160 173 | tmp.171 = alloc.158 174 | tmp.172 = fun.166(fun.167, tmp.171) 175 | tmp.173 = tmp.172[1] 176 | print(tmp.173) 177 | return 0 178 | _block.178: 179 | goto _block.177 180 | _block.179: 181 | collect(24) 182 | goto _block.177 183 | _mainstart: 184 | n = input_int() 185 | fun.166 = {map} 186 | fun.167 = {inc} 187 | init.159 = 0 188 | init.160 = n 189 | tmp.168 = free_ptr 190 | tmp.169 = tmp.168 + 24 191 | tmp.170 = fromspace_end 192 | if tmp.169 < tmp.170: 193 | goto _block.178 194 | else: 195 | goto _block.179 196 | 197 | 198 | 199 | type_check_Cfun iterating {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'f': FunctionType(param_types=[IntType()], ret_type=IntType()), 'v': TupleType(types=[IntType(), IntType()]), 'alloc.155': TupleType(types=[IntType(), IntType()]), 'tmp.161': IntType(), 'init.156': IntType(), 'tmp.162': IntType(), 'init.157': IntType(), 'tmp.163': IntType(), 'tmp.164': IntType(), 'tmp.165': IntType()} 200 | type_check_Cfun iterating {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'f': FunctionType(param_types=[IntType()], ret_type=IntType()), 'v': TupleType(types=[IntType(), IntType()]), 'alloc.155': TupleType(types=[IntType(), IntType()]), 'tmp.161': IntType(), 'init.156': IntType(), 'tmp.162': IntType(), 'init.157': IntType(), 'tmp.163': IntType(), 'tmp.164': IntType(), 'tmp.165': IntType()} 201 | type_check_Cfun var_types for map 202 | {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'f': FunctionType(param_types=[IntType()], ret_type=IntType()), 'v': TupleType(types=[IntType(), IntType()]), 'alloc.155': TupleType(types=[IntType(), IntType()]), 'tmp.161': IntType(), 'init.156': IntType(), 'tmp.162': IntType(), 'init.157': IntType(), 'tmp.163': IntType(), 'tmp.164': IntType(), 'tmp.165': IntType()} 203 | type_check_Cfun iterating {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'x': IntType()} 204 | type_check_Cfun var_types for inc 205 | {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'x': IntType()} 206 | type_check_Cfun iterating {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'alloc.158': TupleType(types=[IntType(), IntType()]), 'tmp.171': TupleType(types=[IntType(), IntType()]), 'tmp.172': Bottom(), 'tmp.173': Bottom(), 'n': IntType(), 'fun.166': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'fun.167': FunctionType(param_types=[IntType()], ret_type=IntType()), 'init.159': IntType(), 'init.160': IntType(), 'tmp.168': IntType(), 'tmp.169': IntType(), 'tmp.170': IntType()} 207 | type_check_Cfun iterating {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'alloc.158': TupleType(types=[IntType(), IntType()]), 'tmp.171': TupleType(types=[IntType(), IntType()]), 'tmp.172': TupleType(types=[IntType(), IntType()]), 'tmp.173': IntType(), 'n': IntType(), 'fun.166': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'fun.167': FunctionType(param_types=[IntType()], ret_type=IntType()), 'init.159': IntType(), 'init.160': IntType(), 'tmp.168': IntType(), 'tmp.169': IntType(), 'tmp.170': IntType()} 208 | type_check_Cfun iterating {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'alloc.158': TupleType(types=[IntType(), IntType()]), 'tmp.171': TupleType(types=[IntType(), IntType()]), 'tmp.172': TupleType(types=[IntType(), IntType()]), 'tmp.173': IntType(), 'n': IntType(), 'fun.166': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'fun.167': FunctionType(param_types=[IntType()], ret_type=IntType()), 'init.159': IntType(), 'init.160': IntType(), 'tmp.168': IntType(), 'tmp.169': IntType(), 'tmp.170': IntType()} 209 | type_check_Cfun var_types for main 210 | {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'alloc.158': TupleType(types=[IntType(), IntType()]), 'tmp.171': TupleType(types=[IntType(), IntType()]), 'tmp.172': TupleType(types=[IntType(), IntType()]), 'tmp.173': IntType(), 'n': IntType(), 'fun.166': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'fun.167': FunctionType(param_types=[IntType()], ret_type=IntType()), 'init.159': IntType(), 'init.160': IntType(), 'tmp.168': IntType(), 'tmp.169': IntType(), 'tmp.170': IntType()} 211 | 212 | 213 | ## select_instructions 214 | 215 | def _map() -> tuple[int,int]: 216 | _block.174: 217 | movq _free_ptr(%rip), %r11 218 | addq $24, _free_ptr(%rip) 219 | movq $5, 0(%r11) 220 | movq %r11, alloc.155 221 | movq alloc.155, %r11 222 | movq init.156, 8(%r11) 223 | movq alloc.155, %r11 224 | movq init.157, 16(%r11) 225 | movq alloc.155, %rax 226 | jmp _mapconclusion 227 | _block.175: 228 | jmp _block.174 229 | _block.176: 230 | movq %r15, %rdi 231 | movq $24, %rsi 232 | callq _collect 233 | jmp _block.174 234 | _mapstart: 235 | movq %rdi, f 236 | movq %rsi, v 237 | movq v, %r11 238 | movq 8(%r11), %r11 239 | movq %r11, tmp.161 240 | movq tmp.161, %rdi 241 | callq *f 242 | movq %rax, init.156 243 | movq v, %r11 244 | movq 16(%r11), %r11 245 | movq %r11, tmp.162 246 | movq tmp.162, %rdi 247 | callq *f 248 | movq %rax, init.157 249 | movq _free_ptr(%rip), tmp.163 250 | movq tmp.163, tmp.164 251 | addq $24, tmp.164 252 | movq _fromspace_end(%rip), tmp.165 253 | cmpq tmp.165, tmp.164 254 | jl _block.175 255 | jmp _block.176 256 | 257 | 258 | def _inc() -> int: 259 | _incstart: 260 | movq %rdi, x 261 | movq x, %rax 262 | addq $1, %rax 263 | jmp _incconclusion 264 | 265 | 266 | def _main() -> int: 267 | _block.177: 268 | movq _free_ptr(%rip), %r11 269 | addq $24, _free_ptr(%rip) 270 | movq $5, 0(%r11) 271 | movq %r11, alloc.158 272 | movq alloc.158, %r11 273 | movq init.159, 8(%r11) 274 | movq alloc.158, %r11 275 | movq init.160, 16(%r11) 276 | movq alloc.158, tmp.171 277 | movq fun.167, %rdi 278 | movq tmp.171, %rsi 279 | callq *fun.166 280 | movq %rax, tmp.172 281 | movq tmp.172, %r11 282 | movq 16(%r11), %r11 283 | movq %r11, tmp.173 284 | movq tmp.173, %rdi 285 | callq _print_int 286 | movq $0, %rax 287 | jmp _mainconclusion 288 | _block.178: 289 | jmp _block.177 290 | _block.179: 291 | movq %r15, %rdi 292 | movq $24, %rsi 293 | callq _collect 294 | jmp _block.177 295 | _mainstart: 296 | callq _read_int 297 | movq %rax, n 298 | leaq _map(%rip), fun.166 299 | leaq _inc(%rip), fun.167 300 | movq $0, init.159 301 | movq n, init.160 302 | movq _free_ptr(%rip), tmp.168 303 | movq tmp.168, tmp.169 304 | addq $24, tmp.169 305 | movq _fromspace_end(%rip), tmp.170 306 | cmpq tmp.170, tmp.169 307 | jl _block.178 308 | jmp _block.179 309 | 310 | 311 | ## assign_homes 312 | 313 | uncover live: 314 | _block.174: 315 | 316 | { %rsp, init.157, init.156} 317 | movq _free_ptr(%rip), %r11 318 | 319 | { %rsp, init.157, init.156} 320 | addq $24, _free_ptr(%rip) 321 | 322 | { %rsp, init.157, init.156} 323 | movq $5, 0(%r11) 324 | 325 | { %rsp, init.157, init.156} 326 | movq %r11, alloc.155 327 | 328 | { %r11, %rsp, init.157, init.156} 329 | movq alloc.155, %r11 330 | 331 | {init.157, alloc.155, %rsp, init.156} 332 | movq init.156, 8(%r11) 333 | 334 | {alloc.155, %rsp, init.157, init.156} 335 | movq alloc.155, %r11 336 | 337 | {alloc.155, %rsp, init.157} 338 | movq init.157, 16(%r11) 339 | 340 | {alloc.155, %rsp, init.157} 341 | movq alloc.155, %rax 342 | 343 | {alloc.155, %rsp} 344 | jmp _mapconclusion 345 | 346 | { %rsp, %rax} 347 | _block.175: 348 | 349 | { %rsp, init.157, init.156} 350 | jmp _block.174 351 | 352 | { %rsp, init.157, init.156} 353 | _block.176: 354 | 355 | { %rsp, init.157, init.156, %r15} 356 | movq %r15, %rdi 357 | 358 | { %rsp, init.157, init.156, %r15} 359 | movq $24, %rsi 360 | 361 | { %rsp, init.157, %rdi, init.156} 362 | callq _collect 363 | 364 | {init.157, %rsp, %rsi, %rdi, init.156} 365 | jmp _block.174 366 | 367 | { %rsp, init.157, init.156} 368 | _mapstart: 369 | 370 | { %rdi, %rsp, %rsi, %rax, %r15} 371 | movq %rdi, f 372 | 373 | { %rdi, %rsp, %rsi, %rax, %r15} 374 | movq %rsi, v 375 | 376 | { %rsp, %rsi, %rax, %r15, f} 377 | movq v, %r11 378 | 379 | { %rax, %rsp, v, %r15, f} 380 | movq 8(%r11), %r11 381 | 382 | { %rax, %r11, %rsp, v, %r15, f} 383 | movq %r11, tmp.161 384 | 385 | { %rax, %r11, %rsp, v, %r15, f} 386 | movq tmp.161, %rdi 387 | 388 | { %rax, tmp.161, %rsp, v, %r15, f} 389 | callq *f 390 | 391 | { %rax, %rdi, %rsp, v, %r15, f} 392 | movq %rax, init.156 393 | 394 | { %rax, %rsp, v, %r15, f} 395 | movq v, %r11 396 | 397 | {init.156, %rsp, v, %rax, %r15, f} 398 | movq 16(%r11), %r11 399 | 400 | { %r11, init.156, %rsp, %rax, %r15, f} 401 | movq %r11, tmp.162 402 | 403 | { %r11, init.156, %rsp, %rax, %r15, f} 404 | movq tmp.162, %rdi 405 | 406 | {tmp.162, init.156, %rsp, %rax, %r15, f} 407 | callq *f 408 | 409 | { %rdi, %rsp, f, %rax, %r15, init.156} 410 | movq %rax, init.157 411 | 412 | { %rsp, %rax, %r15, init.156} 413 | movq _free_ptr(%rip), tmp.163 414 | 415 | { %rsp, init.157, %r15, init.156} 416 | movq tmp.163, tmp.164 417 | 418 | {init.157, %rsp, tmp.163, %r15, init.156} 419 | addq $24, tmp.164 420 | 421 | {init.157, tmp.164, %rsp, %r15, init.156} 422 | movq _fromspace_end(%rip), tmp.165 423 | 424 | {init.157, tmp.164, %rsp, %r15, init.156} 425 | cmpq tmp.165, tmp.164 426 | 427 | {init.157, tmp.164, %rsp, tmp.165, %r15, init.156} 428 | jl _block.175 429 | 430 | { %rsp, init.157, %r15, init.156} 431 | jmp _block.176 432 | 433 | { %rsp, init.157, %r15, init.156} 434 | var_types: 435 | {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'f': FunctionType(param_types=[IntType()], ret_type=IntType()), 'v': TupleType(types=[IntType(), IntType()]), 'alloc.155': TupleType(types=[IntType(), IntType()]), 'tmp.161': IntType(), 'init.156': IntType(), 'tmp.162': IntType(), 'init.157': IntType(), 'tmp.163': IntType(), 'tmp.164': IntType(), 'tmp.165': IntType()} 436 | home: 437 | {Variable('init.157'): Reg('rbx'), Variable('tmp.161'): Reg('rcx'), Variable('alloc.155'): Reg('rcx'), Variable('tmp.164'): Reg('rdx'), Variable('tmp.165'): Reg('rcx'), Variable('tmp.163'): Reg('rcx'), Variable('v'): Reg('rbx'), Variable('f'): Reg('r13'), Variable('tmp.162'): Reg('rcx'), Variable('init.156'): Reg('r12')} 438 | uncover live: 439 | _incstart: 440 | 441 | { %rsp, %rdi} 442 | movq %rdi, x 443 | 444 | { %rsp, %rdi} 445 | movq x, %rax 446 | 447 | { %rsp, x} 448 | addq $1, %rax 449 | 450 | { %rsp, %rax} 451 | jmp _incconclusion 452 | 453 | { %rsp, %rax} 454 | var_types: 455 | {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'x': IntType()} 456 | home: 457 | {Variable('x'): Reg('rcx')} 458 | uncover live: 459 | _block.177: 460 | 461 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 462 | movq _free_ptr(%rip), %r11 463 | 464 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 465 | addq $24, _free_ptr(%rip) 466 | 467 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 468 | movq $5, 0(%r11) 469 | 470 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 471 | movq %r11, alloc.158 472 | 473 | { %r11, fun.167, fun.166, init.159, %rsp, init.160, %rax} 474 | movq alloc.158, %r11 475 | 476 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, alloc.158} 477 | movq init.159, 8(%r11) 478 | 479 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, alloc.158} 480 | movq alloc.158, %r11 481 | 482 | {fun.167, fun.166, %rsp, init.160, %rax, alloc.158} 483 | movq init.160, 16(%r11) 484 | 485 | {fun.167, fun.166, %rsp, init.160, %rax, alloc.158} 486 | movq alloc.158, tmp.171 487 | 488 | { %rax, fun.167, fun.166, %rsp, alloc.158} 489 | movq fun.167, %rdi 490 | 491 | {fun.167, %rsp, fun.166, tmp.171, %rax} 492 | movq tmp.171, %rsi 493 | 494 | { %rdi, fun.166, tmp.171, %rax, %rsp} 495 | callq *fun.166 496 | 497 | {fun.166, %rdi, %rsp, %rsi, %rax} 498 | movq %rax, tmp.172 499 | 500 | { %rsp, %rax} 501 | movq tmp.172, %r11 502 | 503 | { %rsp, tmp.172} 504 | movq 16(%r11), %r11 505 | 506 | { %r11, %rsp} 507 | movq %r11, tmp.173 508 | 509 | { %r11, %rsp} 510 | movq tmp.173, %rdi 511 | 512 | { %rsp, tmp.173} 513 | callq _print_int 514 | 515 | { %rsp, %rdi} 516 | movq $0, %rax 517 | 518 | { %rsp} 519 | jmp _mainconclusion 520 | 521 | { %rsp, %rax} 522 | _block.178: 523 | 524 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 525 | jmp _block.177 526 | 527 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 528 | _block.179: 529 | 530 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 531 | movq %r15, %rdi 532 | 533 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 534 | movq $24, %rsi 535 | 536 | {fun.167, fun.166, init.159, %rsp, init.160, %rdi, %rax} 537 | callq _collect 538 | 539 | {fun.167, fun.166, init.159, %rsp, init.160, %rdi, %rsi, %rax} 540 | jmp _block.177 541 | 542 | {fun.167, fun.166, init.159, %rsp, init.160, %rax} 543 | _mainstart: 544 | 545 | { %rsp, %rax, %r15} 546 | callq _read_int 547 | 548 | { %rsp, %rax, %r15} 549 | movq %rax, n 550 | 551 | { %rsp, %rax, %r15} 552 | leaq _map(%rip), fun.166 553 | 554 | { %rsp, n, %r15, %rax} 555 | leaq _inc(%rip), fun.167 556 | 557 | {n, fun.166, %rsp, %rax, %r15} 558 | movq $0, init.159 559 | 560 | {n, fun.167, fun.166, %rsp, %rax, %r15} 561 | movq n, init.160 562 | 563 | {n, fun.167, fun.166, init.159, %rsp, %rax, %r15} 564 | movq _free_ptr(%rip), tmp.168 565 | 566 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 567 | movq tmp.168, tmp.169 568 | 569 | {tmp.168, fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 570 | addq $24, tmp.169 571 | 572 | {tmp.169, fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 573 | movq _fromspace_end(%rip), tmp.170 574 | 575 | {init.160, %rax, %r15, fun.167, tmp.169, init.159, %rsp, fun.166} 576 | cmpq tmp.170, tmp.169 577 | 578 | {init.160, %rsp, %rax, %r15, tmp.170, fun.167, tmp.169, init.159, fun.166} 579 | jl _block.178 580 | 581 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 582 | jmp _block.179 583 | 584 | {fun.167, fun.166, init.159, %rsp, init.160, %rax, %r15} 585 | var_types: 586 | {'map': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'inc': FunctionType(param_types=[IntType()], ret_type=IntType()), 'main': FunctionType(param_types=[], ret_type=IntType()), 'alloc.158': TupleType(types=[IntType(), IntType()]), 'tmp.171': TupleType(types=[IntType(), IntType()]), 'tmp.172': TupleType(types=[IntType(), IntType()]), 'tmp.173': IntType(), 'n': IntType(), 'fun.166': FunctionType(param_types=[FunctionType(param_types=[IntType()], ret_type=IntType()), TupleType(types=[IntType(), IntType()])], ret_type=TupleType(types=[IntType(), IntType()])), 'fun.167': FunctionType(param_types=[IntType()], ret_type=IntType()), 'init.159': IntType(), 'init.160': IntType(), 'tmp.168': IntType(), 'tmp.169': IntType(), 'tmp.170': IntType()} 587 | home: 588 | {Variable('tmp.169'): Reg('rdx'), Variable('n'): Reg('rcx'), Variable('tmp.173'): Reg('rcx'), Variable('init.160'): Reg('rbx'), Variable('tmp.172'): Reg('rcx'), Variable('alloc.158'): Reg('rcx'), Variable('tmp.168'): Reg('rcx'), Variable('fun.167'): Reg('r12'), Variable('tmp.170'): Reg('rcx'), Variable('init.159'): Reg('r14'), Variable('fun.166'): Reg('r13'), Variable('tmp.171'): Reg('rcx')} 589 | def _map() -> tuple[int,int]: 590 | _block.174: 591 | movq _free_ptr(%rip), %r11 592 | addq $24, _free_ptr(%rip) 593 | movq $5, 0(%r11) 594 | movq %r11, %rcx 595 | movq %rcx, %r11 596 | movq %r12, 8(%r11) 597 | movq %rcx, %r11 598 | movq %rbx, 16(%r11) 599 | movq %rcx, %rax 600 | jmp _mapconclusion 601 | _block.175: 602 | jmp _block.174 603 | _block.176: 604 | movq %r15, %rdi 605 | movq $24, %rsi 606 | callq _collect 607 | jmp _block.174 608 | _mapstart: 609 | movq %rdi, %r13 610 | movq %rsi, %rbx 611 | movq %rbx, %r11 612 | movq 8(%r11), %r11 613 | movq %r11, %rcx 614 | movq %rcx, %rdi 615 | callq *%r13 616 | movq %rax, %r12 617 | movq %rbx, %r11 618 | movq 16(%r11), %r11 619 | movq %r11, %rcx 620 | movq %rcx, %rdi 621 | callq *%r13 622 | movq %rax, %rbx 623 | movq _free_ptr(%rip), %rcx 624 | movq %rcx, %rdx 625 | addq $24, %rdx 626 | movq _fromspace_end(%rip), %rcx 627 | cmpq %rcx, %rdx 628 | jl _block.175 629 | jmp _block.176 630 | 631 | 632 | def _inc() -> int: 633 | _incstart: 634 | movq %rdi, %rcx 635 | movq %rcx, %rax 636 | addq $1, %rax 637 | jmp _incconclusion 638 | 639 | 640 | def _main() -> int: 641 | _block.177: 642 | movq _free_ptr(%rip), %r11 643 | addq $24, _free_ptr(%rip) 644 | movq $5, 0(%r11) 645 | movq %r11, %rcx 646 | movq %rcx, %r11 647 | movq %r14, 8(%r11) 648 | movq %rcx, %r11 649 | movq %rbx, 16(%r11) 650 | movq %rcx, %rcx 651 | movq %r12, %rdi 652 | movq %rcx, %rsi 653 | callq *%r13 654 | movq %rax, %rcx 655 | movq %rcx, %r11 656 | movq 16(%r11), %r11 657 | movq %r11, %rcx 658 | movq %rcx, %rdi 659 | callq _print_int 660 | movq $0, %rax 661 | jmp _mainconclusion 662 | _block.178: 663 | jmp _block.177 664 | _block.179: 665 | movq %r15, %rdi 666 | movq $24, %rsi 667 | callq _collect 668 | jmp _block.177 669 | _mainstart: 670 | callq _read_int 671 | movq %rax, %rcx 672 | leaq _map(%rip), %r13 673 | leaq _inc(%rip), %r12 674 | movq $0, %r14 675 | movq %rcx, %rbx 676 | movq _free_ptr(%rip), %rcx 677 | movq %rcx, %rdx 678 | addq $24, %rdx 679 | movq _fromspace_end(%rip), %rcx 680 | cmpq %rcx, %rdx 681 | jl _block.178 682 | jmp _block.179 683 | 684 | 685 | ## patch_instructions 686 | 687 | def _map() -> tuple[int,int]: 688 | _block.174: 689 | movq _free_ptr(%rip), %r11 690 | addq $24, _free_ptr(%rip) 691 | movq $5, 0(%r11) 692 | movq %r11, %rcx 693 | movq %rcx, %r11 694 | movq %r12, 8(%r11) 695 | movq %rcx, %r11 696 | movq %rbx, 16(%r11) 697 | movq %rcx, %rax 698 | jmp _mapconclusion 699 | _block.175: 700 | jmp _block.174 701 | _block.176: 702 | movq %r15, %rdi 703 | movq $24, %rsi 704 | callq _collect 705 | jmp _block.174 706 | _mapstart: 707 | movq %rdi, %r13 708 | movq %rsi, %rbx 709 | movq %rbx, %r11 710 | movq 8(%r11), %r11 711 | movq %r11, %rcx 712 | movq %rcx, %rdi 713 | callq *%r13 714 | movq %rax, %r12 715 | movq %rbx, %r11 716 | movq 16(%r11), %r11 717 | movq %r11, %rcx 718 | movq %rcx, %rdi 719 | callq *%r13 720 | movq %rax, %rbx 721 | movq _free_ptr(%rip), %rcx 722 | movq %rcx, %rdx 723 | addq $24, %rdx 724 | movq _fromspace_end(%rip), %rcx 725 | cmpq %rcx, %rdx 726 | jl _block.175 727 | jmp _block.176 728 | 729 | 730 | def _inc() -> int: 731 | _incstart: 732 | movq %rdi, %rcx 733 | movq %rcx, %rax 734 | addq $1, %rax 735 | jmp _incconclusion 736 | 737 | 738 | def _main() -> int: 739 | _block.177: 740 | movq _free_ptr(%rip), %r11 741 | addq $24, _free_ptr(%rip) 742 | movq $5, 0(%r11) 743 | movq %r11, %rcx 744 | movq %rcx, %r11 745 | movq %r14, 8(%r11) 746 | movq %rcx, %r11 747 | movq %rbx, 16(%r11) 748 | movq %r12, %rdi 749 | movq %rcx, %rsi 750 | callq *%r13 751 | movq %rax, %rcx 752 | movq %rcx, %r11 753 | movq 16(%r11), %r11 754 | movq %r11, %rcx 755 | movq %rcx, %rdi 756 | callq _print_int 757 | movq $0, %rax 758 | jmp _mainconclusion 759 | _block.178: 760 | jmp _block.177 761 | _block.179: 762 | movq %r15, %rdi 763 | movq $24, %rsi 764 | callq _collect 765 | jmp _block.177 766 | _mainstart: 767 | callq _read_int 768 | movq %rax, %rcx 769 | leaq _map(%rip), %r13 770 | leaq _inc(%rip), %r12 771 | movq $0, %r14 772 | movq %rcx, %rbx 773 | movq _free_ptr(%rip), %rcx 774 | movq %rcx, %rdx 775 | addq $24, %rdx 776 | movq _fromspace_end(%rip), %rcx 777 | cmpq %rcx, %rdx 778 | jl _block.178 779 | jmp _block.179 780 | 781 | 782 | ## prelude and conclusion 783 | 784 | .align 16 785 | _block.174: 786 | movq _free_ptr(%rip), %r11 787 | addq $24, _free_ptr(%rip) 788 | movq $5, 0(%r11) 789 | movq %r11, %rcx 790 | movq %rcx, %r11 791 | movq %r12, 8(%r11) 792 | movq %rcx, %r11 793 | movq %rbx, 16(%r11) 794 | movq %rcx, %rax 795 | jmp _mapconclusion 796 | 797 | .align 16 798 | _block.175: 799 | jmp _block.174 800 | 801 | .align 16 802 | _block.176: 803 | movq %r15, %rdi 804 | movq $24, %rsi 805 | callq _collect 806 | jmp _block.174 807 | 808 | .align 16 809 | _mapstart: 810 | movq %rdi, %r13 811 | movq %rsi, %rbx 812 | movq %rbx, %r11 813 | movq 8(%r11), %r11 814 | movq %r11, %rcx 815 | movq %rcx, %rdi 816 | callq *%r13 817 | movq %rax, %r12 818 | movq %rbx, %r11 819 | movq 16(%r11), %r11 820 | movq %r11, %rcx 821 | movq %rcx, %rdi 822 | callq *%r13 823 | movq %rax, %rbx 824 | movq _free_ptr(%rip), %rcx 825 | movq %rcx, %rdx 826 | addq $24, %rdx 827 | movq _fromspace_end(%rip), %rcx 828 | cmpq %rcx, %rdx 829 | jl _block.175 830 | jmp _block.176 831 | 832 | .align 16 833 | _map: 834 | pushq %rbp 835 | movq %rsp, %rbp 836 | pushq %rbx 837 | pushq %r13 838 | pushq %r12 839 | subq $8, %rsp 840 | jmp _mapstart 841 | 842 | .align 16 843 | _mapconclusion: 844 | subq $0, %r15 845 | addq $8, %rsp 846 | popq %r12 847 | popq %r13 848 | popq %rbx 849 | popq %rbp 850 | retq 851 | 852 | .align 16 853 | _incstart: 854 | movq %rdi, %rcx 855 | movq %rcx, %rax 856 | addq $1, %rax 857 | jmp _incconclusion 858 | 859 | .align 16 860 | _inc: 861 | pushq %rbp 862 | movq %rsp, %rbp 863 | subq $0, %rsp 864 | jmp _incstart 865 | 866 | .align 16 867 | _incconclusion: 868 | subq $0, %r15 869 | addq $0, %rsp 870 | popq %rbp 871 | retq 872 | 873 | .align 16 874 | _block.177: 875 | movq _free_ptr(%rip), %r11 876 | addq $24, _free_ptr(%rip) 877 | movq $5, 0(%r11) 878 | movq %r11, %rcx 879 | movq %rcx, %r11 880 | movq %r14, 8(%r11) 881 | movq %rcx, %r11 882 | movq %rbx, 16(%r11) 883 | movq %r12, %rdi 884 | movq %rcx, %rsi 885 | callq *%r13 886 | movq %rax, %rcx 887 | movq %rcx, %r11 888 | movq 16(%r11), %r11 889 | movq %r11, %rcx 890 | movq %rcx, %rdi 891 | callq _print_int 892 | movq $0, %rax 893 | jmp _mainconclusion 894 | 895 | .align 16 896 | _block.178: 897 | jmp _block.177 898 | 899 | .align 16 900 | _block.179: 901 | movq %r15, %rdi 902 | movq $24, %rsi 903 | callq _collect 904 | jmp _block.177 905 | 906 | .align 16 907 | _mainstart: 908 | callq _read_int 909 | movq %rax, %rcx 910 | leaq _map(%rip), %r13 911 | leaq _inc(%rip), %r12 912 | movq $0, %r14 913 | movq %rcx, %rbx 914 | movq _free_ptr(%rip), %rcx 915 | movq %rcx, %rdx 916 | addq $24, %rdx 917 | movq _fromspace_end(%rip), %rcx 918 | cmpq %rcx, %rdx 919 | jl _block.178 920 | jmp _block.179 921 | 922 | .globl _main 923 | .align 16 924 | _main: 925 | pushq %rbp 926 | movq %rsp, %rbp 927 | pushq %r14 928 | pushq %rbx 929 | pushq %r13 930 | pushq %r12 931 | subq $0, %rsp 932 | movq $65536, %rdi 933 | movq $65536, %rsi 934 | callq _initialize 935 | movq _rootstack_begin(%rip), %r15 936 | jmp _mainstart 937 | 938 | .align 16 939 | _mainconclusion: 940 | subq $0, %r15 941 | addq $0, %rsp 942 | popq %r12 943 | popq %r13 944 | popq %rbx 945 | popq %r14 946 | popq %rbp 947 | retq 948 | 949 | 950 | 951 | -------------------------------------------------------------------------------- /lectures/L_Int_height.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | 3 | # s is in stmt of L_int 4 | def stmt_height(s): 5 | match s: 6 | case Expr(Call(Name('print')), [e]): 7 | return 1 + height(e) 8 | 9 | # p is in L_int 10 | def program_height(p): 11 | match p: 12 | case Module(stmts): 13 | return 1 + max([stmt_height(s) for s in stmts]) 14 | 15 | # e is an exp in L_int 16 | def height(e): 17 | match e: 18 | case Constant(value): 19 | return 1 20 | case Name(id): 21 | return 1 22 | case BinOp(left, op, right): 23 | return 1 + max(height(left), height(right)) 24 | case UnaryOp(op, operand): 25 | return 1 + height(operand) 26 | case Call(func, args): 27 | return 1 + max([height(a) for a in [func] + args]) 28 | 29 | E1 = Constant(42) 30 | E2 = Call(Name('input_int'), []) 31 | E3 = UnaryOp(USub(), E1) 32 | E4 = BinOp(E3, Add(), Constant(5)) 33 | E5 = BinOp(E2, Add(), UnaryOp(USub(), E2)) 34 | 35 | print(height(E1)) 36 | print(height(E2)) 37 | print(height(E3)) 38 | print(height(E4)) 39 | print(height(E5)) 40 | -------------------------------------------------------------------------------- /lectures/L_Int_height.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (struct Int (value)) 4 | (struct Prim (op arg*)) 5 | ;; alternative to Prim: 6 | ;;(struct Add (left right)) 7 | ;;(struct Neg (value)) 8 | ;;(struct Read ()) 9 | 10 | (define E1 (Int 42)) 11 | (define E2 (Prim 'read '())) 12 | (define E3 (Prim '- (list E1))) 13 | (define E4 (Prim '+ (list E3 (Int 5)))) 14 | (define E5 (Prim '+ (list E2 (Prim '- (list E2))))) 15 | 16 | (define (list-max ls) 17 | (foldl max 0 ls)) 18 | 19 | (define (height e) 20 | (match e 21 | [(Int n) 1] 22 | [(Prim op e*) 23 | ( + 1 (for/fold ([curr-max 0]) 24 | ([h (map height e*)]) 25 | (max curr-max h)))] 26 | )) 27 | 28 | (height E1) 29 | (height E2) 30 | (height E3) 31 | (height E4) 32 | (height E5) 33 | -------------------------------------------------------------------------------- /lectures/Nov-11.md: -------------------------------------------------------------------------------- 1 | # Compiling Functions, Continued 2 | 3 | 4 | ## Example of a tail call 5 | 6 | source program 7 | 8 | def sum(x:int,s:int)-> int : 9 | if x == 0: 10 | return s 11 | else: 12 | return sum(x - 1, x + s) 13 | 14 | print(sum(3, 0) + 36) 15 | 16 | shrink 17 | 18 | def sum(x:,s:)-> : 19 | if x == 0: 20 | return s 21 | else: 22 | return sum(x - 1, x + s) 23 | 24 | def main()-> : 25 | print(sum(3, 0) + 36) 26 | return 0 27 | 28 | reveal functions 29 | 30 | def sum(x:,s:)-> : 31 | if x == 0: 32 | return s 33 | else: 34 | return sum(%rip)(x - 1, x + s) 35 | 36 | def main()-> : 37 | print(sum(%rip)(3, 0) + 36) 38 | return 0 39 | 40 | limit functions 41 | 42 | def sum(x:,s:)-> : 43 | if x == 0: 44 | return s 45 | else: 46 | return sum(%rip)(x - 1, x + s) 47 | def main()-> : 48 | print(sum(%rip)(3, 0) + 36) 49 | return 0 50 | 51 | expose allocation 52 | 53 | def sum(x:,s:)-> : 54 | if x == 0: 55 | return s 56 | else: 57 | return sum(%rip)(x - 1, x + s) 58 | def main()-> : 59 | print(sum(%rip)(3, 0) + 36) 60 | return 0 61 | 62 | remove complex operands 63 | 64 | def sum(x:,s:)-> : 65 | if x == 0: 66 | return s 67 | else: 68 | fun.0 = sum(%rip) 69 | tmp.1 = x - 1 70 | tmp.2 = x + s 71 | return fun.0(tmp.1, tmp.2) 72 | 73 | def main()-> : 74 | fun.3 = sum(%rip) 75 | tmp.4 = fun.3(3, 0) 76 | tmp.5 = tmp.4 + 36 77 | print(tmp.5) 78 | return 0 79 | 80 | explicate control 81 | 82 | def sum(x:,s:)-> : 83 | _block.7: 84 | return s 85 | _block.8: 86 | fun.0 = sum(%rip) 87 | tmp.1 = x - 1 88 | tmp.2 = x + s 89 | fun.0(tmp.1,tmp.2) 90 | 91 | _sumstart: 92 | if x == 0: 93 | goto _block.7 94 | else: 95 | goto _block.8 96 | 97 | def main()-> : 98 | _mainstart: 99 | fun.3 = sum(%rip) 100 | tmp.4 = fun.3(3, 0) 101 | tmp.5 = tmp.4 + 36 102 | print(tmp.5) 103 | return 0 104 | 105 | select instructions 106 | 107 | def _sum()-> : 108 | _block.7: 109 | movq s, %rax 110 | jmp _sumconclusion 111 | _block.8: 112 | leaq _sum(%rip), fun.0 113 | movq x, tmp.1 114 | subq $1, tmp.1 115 | movq x, tmp.2 116 | addq s, tmp.2 117 | movq tmp.1, %rdi 118 | movq tmp.2, %rsi 119 | tailjmp fun.0 120 | _sumstart: 121 | movq %rdi, x 122 | movq %rsi, s 123 | cmpq $0, x 124 | je _block.7 125 | jmp _block.8 126 | 127 | def _main()-> : 128 | _mainstart: 129 | leaq _sum(%rip), fun.3 130 | movq $3, %rdi 131 | movq $0, %rsi 132 | callq *fun.3 133 | movq %rax, tmp.4 134 | movq tmp.4, tmp.5 135 | addq $36, tmp.5 136 | movq tmp.5, %rdi 137 | callq _print_int 138 | movq $0, %rax 139 | jmp _mainconclusion 140 | 141 | register allocation 142 | 143 | def _sum(x:,s:)-> : 144 | _block.6: 145 | _block.7: 146 | movq %rsi, %rax 147 | jmp _sumconclusion 148 | _block.8: 149 | leaq _sum(%rip), %rdx 150 | movq %rcx, %rdi 151 | subq $1, %rdi 152 | movq %rcx, %rcx 153 | addq %rsi, %rcx 154 | movq %rdi, %rdi 155 | movq %rcx, %rsi 156 | tailjmp %rdx 157 | _sumstart: 158 | movq %rdi, %rcx 159 | movq %rsi, %rsi 160 | cmpq $0, %rcx 161 | je _block.7 162 | jmp _block.8 163 | 164 | def _main()-> : 165 | _mainstart: 166 | leaq _sum(%rip), %rcx 167 | movq $3, %rdi 168 | movq $0, %rsi 169 | callq *%rcx 170 | movq %rax, %rcx 171 | movq %rcx, %rcx 172 | addq $36, %rcx 173 | movq %rcx, %rdi 174 | callq _print_int 175 | movq $0, %rax 176 | jmp _mainconclusion 177 | 178 | patch instructions 179 | 180 | def _sum(x:,s:)-> : 181 | _block.6: 182 | _block.7: 183 | movq %rsi, %rax 184 | jmp _sumconclusion 185 | _block.8: 186 | leaq _sum(%rip), %rdx 187 | movq %rcx, %rdi 188 | subq $1, %rdi 189 | addq %rsi, %rcx 190 | movq %rcx, %rsi 191 | movq %rdx, %rax 192 | tailjmp %rax 193 | _sumstart: 194 | movq %rdi, %rcx 195 | cmpq $0, %rcx 196 | je _block.7 197 | jmp _block.8 198 | 199 | def _main()-> : 200 | _mainstart: 201 | leaq _sum(%rip), %rcx 202 | movq $3, %rdi 203 | movq $0, %rsi 204 | callq *%rcx 205 | movq %rax, %rcx 206 | addq $36, %rcx 207 | movq %rcx, %rdi 208 | callq _print_int 209 | movq $0, %rax 210 | jmp _mainconclusion 211 | 212 | prelude and conclusion 213 | 214 | .align 16 215 | _block.6: 216 | 217 | .align 16 218 | _block.7: 219 | movq %rsi, %rax 220 | jmp _sumconclusion 221 | 222 | .align 16 223 | _block.8: 224 | leaq _sum(%rip), %rdx 225 | movq %rcx, %rdi 226 | subq $1, %rdi 227 | addq %rsi, %rcx 228 | movq %rcx, %rsi 229 | movq %rdx, %rax 230 | subq $0, %r15 231 | addq $0, %rsp 232 | popq %rbp 233 | jmp *%rax 234 | 235 | .align 16 236 | _sumstart: 237 | movq %rdi, %rcx 238 | cmpq $0, %rcx 239 | je _block.7 240 | jmp _block.8 241 | 242 | .align 16 243 | _sum: 244 | pushq %rbp 245 | movq %rsp, %rbp 246 | subq $0, %rsp 247 | jmp _sumstart 248 | 249 | .align 16 250 | _sumconclusion: 251 | subq $0, %r15 252 | addq $0, %rsp 253 | popq %rbp 254 | retq 255 | 256 | .align 16 257 | _mainstart: 258 | leaq _sum(%rip), %rcx 259 | movq $3, %rdi 260 | movq $0, %rsi 261 | callq *%rcx 262 | movq %rax, %rcx 263 | addq $36, %rcx 264 | movq %rcx, %rdi 265 | callq _print_int 266 | movq $0, %rax 267 | jmp _mainconclusion 268 | 269 | .globl _main 270 | .align 16 271 | _main: 272 | pushq %rbp 273 | movq %rsp, %rbp 274 | subq $0, %rsp 275 | movq $65536, %rdi 276 | movq $65536, %rsi 277 | callq _initialize 278 | movq _rootstack_begin(%rip), %r15 279 | jmp _mainstart 280 | 281 | .align 16 282 | _mainconclusion: 283 | subq $0, %r15 284 | addq $0, %rsp 285 | popq %rbp 286 | retq 287 | 288 | 289 | 290 | ## Example of function with too many parameters 291 | 292 | source program 293 | 294 | def sum(a:int,b:int,c:int,d:int,e:int,f:int,g:int,h:int)-> int : 295 | return a + b + c + d + e + f + g + h 296 | print(sum(5, 5, 5, 5, 5, 5, 5, 7)) 297 | 298 | 299 | shrink 300 | 301 | def sum(a:,b:,c:,d:,e:,f:,g:,h:)-> : 302 | return a + b + c + d + e + f + g + h 303 | 304 | def main()-> : 305 | print(sum(5, 5, 5, 5, 5, 5, 5, 7)) 306 | return 0 307 | 308 | 309 | reveal functions 310 | 311 | def sum(a:int,b:int,c:int,d:int,e:int,f:int,g:int,h:int)-> int : 312 | return a + b + c + d + e + f + g + h 313 | 314 | def main()-> int : 315 | print(sum(%rip)(5, 5, 5, 5, 5, 5, 5, 7)) 316 | return 0 317 | 318 | limit functions 319 | 320 | def sum(a:int,b:int,c:int,d:int,e:int,tup.0:(int,int,int))-> int : 321 | return a + b + c + d + e + tup.0[0] + tup.0[1] + tup.0[2] 322 | def main()-> int : 323 | print(sum(%rip)(5, 5, 5, 5, 5, (5,5,7,))) 324 | return 0 325 | 326 | expose allocation 327 | 328 | def sum(a:,b:,c:,d:,e:,tup.0:(,,))-> : 329 | return a + b + c + d + e + tup.0[0] + tup.0[1] + tup.0[2] 330 | def main()-> : 331 | print(sum(%rip)(5, 5, 5, 5, 5, begin: 332 | if free_ptr + 32 < fromspace_end: 333 | else: 334 | collect(32) 335 | alloc.1 = allocate(3,(,,)) 336 | alloc.1[0] = 5 337 | alloc.1[1] = 5 338 | alloc.1[2] = 7 339 | alloc.1)) 340 | return 0 341 | 342 | remove complex operands 343 | 344 | def sum(a:,b:,c:,d:,e:,tup.0:(,,))-> : 345 | tmp.2 = a + b 346 | tmp.3 = tmp.2 + c 347 | tmp.4 = tmp.3 + d 348 | tmp.5 = tmp.4 + e 349 | tmp.6 = tup.0[0] 350 | tmp.7 = tmp.5 + tmp.6 351 | tmp.8 = tup.0[1] 352 | tmp.9 = tmp.7 + tmp.8 353 | tmp.10 = tup.0[2] 354 | return tmp.9 + tmp.10 355 | def main()-> : 356 | fun.11 = sum(%rip) 357 | tmp.15 = begin: 358 | tmp.12 = free_ptr 359 | tmp.13 = tmp.12 + 32 360 | tmp.14 = fromspace_end 361 | if tmp.13 < tmp.14: 362 | else: 363 | collect(32) 364 | alloc.1 = allocate(3,(,,)) 365 | alloc.1[0] = 5 366 | alloc.1[1] = 5 367 | alloc.1[2] = 7 368 | alloc.1 369 | tmp.16 = fun.11(5, 5, 5, 5, 5, tmp.15) 370 | print(tmp.16) 371 | return 0 372 | 373 | explicate control 374 | 375 | def sum(a:,b:,c:,d:,e:,tup.0:(,,))-> : 376 | _sumstart: 377 | tmp.2 = a + b 378 | tmp.3 = tmp.2 + c 379 | tmp.4 = tmp.3 + d 380 | tmp.5 = tmp.4 + e 381 | tmp.6 = tup.0[0] 382 | tmp.7 = tmp.5 + tmp.6 383 | tmp.8 = tup.0[1] 384 | tmp.9 = tmp.7 + tmp.8 385 | tmp.10 = tup.0[2] 386 | return tmp.9 + tmp.10 387 | 388 | def main()-> : 389 | _block.17: 390 | alloc.1 = allocate(3,(,,)) 391 | alloc.1[0] = 5 392 | alloc.1[1] = 5 393 | alloc.1[2] = 7 394 | tmp.15 = alloc.1 395 | tmp.16 = fun.11(5, 5, 5, 5, 5, tmp.15) 396 | print(tmp.16) 397 | return 0 398 | _block.18: 399 | goto _block.17 400 | _block.19: 401 | collect(32) 402 | goto _block.17 403 | _mainstart: 404 | fun.11 = sum(%rip) 405 | tmp.12 = free_ptr 406 | tmp.13 = tmp.12 + 32 407 | tmp.14 = fromspace_end 408 | if tmp.13 < tmp.14: 409 | goto _block.18 410 | else: 411 | goto _block.19 412 | 413 | select instructions 414 | 415 | def _sum(a:,b:,c:,d:,e:,tup.0:(,,))-> : 416 | _sumstart: 417 | movq %rdi, a 418 | movq %rsi, b 419 | movq %rdx, c 420 | movq %rcx, d 421 | movq %r8, e 422 | movq %r9, tup.0 423 | movq a, tmp.2 424 | addq b, tmp.2 425 | movq tmp.2, tmp.3 426 | addq c, tmp.3 427 | movq tmp.3, tmp.4 428 | addq d, tmp.4 429 | movq tmp.4, tmp.5 430 | addq e, tmp.5 431 | movq tup.0, %r11 432 | movq 8(%r11), %r11 433 | movq %r11, tmp.6 434 | movq tmp.5, tmp.7 435 | addq tmp.6, tmp.7 436 | movq tup.0, %r11 437 | movq 16(%r11), %r11 438 | movq %r11, tmp.8 439 | movq tmp.7, tmp.9 440 | addq tmp.8, tmp.9 441 | movq tup.0, %r11 442 | movq 24(%r11), %r11 443 | movq %r11, tmp.10 444 | movq tmp.9, %rax 445 | addq tmp.10, %rax 446 | jmp _sumconclusion 447 | 448 | def _main()-> : 449 | _block.17: 450 | movq _free_ptr(%rip), %r11 451 | addq $32, _free_ptr(%rip) 452 | movq $7, 0(%r11) 453 | movq %r11, alloc.1 454 | movq alloc.1, %r11 455 | movq $5, 8(%r11) 456 | movq alloc.1, %r11 457 | movq $5, 16(%r11) 458 | movq alloc.1, %r11 459 | movq $7, 24(%r11) 460 | movq alloc.1, tmp.15 461 | movq $5, %rdi 462 | movq $5, %rsi 463 | movq $5, %rdx 464 | movq $5, %rcx 465 | movq $5, %r8 466 | movq tmp.15, %r9 467 | callq *fun.11 468 | movq %rax, tmp.16 469 | movq tmp.16, %rdi 470 | callq _print_int 471 | movq $0, %rax 472 | jmp _mainconclusion 473 | _block.18: 474 | jmp _block.17 475 | _block.19: 476 | movq %r15, %rdi 477 | movq $32, %rsi 478 | callq _collect 479 | jmp _block.17 480 | _mainstart: 481 | leaq _sum(%rip), fun.11 482 | movq _free_ptr(%rip), tmp.12 483 | movq tmp.12, tmp.13 484 | addq $32, tmp.13 485 | movq _fromspace_end(%rip), tmp.14 486 | cmpq tmp.14, tmp.13 487 | jl _block.18 488 | jmp _block.19 489 | 490 | 491 | 492 | register allocation 493 | 494 | def _sum(a:,b:,c:,d:,e:,tup.0:(,,))-> : 495 | _sumstart: 496 | movq %rdi, %rdi 497 | movq %rsi, %rsi 498 | movq %rdx, %rdx 499 | movq %rcx, %rcx 500 | movq %r8, %r8 501 | movq %r9, %r9 502 | movq %rdi, %rdi 503 | addq %rsi, %rdi 504 | movq %rdi, %rsi 505 | addq %rdx, %rsi 506 | movq %rsi, %rdx 507 | addq %rcx, %rdx 508 | movq %rdx, %rcx 509 | addq %r8, %rcx 510 | movq %r9, %r11 511 | movq 8(%r11), %r11 512 | movq %r11, %rdx 513 | movq %rcx, %rcx 514 | addq %rdx, %rcx 515 | movq %r9, %r11 516 | movq 16(%r11), %r11 517 | movq %r11, %rdx 518 | movq %rcx, %rcx 519 | addq %rdx, %rcx 520 | movq %r9, %r11 521 | movq 24(%r11), %r11 522 | movq %r11, %rdx 523 | movq %rcx, %rax 524 | addq %rdx, %rax 525 | jmp _sumconclusion 526 | 527 | def _main()-> : 528 | _block.17: 529 | movq _free_ptr(%rip), %r11 530 | addq $32, _free_ptr(%rip) 531 | movq $7, 0(%r11) 532 | movq %r11, %rcx 533 | movq %rcx, %r11 534 | movq $5, 8(%r11) 535 | movq %rcx, %r11 536 | movq $5, 16(%r11) 537 | movq %rcx, %r11 538 | movq $7, 24(%r11) 539 | movq %rcx, %r9 540 | movq $5, %rdi 541 | movq $5, %rsi 542 | movq $5, %rdx 543 | movq $5, %rcx 544 | movq $5, %r8 545 | movq %r9, %r9 546 | callq *%rbx 547 | movq %rax, %rcx 548 | movq %rcx, %rdi 549 | callq _print_int 550 | movq $0, %rax 551 | jmp _mainconclusion 552 | _block.18: 553 | jmp _block.17 554 | _block.19: 555 | movq %r15, %rdi 556 | movq $32, %rsi 557 | callq _collect 558 | jmp _block.17 559 | _mainstart: 560 | leaq _sum(%rip), %rbx 561 | movq _free_ptr(%rip), %rcx 562 | movq %rcx, %rdx 563 | addq $32, %rdx 564 | movq _fromspace_end(%rip), %rcx 565 | cmpq %rcx, %rdx 566 | jl _block.18 567 | jmp _block.19 568 | 569 | 570 | patch instructions 571 | 572 | def _sum(a:,b:,c:,d:,e:,tup.0:(,,))-> : 573 | _sumstart: 574 | addq %rsi, %rdi 575 | movq %rdi, %rsi 576 | addq %rdx, %rsi 577 | movq %rsi, %rdx 578 | addq %rcx, %rdx 579 | movq %rdx, %rcx 580 | addq %r8, %rcx 581 | movq %r9, %r11 582 | movq 8(%r11), %r11 583 | movq %r11, %rdx 584 | addq %rdx, %rcx 585 | movq %r9, %r11 586 | movq 16(%r11), %r11 587 | movq %r11, %rdx 588 | addq %rdx, %rcx 589 | movq %r9, %r11 590 | movq 24(%r11), %r11 591 | movq %r11, %rdx 592 | movq %rcx, %rax 593 | addq %rdx, %rax 594 | jmp _sumconclusion 595 | 596 | def _main()-> : 597 | _block.17: 598 | movq _free_ptr(%rip), %r11 599 | addq $32, _free_ptr(%rip) 600 | movq $7, 0(%r11) 601 | movq %r11, %rcx 602 | movq %rcx, %r11 603 | movq $5, 8(%r11) 604 | movq %rcx, %r11 605 | movq $5, 16(%r11) 606 | movq %rcx, %r11 607 | movq $7, 24(%r11) 608 | movq %rcx, %r9 609 | movq $5, %rdi 610 | movq $5, %rsi 611 | movq $5, %rdx 612 | movq $5, %rcx 613 | movq $5, %r8 614 | callq *%rbx 615 | movq %rax, %rcx 616 | movq %rcx, %rdi 617 | callq _print_int 618 | movq $0, %rax 619 | jmp _mainconclusion 620 | _block.18: 621 | jmp _block.17 622 | _block.19: 623 | movq %r15, %rdi 624 | movq $32, %rsi 625 | callq _collect 626 | jmp _block.17 627 | _mainstart: 628 | leaq _sum(%rip), %rbx 629 | movq _free_ptr(%rip), %rcx 630 | movq %rcx, %rdx 631 | addq $32, %rdx 632 | movq _fromspace_end(%rip), %rcx 633 | cmpq %rcx, %rdx 634 | jl _block.18 635 | jmp _block.19 636 | 637 | 638 | prelude and conclusion 639 | 640 | .align 16 641 | _sumstart: 642 | addq %rsi, %rdi 643 | movq %rdi, %rsi 644 | addq %rdx, %rsi 645 | movq %rsi, %rdx 646 | addq %rcx, %rdx 647 | movq %rdx, %rcx 648 | addq %r8, %rcx 649 | movq %r9, %r11 650 | movq 8(%r11), %r11 651 | movq %r11, %rdx 652 | addq %rdx, %rcx 653 | movq %r9, %r11 654 | movq 16(%r11), %r11 655 | movq %r11, %rdx 656 | addq %rdx, %rcx 657 | movq %r9, %r11 658 | movq 24(%r11), %r11 659 | movq %r11, %rdx 660 | movq %rcx, %rax 661 | addq %rdx, %rax 662 | jmp _sumconclusion 663 | 664 | .align 16 665 | _sum: 666 | pushq %rbp 667 | movq %rsp, %rbp 668 | subq $0, %rsp 669 | jmp _sumstart 670 | 671 | .align 16 672 | _sumconclusion: 673 | subq $0, %r15 674 | addq $0, %rsp 675 | popq %rbp 676 | retq 677 | 678 | .align 16 679 | _block.17: 680 | movq _free_ptr(%rip), %r11 681 | addq $32, _free_ptr(%rip) 682 | movq $7, 0(%r11) 683 | movq %r11, %rcx 684 | movq %rcx, %r11 685 | movq $5, 8(%r11) 686 | movq %rcx, %r11 687 | movq $5, 16(%r11) 688 | movq %rcx, %r11 689 | movq $7, 24(%r11) 690 | movq %rcx, %r9 691 | movq $5, %rdi 692 | movq $5, %rsi 693 | movq $5, %rdx 694 | movq $5, %rcx 695 | movq $5, %r8 696 | callq *%rbx 697 | movq %rax, %rcx 698 | movq %rcx, %rdi 699 | callq _print_int 700 | movq $0, %rax 701 | jmp _mainconclusion 702 | 703 | .align 16 704 | _block.18: 705 | jmp _block.17 706 | 707 | .align 16 708 | _block.19: 709 | movq %r15, %rdi 710 | movq $32, %rsi 711 | callq _collect 712 | jmp _block.17 713 | 714 | .align 16 715 | _mainstart: 716 | leaq _sum(%rip), %rbx 717 | movq _free_ptr(%rip), %rcx 718 | movq %rcx, %rdx 719 | addq $32, %rdx 720 | movq _fromspace_end(%rip), %rcx 721 | cmpq %rcx, %rdx 722 | jl _block.18 723 | jmp _block.19 724 | 725 | .globl _main 726 | .align 16 727 | _main: 728 | pushq %rbp 729 | movq %rsp, %rbp 730 | pushq %rbx 731 | subq $8, %rsp 732 | movq $65536, %rdi 733 | movq $65536, %rsi 734 | callq _initialize 735 | movq _rootstack_begin(%rip), %r15 736 | jmp _mainstart 737 | 738 | .align 16 739 | _mainconclusion: 740 | subq $0, %r15 741 | addq $8, %rsp 742 | popq %rbx 743 | popq %rbp 744 | retq 745 | 746 | -------------------------------------------------------------------------------- /lectures/Nov-16.md: -------------------------------------------------------------------------------- 1 | # Lambda: Lexically Scoped Functions 2 | 3 | ## Example 4 | 5 | Racket: 6 | 7 | (define (f [x : Integer]) : (Integer -> Integer) 8 | (let ([y 4]) 9 | (lambda: ([z : Integer]) : Integer 10 | (+ x (+ y z))))) 11 | 12 | (let ([g (f 5)]) 13 | (let ([h (f 3)]) 14 | (+ (g 11) (h 15)))) 15 | 16 | Python: 17 | 18 | def f(x : int) -> Callable[[int], int]: 19 | y = 4 20 | return lambda z: x + y + z 21 | 22 | g = f(5) 23 | h = f(3) 24 | print( g(11) + h(15) ) 25 | 26 | 27 | ## Syntax 28 | 29 | concrete syntax: 30 | 31 | exp ::= ... | (lambda: ([var : type]...) : type exp) 32 | Llambda ::= def* exp 33 | 34 | abstract syntax: 35 | 36 | exp ::= ... | (Lambda ([var : type]...) type exp) 37 | Llambda ::= (ProgramDefsExp info def* exp) 38 | 39 | (Let var exp exp) 40 | 41 | ## Interpreter for Llambda 42 | 43 | see `interp-Rlambda.rkt`: 44 | 45 | * case for lambda, 46 | * case for application, 47 | * case for define (mcons), 48 | * case for program (backpatching). 49 | 50 | ## Type Checker for Llambda 51 | 52 | see `type-check-Rlambda.rkt`: 53 | 54 | The case for lambda. 55 | 56 | ## Free Variables 57 | 58 | Def. A variable is *free with respect to an expression* e if the 59 | variable occurs inside e but does not have an enclosing binding in e. 60 | 61 | Use above example to show examples of free variables. 62 | 63 | ## Closure Representation 64 | 65 | Figure 7.2 in book, diagram of g and h from above example. 66 | 67 | # Closure Conversion Pass (after reveal-functions) 68 | 69 | 1. Translate each lambda into a "flat closure" 70 | 71 | (lambda: (ps ...) : rt body) 72 | ==> 73 | (vector (function-ref name) fvs ...) 74 | 75 | 2. Generate a top-level function for each lambda 76 | 77 | (define (lambda_i [clos : _] ps ...) 78 | (let ([fv_1 (vector-ref clos 1)]) 79 | (let ([fv_2 (vector-ref clos 2)]) 80 | ... 81 | body'))) 82 | 83 | 3. Translate every function application into an application of a closure: 84 | 85 | (e es ...) 86 | ==> 87 | (let ([tmp e']) 88 | ((vector-ref tmp 0) tmp es' ...)) 89 | 90 | ## Example 91 | 92 | (define (f (x : Integer)) : (Integer -> Integer) 93 | (let ((y 4)) 94 | (lambda: ((z : Integer)) : Integer 95 | (+ x (+ y z))))) 96 | 97 | (let ((g ((fun-ref f) 5))) 98 | (let ((h ((fun-ref f) 3))) 99 | (+ (g 11) (h 15)))) 100 | 101 | ==> 102 | 103 | (define (f (clos.1 : _) (x : Integer)) : (Vector ((Vector _) Integer -> Integer)) 104 | (let ((y 4)) 105 | (vector (fun-ref lam.1) x y))) 106 | 107 | (define (lam.1 (clos.2 : (Vector _ Integer Integer)) (z : Integer)) : Integer 108 | (let ((x (vector-ref clos.2 1))) 109 | (let ((y (vector-ref clos.2 2))) 110 | (+ x (+ y z))))) 111 | 112 | (let ((g (let ((t.1 (vector (fun-ref f)))) 113 | ((vector-ref t.1 0) t.1 5)))) 114 | (let ((h (let ((t.2 (vector (fun-ref f)))) 115 | ((vector-ref t.2 0) t.2 3)))) 116 | (+ (let ((t.3 g)) ((vector-ref t.3 0) t.3 11)) 117 | (let ((t.4 h)) ((vector-ref t.4 0) t.4 15))))) 118 | -------------------------------------------------------------------------------- /lectures/Nov-18.md: -------------------------------------------------------------------------------- 1 | # Closure Conversion Pass (after reveal-functions) 2 | 3 | 1. Translate each lambda into a "flat closure" 4 | 5 | Let `fv1, fv2, ...` be the free variables of the lambda. 6 | 7 | Racket 8 | 9 | (lambda: (ps ...) : rt body) 10 | ==> 11 | (vector lambda_name fv1 fv2 ...) 12 | 13 | Python 14 | 15 | lambda ps... : body 16 | ==> 17 | (lambda_name, fv1, fv2, ...) 18 | 19 | 2. Generate a top-level function for each lambda 20 | 21 | Let `FT1, FT2, ...` be the types of the free variables 22 | and the `has_type` of the lambda is `FunctionType([PT1, ...], RT)`. 23 | 24 | Racket 25 | 26 | (define (lambda_name [clos : _] ps ...) -> rt 27 | (let ([fv1 (vector-ref clos 1)]) 28 | (let ([fv2 (vector-ref clos 2)]) 29 | ... 30 | body'))) 31 | 32 | Python 33 | 34 | def lambda_name(clos : TupleType([_,FT1,FT2,...]), p1:PT1, ...) -> RT: 35 | fv1 = clos[1] 36 | fv2 = clos[2] 37 | ... 38 | return body' 39 | 40 | 3. Translate every function application into an application of a closure: 41 | 42 | Racket 43 | 44 | (e es ...) 45 | ==> 46 | (let ([tmp e']) 47 | ((vector-ref tmp 0) tmp es' ...)) 48 | 49 | Python 50 | 51 | e0(e1, ..., en) 52 | ==> 53 | let tmp = e0' in tmp[0](tmp, e1', ..., en') 54 | 55 | 56 | 57 | 58 | # Basic Example of Closure Conversion 59 | 60 | ## source program 61 | 62 | def f(x:int)-> Callable[[int],int] : 63 | y = 4 64 | return (lambda z: x + y + z) 65 | g = f(5) 66 | h = f(3) 67 | print(g(11) + h(15)) 68 | 69 | 70 | ## box free variables 71 | 72 | def f(x.0:int)-> Callable[[int], int] : 73 | x = (x.0,) 74 | y = (777,) 75 | y[0] = 4 76 | return (lambda z: x[0] + y[0] + z) 77 | 78 | def main()-> int : 79 | g = {f}(5) 80 | h = {f}(3) 81 | print(g(11) + h(15)) 82 | return 0 83 | 84 | ## closure conversion 85 | 86 | def lambda.1(fvs.2:(bot,(int),(int)),z:int)-> int : 87 | y = fvs.2[1] 88 | x = fvs.2[2] 89 | return x[0] + y[0] + z 90 | 91 | def f(fvs.3:bot,x.0:int)-> (Callable[[(),int], int]) : 92 | x = (x.0,) 93 | y = (777,) 94 | y[0] = 4 95 | return closure({lambda.1},y,x) 96 | 97 | def main()-> int : 98 | g = (let clos.4 = closure({f}) in clos.4[0](clos.4, 5)) 99 | h = (let clos.5 = closure({f}) in clos.5[0](clos.5, 3)) 100 | print((let clos.6 = g in clos.6[0](clos.6, 11)) + (let clos.7 = h in clos.7[0](clos.7, 15))) 101 | return 0 102 | 103 | 104 | # Example that Motivates Boxing Free Variables 105 | 106 | ## source program 107 | 108 | x = 0 109 | y = 0 110 | z = 20 111 | f : Callable[[int], int] = (lambda a: a + x + z) 112 | x = 10 113 | y = 12 114 | print(f(y)) 115 | 116 | ## box free variables 117 | 118 | def main()-> int: 119 | x = (777,) 120 | z = (777,) 121 | x[0] = 0 122 | y = 0 123 | z[0] = 20 124 | f : Callable[[int], int] = (lambda a: a + x[0] + z[0]) 125 | x[0] = 10 126 | y = 12 127 | print(f(y)) 128 | return 0 129 | 130 | ## closure conversion 131 | 132 | def lambda.0(fvs.1:(bot,(int),(int)),a:int)-> int : 133 | z = fvs.1[1] 134 | x = fvs.1[2] 135 | return a + x[0] + z[0] 136 | 137 | def main()-> int : 138 | x = (777,) 139 | z = (777,) 140 | x[0] = 0 141 | y = 0 142 | z[0] = 20 143 | f = closure({lambda.0},z,x) 144 | x[0] = 10 145 | y = 12 146 | print((let clos.2 = f in clos.2[0](clos.2, y))) 147 | return 0 148 | 149 | 150 | -------------------------------------------------------------------------------- /lectures/Nov-2.md: -------------------------------------------------------------------------------- 1 | # Compiling Functions 2 | 3 | ## The Lfun Language 4 | 5 | Concrete Syntax for Racket: 6 | 7 | type ::= ... | (type... -> type) 8 | exp ::= ... | (exp exp...) 9 | def ::= (define (var [var : type]...) : type exp) 10 | Lfun ::= def... exp 11 | 12 | Concrete Syntax for Python: 13 | 14 | type ::= ... | Callable[[type, ...], type] 15 | exp ::= ... | exp(exp, ...) 16 | def ::= def var(var : type,...) -> type: stmt ... 17 | Lfun ::= def... stmt... 18 | 19 | Abstract Syntax for Racket: 20 | 21 | exp ::= ... | (Apply exp exp...) 22 | def ::= (Def var ([var : type] ...) type '() exp) 23 | Lfun ::= (ProgramDefsExp '() (def ...) exp) 24 | 25 | Abstract Syntax for Python: 26 | 27 | type ::= ... | FunctionType(type*, type) 28 | exp ::= ... | Call(exp, exp*) 29 | def ::= FunctionDef(var, [(var, type) ,...], type, stmt*) 30 | Lfun ::= Module([def... stmt...]) 31 | 32 | * Because of the function type, functions are first-class in that they 33 | can be passed as arguments to other functions and returned from 34 | them. They can also be stored inside tuples. 35 | 36 | * Functions may be recursive and even mutually recursive. That is, 37 | each function name is in scope for the entire program. 38 | 39 | Example program in Racket: 40 | 41 | (define (map [f : (Integer -> Integer)] 42 | [v : (Vector Integer Integer)]) : (Vector Integer Integer) 43 | (vector (f (vector-ref v 0)) (f (vector-ref v 1)))) 44 | 45 | (define (add1 [x : Integer]) : Integer 46 | (+ x 1)) 47 | 48 | (vector-ref (map add1 (vector 0 41)) 1) 49 | 50 | Example program in Python: 51 | 52 | def map(f : Callable[[int], int], v : tuple[int,int]) -> tuple[int,int]: 53 | return f(v[0]), f(v[1]) 54 | 55 | def inc(x : int) -> int: 56 | return x + 1 57 | 58 | print( map(inc, (0, 41))[1] ) 59 | 60 | Go over the interpreter (Fig. 6.4) 61 | 62 | Go over the type checker. 63 | 64 | ## Functions in x86 65 | 66 | Labels can be used to mark the beginning of a function 67 | 68 | The address of a label can be obtained using the `leaq` instruction 69 | and PC-relative addressing: 70 | 71 | leaq add1(%rip), %rbx 72 | 73 | Calling a function whose address is in a register, i.e., indirect 74 | function call. 75 | 76 | callq *%rbx 77 | 78 | -------------------------------------------------------------------------------- /lectures/Nov-4.md: -------------------------------------------------------------------------------- 1 | ## Functions in x86, continued 2 | 3 | Last time we finished with a discussion of taking a function's address 4 | and then doing an indirect call. 5 | 6 | The address of a label can be obtained using the `leaq` instruction 7 | and PC-relative addressing: 8 | 9 | leaq add1(%rip), %rbx 10 | 11 | Calling a function whose address is in a register, i.e., indirect 12 | function call. 13 | 14 | callq *%rbx 15 | 16 | 17 | ### Abstract Syntax: 18 | 19 | arg ::= ... | (FunRef label) 20 | instr ::= ... | (IndirectCallq arg) | (TailJmp arg) 21 | | (Instr 'leaq (list arg arg)) 22 | def ::= (Def label '() '() info ((label . block) ...)) 23 | pseudo-x86 ::= (ProgramDefs info (def...)) 24 | 25 | ### Calling Conventions 26 | 27 | The `callq` instruction 28 | 1. pushes the return address onto the stack 29 | 2. jumps to the target label or address (for indirect call) 30 | 31 | But there is more to do to make a function call: 32 | 1. parameter passing 33 | 2. pushing and popping frames on the procedure call stack 34 | 3. coordinating the use of registers for local variables 35 | 36 | 37 | #### Parameter Passing 38 | 39 | The C calling convention uses the following six registers (in that order) 40 | for argument passing: 41 | 42 | rdi, rsi, rdx, rcx, r8, r9 43 | 44 | The calling convention says that the stack may be used for argument 45 | passing if there are more than six arguments, but we shall take an 46 | alternate approach that makes it easier to implement efficient tail 47 | calls. If there are more than six arguments, then `r9` will store a 48 | tuple containing the sixth argument and the rest of the arguments. 49 | 50 | #### Pushing and Popping Frames 51 | 52 | The instructions for each function will have a prelude and conclusion 53 | similar to the one we've been generating for `main`. 54 | 55 | The most important aspect of the prelude is moving the stack pointer 56 | down by the size needed the function's frame. Similarly, the 57 | conclusion needs to move the stack pointer back up. 58 | 59 | Recall that we are storing variables of vector type on the root stack. 60 | So the prelude needs to move the root stack pointer `r15` up and the 61 | conclusion needs to move the root stack pointer back down. Also, in 62 | the prelude, this frame's slots in the root stack must be initialized 63 | to `0` to signal to the garbage collector that those slots do not yet 64 | contain a pointer to a vector. 65 | 66 | As we did for `main`, the prelude must also save the contents of the 67 | old base pointer `rbp` and set it to the top of the frame, so that we 68 | can use it for accessing local variables that have been spilled to the 69 | stack. 70 | 71 | |Caller View | Callee View | Contents | Frame 72 | |---------------|---------------|----------------|--------- 73 | | 8(%rbp) | | return address | 74 | | 0(%rbp) | | old rbp | 75 | | -8(%rbp) | | callee-saved | Caller (e.g. map) 76 | | ... | | ... | 77 | | -8(j+1)(%rbp) | | spill | 78 | | ... | | ... | 79 | | | 8(%rbp) | return address | 80 | | | 0(%rbp) | old rbp | 81 | | | -8(%rbp) | callee-saved | Callee (e.g. add1 as f) 82 | | | ... | ... | 83 | | | -8(j+1)(%rbp) | spill | 84 | | | ... | ... | 85 | 86 | 87 | #### Coordinating Registers 88 | 89 | Recall that the registers are categorized as either caller-saved or 90 | callee-saved. 91 | 92 | If the function uses any of the callee-saved registers, then the 93 | previous contents of those registers needs to be saved and restored in 94 | the prelude and conclusion of the function. 95 | 96 | Regarding caller-saved registers, nothing new needs to be done. 97 | Recall that we make sure not to assign call-live variables to 98 | caller-saved registers. 99 | 100 | #### Efficient Tail Calls 101 | 102 | Normally the amount of stack space used by a program is O(d) where d 103 | is the depth of nested function calls. 104 | 105 | This means that recursive functions almost always use at least O(n) 106 | space. 107 | 108 | However, we can sometimes use much less space. 109 | 110 | A *tail call* is a function call that is the last thing to happen 111 | inside another function. 112 | 113 | Example: the recursive call to `tail_sum` is a tail call. 114 | 115 | (define (tail_sum [n : Integer] [r : Integer]) : Integer 116 | (if (eq? n 0) 117 | r 118 | (tail_sum (- n 1) (+ n r)))) 119 | 120 | (+ (tail_sum 3 0) 36) 121 | 122 | In Python: 123 | 124 | def tail_sum(n : int, r : int) -> int: 125 | if n == 0: 126 | return r 127 | else: 128 | return tail_sum(n - 1, n + r) 129 | 130 | print( tail_sum(3, 0) + 36) 131 | 132 | Because a tail call is the last thing to happen, we no longer need the 133 | caller's frame and can reuse that stack space for the callee's frame. 134 | So we can clean up the current frame and then jump to the callee. 135 | However, some care must be taken regarding argument passing. 136 | 137 | The standard convention for passing more than 6 arguments is to use 138 | slots in the caller's frame. But we're deleting the caller's frame. 139 | We could use the callee's frame, but its difficult to move all the 140 | variables without stomping on eachother because the caller and callee 141 | frames overlap in memory. This could be solved by using auxilliary 142 | memory somewhere else, but that increases the amount of memory 143 | traffic. 144 | 145 | We instead recommend using the heap to pass the arguments that don't 146 | fit in the 6 registers. 147 | 148 | Instead of `callq`, use `jmp` for the tail call because the return 149 | address that is already on the stack is the correct one. 150 | 151 | Use `rax` to hold the target address for an indirect jump. 152 | 153 | ## Shrink 154 | 155 | (ProgramDefsExp info defs exp) 156 | ==> 157 | (ProgramDefs info (append defs (list mainDef))) 158 | 159 | where `mainDef` is 160 | 161 | (Def 'main '() 'Integer '() exp') 162 | 163 | ## Reveal Functions (new) 164 | 165 | We'll need to generate `leaq` instructions for references to 166 | functions, so it makes sense to differentiate them from let-bound 167 | variables. 168 | 169 | (Var x) 170 | ==> 171 | (Var x) 172 | 173 | (Var f) 174 | ==> 175 | (FunRef f) 176 | 177 | 178 | (Let x (Bool #t) 179 | (Apply (If (Var x) (Var 'add1) (Var 'sub1)) 180 | (Int 41))) 181 | => 182 | (Let x (Bool #t) 183 | (Apply (If (Var x) (FunRef 'add1) (FunRef 'sub1)) 184 | (Int 41))) 185 | 186 | 187 | ## Limit Functions (new) 188 | 189 | Transform functions so that have at most 6 parameters. 190 | 191 | ### Function definition 192 | 193 | (Def f ([x1 : t1] ... [xn : tn]) rt info body) 194 | ==> 195 | (Def f ([x1 : t1] ... [x5 : t5] [vec : (Vector t6 ... tn)]) rt info 196 | new-body) 197 | 198 | and transform the `body`, replace occurences of parameters `x6` and 199 | higher as follows 200 | 201 | x6 202 | ==> 203 | (vector-ref vec 0) 204 | 205 | x7 206 | ==> 207 | (vector-ref vec 1) 208 | 209 | ... 210 | 211 | ### Function application 212 | 213 | If there are more than 6 arguments, pass arguments 6 and higher in a 214 | vector: 215 | 216 | (Apply e0 (e1 ... en)) 217 | ==> 218 | (Apply e0 (e1 ... e5 (vector e6 ... en))) 219 | 220 | 221 | ## Remove Complex Operands 222 | 223 | Treat `FunRef` and `Apply` as complex operands. 224 | 225 | (Prim '+ (list (Int 5) (FunRef add1))) 226 | => 227 | (Let ([tmp (FunRef add1)]) 228 | (Prim '+ (list (Int 5) (Var tmp)))) 229 | 230 | Arguments of `Apply` need to be atomic expressions. 231 | 232 | 233 | ## Explicate Control 234 | 235 | * assignment 236 | * predicate 237 | * statement (python) 238 | * tail (new for python) 239 | 240 | Add cases for `FunRef` and `Apply` to the three helper functions 241 | for assignment, tail, and predicate contexts. 242 | 243 | In assignment and predicate contexts, `Apply` becomes `Call`. 244 | 245 | In tail contexts, `Apply` becomes `TailCall`. 246 | 247 | You'll need a new helper function for function definitions. 248 | The code will be similar to the previous code for `Program` 249 | 250 | Previous assignment: 251 | 252 | (define/override (explicate-control p) 253 | (match p 254 | [(Program info body) 255 | (set! control-flow-graph '()) 256 | (define-values (body-block vars) (explicate-tail body)) 257 | (define new-info (dict-set info 'locals vars)) 258 | (Program new-info 259 | (CFG (dict-set control-flow-graph 'start body-block)))] 260 | )) 261 | 262 | adapt the above to process every function definition. 263 | 264 | 265 | ## Select Instructions 266 | 267 | ### `FunRef` becomes `leaq` 268 | 269 | We'll keep `FunRef` as an instruction argument for now, 270 | placing it in a `leaq` instruction. 271 | 272 | (Assign lhs (FunRef f)) 273 | ==> 274 | (Instr 'leaq (list (FunRef f) lhs')) 275 | 276 | ### `Call` becomes `IndirectCallq` 277 | 278 | (Assign lhs (Call fun (arg1 ... argn))) 279 | ==> 280 | movq arg'1 rdi 281 | movq arg'2 rsi 282 | ... 283 | (IndirectCallq fun') 284 | (Instr 'movq (Reg 'rax) lhs') 285 | 286 | ### `TailCall` becomes `TailJmp` 287 | 288 | We postpone the work of popping the frame until later by inventing an 289 | instruction we'll call `TailJmp`. 290 | 291 | (TailCall fun (arg1 ... argn)) 292 | ==> 293 | movq arg'1 rdi 294 | movq arg'2 rsi 295 | ... 296 | (TailJmp fun') 297 | 298 | ### Function Definitions 299 | 300 | (Def f ([x1 : T1] ... [xn : Tn]) rt info blocks) 301 | 1. blocks => blocks' 302 | 2. prepend to start block from blocks' 303 | movq rdi x1 304 | ... 305 | 4. parameters get added to the list of local variables 306 | => 307 | (Def f '() '() new-info new-CFG) 308 | 309 | alternative: 310 | replace parameters (in the CFG) with argument registers 311 | 312 | 313 | ## Uncover Live 314 | 315 | New helper function for function definitions. 316 | 317 | `leaq` reads from the first argument and writes to the second. 318 | 319 | `IndirectCallq` and `TailJmp` read from their argument and you must 320 | assume they write to all the caller-saved registers. 321 | 322 | ## Build Interference Graph 323 | 324 | New helper function for function definitions. 325 | 326 | Compute one interference graph per function. 327 | 328 | Spill vector-typed variables that are live during a function call. 329 | (Because our functions make trigger `collect`.) So add interference 330 | edges between those variables and the callee-saved registers. 331 | 332 | ## Patch Instructions 333 | 334 | The destination of `leaq` must be a register. 335 | 336 | The destination of `TailJmp` should be `rax`. 337 | 338 | (TailJmp %rbx) 339 | ==> 340 | movq %rbx, %rax 341 | (TailJmp rax) 342 | 343 | ## Print x86 344 | 345 | 346 | (FunRef label) => label(%rip) 347 | 348 | (IndirectCallq arg) => callq *arg 349 | 350 | (TailJmp rax) 351 | => 352 | addq frame-size, %rsp move stack pointer up 353 | popq %rbx callee-saved registers 354 | ... 355 | subq root-frame-size, %r15 move root-stack pointer 356 | popq %rbp restore rbp 357 | jmp *%rax jump to the target function 358 | 359 | -------------------------------------------------------------------------------- /lectures/Nov-9.md: -------------------------------------------------------------------------------- 1 | # Compiling Functions, Continued 2 | 3 | ## Example of a simple function 4 | 5 | source program 6 | 7 | def add(x:int,y:int)-> int : 8 | return x + y 9 | print(add(40, 2)) 10 | 11 | shrink 12 | 13 | def add(x:,y:)-> : 14 | return x + y 15 | def main()-> : 16 | print(add(40, 2)) 17 | return 0 18 | 19 | reveal functions 20 | 21 | def add(x:,y:)-> : 22 | return x + y 23 | def main()-> : 24 | print(add(%rip)(40, 2)) 25 | return 0 26 | 27 | limit functions 28 | 29 | def add(x:,y:)-> : 30 | return x + y 31 | def main()-> : 32 | print(add(%rip)(40, 2)) 33 | return 0 34 | 35 | expose allocation 36 | 37 | def add(x:,y:)-> : 38 | return x + y 39 | def main()-> : 40 | print(add(%rip)(40, 2)) 41 | return 0 42 | 43 | remove complex operands 44 | 45 | def add(x:,y:)-> : 46 | return x + y 47 | def main()-> : 48 | fun.0 = add(%rip) 49 | tmp.1 = fun.0(40, 2) 50 | print(tmp.1) 51 | return 0 52 | 53 | explicate control 54 | 55 | def add(x:,y:)-> : 56 | _addstart: 57 | return x + y 58 | 59 | def main()-> : 60 | _mainstart: 61 | fun.0 = add(%rip) 62 | tmp.1 = fun.0(40, 2) 63 | print(tmp.1) 64 | return 0 65 | 66 | select 67 | 68 | def _add()-> : 69 | _addstart: 70 | movq %rdi, x 71 | movq %rsi, y 72 | movq x, %rax 73 | addq y, %rax 74 | jmp _addconclusion 75 | 76 | def _main()-> : 77 | _mainstart: 78 | leaq _add(%rip), fun.0 79 | movq $40, %rdi 80 | movq $2, %rsi 81 | callq *fun.0 82 | movq %rax, tmp.1 83 | movq tmp.1, %rdi 84 | callq _print_int 85 | movq $0, %rax 86 | jmp _mainconclusion 87 | 88 | register allocation 89 | 90 | def _add(x:,y:)-> : 91 | _addstart: 92 | movq %rdi, %rcx 93 | movq %rsi, %rdx 94 | movq %rcx, %rax 95 | addq %rdx, %rax 96 | jmp _addconclusion 97 | 98 | def _main()-> : 99 | _mainstart: 100 | leaq _add(%rip), %rcx 101 | movq $40, %rdi 102 | movq $2, %rsi 103 | callq *%rcx 104 | movq %rax, %rcx 105 | movq %rcx, %rdi 106 | callq _print_int 107 | movq $0, %rax 108 | jmp _mainconclusion 109 | 110 | patch instructions 111 | 112 | def _add(x:,y:)-> : 113 | _addstart: 114 | movq %rdi, %rcx 115 | movq %rsi, %rdx 116 | movq %rcx, %rax 117 | addq %rdx, %rax 118 | jmp _addconclusion 119 | 120 | def _main()-> : 121 | _mainstart: 122 | leaq _add(%rip), %rcx 123 | movq $40, %rdi 124 | movq $2, %rsi 125 | callq *%rcx 126 | movq %rax, %rcx 127 | movq %rcx, %rdi 128 | callq _print_int 129 | movq $0, %rax 130 | jmp _mainconclusion 131 | 132 | prelude and conclusion 133 | 134 | .align 16 135 | _addstart: 136 | movq %rdi, %rcx 137 | movq %rsi, %rdx 138 | movq %rcx, %rax 139 | addq %rdx, %rax 140 | jmp _addconclusion 141 | 142 | .align 16 143 | _add: 144 | pushq %rbp 145 | movq %rsp, %rbp 146 | subq $0, %rsp 147 | jmp _addstart 148 | 149 | .align 16 150 | _addconclusion: 151 | subq $0, %r15 152 | addq $0, %rsp 153 | popq %rbp 154 | retq 155 | 156 | .align 16 157 | _mainstart: 158 | leaq _add(%rip), %rcx 159 | movq $40, %rdi 160 | movq $2, %rsi 161 | callq *%rcx 162 | movq %rax, %rcx 163 | movq %rcx, %rdi 164 | callq _print_int 165 | movq $0, %rax 166 | jmp _mainconclusion 167 | 168 | .globl _main 169 | .align 16 170 | _main: 171 | pushq %rbp 172 | movq %rsp, %rbp 173 | subq $0, %rsp 174 | movq $65536, %rdi 175 | movq $65536, %rsi 176 | callq _initialize 177 | movq _rootstack_begin(%rip), %r15 178 | jmp _mainstart 179 | 180 | .align 16 181 | _mainconclusion: 182 | subq $0, %r15 183 | addq $0, %rsp 184 | popq %rbp 185 | retq 186 | 187 | -------------------------------------------------------------------------------- /lectures/Oct-12.md: -------------------------------------------------------------------------------- 1 | # Lecture: Compiling Loops 2 | 3 | 4 | ## Remove Complex Operands 5 | 6 | The condition of `while` may be a complex expression. 7 | 8 | For Racketeers, `while`, `set!`, and `begin` are complex expressions 9 | and all their subexpressions are allowed to be complex. 10 | 11 | 12 | ## Explicate Control 13 | 14 | For Racketeers, the `begin` expression introduces the need for a 15 | new helper function: 16 | 17 | explicate_effect : exp -> tail -> tail 18 | 19 | which is a lot like `explicate_tail` except that when the expression 20 | is obviously pure (no side effects) it can be discarded. 21 | 22 | explicate_effect (WhileLoop cnd body) cont 23 | => 24 | goto loop 25 | 26 | where 27 | body' = explicate_effect body (goto loop) 28 | loop-body = explciate_effect cnd body' cont 29 | 30 | loop: 31 | loop-body 32 | 33 | 34 | ## Select Instructions 35 | 36 | Racket: A call to `read` may now appear as a stand-alone statements. 37 | 38 | 39 | ## Challenge: Constant Propagation 40 | 41 | The idea is that when a variable's value is a constant, replace 42 | uses of the variable with that constant. 43 | 44 | 45 | ### Example 1 46 | 47 | Racket program: 48 | 49 | (let ([a 42]) 50 | (let ([b a]) 51 | b)) 52 | 53 | after instruction selection: 54 | 55 | start: 56 | movq $42, a63570 57 | movq a63570, b63571 58 | movq b63571, %rax 59 | jmp conclusion 60 | 61 | 62 | after constant propagation: 63 | 64 | start: 65 | movq $42, a63570 66 | movq $42, b63571 67 | movq $42, %rax 68 | jmp conclusion 69 | 70 | 71 | ### Example 2 72 | 73 | Racket program: 74 | 75 | (let ([y (read)]) 76 | (let ([x (if (eq? y 0) 77 | 40 78 | 777) 79 | ]) 80 | (+ x 2))) 81 | 82 | after instruction selection: 83 | 84 | start: 85 | callq read_int 86 | movq %rax, y 87 | cmpq $0, y 88 | je block3 89 | jmp block4 90 | block4: 91 | movq $777, x 92 | jmp block2 93 | block3: 94 | movq $40, x 95 | jmp block2 96 | block2: 97 | movq x, %rax 98 | addq $2, %rax 99 | jmp conclusion 100 | 101 | after constant propagation: (no change) 102 | 103 | start: 104 | callq read_int 105 | movq %rax, y 106 | cmpq $0, y 107 | je block3 108 | jmp block4 109 | block4: 110 | movq $777, x 111 | jmp block2 112 | block3: 113 | movq $40, x 114 | jmp block2 115 | block2: 116 | movq x, %rax 117 | addq $2, %rax 118 | jmp conclusion 119 | 120 | -------------------------------------------------------------------------------- /lectures/Oct-14.md: -------------------------------------------------------------------------------- 1 | # Lecture: Tuples and Garbage Collection 2 | 3 | The language Lvec 4 | 5 | Racket: 6 | 7 | exp ::= ... 8 | | (vector exp+) create a tuple 9 | | (vector-ref exp int) read the nth element 10 | | (vector-set! exp int exp) write to the nth element 11 | 12 | Python: 13 | 14 | exp ::= ... 15 | | ( exp , ... ) create a tuple 16 | | exp [ exp ] read the nth element 17 | 18 | Python tuples do not support writing, they are immutable. 19 | 20 | Racket example: 21 | 22 | (let ([t (vector 40 #t (vector 2))]) 23 | (if (vector-ref t 1) 24 | (+ (vector-ref t 0) 25 | (vector-ref (vector-ref t 2) 0)) 26 | 44)) 27 | ==> 28 | 42 29 | 30 | Python example: 31 | 32 | t = (40, True, (2,)) 33 | print( t[0] + t[2][0] if t[1] else 44 ) 34 | ==> 35 | 42 36 | 37 | ## Aliasing 38 | 39 | (let ([t1 (vector 3 7)]) 40 | (let ([t2 t1]) 41 | (let ([_ (vector-set! t2 0 42)]) 42 | (vector-ref t1 0)))) 43 | ==> 44 | 42 45 | 46 | 47 | ## Tuple Lifetime 48 | 49 | (let ([v (vector (vector 44))]) 50 | (let ([x (let ([w (vector 42)]) 51 | (let ([_ (vector-set! v 0 w)]) 52 | 0))]) 53 | (+ x (vector-ref (vector-ref v 0) 0)))) 54 | ===> 55 | 42 56 | 57 | 58 | 59 | ## Garbage Collection 60 | 61 | Def. The *live data* are all of the tuples that might be accessed by 62 | the program in the future. We can overapproximate this as all of the 63 | tuples that are reachable, transitively, from the registers or 64 | procedure call stack. We refer to the registers and stack collectively 65 | as the *root set*. 66 | 67 | The goal of a garbage collector is to reclaim the data that is not 68 | live. 69 | 70 | We shall use a 2-space copying collector, using Cheney's algorithm 71 | (BFS) for the copy. 72 | 73 | Alternative garbage collection techniques: 74 | * generational copy collectors 75 | * mark and sweep 76 | * reference counting + mark and sweep 77 | 78 | Overview of how GC fits into a running program.: 79 | 80 | 0. Ask the OS for 2 big chunks of memory. Call them FromSpace and ToSpace. 81 | 1. Run the program, allocating tuples into the FromSpace. 82 | 2. When the FromSpace is full, copy the *live data* into the ToSpace. 83 | 3. Swap the roles of the ToSpace and FromSpace and go back to step 1. 84 | 85 | Draw Fig. 5.6. (just the FromSpace) 86 | 87 | -------------------------------------------------------------------------------- /lectures/Oct-26.md: -------------------------------------------------------------------------------- 1 | ### Graph Copy via Cheney's Algorithm 2 | 3 | * breadth-first search (quick reminder what that is) uses a queue 4 | * Cheney: use the ToSpace as the queue, use two pointers to keep 5 | track of the front (scan pointer) and back (free pointer) of the queue. 6 | 1. Copy tuples pointed to by the root set into the ToSpace 7 | to form the initial queue. 8 | 2. While copying a tuple, mark the old one and store the 9 | address of the new tuple inside the old tuple. 10 | This is called a *forwarding pointer*. 11 | 3. Start processing tuples from the front of the queue. For 12 | each tuple, copy the tuples that are directly reachable from 13 | it to the back of the queue in the ToSpace, unless the tuple 14 | has already been copied. Update the pointers in the 15 | processed tuple to the copies or the forwarding pointer. 16 | * Draw Fig. 5.7 17 | 18 | An implementation of a garbage collector is in `runtime.c`. 19 | 20 | 21 | ### Data Representation 22 | 23 | Problems: 24 | 1. how to differentiate pointers from other things on the 25 | procedure call stack? 26 | 2. how can the GC access the pointers that are in registers? 27 | 3. how to differentiate poitners from other things inside tuples? 28 | 29 | Solutions: 30 | 1. Use a root stack (aka. shadow stack), i.e., place all 31 | tuples in a separate stack that works in parallel to the 32 | normal stack. Draw Fig. 5.7. 33 | 2. Spill vector-typed variables to the root stack if they are 34 | live during a call to the collector. 35 | 3. Add a 64-bit header or "tag" to each tuple. (Fig. 5.8) 36 | The header includes 37 | * 1 bit to indicate forwarding (0) or not (1). If 0, then 38 | the header is the forwarding pointer. 39 | * 6 bits to store the length of the tuple (max of 50) 40 | * 50 bits for the pointer mask to indicate which elements 41 | of the tuple are pointers. 42 | 43 | 44 | 45 | ### type checking and type information 46 | 47 | Racket: The `type-check-Rvec` function wraps a `HasType` around each 48 | `vector` creation expression. 49 | 50 | (HasType (Prim 'vector es) (Vector ts)) 51 | 52 | Python: The `type_check_Ltup` writes the tuple type into a new 53 | `has_type` field in the `Tuple` AST node. 54 | 55 | This type information is used in the `expose_allocation` pass. 56 | 57 | Racket: The `type-check-Cvec` function stores an alist mapping 58 | variables to their types in the `info` field, under the key 59 | `locals-types`, of the `CProgram` AST node. 60 | 61 | Python: The `type_check_Ctup` stores a dictionary mapping variables to 62 | their types into a new `var_types` field of the `CProgram` AST node. 63 | 64 | This type information is used in the register allocator and in the 65 | generation of the prelude and conclusion. 66 | 67 | 68 | ### expose-allocation (new) 69 | 70 | Lower tuple creation into a call to collect, a call to allocate, and 71 | then initialize the memory (see 5.3.1). 72 | 73 | Make sure to place the code for sub-expressions prior to the call to 74 | collect. Sub-expressions may also call collect, and we can't have 75 | partially constructed tuples during collect! 76 | 77 | New forms in the output language: 78 | 79 | exp ::= ... 80 | | (Collect int) call the GC and you're going to need `int` bytes 81 | | (Allocate int type) allocate `int` many bytes, `type` is the type of the tuple 82 | | (GlobalValue name) access global variables e.g. free_ptr, fromspace_end 83 | 84 | * `free_ptr`: the next empty spot in the FromSpace 85 | * `fromspace_end`: the end of the FromSpace 86 | 87 | For Python, we need a way to intialize the tuple elements. 88 | 89 | 1. We introduce a `Begin` expression that contains a list of statements 90 | and a result expression, and 91 | 2. Allow `Subscript` on the left-hand side of an assignment 92 | 93 | Grammar: 94 | 95 | exp ::= ... | (Begin stmt ... exp) 96 | 97 | lhs ::= Name(var) | Subscript(exp, exp) 98 | stmt ::= ... | Assign(lhs, exp) 99 | 100 | 101 | ### remove-complex-opera* 102 | 103 | The new forms Collect, Allocate, GlobalValue, Begin, Subscript should 104 | be treated as complex operands. Operands of Subscript need to be 105 | atomic. 106 | 107 | ### explicate-control 108 | 109 | straightforward additions to handle the new forms 110 | 111 | ### select-instructions 112 | 113 | Here is where we implement the new operations needed for tuples. 114 | 115 | example: block9056 116 | 117 | * tuple write turns into movq with a deref in the target 118 | 119 | Racket: 120 | 121 | lhs = (vector-set! tup n arg); 122 | 123 | becomes 124 | 125 | movq tup', %r11 126 | movq arg', 8(n+1)(%r11) 127 | movq $0, lhs' 128 | 129 | Python: 130 | 131 | tup[n] = arg 132 | 133 | becomes 134 | 135 | movq tup', %r11 136 | movq arg', 8(n+1)(%r11) 137 | 138 | what if we use `rax` instead of `r11`: 139 | 140 | movq tup', %rax 141 | movq -16(%rbp), 8(n+1)(%rax) 142 | movq $0, lhs' 143 | 144 | movq tup', %rax 145 | movq -16(%rbp), %rax 146 | movq %rax, 8(n+1)(%rax) 147 | movq $0, lhs' 148 | 149 | 150 | We use `r11` for temporary storage, so we remove it from the list 151 | of registers used for register allocation. 152 | 153 | * tuple read turns into a movq with deref in the source 154 | 155 | Racket/Python: 156 | 157 | lhs = (vector-ref tup n); 158 | 159 | lhs = tup[n] 160 | 161 | becomes 162 | 163 | movq tup', %r11 164 | movq 8(n+1)(%r11), lhs' 165 | 166 | * `allocate` 167 | 168 | 1. put the current `free_ptr` into lhs 169 | 2. move the `free_ptr` forward by 8(len+1) (room for tag) 170 | 3. initialize the tag (use bitwise-ior and arithmetic-shift) 171 | using the type information for the pointer mask. 172 | 173 | So 174 | 175 | lhs = (allocate len (Vector type ...)); 176 | 177 | lhs = allocate(len, TupleType([type, ...])) 178 | 179 | becomes 180 | 181 | movq free_ptr(%rip), lhs' 182 | addq 8(len+1), free_ptr(%rip) 183 | movq lhs', %r11 184 | movq $tag, 0(%r11) 185 | 186 | * `collect` turns into a `callq` to the collect function. 187 | 188 | Pass the top of the root stack (`r15`) in register `rdi` and 189 | the number of bytes in `rsi`. 190 | 191 | (collect bytes) 192 | 193 | collect(bytes) 194 | 195 | becomes 196 | 197 | movq %r15, %rdi 198 | movq $bytes, %rsi 199 | callq collect 200 | 201 | ### allocate-registers 202 | 203 | * Spill tuple-typed variables to the root stack. Handle this 204 | in the code for assigning homes (converting colors to 205 | stack locations and registers.) 206 | 207 | Use `r15` for the top of the root stack. Remove it from 208 | consideration by the register allocator. 209 | 210 | * If a tuple variable is live during a call to collect, 211 | make sure to spill it. Do this by adding interference edges 212 | between the call-live tuple variables and the callee-saved 213 | registers. 214 | 215 | ### prelude-and-conclusion 216 | 217 | * Insert a call to `initialize`, passing in two arguments for the size 218 | of the rootstack and the size of the heap. 219 | 220 | * Move the root stack forward to make room for the tuple spills. 221 | 222 | * The first call to collect might happen before all the 223 | slots in the root stack have been initialized. 224 | So make sure to zero-initialize the root stack in the prelude! 225 | -------------------------------------------------------------------------------- /lectures/Oct-28.md: -------------------------------------------------------------------------------- 1 | # Lecture: Tuples continued 2 | 3 | ## select-instructions 4 | 5 | Here is where we implement the new operations needed for tuples. 6 | 7 | example: block9056 8 | 9 | * tuple write turns into movq with a deref in the target 10 | 11 | Racket: 12 | 13 | lhs = (vector-set! tup n arg); 14 | 15 | becomes 16 | 17 | movq tup', %r11 18 | movq arg', 8(n+1)(%r11) 19 | movq $0, lhs' 20 | 21 | Python: 22 | 23 | tup[n] = arg 24 | 25 | becomes 26 | 27 | movq tup', %r11 28 | movq arg', 8(n+1)(%r11) 29 | 30 | what if we use `rax` instead of `r11`: 31 | 32 | movq tup', %rax 33 | movq -16(%rbp), 8(n+1)(%rax) 34 | movq $0, lhs' 35 | 36 | movq tup', %rax 37 | movq -16(%rbp), %rax 38 | movq %rax, 8(n+1)(%rax) 39 | movq $0, lhs' 40 | 41 | 42 | We use `r11` for temporary storage, so we remove it from the list 43 | of registers used for register allocation. 44 | 45 | * tuple read turns into a movq with deref in the source 46 | 47 | Racket/Python: 48 | 49 | lhs = (vector-ref tup n); 50 | 51 | lhs = tup[n] 52 | 53 | becomes 54 | 55 | movq tup', %r11 56 | movq 8(n+1)(%r11), lhs' 57 | 58 | * `allocate` 59 | 60 | 1. put the current `free_ptr` into lhs 61 | 2. move the `free_ptr` forward by 8(len+1) (room for tag) 62 | 3. initialize the tag (use bitwise-ior and arithmetic-shift) 63 | using the type information for the pointer mask. 64 | 65 | So 66 | 67 | lhs = (Allocate len (Vector type ...)); 68 | 69 | lhs = Allocate(len, TupleType([type, ...])) 70 | 71 | becomes 72 | 73 | movq free_ptr(%rip), lhs' 74 | addq 8(len+1), free_ptr(%rip) 75 | movq lhs', %r11 76 | movq $tag, 0(%r11) 77 | 78 | * `collect` turns into a `callq` to the collect function. 79 | 80 | Pass the top of the root stack (`r15`) in register `rdi` and 81 | the number of bytes in `rsi`. 82 | 83 | (Collect bytes) 84 | 85 | Collect(bytes) 86 | 87 | becomes 88 | 89 | movq %r15, %rdi 90 | movq $bytes, %rsi 91 | callq collect 92 | 93 | ## allocate-registers 94 | 95 | * Spill tuple-typed variables to the root stack. Handle this 96 | in the code for assigning homes (converting colors to 97 | stack locations and registers.) 98 | 99 | Use `r15` for the top of the root stack. Remove it from 100 | consideration by the register allocator. 101 | 102 | * If a tuple variable is live during a call to collect, 103 | make sure to spill it. Do this by adding interference edges 104 | between the call-live tuple variables and the callee-saved 105 | registers. 106 | 107 | ## prelude-and-conclusion 108 | 109 | * Insert a call to `initialize`, passing in two arguments for the size 110 | of the rootstack and the size of the heap. 111 | 112 | * Move the root stack forward to make room for the tuple spills. 113 | 114 | * The first call to collect might happen before all the 115 | slots in the root stack have been initialized. 116 | So make sure to zero-initialize the root stack in the prelude! 117 | 118 | ## Example 119 | 120 | Source program: 121 | 122 | (vector-ref (vector 42) 0) 123 | 124 | print( (42,)[0] ) 125 | 126 | expose allocation: 127 | 128 | (vector-ref T 0) 129 | 130 | 131 | where `T` is 132 | 133 | (let ([_ (if (< (+ (global-value free_ptr) 16) 134 | (global-value fromspace_end)) 135 | (void) 136 | (collect 16))]) 137 | (let ([alloc5 (allocate 1 (Vector Integer))]) 138 | (let ([_6 (vector-set! alloc5 0 42)]) 139 | alloc5))) 140 | 141 | remove complex operands: 142 | 143 | (let ([_ (if (let ([tmp8 (global-value free_ptr)]) 144 | (let ([tmp9 (+ tmp8 16)]) 145 | (let ([tmp46150 (global-value fromspace_end)]) 146 | (< tmp9 tmp46150)))) 147 | (void) 148 | (collect 16))]) 149 | (let ([alloc5 (allocate 1 (Vector Integer))]) 150 | (let ([_6 (vector-set! alloc5 0 42)]) 151 | (vector-ref alloc5 0)))) 152 | 153 | explicate control: 154 | 155 | start: 156 | tmp8 = (global-value free_ptr); 157 | tmp9 = (+ tmp8 16); 158 | tmp46150 = (global-value fromspace_end); 159 | if (< tmp9 tmp46150) 160 | goto block46152; 161 | else 162 | goto block46153; 163 | block46152: 164 | _ = (void); 165 | goto block46151; 166 | block46153: 167 | (collect 16) 168 | goto block46151; 169 | block46151: 170 | alloc5 = (allocate 1 (Vector Integer)); 171 | _6 = (vector-set! alloc5 0 42); 172 | return (vector-ref alloc5 0); 173 | 174 | select instructions: 175 | 176 | start: 177 | movq free_ptr(%rip), tmp8 178 | movq tmp8, tmp9 179 | addq $16, tmp9 180 | movq fromspace_end(%rip), tmp46150 181 | cmpq tmp46150, tmp9 182 | jl block46152 183 | jmp block46153 184 | block46152: 185 | movq $0, _7 186 | jmp block46151 187 | block46153: 188 | movq %r15, %rdi 189 | movq $16, %rsi 190 | callq collect 191 | jmp block46151 192 | block46151: 193 | movq free_ptr(%rip), %r11 194 | addq $16, free_ptr(%rip) 195 | movq $3, 0(%r11) 196 | movq %r11, alloc5 197 | movq alloc5, %r11 198 | movq $42, 8(%r11) 199 | movq $0, _6 200 | movq alloc5, %r11 201 | movq 8(%r11), %rax 202 | jmp conclusion 203 | 204 | prelude and conclusion: 205 | 206 | main: 207 | pushq %rbp 208 | movq %rsp, %rbp 209 | subq $0, %rsp 210 | movq $65536, %rdi 211 | movq $65536, %rsi 212 | callq initialize 213 | movq rootstack_begin(%rip), %r15 214 | jmp start 215 | start: 216 | movq free_ptr(%rip), %rcx 217 | addq $16, %rcx 218 | movq fromspace_end(%rip), %rdx 219 | cmpq %rdx, %rcx 220 | jl block46185 221 | jmp block46186 222 | block46185: 223 | movq $0, %rcx 224 | jmp block46184 225 | block46186: 226 | movq %r15, %rdi 227 | movq $16, %rsi 228 | callq collect 229 | jmp block46184 230 | block46184: 231 | movq free_ptr(%rip), %r11 232 | addq $16, free_ptr(%rip) 233 | movq $3, 0(%r11) 234 | movq %r11, %rcx 235 | movq %rcx, %r11 236 | movq $42, 8(%r11) 237 | movq $0, %rdx 238 | movq %rcx, %r11 239 | movq 8(%r11), %rax 240 | jmp conclusion 241 | conclusion: 242 | subq $0, %r15 243 | addq $0, %rsp 244 | popq %rbp 245 | retq 246 | -------------------------------------------------------------------------------- /lectures/Oct-5.md: -------------------------------------------------------------------------------- 1 | ### Kleene Fixed-Point Theorem 2 | 3 | A function F is **monotone** iff x ⊑ y implies F(x) ⊑ F(y). 4 | 5 | 6 | **Theorem** (Kleene Fixed-Point Theorem) 7 | If a function F is monotone, then the least fixed point of F is the 8 | least upper bound of the ascending Kleene chain: 9 | 10 | ⊥ ⊑ F(⊥) ⊑ F(F(⊥)) ⊑ ... ⊑ F^k(⊥) ⊑ ... 11 | 12 | We don't need a theorem quite that general, but we'll use the idea of 13 | ascending Kleene chains. 14 | 15 | 16 | **Theorem** (Yet Another Fixed-Point Theorem) 17 | Suppose L is a lattice where every ascending chain is finitely long. 18 | Let F be monotone function. 19 | There exists some k such that F^k(⊥) is the least fixed point of F. 20 | 21 | Proof. 22 | 23 | First, we construct the ascending Kleene chain by showing that 24 | for any i, 25 | 26 | F^i(⊥) ⊑ F^(i+1)(⊥) 27 | 28 | Base case: 29 | 30 | ⊥ ⊑ F(⊥) 31 | 32 | by the definition of ⊥. 33 | 34 | Inductive case: the induction hypothesis states that 35 | 36 | F^k(⊥) ⊑ F^(k+1)(⊥) 37 | 38 | Then because F is monotone, we have 39 | 40 | F(F^k(⊥)) ⊑ F(F^(k+1)(⊥)) 41 | = = 42 | F^(k+1)(⊥) ⊑ F^(k+2)(⊥) 43 | 44 | Thus we have the ascending chain: 45 | 46 | ⊥ ⊑ F(⊥) ⊑ F(F(⊥)) ⊑ ... ⊑ F^i(⊥) ⊑ ... 47 | 48 | But the chain must be finitely long, so it tops out at some k. 49 | 50 | ⊥ ⊑ F(⊥) ⊑ F(F(⊥)) ⊑ ... ⊑ F^k(⊥) = F^(k+1)(⊥) 51 | 52 | So F^k(⊥) is a fixed point. 53 | 54 | It remains to show that F^k(⊥) is the least of all fixed points. 55 | Suppose x is another fixed point of F, so F(x) = x. 56 | We prove by induction that for all i, F^i(⊥) ⊑ x. 57 | Base case: ⊥ ⊑ x by the definition of ⊥. 58 | Inductive case: the induction hypothesis gives us 59 | 60 | F^i(⊥) ⊑ x 61 | 62 | then by monotonicity 63 | 64 | F^(i+1)(⊥) ⊑ F(x) 65 | 66 | But x is a fixed point of F, so F(x) = x. 67 | 68 | F^(i+1)(⊥) ⊑ x 69 | 70 | Therefore, in particular, F^k(⊥) ⊑ x, so it is the least fixed point. 71 | 72 | QED 73 | 74 | 75 | ### Liveness Analysis 76 | 77 | Let function F be one iteration of liveness analysis applied to all 78 | the blocks in the program. 79 | 80 | The function F is monotone because adding variables to the live-after 81 | set of a block can only increase the size of its live-before set. 82 | 83 | There are a finite number of variables in the program, so the 84 | ascending chains are all finite. 85 | 86 | So iterating the liveness analysis will eventually produce the least 87 | fixed point of F (i.e. a correct solution). 88 | 89 | 90 | ### Worklist Algorithm 91 | 92 | Inputs 93 | * G: control-flow graph 94 | * transfer: applies analysis to one block 95 | * bottom: bottom element of the lattice (e.g. empty set) 96 | * join: join operator (e.g. set union) 97 | 98 | Outputs 99 | * The transfer function can store the results in a location 100 | of your choice. 101 | 102 | Algorithm: 103 | 104 | def analyze_dataflow(G, transfer, bottom, join): 105 | trans_G = transpose(G) 106 | mapping = {} 107 | for v in G.vertices(): 108 | mapping[v] = bottom 109 | worklist = deque() 110 | for v in G.vertices(): 111 | worklist.append(v) 112 | while worklist: 113 | node = worklist.pop() 114 | input = reduce(join, [mapping[v] for v in trans_G.adjacent(node)], bottom) 115 | output = transfer(node, input) 116 | if output != mapping[node]: 117 | mapping[node] = output 118 | for v in G.adjacent(node): 119 | worklist.append(v) 120 | 121 | -------------------------------------------------------------------------------- /lectures/Rint-interp-example.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require "utilities.rkt") 3 | (require "interp-Rint.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-Rint (Program '() E1)) 21 | (interp-Rint (Program '() E2)) 22 | (interp-Rint (Program '() E3)) 23 | (interp-Rint (Program '() E4)) 24 | (interp-Rint (Program '() E5)) 25 | -------------------------------------------------------------------------------- /lectures/Sep-14.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 | 305 | -------------------------------------------------------------------------------- /lectures/Sep-16.md: -------------------------------------------------------------------------------- 1 | # Concrete Syntax of L_If 2 | 3 | Racket 4 | 5 | bool ::= #t | #f 6 | cmp ::= eq? | < | <= | > | >= 7 | exp ::= int | (read) | (- exp) | (+ exp exp) | (- exp exp) 8 | | var | (let ([var exp]) exp) 9 | | bool | (and exp exp) | (or exp exp) | (not exp) 10 | | (cmp exp exp) | (if exp exp exp) 11 | L_If ::= exp 12 | 13 | Python 14 | 15 | binop ::= + | - | and | or | == | != | < | <= | > | >= 16 | uniop ::= - | not 17 | exp ::= int | input_int() | uniop exp | exp binop exp | var 18 | | True | False | exp if exp else exp 19 | stmt ::= print(exp) | exp | var = exp | if exp: stmt+ else: stmt+ 20 | L_If ::= stmt* 21 | 22 | New things: 23 | * Boolean literals: true and false. 24 | * Logical operators on Booleans: `and`, `or`, `not`. 25 | * Comparison operators: equal, less than, etc. 26 | * The `if` conditional expression. Branching! 27 | * Subtraction on integers. 28 | 29 | 30 | # Semantics of L_If 31 | 32 | class InterpPif(InterpPvar): 33 | 34 | def interp_cmp(self, cmp): 35 | match cmp: 36 | case Lt(): 37 | return lambda x, y: x < y 38 | case LtE(): 39 | return lambda x, y: x <= y 40 | case Gt(): 41 | return lambda x, y: x > y 42 | case GtE(): 43 | return lambda x, y: x >= y 44 | case Eq(): 45 | return lambda x, y: x == y 46 | case NotEq(): 47 | return lambda x, y: x != y 48 | 49 | def interp_exp(self, e, env): 50 | match e: 51 | case IfExp(test, body, orelse): 52 | match self.interp_exp(test, env): 53 | case True: 54 | return self.interp_exp(body, env) 55 | case False: 56 | return self.interp_exp(orelse, env) 57 | case BinOp(left, Sub(), right): 58 | l = self.interp_exp(left, env) 59 | r = self.interp_exp(right, env) 60 | return l - r 61 | case UnaryOp(Not(), v): 62 | return not self.interp_exp(v, env) 63 | case BoolOp(And(), values): 64 | left = values[0]; right = values[1] 65 | match self.interp_exp(left, env): 66 | case True: 67 | return self.interp_exp(right, env) 68 | case False: 69 | return False 70 | case BoolOp(Or(), values): 71 | left = values[0]; right = values[1] 72 | match self.interp_exp(left, env): 73 | case True: 74 | return True 75 | case False: 76 | return self.interp_exp(right, env) 77 | case Compare(left, [cmp], [right]): 78 | l = self.interp_exp(left, env) 79 | r = self.interp_exp(right, env) 80 | return self.interp_cmp(cmp)(l, r) 81 | case Let(Name(x), rhs, body): 82 | v = self.interp_exp(rhs, env) 83 | new_env = dict(env) 84 | new_env[x] = v 85 | return self.interp_exp(body, new_env) 86 | case _: 87 | return super().interp_exp(e, env) 88 | 89 | def interp_stmts(self, ss, env): 90 | if len(ss) == 0: 91 | return 92 | match ss[0]: 93 | case If(test, body, orelse): 94 | match self.interp_exp(test, env): 95 | case True: 96 | return self.interp_stmts(body + ss[1:], env) 97 | case False: 98 | return self.interp_stmts(orelse + ss[1:], env) 99 | case _: 100 | return super().interp_stmts(ss, env) 101 | 102 | Things to note: 103 | * Our treatment of Booleans and operations on them is strict: we don't 104 | allow other kinds of values (such as integers) to be treated as if 105 | they are Booleans. 106 | * `and` is short-circuiting. 107 | * The handling of comparison operators has been factored out into an 108 | auxilliary function named `interp_cmp`. 109 | 110 | 111 | # Type errors and static type checking 112 | 113 | In Racket: 114 | 115 | > (not 1) 116 | #f 117 | 118 | > (car 1) 119 | car: contract violation 120 | expected: pair? 121 | given: 1 122 | 123 | In Typed Racket: 124 | 125 | > (not 1) 126 | #f 127 | 128 | > (car 1) 129 | Type Checker: Polymorphic function `car' could not be applied to arguments: 130 | Domains: (Listof a) 131 | (Pairof a b) 132 | Arguments: One 133 | in: (car 1) 134 | 135 | In Python: 136 | 137 | >>> not 1 138 | False 139 | 140 | >>> 1[0] 141 | TypeError: 'int' object is not subscriptable 142 | 143 | In PyCharm: 144 | 145 | >>> not 1 146 | False 147 | 148 | >>> 1[0] 149 | Class 'int' does not define '__getitem__', so the '[]' operator cannot 150 | be used on its instances 151 | 152 | A type checker (aka. type system) enforces at compile-time that only 153 | the appropriate operations are applied to values of a given type. 154 | 155 | To accomplish this, a type checker must predict what kind of value 156 | will be produced by an expression at runtime. 157 | 158 | Type checker: 159 | 160 | class TypeCheckPvar: 161 | 162 | def type_check_exp(self, e, env): 163 | match e: 164 | case BinOp(left, Add(), right): 165 | l = self.type_check_exp(left, env) 166 | check_type_equal(l, int, left) 167 | r = self.type_check_exp(right, env) 168 | check_type_equal(r, int, right) 169 | return int 170 | case UnaryOp(USub(), v): 171 | t = self.type_check_exp(v, env) 172 | check_type_equal(t, int, v) 173 | return int 174 | case Name(id): 175 | return env[id] 176 | case Constant(value) if isinstance(value, int): 177 | return int 178 | case Call(Name('input_int'), []): 179 | return int 180 | case _: 181 | raise Exception('error in TypeCheckPvar.type_check_exp, unhandled ' + repr(e)) 182 | 183 | def type_check_stmts(self, ss, env): 184 | if len(ss) == 0: 185 | return 186 | match ss[0]: 187 | case Assign([lhs], value): 188 | t = self.type_check_exp(value, env) 189 | if lhs.id in env: 190 | check_type_equal(env[lhs.id], t, value) 191 | else: 192 | env[lhs.id] = t 193 | return self.type_check_stmts(ss[1:], env) 194 | case Expr(Call(Name('print'), [arg])): 195 | t = self.type_check_exp(arg, env) 196 | check_type_equal(t, int, arg) 197 | return self.type_check_stmts(ss[1:], env) 198 | case Expr(value): 199 | self.type_check_exp(value, env) 200 | return self.type_check_stmts(ss[1:], env) 201 | case _: 202 | raise Exception('error in TypeCheckPvar.type_check_stmt, unhandled ' + repr(s)) 203 | 204 | def type_check_P(self, p): 205 | match p: 206 | case Module(body): 207 | self.type_check_stmts(body, {}) 208 | 209 | 210 | class TypeCheckPif(TypeCheckPvar): 211 | 212 | def type_check_exp(self, e, env): 213 | match e: 214 | case Constant(value) if isinstance(value, bool): 215 | return bool 216 | case IfExp(test, body, orelse): 217 | test_t = self.type_check_exp(test, env) 218 | check_type_equal(bool, test_t, test) 219 | body_t = self.type_check_exp(body, env) 220 | orelse_t = self.type_check_exp(orelse, env) 221 | check_type_equal(body_t, orelse_t, e) 222 | return body_t 223 | case BinOp(left, Sub(), right): 224 | l = self.type_check_exp(left, env) 225 | check_type_equal(l, int, left) 226 | r = self.type_check_exp(right, env) 227 | check_type_equal(r, int, right) 228 | return int 229 | case UnaryOp(Not(), v): 230 | t = self.type_check_exp(v, env) 231 | check_type_equal(t, bool, v) 232 | return bool 233 | case BoolOp(op, values): 234 | left = values[0]; right = values[1] 235 | l = self.type_check_exp(left, env) 236 | check_type_equal(l, bool, left) 237 | r = self.type_check_exp(right, env) 238 | check_type_equal(r, bool, right) 239 | return bool 240 | case Compare(left, [cmp], [right]) if isinstance(cmp, Eq) or isinstance(cmp, NotEq): 241 | l = self.type_check_exp(left, env) 242 | r = self.type_check_exp(right, env) 243 | check_type_equal(l, r, e) 244 | return bool 245 | case Compare(left, [cmp], [right]): 246 | l = self.type_check_exp(left, env) 247 | check_type_equal(l, int, left) 248 | r = self.type_check_exp(right, env) 249 | check_type_equal(r, int, right) 250 | return bool 251 | case Let(Name(x), rhs, body): 252 | t = self.type_check_exp(rhs, env) 253 | new_env = dict(env); new_env[x] = t 254 | return self.type_check_exp(body, new_env) 255 | case _: 256 | return super().type_check_exp(e, env) 257 | 258 | def type_check_stmts(self, ss, env): 259 | if len(ss) == 0: 260 | return 261 | match ss[0]: 262 | case If(test, body, orelse): 263 | test_t = self.type_check_exp(test, env) 264 | check_type_equal(bool, test_t, test) 265 | body_t = self.type_check_stmts(body, env) 266 | orelse_t = self.type_check_stmts(orelse, env) 267 | check_type_equal(body_t, orelse_t, ss[0]) 268 | return self.type_check_stmts(ss[1:], env) 269 | case _: 270 | return super().type_check_stmts(ss, env) 271 | 272 | 273 | How should the type checker handle the `if` expression? 274 | 275 | 276 | # Shrinking L_If 277 | 278 | Several of the language forms in L_If are redundant and can be easily 279 | expressed using other language forms. They are present in L_If to make 280 | programming more convenient, but they do not fundamentally increase 281 | the expressiveness of the language. 282 | 283 | To reduce the number of language forms that later compiler passes have 284 | to deal with, we shrink L_If by translating away some of the forms. 285 | For example, subtraction is expressible as addition and negation. 286 | 287 | (and e1 e2) => (if e1 e2 #f) 288 | (or e1 e2) => (if e1 #t e2) 289 | 290 | It is possible to shrink many more of the language forms, but 291 | sometimes it can hurt the efficiency of the generated code. 292 | For example, we could express subtraction in terms of addition 293 | and negation: 294 | 295 | (- e1 e2) => (+ e1 (- e2)) 296 | 297 | but that would result in two x86 instructions instead of one. 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /lectures/Sep-2.md: -------------------------------------------------------------------------------- 1 | # Lecture: Explicate, Select, Assign, Patch, Prelude & Conclusion 2 | 3 | L_Var L_Var 4 | | uniquify | remove complex operands 5 | V V 6 | L_Var L_Var 7 | | remove complex operands | select instructions 8 | V V 9 | L_Var x86_Var 10 | | explicate control | assign homes 11 | V V 12 | C_Var x86* 13 | | select instructions | patch instructions 14 | V V 15 | x86_Var x86* 16 | | assign homes | prelude & conclusion 17 | V V 18 | x86* x86 19 | | patch instructions 20 | V 21 | x86* 22 | | prelude & conclusion 23 | V 24 | x86 25 | 26 | ## Explicate Control (Racket only) 27 | 28 | This pass makes the order of evaluation simple and explicit in the 29 | syntax. For now, this means flattening `let` into a sequence of 30 | assignment statements. 31 | 32 | The target of this pass is the C_Var language. 33 | Here is the grammar for C_Var. 34 | 35 | atm ::= int | var 36 | exp ::= atm | (read) | (- atm) | (+ atm atm) 37 | stmt ::= var = exp; 38 | tail ::= return exp; | stmt tail 39 | C_Var ::= (label: tail)^+ 40 | 41 | Example: 42 | 43 | (let ([x (let ([y (- 42)]) 44 | y)]) 45 | (- x)) 46 | => 47 | start: 48 | y = (- 42); 49 | x = y; 50 | return (- x); 51 | 52 | Aside regarding **tail position**. Here is the grammar for L^ANF_Var again 53 | but splitting the exp non-terminal into two, one for `tail` position 54 | and one for not-tail `nt` position. 55 | 56 | atm ::= var | int 57 | nt ::= atm | (read) | (- atm) | (+ atm atm) 58 | | (let ([var nt]) nt) 59 | tail ::= atm | (read) | (- atm) | (+ atm atm) 60 | | (let ([var nt]) tail) 61 | L^ANF_Var' ::= tail 62 | 63 | Recommended function organization: 64 | 65 | explicate-tail : exp -> tail 66 | 67 | explicate-assign : exp -> var -> tail -> tail 68 | 69 | The `explicate-tail` function takes and L^ANF_Var expression in tail position 70 | and returns a C_Var tail. 71 | 72 | The `explicate-assign` function takes 1) an R1 expression that is not 73 | in tail position, that is, the right-hand side of a `let`, 2) the 74 | `let`-bound variable, and 3) the C0 tail for the body of the `let`. 75 | The output of `explicate-assign` is a C0 tail. 76 | 77 | Here's a trace of these two functions on the above example. 78 | 79 | explicate-tail (let ([x (let ([y (- 42)]) y)]) (- x)) 80 | explicate-tail (- x) 81 | => {return (- x);} 82 | explicate-assign (let ([y (- 42)]) y) x {return (- x);} 83 | explicate-assign y x {return (- x);} 84 | => {x = y; return (- x)} 85 | explicate-assign (- 42) y {x = y; return (- x);} 86 | => {y = (- 42); x = y; return (- x);} 87 | => {y = (- 42); x = y; return (- x);} 88 | => {y = (- 42); x = y; return (- x);} 89 | 90 | ## Select Instructions 91 | 92 | Translate statements into x86-style instructions. 93 | 94 | For example 95 | 96 | x = (+ 10 32); 97 | => 98 | movq $10, x 99 | addq $32, x 100 | 101 | Some cases can be handled with a single instruction. 102 | 103 | x = (+ 10 x); 104 | => 105 | addq $10, x 106 | 107 | 108 | The `read` operation must be turned into a 109 | call to the `read_int` function in `runtime.c`. 110 | 111 | x = (read); x = input_int() 112 | => 113 | callq read_int 114 | movq %rax, x 115 | 116 | The return statement is treated like an assignment to `rax` followed 117 | by a jump to the `conclusion` label. 118 | 119 | return e; 120 | => 121 | instr 122 | jmp conclusion 123 | 124 | where 125 | 126 | rax = e; 127 | => 128 | instr 129 | 130 | 131 | ## The Stack and Procedure Call Frames 132 | 133 | The stack is a conceptually sequence of frames, one for each procedure 134 | call. The stack grows down. 135 | 136 | The *base pointer* `rbp` is used for indexing into the frame. 137 | 138 | The *stack poitner* `rsp` points to the top of the stack. 139 | 140 | | Position | Contents | 141 | | --------- | -------------- | 142 | | 8(%rbp) | return address | 143 | | 0(%rbp) | old rbp | 144 | | -8(%rbp) | variable 1 | 145 | | -16(%rbp) | variable 2 | 146 | | -24(%rbp) | variable 3 | 147 | | ... | ... | 148 | | 0(%rsp) | variable n | 149 | 150 | 151 | ## Assign Homes 152 | 153 | Replace variables with stack locations. 154 | 155 | Consider the program `(+ 52 (- 10))`. 156 | 157 | Suppose we have two variables in the pseudo-x86, `tmp.1` and `tmp.2`. 158 | We places them in the -16 and -8 offsets from the base pointer `rbp` 159 | using the `deref` form. 160 | 161 | movq $10, tmp.1 162 | negq tmp.1 163 | movq tmp.1, tmp.2 164 | addq $52, tmp.2 165 | movq tmp.2, %rax 166 | => 167 | movq $10, -16(%rbp) 168 | negq -16(%rbp) 169 | movq -16(%rbp), -8(%rbp) 170 | addq $52, -8(%rbp) 171 | movq -8(%rbp), %rax 172 | 173 | 174 | ## Patch Instructions 175 | 176 | Continuing the above example, we need to ensure that 177 | each instruction follows the rules of x86. 178 | 179 | For example, the move from stack location -16 to -8 uses two memory 180 | locations in the same instruction. So we split it up into two 181 | instructions and use rax to hold the value at location -16. 182 | 183 | movq $10 -16(%rbp) 184 | negq -16(%rbp) 185 | movq -16(%rbp) -8(%rbp) * 186 | addq $52 -8(%rbp) 187 | movq -8(%rbp) %rax 188 | => 189 | movq $10 -16(%rbp) 190 | negq -16(%rbp) 191 | movq -16(%rbp), %rax * 192 | movq %rax, -8(%rbp) * 193 | addq $52, -8(%rbp) 194 | movq -8(%rbp), %rax 195 | 196 | 197 | ## Prelude and Conclusion 198 | 199 | We generate a prelude and conclusion for the main procedure. 200 | 201 | The prelude 202 | 1. saves the old base pointer, 203 | 2. moves the base pointer to the top of the stack, 204 | 3. moves the stack pointer down passed all the local variables, and 205 | 4. jumps to the start label. 206 | 207 | The conclusion 208 | 1. moves the stack pointer up passed all the local variables, 209 | 2. pops the old base pointer, and 210 | 3. returns from the `main` function via `retq` . 211 | 212 | Continuing the above example 213 | 214 | .globl _main 215 | main: 216 | pushq %rbp 217 | movq %rsp, %rbp 218 | subq $16, %rsp 219 | jmp start 220 | 221 | start: 222 | movq $10, -16(%rbp) 223 | negq -16(%rbp) 224 | movq -16(%rbp), %rax 225 | movq %rax, -8(%rbp) 226 | addq $52, -8(%rbp) 227 | movq -8(%rbp), %rax 228 | jmp conclusion 229 | 230 | conclusion: 231 | addq $16, %rsp 232 | popq %rbp 233 | retq 234 | 235 | -------------------------------------------------------------------------------- /lectures/Sep-21.md: -------------------------------------------------------------------------------- 1 | Code Review: Register Allocation 2 | -------------------------------------------------------------------------------- /lectures/Sep-23.md: -------------------------------------------------------------------------------- 1 | # More x86 with an eye toward instruction selection for LIf 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. But 22 | it is strange. It puts the result in a mysterious EFLAGS register. 23 | 24 | var = (< arg1 arg2) => cmpq arg2, arg1 25 | setl %al 26 | movzbq %al, var 27 | 28 | The `cmpq` instruction can also be used for conditional branching. 29 | The conditional jump instructions `je`, `jl`, etc. also read 30 | from the EFLAGS register. 31 | 32 | if (eq? arg1 arg2) => cmpq arg2, arg1 33 | goto l1; je l1 34 | else jmp l2 35 | goto l2; 36 | 37 | 38 | # The CIf intermediate language 39 | 40 | Syntax of CIf 41 | 42 | bool ::= #t | #f 43 | atm ::= int | var | bool 44 | cmp ::= eq? | < | <= | > | >= 45 | exp ::= ... | (not atm) | (cmp atm atm) 46 | stmt ::= ... 47 | tail ::= ... 48 | | goto label; 49 | | if (cmp atm atm) 50 | goto label; 51 | else 52 | goto label; 53 | CIf ::= label1: 54 | tail1 55 | label2: 56 | tail2 57 | ... 58 | 59 | # Explicate Control 60 | 61 | Consider the following Racket and Python programs 62 | 63 | Racket: 64 | 65 | (let ([x (read)]) 66 | (let ([y (read)]) 67 | (if (if (< x 1) (eq? x 0) (eq? x 2)) 68 | (+ y 2) 69 | (+ y 10)))) 70 | 71 | Python: 72 | 73 | x = input_int() 74 | y = input_int() 75 | print(y + 2 if (x == 0 if x < 1 else x == 2) else y + 10) 76 | 77 | A straightforward way to compile an `if` expression is to recursively 78 | compile the condition, and then use the `cmpq` and `je` instructions 79 | to branch on its Boolean result. Let's first focus in the `(if (< x 1) ...)`. 80 | 81 | ... 82 | cmpq $1, x ;; (< x 1) 83 | setl %al 84 | movzbq %al, tmp 85 | cmpq $1, tmp ;; (if (< x 1) ...) 86 | je then_branch1 87 | jmp else_branch1 88 | ... 89 | 90 | But notice that we used two `cmpq`, a `setl`, and a `movzbq` 91 | when it would have been better to use a single `cmpq` with 92 | a `jl`. 93 | 94 | ... 95 | cmpq $1, x ;; (if (< x 1) ...) 96 | jl then_branch1 97 | jmp else_branch1 98 | ... 99 | 100 | Ok, so we should recognize when the condition of an `if` is a 101 | comparison, and specialize our code generation. But can we do even 102 | better? Consider the outer `if` in the example program, whose 103 | condition is not a comparison, but another `if`. Can we rearrange the 104 | program so that the condition of the `if` is a comparison? How about 105 | pushing the outer `if` inside the inner `if`: 106 | 107 | Racket: 108 | 109 | (let ([x (read)]) 110 | (let ([y (read)]) 111 | (if (< x 1) 112 | (if (eq? x 0) 113 | (+ y 2) 114 | (+ y 10)) 115 | (if (eq? x 2) 116 | (+ y 2) 117 | (+ y 10))))) 118 | 119 | Python: 120 | 121 | x = input_int() 122 | y = intput_int() 123 | print(((y + 2) if x == 0 else (y + 10)) \ 124 | if (x < 1) \ 125 | else ((y + 2) if (x == 2) else (y + 10))) 126 | 127 | Unfortunately, now we've duplicated the two branches of the outer `if`. 128 | A compiler must *never* duplicate code! 129 | 130 | Now we come to the reason that our Cn programs take the forms of a 131 | *graph* instead of a *tree*. A graph allows multiple edges to point to 132 | the same vertex, thereby enabling sharing instead of duplication. The 133 | nodes of this graph are the labeled `tail` statements and the edges 134 | are expressed with `goto`. 135 | 136 | Using these insights, we can compile the example to the following CIf 137 | program. 138 | 139 | (let ([x (read)]) 140 | (let ([y (read)]) 141 | (if (if (< x 1) (eq? x 0) (eq? x 2)) 142 | (+ y 2) 143 | (+ y 10)))) 144 | => 145 | start: 146 | x = (read); 147 | y = (read); 148 | if (< x 1) 149 | goto block_8; 150 | else 151 | goto block_9; 152 | block_8: 153 | if (eq? x 0) 154 | goto block_4; 155 | else 156 | goto block_5; 157 | block_9: 158 | if (eq? x 2) 159 | goto block_6; 160 | else 161 | goto block_7; 162 | block_4: 163 | goto block_2; 164 | block_5: 165 | goto block_3; 166 | block_6: 167 | goto block_2; 168 | block_7: 169 | goto block_3; 170 | block_2: 171 | return (+ y 2); 172 | block_3: 173 | return (+ y 10); 174 | 175 | Python: 176 | 177 | x = input_int() 178 | y = input_int() 179 | print(y + 2 if (x == 0 if x < 1 else x == 2) else y + 10) 180 | => 181 | start: 182 | x = input_int() 183 | y = input_int() 184 | if x < 1: 185 | goto block_8 186 | else: 187 | goto block_9 188 | block_8: 189 | if x == 0: 190 | goto block_4 191 | else: 192 | goto block_5 193 | block_9: 194 | if x == 2: 195 | goto block_6 196 | else: 197 | goto block_7 198 | block_4: 199 | goto block_2 200 | block_5: 201 | goto block_3 202 | block_6: 203 | goto block_2 204 | block_7: 205 | goto block_3 206 | block_2: 207 | tmp_0 = y + 2 208 | goto block_1 209 | block_3: 210 | tmp_0 = y + 10 211 | goto block_1 212 | block_1: 213 | print(tmp_0) 214 | return 0 215 | 216 | 217 | Notice that we've acheived both objectives. 218 | 1. The condition of each `if` is a comparison. 219 | 2. We have not duplicated the two branches of the outer `if`. 220 | 221 | 222 | Racket: 223 | 224 | explicate-tail : LIf_exp -> CIf_tail 225 | generates code for expressions in tail position 226 | explicate-assign : LIf_exp -> var -> CIf_tail -> CIf_tail 227 | generates code for an `let` by cases on the right-hand side expression 228 | explicate-pred : LIf_exp x CIf_tail x CIf_tail -> CIf_tail 229 | generates code for an `if` expression by cases on the condition. 230 | 231 | Python: 232 | 233 | def explicate_stmt(self, s: stmt, cont: List[stmt], 234 | basic_blocks: Dict[str, List[stmt]]) -> List[stmt] 235 | generates code for statements 236 | def explicate_assign(self, e: expr, x: Variable, cont: List[stmt], 237 | basic_blocks: Dict[str, List[stmt]]) -> List[stmt] 238 | generates code for an assignment by cases on the right-hand side expression. 239 | def explicate_effect(self, e: expr, cont: List[stmt], 240 | basic_blocks: Dict[str, List[stmt]]) -> List[stmt] 241 | generates code for expressions as statements, so their result is 242 | ignored and only their side effects matter. 243 | def explicate_pred(self, cnd: expr, thn: List[stmt], els: List[stmt], 244 | basic_blocks: Dict[str, List[stmt]]) -> List[stmt] 245 | generates code for `if` expression or statement by cases on the condition. 246 | 247 | 248 | Example: 249 | 250 | (let ([x (read)]) 251 | (if (eq? x 0) 42 777)) 252 | 253 | x = input_int() 254 | if x == 0: 255 | print(42) 256 | else: 257 | print(777) 258 | 259 | Example: 260 | 261 | (let ([y (read)]) 262 | (let ([x (if (eq? y 0) 40 777)]) 263 | (+ x 2))) 264 | 265 | y = input_int() 266 | x = (40 if y == 0 else 777) 267 | print(x + 2) 268 | 269 | Example: 270 | 271 | (if #t 42 777) 272 | 273 | if True: 274 | print(42) 275 | else: 276 | print(777) 277 | 278 | Example: 279 | 280 | (let ([x (read)]) 281 | (let ([y (read)]) 282 | (if (if (< x 1) (eq? x 0) (eq? x 2)) 283 | (+ y 2) 284 | (+ y 10)))) 285 | 286 | x = input_int() 287 | y = input_int() 288 | print(y + 2 if (x == 0 if x < 1 else x == 2) else y + 10) 289 | 290 | -------------------------------------------------------------------------------- /lectures/Sep-28.md: -------------------------------------------------------------------------------- 1 | # Booleans and Conditionals Continued 2 | 3 | ## Select Instructions 4 | 5 | Thanks to `explicate-control` and the grammar of Cif, 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 | je outer_then 57 | jmp outer_else 58 | outer_then: {y} 59 | movq y, %rax 60 | addq $2, %rax 61 | jmp conclusion 62 | outer_else: {y} 63 | movq y, %rax 64 | addq $10, %rax 65 | jmp conclusion 66 | 67 | 68 | 69 | 70 | 71 | Q: In what *order* should we process the blocks? 72 | A: Reverse topological order. 73 | In other words, first process the blocks with no out-edges, 74 | because the live-after set for the last instruction in each 75 | of those blocks is the empty set. In this example, first 76 | process `outer_then` and `outer_else`. Then imagine that those 77 | blocks are removed from the graph. Again select a block with 78 | no out-edges and repeat the process, continuing until all the 79 | blocks are gone. 80 | 81 | Q: How do we compute the live-after set for the instruction at the end 82 | of each block? After all, we don't know which way the conditional 83 | jumps will go. 84 | A: Take the *union* of the live-before set of the first instruction of 85 | every *successor* block. Thus we compute a conservative 86 | approximation of the real live-before set. 87 | 88 | 89 | ### Build Interference 90 | 91 | Nothing surprising. Need to give `movzbq` special treatment similar to 92 | the `movq` instruction. Also, the register `al` should be considered 93 | the same as `rax`, because it is a part of `rax`. 94 | 95 | 96 | ## Patch Instructions 97 | 98 | * `cmpq` the second argument must not be an immediate. 99 | 100 | * `movzbq` the target argument must be a register. 101 | 102 | 103 | ## Challenge: Optimize and Remove Jumps 104 | 105 | The output of `explicate-control` for our running example is not quite 106 | as nice as we advertised above. It generates lots of trivial blocks 107 | that just goto another block. 108 | 109 | block8482: 110 | if (eq? x8473 2) 111 | goto block8479; 112 | else 113 | goto block8480; // block8476 114 | block8481: 115 | if (eq? x8473 0) 116 | goto block8477; 117 | else 118 | goto block8478; 119 | block8480: 120 | goto block8476; 121 | block8479: 122 | goto block8475; 123 | block8478: 124 | goto block8476; 125 | block8477: 126 | goto block8475; 127 | block8476: 128 | return (+ y8474 10); 129 | block8475: 130 | return (+ y8474 2); 131 | start: 132 | x8473 = (read); 133 | y8474 = (read); 134 | if (< x8473 1) 135 | goto block8481; 136 | else 137 | goto block8482; 138 | 139 | ### Optimize Blocks 140 | 141 | Collapse sequences of jumps through trivial blocks (marked with * 142 | below) into a single jump and remove the trivial blocks. 143 | 144 | B1 -> B2* -> B3* -> B4 -> B5* -> B6 145 | => 146 | B1 -> B4 -> B6 147 | 148 | Helper function: `create_block` that takes a sequence of statements 149 | (Python) or a tail (Racket). If the input is just a `goto`, then 150 | return the `goto`. Otherwise, generate a label for the block and add 151 | it to the dictionary of blocks. Return a `goto` to the new label. 152 | 153 | Lazy evaluation: to avoid adding blocks that are not actually 154 | **needed** we can delay any code in explicate control that creates 155 | blocks. So instead of producing code, we'll produces promises of code. 156 | In racket, use `delay` to create a promise. Then, in places were we 157 | actually need the code, use `force` to run the promise. 158 | 159 | (define (create_block tail) 160 | (delay 161 | (define t (force tail)) 162 | (match t 163 | [(Goto label) (Goto label)] 164 | [else (Goto (add-node t))]))) 165 | 166 | 167 | ### Remove Jumps 168 | 169 | Merge a block with one that comes after if there is only one in-edge 170 | to the later one. 171 | 172 | 173 | B1 B2 B3 B1 B2 B3 174 | | \ / B4 \ / 175 | | \ / \ \ / 176 | B4 B5 => \ B5 177 | \ / \ / 178 | \ / \ / 179 | B6 B6 180 | 181 | B1 B2 B3 B1 B2 B3 182 | | \ / B4 \ / 183 | | \ / \ \ / 184 | B4 B5* => \ \/ 185 | \ / \ / 186 | \ / \ / 187 | B6 B6 188 | 189 | 190 | -------------------------------------------------------------------------------- /lectures/Sep-30.md: -------------------------------------------------------------------------------- 1 | # Loops and Dataflow Analysis 2 | 3 | ## Syntax 4 | 5 | Racket: 6 | 7 | exp ::= ... | (while exp exp) | (set! var exp) | (begin exp ... exp) 8 | 9 | 10 | Python: 11 | 12 | stmt ::= ... | while exp: stmt^+ 13 | 14 | 15 | ## Example 16 | 17 | Racket: 18 | 19 | (let ([sum 0]) 20 | (let ([i 5]) 21 | (begin 22 | (while (> i 0) 23 | (begin 24 | (set! sum (+ sum i)) 25 | (set! i (- i 1)))) 26 | sum))) 27 | 28 | Python: 29 | 30 | sum = 0 31 | i = 5 32 | while i > 0: 33 | sum = sum + i 34 | i = i - 1 35 | print(sum) 36 | 37 | 38 | ## Type Checking 39 | 40 | See `type-check-Rwhile.rkt`. 41 | 42 | See `type_check_Lwhile.py`. 43 | 44 | 45 | ## Interpreter 46 | 47 | See `interp-Rwhile.rkt`. 48 | 49 | See `interp_Lwhile.py`. 50 | 51 | 52 | ## Control-flow Cycles and Dataflow Analysis 53 | 54 | The above example, after instruction selection: 55 | 56 | mainstart: 57 | movq $0, sum 58 | movq $5, i 59 | jmp block5 60 | block5: 61 | movq i, tmp3 62 | cmpq tmp3, $0 63 | jl block7 64 | jmp block8 65 | block7: 66 | addq i, sum 67 | movq $1, tmp4 68 | negq tmp4 69 | addq tmp4, i 70 | jmp block5 71 | block8: 72 | movq $27, %rax 73 | addq sum, %rax 74 | jmp mainconclusion 75 | 76 | Control-flow graph: 77 | 78 | mainstart 79 | | 80 | V 81 | block5 82 | | ^ \_____ 83 | V | \ 84 | block7 block8 --> mainconclusion 85 | 86 | For liveness analysis, we can no longer topologically sort the CFG. 87 | 88 | Observations: 89 | 90 | 1. If we start processing a block using an empty live-after set, we 91 | obtain an under-approximation of it's live-before set. That is, 92 | the elements of the set will be correct ones, we just might be 93 | missing some. 94 | 95 | 2. If we iteratively process all the blocks, we'll eventually come to 96 | a situation where their live-before sets don't change. That's 97 | called a **fixed point**. The Kleene Fixed-Point Theorem tells us 98 | that the fixed point is correct in the sense that the live after 99 | sets are no longer missing any elements. 100 | 101 | 3. While iterating, we don't have to recompute the live-before set of 102 | a block if the live-before set of its successors didn't change on 103 | the previous iteration. 104 | 105 | 106 | ### Example dataflow analysis to determine live-before sets 107 | 108 | Start with empty live-before sets 109 | 110 | mainstart: {} 111 | block5: {} 112 | block7: {} 113 | block8: {} 114 | 115 | Perform liveness analysis on every block: 116 | 117 | mainstart: {} 118 | block5: {i} 119 | block7: {i, sum} 120 | block8: {rsp, sum} 121 | 122 | We see changes in blocks 5, 7, and 8, so we again perform the analysis 123 | on the blocks that depend on them wrt. liveness (i.e. in-edges), 124 | which is start, 5, 7, and 8. 125 | 126 | mainstart: {} 127 | block5: {i, rsp, sum} 128 | block7: {i, sum} 129 | block8: {rsp, sum} 130 | 131 | We see changes only in `block5`, so we recompute the live-before for 132 | `mainstart` and `block7`. 133 | 134 | mainstart: {rsp} 135 | block5: {i, rsp, sum} 136 | block7: {i, rsp, sum} 137 | block8: {rsp, sum} 138 | 139 | We see changes in `block7` and `mainstart`, so we recompute `block5`, 140 | but it doesn't change. So the above is the fixed point. 141 | 142 | 143 | ### Some Lattice Theory 144 | 145 | A **lattice** is a set of elements with a partial ordering, written x ⊑ y, 146 | a bottom element ⊥, and a join operator x ⊔ y. 147 | 148 | The lattice abstraction is often used in situations where the elements 149 | represent differing amounts of information and the partial ordering 150 | x ⊑ y means that element y has more (or equal) information than x. 151 | 152 | The bottom element ⊥ represents a total lack of information. 153 | 154 | The join operator corresponds to combining the information present in 155 | the two elements. 156 | 157 | An element x is an **upper bound** of a set S if 158 | for every element y in S, y ⊑ x. 159 | 160 | An element x is a **least upper bound** of a set S if 161 | x is less-or-equal to any other upper bound of S. 162 | 163 | 164 | ### Dataflow Analysis 165 | 166 | Dataflow analysis is a generic framework for analyzing programs with 167 | cycles in their control flow. 168 | 169 | A dataflow analysis involves two lattices. 170 | 171 | 1. A lattice to represent abstract states of the program. 172 | 173 | 2. A lattice that aggregates the states of all the blocks 174 | in the control-flow graph. 175 | 176 | and a function F over the second lattice that expresses how the 177 | program transforms the abstract states. 178 | 179 | The goal of the analysis is to compute a solution element x such that 180 | 181 | F(x) = x 182 | 183 | A **fixed point** of a function F is an element x such that F(x) = x. 184 | 185 | 186 | Example: (liveness analysis) 187 | 188 | The lattice for abstract states: 189 | 190 | * An element is a set of variables (the ones that may be live). 191 | * The partial ordering is the set containment, i.e. x ⊑ y iff x ⊆ y. 192 | * The bottom element is the empty set. 193 | * The join operator is set union. 194 | 195 | The lattice for the whole CFG: 196 | 197 | * Each element is a mapping M from labels to sets of variables. 198 | * The partial ordering is the pointwise ordering: M ⊑ M' iff for any label 199 | l in the program, M(l) ⊑ M'(l). 200 | * The bottom element is the mapping that sends every label to the empty set. 201 | * The join operator: (M ⊔ M')(l) = M(l) ⊔ M'(l). 202 | 203 | 204 | -------------------------------------------------------------------------------- /lectures/Sep-7.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 | Racket: 25 | 26 | (let ([v 1]) 27 | (let ([w 42]) 28 | (let ([x (+ v 7)]) 29 | (let ([y x]) 30 | (let ([z (+ x w)]) 31 | (+ z (- y))))))) 32 | 33 | Python: 34 | 35 | v = 1 36 | w = 42 37 | x = v + 7 38 | y = x 39 | z = x + w 40 | print(z + (- y)) 41 | 42 | After instruction selection: 43 | 44 | movq $1, v 45 | movq $42, w 46 | movq v, x 47 | addq $7, x 48 | movq x, y 49 | movq x, z 50 | addq w, z 51 | movq y, t 52 | negq t 53 | movq z, %rax 54 | addq t, %rax 55 | 56 | ## Liveness Analysis 57 | 58 | Goal: figure out the program regions where a variable is in use. 59 | 60 | Def. A variable is *live* at a program point if the value in the 61 | variable is used at some later point in the program. 62 | 63 | The following equations compute the live before/after sets 64 | for each instruction. 65 | The instructions of the program are numbered 1 to n. 66 | 67 | L_after(k) = L_before(k + 1) 68 | L_after(n) = {} 69 | 70 | L_before(k) = (L_after(k) - W(k)) U R(k) 71 | 72 | Here's the program with the live-after set next to each instruction. 73 | Compute them from bottom to top. 74 | 75 | {} 76 | movq $1, v 77 | {v} 78 | movq $42, w 79 | {v,w} 80 | movq v, x 81 | {w,x} 82 | addq $7, x 83 | {w,x} 84 | movq x, y 85 | {w,x,y} 86 | movq x, z 87 | {w,y,z} 88 | addq w, z 89 | {y,z} 90 | movq y, t 91 | {t,z} 92 | negq t 93 | {t,z} 94 | movq z, %rax 95 | {t,rax} 96 | addq t, %rax 97 | {} 98 | 99 | Here's another example that involves function calls. 100 | 101 | Racket: 102 | 103 | (let ([x (read)]) 104 | (let ([y (read)]) 105 | (+ (+ x y) 42))) 106 | 107 | Python: 108 | 109 | x = input_int() 110 | y = input_int() 111 | print(x + y + 42) 112 | 113 | Here's the result of select instructions and liveness analysis. 114 | 115 | {} 116 | callq read_int 117 | {rax} 118 | movq %rax, x 119 | {x} 120 | callq read_int 121 | {rax, x} 122 | movq %rax, y 123 | {x, y} 124 | movq x, tmp 125 | {tmp, y} 126 | addq y, tmp 127 | {tmp} 128 | movq tmp, %rax 129 | {rax} 130 | addq $42, %rax 131 | {} 132 | 133 | A variable is *call live* if it appears in the live-after set of a 134 | `callq` instruction. So in the above example, variable `x` is call 135 | live. We assign call live variables into callee-save registers if 136 | possible, and if not, we spill them to the stack. We never assign call 137 | live variables to caller-save registers because then we'd effectively 138 | have to spill them anyways. 139 | 140 | 141 | ## Build the Interference Graph 142 | 143 | Def. An *interference graph* is an undirected graph whose vertices 144 | represent variables and whose edges represent conflicts, i.e., when 145 | two vertices are live at the same time. 146 | 147 | ### Naive Approach 148 | 149 | Inspect each live-after set, and add an edge between every pair of 150 | variables. 151 | 152 | Down sides: 153 | * It is O(n^2) per instruction (for n variables) 154 | * If one variable is assigned to another,then they have the same value 155 | and can be stored in the same register, but the naive approach 156 | would mark them as conflicting. 157 | Example: consider the instruction from the above program 158 | 159 | movq x, y {w,x,y} 160 | 161 | Both x and y are live at this point, so the naive approach 162 | would mark them as conflicting. But because of this assignment 163 | they hold the same value, so they could share the same register. 164 | 165 | ### Analyze Writes 166 | 167 | The better approach focuses on writes: it creates an edge between the 168 | variable being written-to by the current instruction and all the 169 | *other* live variables. (One should not create self edges.) For a call 170 | instruction, all caller-save register must be considered as 171 | written-to. For the move instruction, we skip adding an edge between a 172 | live variable and the destination variable if the live variable 173 | matches the source of the move, as per point 2 above. So we have 174 | the followng three rules. 175 | 176 | 1. For an arithmetic instructions, such as (addq s d) 177 | for each v in L_after, 178 | if v != d then 179 | add edge (d,v) 180 | 181 | 2. For a call instruction (callq label), 182 | for each v in L_after, 183 | for each r in caller-save registers 184 | if r != v then 185 | add edge (r,v) 186 | 187 | 3. For a move instruction (movq s d), 188 | for each v in L_after, 189 | if v != d and v != s then 190 | add edge (d,v) 191 | 192 | Let us walk through the running example, proceeding top to bottom, 193 | apply the three rules to build the interference graph. 194 | 195 | {} 196 | movq $1, v rule 3: no interference (v=v) 197 | {v} 198 | movq $42, w rule 3: edge w-v 199 | {v,w} 200 | movq v, x rule 3: edge x-w 201 | {w,x} 202 | addq $7, x rule 1: edge x-w (dup.) 203 | {w,x} 204 | movq x, y rule 3: edge y-w, no edge y-x 205 | {w,x,y} 206 | movq x, z rule 3: edge z-w, edge z-y 207 | {w,y,z} 208 | addq w, z rule 1: edge z-y (dup.) 209 | {y,z} 210 | movq y, t rule 3: edge t-z 211 | {t,z} 212 | negq t.1 rule 1: edge t-z (dup) 213 | {t,z} 214 | movq z, %rax rule 2: but ignore rax 215 | {t} 216 | addq t, $rax rule 1: but ignore rax 217 | {} 218 | 219 | So the interference graph looks as follows: 220 | 221 | t ---- z x 222 | |\___ | 223 | | \ | 224 | | \| 225 | y ---- w ---- v 226 | 227 | -------------------------------------------------------------------------------- /lectures/interp-Rint.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require racket/fixnum) 3 | (require "utilities.rkt") 4 | (provide interp-Rint) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | ;; Interpreter for Rint: 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-exp "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-Rint p) 32 | (match p 33 | [(Program '() e) (interp-exp e)] 34 | )) 35 | 36 | 37 | -------------------------------------------------------------------------------- /lectures/interp-Rvar.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require racket/fixnum) 3 | (require racket/dict) 4 | (require "utilities.rkt") 5 | (provide interp-Rvar interp-Rvar-class) 6 | 7 | ;; Note to maintainers of this code: 8 | ;; A copy of this interpreter is in the book and should be 9 | ;; kept in sync with this code. 10 | 11 | (define interp-Rvar-class 12 | (class object% 13 | (super-new) 14 | 15 | (define/public ((interp-exp env) e) 16 | (match e 17 | [(Int n) n] 18 | [(Prim 'read '()) 19 | (define r (read)) 20 | (cond [(fixnum? r) r] 21 | [else (error 'interp-exp "expected an integer" r)])] 22 | [(Prim '- (list e)) 23 | (define v ((interp-exp env) e)) 24 | (fx- 0 v)] 25 | [(Prim '+ (list e1 e2)) 26 | (define v1 ((interp-exp env) e1)) 27 | (define v2 ((interp-exp env) e2)) 28 | (fx+ v1 v2)] 29 | [(Var x) (dict-ref env x)] 30 | [(Let x e body) 31 | (define new-env (dict-set env x ((interp-exp env) e))) 32 | ((interp-exp new-env) body)] 33 | )) 34 | 35 | (define/public (interp-program p) 36 | (match p 37 | [(Program '() e) ((interp-exp '()) e)] 38 | )) 39 | )) 40 | 41 | (define (interp-Rvar p) 42 | (send (new interp-Rvar-class) interp-program p)) 43 | 44 | -------------------------------------------------------------------------------- /lectures/interp_Pint.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | from utils import input_int 3 | 4 | def interp_exp(e): 5 | match e: 6 | case BinOp(left, Add(), right): 7 | l = interp_exp(left) 8 | r = interp_exp(right) 9 | return l + r 10 | case UnaryOp(USub(), v): 11 | return - interp_exp(v) 12 | case Constant(value): 13 | return value 14 | case Call(Name('input_int'), []): 15 | return int(input()) 16 | 17 | def interp_stmt(s): 18 | match s: 19 | case Expr(Call(Name('print'), [arg])): 20 | print(interp_exp(arg)) 21 | case Expr(value): 22 | interp_exp(value) 23 | 24 | def interp_P(p): 25 | match p: 26 | case Module(body): 27 | for s in body: 28 | interp_stmt(s) 29 | 30 | if __name__ == "__main__": 31 | eight = Constant(8) 32 | neg_eight = UnaryOp(USub(), eight) 33 | read = Call(Name('input_int'), []) 34 | ast1_1 = BinOp(read, Add(), neg_eight) 35 | pr = Expr(Call(Name('print'), [ast1_1])) 36 | p = Module([pr]) 37 | interp_Pint(p) 38 | -------------------------------------------------------------------------------- /lectures/interp_Pvar.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | from utils import input_int 3 | 4 | class InterpPvar: 5 | def interp_exp(self, e, env): 6 | match e: 7 | case BinOp(left, Add(), right): 8 | l = self.interp_exp(left, env) 9 | r = self.interp_exp(right, env) 10 | return l + r 11 | case UnaryOp(USub(), v): 12 | return - self.interp_exp(v, env) 13 | case Name(id): 14 | return env[id] 15 | case Constant(value): 16 | return value 17 | case Call(Name('input_int'), []): 18 | return int(input()) 19 | case _: 20 | raise Exception('error in InterpPvar.interp_exp, unhandled ' + repr(e)) 21 | 22 | def interp_stmts(self, ss, env): 23 | if len(ss) == 0: 24 | return 25 | match ss[0]: 26 | case Assign([lhs], value): 27 | env[lhs.id] = self.interp_exp(value, env) 28 | return self.interp_stmts(ss[1:], env) 29 | case Expr(Call(Name('print'), [arg])): 30 | print(self.interp_exp(arg, env), end='') 31 | return self.interp_stmts(ss[1:], env) 32 | case Expr(value): 33 | self.interp_exp(value, env) 34 | return self.interp_stmts(ss[1:], env) 35 | case _: 36 | raise Exception('error in InterpPvar.interp_stmt, unhandled ' + repr(ss[0])) 37 | 38 | def interp_P(self, p): 39 | match p: 40 | case Module(body): 41 | self.interp_stmts(body, {}) 42 | 43 | if __name__ == "__main__": 44 | eight = Constant(8) 45 | neg_eight = UnaryOp(USub(), eight) 46 | read = Call(Name('input_int'), []) 47 | ast1_1 = BinOp(read, Add(), neg_eight) 48 | pr = Expr(Call(Name('print'), [ast1_1])) 49 | p = Module([pr]) 50 | interp = InterpPvar() 51 | interp.interp_Pvar(p) 52 | -------------------------------------------------------------------------------- /python/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM eecsautograder/ubuntu18:latest 4 | 5 | RUN apt-get update && apt-get install -y unzip 6 | 7 | RUN apt-get update && apt-get -yq install software-properties-common 8 | 9 | RUN add-apt-repository ppa:deadsnakes/ppa -y 10 | 11 | RUN DEBIAN_FRONTEND=noninteractive apt-get -yq install python3.10 12 | 13 | -------------------------------------------------------------------------------- /python/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | unzip -q python-compiler.zip 4 | 5 | gcc -c -std=c99 runtime.c 6 | -------------------------------------------------------------------------------- /racket/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM eecsautograder/ubuntu18:latest 4 | 5 | RUN apt-get update && apt-get install -y software-properties-common && add-apt-repository ppa:plt/racket && apt-get update && apt-get install -y racket 6 | 7 | RUN raco setup --doc-index --force-user-docs 8 | 9 | RUN raco pkg install -i --batch --auto graph 10 | 11 | RUN apt-get update && apt-get install -y unzip 12 | 13 | -------------------------------------------------------------------------------- /racket/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | unzip -q course-compiler.zip 4 | 5 | gcc -c -std=c99 runtime.c 6 | --------------------------------------------------------------------------------