500 likes | 691 Views
This talk discusses the content, organization, and lessons learned from teaching a class on compilers at the University of Washington. It provides insights into the course, shares experiences from the Professional Masters Program, and highlights the importance of studying compilers in computer science.
E N D
Compiler Construction Overview & Lessons Learned from Teaching a class at UW Jim Hogg Program Manager - C++ Compiler Team - Microsoft October 2014
What is this Talk About? • I taught a class in Compilers, earlier this year, at UW • Part of UW's "Professional Masters Program" • I'll describe: • The Course - content, organization • "Inside a Compiler" (at a gallop) • Lessons Learned
UW's "Professional Master's Program • For "fully-employed professionals" • Part-time (1 evening per week) • Average 2.5 years to reach Masters degree • ~ $14k per year tuition fees • Average student: 5 years experience; GRE 83%; GPA 3.5 "The best computer science department you've never heard of" (NY Times) In recent years, ~50% of attendees employed at Microsoft
PMP - Classes • Compiler Construction • Principles of Software Engineering • Programming Languages • Concurrency • Human Computer Interaction • Applied Algorithms • Parallel Computations • Database Management Systems • Transaction Processing • Data Mining • Computer Architecture • Computer Operating Systems • Current Trends in Computer Graphics • Network Systems • Design & Implementation of Digital Systems • Applications of Artificial Intelligence • Computer Vision • Distributed Systems • Computer Animation Production • Computational Biology • Practical Aspects of Modern Cryptograph • Complexity Theory • Alternative Programming Paradigms • Software Entrepreneurship • Business Basics for CS Professionals
How did I get into this? One day, at a seminar, I was talking to the guy beside me . . .
Course Admin 10 lectures. One each week. Tuesday evenings 6:30-9:30 Broadcast from Microsoft Campus (Building 99) Recorded at: http://courses.cs.washington.edu/courses/csep501/14sp/ Homework, Tutorials, Project, Final Exam Set of slides from previous years (Hal Perkins) Teaching Assistant (Nat Mote)
My First Lecture It went like this . . .
Who Am I? • Jim Hogg • Part-time Job: Visiting Lecturer at UW • Day Job: Program Manager in Microsoft’s C++ Compiler Team • Backend – optimizations & codegen • Previous Software • Computational Physics; Operations Research; Oil Exploration • Operating Systems; Device Drivers • Languages & Compilers • Previous Hardware • IBM; Cray; Xerox; CDC; DEC-10; VAX; Alpha; PC Jim Hogg - UW - CSE - P501
Compilers, from 10,000 feet • Execute this: • How? Computers only know 1’s and 0’s • 55 8b . . . int p = 0; int k = 0; while (k < length) { if (a[k] > 0) p++; k++; } Jim Hogg - UW - CSE - P501
x86 Target Code 55 push ebp 8b ecmovebp, esp 83 ec58 sub esp, 88 c7 45 f8 00 00 00 00 mov _p$[ebp], 0 c7 45 fc 00 00 00 00 mov _k$[ebp], 0 $LN3: 83 7d fc 14 cmp _k$[ebp], 20 7d 1e jge $LN4 8b 45 fc moveax, _k$[ebp] 83 7c 85 a8 00 cmp _a$[ebp+eax*4], 0 . . . . . Jim Hogg - UW - CSE - P501
Why Study Compilers? • Be a better programmer • Insight into languages, compilers, and hardware • What’s all that stuff in the debugger? • Compiler techniques are everywhere • Little languages, verifiers, Lint, query languages, Verilog, Mathematica • Draws from many corners of CS • Finite automata, regex, grammars, graphs • Links to Hardware • ISA, pipeline, multi-issue, cache, SIMD, multi-core, memory consistency • Jobs available! • http://www.compilerjobs.com/ Jim Hogg - UW - CSE - P501
Compiler-related Turing Awards • 1966 Alan Perlis • 1972 EdsgerDijkstra • 1974 Donald Knuth • 1976 Rabin & Scott • 1977 John Backus • 1978 Bob Floyd • 1979 Ken Iverson • 1980 Tony Hoare • 1984 Niklaus Wirth • 1987 John Cocke • 1991 Robin Milner • 2001 Dahl & Nygaard • 2003 Alan Kay • 2005 Peter Naur • 2006 Fran Allen • 2008 Barbara Liskov Jim Hogg - UW - CSE - P501
Compilers, from 1,000 feet • Structure of a Compiler, approximately • Front end: analyze • Read source program; understand its structure and meaning • Specific to the source language used • Back end: synthesize (well, partly) • Generate equivalent target language program • Mostly unaware of the source language use Source Front End Back End Target Jim Hogg - UW - CSE - P501
Calendar 01-Apr Tue Class 1 – Overview, Regex 07-Apr Mon Homework 1 – regex 08-Apr Tue Class 2 – grammar, LR(1) 14-Apr Mon Homework 2 - grammars 15-Apr Tue Class 3 – LL(1), ASTs, IR 21-Apr Mon Project 1 - Scanner 21-Apr Mon Homework 3 - grammars 22-Apr Tue Class 4 – Semantics, x86 28-Apr Mon Project 2 – Parser, ASTs 29-Apr Tue Class 5 – Codeshape 06-May Tue Class 6 – Optimizations, Dataflow 13-May Tue Class 7 – Loops, SSA 19-May Mon Project 3 – Semantics, Symbol Table 20-May Tue Class 8 – Instruction Selection, Scheduling, Regalloc 27-May Tue Class 9 – Calling Conventions 28-May Wed Exam 03-Jun Tue Class 10 – Inlining, Multi-thread, GC 09-Jun Mon Project 4 – CodeGen 10-Jun Tue Project 5 - Report Jim Hogg - UW - CSE - P501
Books • Engineering a Compiler • Cooper & Torczon; 2e; 2011 • Solid; understandable; practical advice from the coal-face • Compilers: Principles, Techniques and Tools • Aho, Lam, Sethi, Ullman; 2e; 2011; • “The Dragon Book” • A Classic • Modern Compiler Implementation in Java • Appel; 2e; 2013 • Where the project comes from; good, but tough, text • Optimizing Compilers for Modern Architectures • Allen & Kennedy; 2001 • Good on SIMD & multi-core
More Books • Advanced Compiler Design & Implementation • Muchnick; 1e; 1997 • Detailed optimization text; magnum opus • Compiler Construction • Wirth; 1996 • “A refreshing antidote to heavy theoretical tomes” • Now free, from: http://www.ethoberon.ethz.ch/WirthPubl/CBEAll.pdf • Programming Language Processors in Java • Watt & Brown; 2000 • Pragmatic; lots of code (but no LR parsing) Jim Hogg - UW - CSE - P501
http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.htmlhttp://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html 1801 - Joseph Marie Jacquard uses punch cards to instruct a loom to weave "hello, world" into a tapestry. Redditers of the time are not impressed due to the lack of tail call recursion, concurrency, or proper capitalization 1940s - Various "computers" are "programmed" using direct wiring and switches. Engineers do this in order to avoid the tabs vs spaces debate 1970 - Niklaus Wirth creates Pascal, a procedural language. Critics immediately denounce Pascal because it uses "x := x + y" syntax instead of the more familiar C-like "x = x + y". This criticism happens in spite of the fact that C has not yet been invented. 1983 - Bjarne Stroustrup bolts everything he's ever heard of onto C to create C++. The resulting language is so complex that programs must be sent to the future to be compiled by the Skynet artificial intelligence. Build times suffer. http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html
The Project • Best way to learn about compilers is to build one! • Course project • MiniJava compiler: classes, objects, inheritance • Built using Java • Build scanner using JFlex; and parser using CUP • Optionally use an IDE (Eclipse, etc) • Optionally use a source code repository (SVN, etc) • Generate executable x86 code (via assembler) & run it • Complete in steps through the quarter • See MiniJavapage at:http://www.cambridge.org/resources/052182060X/ Jim Hogg - UW - CSE - P501
Code Flow (for Windows) parser.java parser.cup CUP sym.java prog.java javac scanner.jflex JFlex scanner.java mjc.class mjc.java prog.asm boot.c ast CL ML boot.obj prog.obj visitor link . . . prog.exe
Inside a Compiler - at a gallop 8 Phases ("passes"), as follows:
AST ‘Middle End’ Back End Target Source Front End chars IR IR Scan Select Instructions Optimize tokens IR Allocate Registers Parse IR AST Emit Semantics AST IR Machine Code Convert IR AST = Abstract Syntax Tree IR = Intermediate Representation Jim Hogg - UW - CSE - P501
Reminder: a token is . . . class C { public intfac(int n) { // factorial intnn; if (n < 1) nn= 1; else nn = n * this.fac(n-1); return nn; } } Key for Char Stream: ◊ newline \n ∙ space class∙C∙{◊∙∙public∙int∙fac(int∙n)∙{∙∙//∙factorial◊∙∙∙∙int∙nn;◊∙∙∙∙if(n∙<∙1)◊∙∙∙∙∙∙nn∙=∙1;◊∙∙∙∙else◊∙∙∙∙nn∙=∙n∙*∙(this.fac(n-1));◊∙∙∙∙return∙nn;◊∙∙}◊} CLASS ID:C LBRACE PUBLIC INT ID:fac LPAREN INT ID:n RPAREN LBRACE INT ID:nn SEMI IF LPAREN ID:n LT ILIT:1 RPAREN ID:nn EQ ILIT:1 ELSE ID:nn EQ ID:n TIMES LPAREN ID:this DOT ID:fac LPAREN ID:n MINUS ILIT:1 RPAREN RPAREN SEMI RETURN ID:nn SEMI RBRACE RBRACE Jim Hogg - UW - CSE P501
Token Spotting if(a<=3)++grades[1]; // what are the tokens? (no spaces) public intfac(int n) { // what are the tokens? (need spaces?) • Counter-example: fixed-format FORTRAN: • DO 50 I = 1,99 // DO loop • DO 50 I = 1.2 // assignment: DO50I = 1.2 Jim Hogg - UW - CSE P501
AST ‘Middle End’ Back End Target Source Front End chars IR IR Scan Select Instructions Optimize tokens IR Allocate Registers Parse IR AST Emit Semantics AST IR Machine Code Convert IR AST = Abstract Syntax Tree IR = Intermediate Representation Jim Hogg - UW - CSE - P501
Valid Tokens != Valid Program MiniJava includes the following tokens (among many others): • class int [ ( . true < this ) + * ; while = if id ilit ! / new { So a MiniJavaScanner would happily accept the following program: • int ; = true { while ( x < true * if { or 123 ) goto count_99 We rely on a MiniJavaParser to reject this kind of gibberish But how do we specify what makes a validMiniJava program? Jim Hogg - UW - CSE - P501
Context-Free Grammars (CFG) Grammar for the Hokum Language • ProgStm;Prog|Stm • StmAsStm|IfStm • AsStmVar=Exp • IfStmifExpthenAsStm • VorCVar|Const • ExpVorC|VorC+VorC • Var[a-z] • Const[0-9] • Context-Free Grammar ~ CFG ~ Grammar ~ Backus-Naur Form ~ BNF • Productions, or Rules • Terminals & Non-Terminals; Start (Symbol) • Multiple languagespresent in the description Jim Hogg - UW - CSE - P501
Example Hokum Programs Legal Hokum BNF Grammar a= 1; b = a + 4 z = 1 if b + 3 then z = 2 ProgStm;Prog|Stm StmAsStm|IfStm AsStm Var=Exp IfStm ifExpthenAsStm VorC Var|Const Exp VorC|VorC+VorC Var [a-z] Const [0-9] Illegal a= x < 20 b= a + 4 + 5 ; z = 1 ; if (a == 33) z < 2 ; But how do we know which programs are legal or illegal, in Hokum? Jim Hogg - UW - CSE - P501
ProgStm;Prog|Stm StmAsStm|IfStm AsStm Var=Exp IfStm ifExpthenAsStm VorC Var|Const Exp VorC|VorC+VorC Var [a-z] Const [0-9] Derivation Prog => Stm; Prog => AsStm; Prog => Var= Exp ; Prog => a = Exp; Prog => a = VorC ; Prog => a = Const ; Prog => a = 1 ; Prog => a = 1 ; Stm => a = 1 ; IfStm => a = 1 ; if Expthen AsStm => a = 1 ; if VorC+ VorC then AsStm => a = 1 ; if Var + VorC then AsStm => a = 1 ; if a + VorCthen AsStm => a = 1 ; if a + Const then AsStm => a = 1 ; if a + 1 then AsStm => a = 1 ; if a + 1 then Var = Exp => a = 1 ; if a + 1 then b = Exp => a = 1 ; if a + 1 then b = VorC => a = 1 ; if a + 1 then b = Const => a = 1 ; if a + 1 then b = 2 • => versus • Leftmost, rightmost, middlemost • Sentential Form & Sentence • What is a Context-Sensitive Grammar? Jim Hogg - UW - CSE - P501
ProgStm;Prog|Stm StmAsStm|IfStm AsStm Var=Exp IfStm ifExpthenAsStm VorC Var|Const Exp VorC|VorC+VorC Var [a-z] Const [0-9] Parse Tree Prog Prog ; Stm Stm AsStm IfStm Var = Exp then Exp if AsStm a VorC Var = Exp VorC + VorC Const VorC b Var Const Const 1 1 a 2 Jim Hogg - UW - CSE - P501
Junk Nodes in the Parse Tree Prog Prog ; Stm Stm AsStm IfStm Var = Exp then Exp if AsStm a VorC Var = Exp VorC + VorC Const VorC b Var Const Const 1 1 a 2 Jim Hogg - UW - CSE - P501
AST (Abstract Syntax Tree) Prog = IfStm Var:a Const:1 + = Const:2 Var:b Var:a Const:1 Jim Hogg - UW - CSE - P501
AST ‘Middle End’ Back End Target Source Front End chars IR IR Scan Select Instructions Optimize tokens IR Allocate Registers Parse IR AST Emit Semantics AST IR Machine Code Convert IR AST = Abstract Syntax Tree IR = Intermediate Representation Jim Hogg - UW - CSE - P501
Beyond Syntax - Semantic Checks There is a level of correctness not captured by a CFG: • Has a variable been declared before it is used? • Are types consistent in an expression? • In the assignment x=y, is y assignable to x? • Does a method call have right number and types of parameters? • In a selector p.q, is q a method or field of object p? • Is variable x guaranteed to be initialized before it is used? • Could p be null when p.q is executed? • Etc Jim Hogg - UW - CSE - P501
It gets progressively harder • We can specify Java micro-syntax with a few dozen regex • Then find a tool (JFlex) to create a scanner from these regex • We can specify Java syntax with a few pages of BNF • Then find a tool (CUP) to create a parser from that BNF • What about the huge collection of constraint checks? (760 pages in the Java Language Reference Manual) • Attribute Grammars? • Then find a tool (???) to create a semantic checker for that Attribute Grammar? Jim Hogg - UW - CSE - P501
AST ‘Middle End’ Back End Target Source Front End chars IR IR Scan Select Instructions Optimize tokens IR Allocate Registers Parse IR AST Emit Semantics AST IR Machine Code Convert IR AST = Abstract Syntax Tree IR = Intermediate Representation Jim Hogg - UW - CSE - P501
Example Optimization • Classic example: Array references in a loop • for (k = 0; k < n; k++) a[k] = 0; • Naive codegen for a[k] = 0 in loop body • moveax, 4// elemsize = 4 bytes • imuleax, [ebp+offsetk]// k * elemsize • add eax, [ebp+offseta]// &a[0] + k * elemsize • mov [eax], 0 // a[k] = 0 • Better! • moveax, [ebp+offseta] // &a[0], once-off • mov [eax], 0 // a[0]=0, a[1]=0, etc • add eax, 4 // eax = &a[1], &a[2], etc Note: pointers allow a user to do this directly in C or C++ Eg: for (p = a; p < a + n; ) *p++ = 0; Jim Hogg - UW - CSE - P501
Any Loops in this Code? i = 0 goto L8 L7: i++ L8: if (i < N) goto L9 s = 0 j = 0 goto L5 L4: j++ L5: N-- if(j >= N) goto L3 if (a[j+1] >= a[j]) goto L2 t = a[j+1] a[j+1] = a[j] a[j] = t s = 1 L2: gotoL4 L3: if(s != ) goto L1 else goto L9 L1: gotoL7 L9: return Anyone recognize or guess the algorithm? Jim Hogg - UW - CSE - P501
Basic Blocks 1 i = 1 2 j = 1 3 t1 = 10 * i 4 t2 = t1 + j 5 t3 = 8 * t2 6 t4 = t3 - 88 7 a[t4] = 0 8 j = j + 1 9 if j <= 10 goto #3 10 i = i + 1 11 if i <= 10 goto #2 12 i = 1 13 t5 = i - 1 14 t6 = 88 * t5 15 a[t6] = 1 16 i = i + 1 17 if i <= 10 goto #13 Typical "tuple stew" - IR generated by traversing an AST • Partition into Basic Blocks: • Sequence of consecutive instructions • No jumps into the middle of a BB • No jumps out of the middles of a BB • "I've started, so I'll finish" • (Ignore exceptions) Jim Hogg - UW - CSE - P501
Basic Blocks : Leaders 1 i = 1 2 j = 1 3 t1 = 10 * i 4 t2 = t1 + j 5 t3 = 8 * t2 6 t4 = t3 - 88 7 a[t4] = 0 8 j = j + 1 9 if j <= 10 goto #3 10 i = i + 1 11 if i <= 10 goto #2 12 i = 1 13 t5 = i - 1 14 t6 = 88 * t5 15 a[t6] = 1 16 i = i + 1 17 if i <= 10 goto #13 • Identify Leaders (first instruction in a basic block): • First instruction is a leader • Any target of a branch/jump/goto • Any instruction immediately after a branch/jump/goto Leaders in red. Why is each leader a leader? Jim Hogg - UW - CSE - P501
ENTRY Basic Blocks : Flowgraph B1 i = 1 j = 1 B2 Control Flow Graph ("CFG", again!) B3 t1 = 10 * i t2 = t1 + j t3 = 8 * t2 t4 = t3 - 88 a[t4] = 0 j = j + 1 if j <= 10 goto B3 • 3 loops total • 2 of the loops are nested B4 i = i + 1 if i <= 10 goto B2 B5 i = 1 B6 t5 = i - 1 t6 = 88 * t5 a[t6] = 1 i = i + 1 if i <= 10 goto B6 Most of the executions likely spent in loop bodies; that's where to focus efforts at optimization Jim Hogg - UW - CSE - P501 EXIT
Loop in a Flowgraph: Intuition Header Node • Cluster of nodes, such that: • There's one node called the "header" • I can reach all nodes in the cluster from the header • I can get back to the header from all nodes in the cluster • Only once entrance - via the header • One or more exits Jim Hogg - UW - CSE - P501
AST ‘Middle End’ Back End Target Source Front End chars IR IR Scan Select Instructions Optimize tokens IR Allocate Registers Parse IR AST Emit Semantics AST IR Machine Code Convert IR AST = Abstract Syntax Tree IR = Intermediate Representation Jim Hogg - UW - CSE - P501
Call Example ; n = sum(1, 2) push 2 ; push args push 1 call sum ; push L and jump L: add esp, 8 ; pop args mov [ebp+offsetn], eax ; store result into n Jim Hogg - UW - CSE - P501
Example Function int sum(int x, int y) { int a; int b; a = x; b = a + y; return b; } Simply calculates x+y, but with a few extraneous statements to illustrate the generated code Jim Hogg - UW - CSE - P501
Assembly Language Version sum PROC pushebp; prolog movebp, esp; prolog subesp, 8 ; prolog moveax, [ebp+8] ; eax = x mov[ebp-4], eax; a = eax movecx, [ebp-4] ; ecx = a addecx, [ebp+12] ; ecx += y mov[ebp-8], ecx; b = ecx moveax, [ebp-8] ; eax = b movesp, ebp; epilog popebp; epilog ret0 sum ENDP int sum(int x, int y) { int a; int b; a = x; b = a + y; return b; } Jim Hogg - UW - CSE - P501
Assembly Language Version retaddr 00A5 F988 esp x 0000 0001 sum PROC pushebp; prolog movebp, esp; prolog subesp, 8 ; prolog moveax, [ebp+8] ; eax = x mov[ebp-4], eax; a = eax movecx, [ebp-4] ; ecx = a addecx, [ebp+12] ; ecx += y mov[ebp-8], ecx; b = ecx moveax, [ebp-8] ; eax = b movesp, ebp; epilog popebp; epilog ret sum ENDP y 0000 0002 stack grows up the page b ???? ???? esp ???? ???? a - old ebp ebp + 00A5 F988 x 0000 0001 y 0000 0002 Jim Hogg - UW - CSE - P501
cdecl - Responsibilities • Caller: • if you need the values currently in eax, ecx or edx then save them! • push arguments right-to-left • execute call instruction • pop arguments from stack after ret • restore any of eax, ecx or edx that you saved before call • Callee: • allocate space in stack frame for local variables • if you will use any of ebx, edi or esi then save them! • execute function body • move result into eax • restore any of ebx, edi or esi that you saved earlier • Pop the stack frame so that return-address is top-of-stack • execute ret instruction Jim Hogg - UW - CSE - P501
Lessons Learned • Example 1st - Theory 2nd • Students are smarter than you think • Check for solutions to the project on the Internet! • You are bound by the syllabus • Thinking on your feet is impossible • Don't try to explain "The Visitor Pattern" • It's much more work than you thought • We reach the exciting stuff, just as the course ends • Dry-run first with your colleagues
Future? • Repeat the course, but better? • Skip the classic lexer/parser tools? • Extend the project? • Devise a new, follow-on course on Optimizations? • LLVMbased?