Download presentation
Presentation is loading. Please wait.
1
Theory of Compilation 236360 Erez Petrank Lecture 7: Intermediate Representation Part 2: Backpatching 1
2
You are here 2 Executable code Executable code exe Source text Source text txt Compiler Lexical Analysis Syntax Analysis Semantic Analysis Inter. Rep. (IR) Code Gen. characterstokens AST ( Abstract Syntax Tree) Annotated AST
3
GENERIC Intermediate Representation “neutral” representation between the front-end and the back-end – Abstracts away details of the source language – Abstract away details of the target language A compiler may have multiple intermediate representations and move between them 3 Java C Objective C Ada arm x86ia64 … …
4
Our Representation: Three Address Code (3AC) Every instruction operates on three addresses – result = operand1 operator operand2 Close to low-level operations in the machine language – Operator is a basic operation Statements in the source language may be mapped to multiple instructions in three address code 4
5
Three address code: example 5 assign a a + + * * b b uminus c c * * b b c c t5t5 :=a t 2 + t 4 :=t5t5 b * t 3 :=t4t4 – c:=t3t3 b * t 1 :=t2t2 – c:=t1t1
6
Three address code: example instructions 6 instructionmeaning x := y op zassignment with binary operator x := op yassignment unary operator x:= yassignment x := &yassign address of y x:=*yassignment from deref y *x := yassignment to deref x instructionmeaning goto Lunconditional jump if x relop y goto Lconditional jump
7
Creating 3AC Assume bottom up parser – Covers a wider range of grammars – LALR sufficient to cover most programming languages Creating 3AC via syntax directed translation Attributes examples: – code – code generated for a nonterminal – var – name of variable that stores result of nonterminal freshVar() – helper function that returns the name of a fresh variable 7
8
Creating 3AC: expressions 8 productionsemantic rule S ➞ id := E S.code := E. code || gen(id.var ‘:=‘ E.var) E ➞ E1 + E2 E.var := freshVar(); E.code = E1.code || E2.code || gen(E.var ‘:=‘ E1.var ‘+’ E2.var) E ➞ E1 * E2 E.var := freshVar(); E.code = E1.code || E2.code || gen(E.var ‘:=‘ E1.var ‘*’ E2.var) E ➞ - E1 E.var := freshVar(); E.code = E1.code || gen(E.var ‘:=‘ ‘uminu’ E1.var) E ➞ (E1) E.var := E1.var E.code = ‘(‘ || E1.code || ‘)’ E ➞ id E.var := id.var; E.code = ‘’ (we use || to denote concatenation of intermediate code fragments)
9
example 9 assign a a + + * * b b uminus c c * * b b c c E.var = c E.code =‘’ E.var = b E.code =‘’ E.var = t2 E.code =‘t1 = -c t2 = b*t1’ E.var = t1 E.code =‘t1 = -c’ E.var = b E.code =‘’ E.var = c E.code =‘’ E.var = t3 E.code =‘t3 = -c’ E.var = t4 E.code =‘t3 = -c t4 = b*t3’ E.var = t5 E.code =‘t1 = -c t2 = b*t1 t3 = -c t4 = b*t3 t5 = t2+t4’ a = b * -c + b* -c
10
Creating 3AC: control statements S while E do S1 10 productionsemantic rule S while E do S1S.begin := freshLabel(); S.after = freshLabel(); S.code := gen(S.begin ‘:’) || E.code || gen(‘if’ E.var ‘=‘ ‘0’ ‘goto’ S.after) || S1.code || gen(‘goto’ S.begin) || gen(S.after ‘:’) E.code S.begin: if E.var = 0 goto S.after S 1.code goto S.begin · · ·S.after:
11
Generating IR code Option 1 accumulate code in AST attributes Option 2 emit IR code to a file during compilation – If for every production the code of the left-hand-side is constructed from a concatenation of the code of the RHS in some fixed order 11
12
Expressions and assignments 12 productionsemantic action S ➞ id := E { p:= lookup(id.name); if p ≠ null then emit(p ‘:=‘ E.var) else error } E ➞ E1 op E2 { E.var := freshVar(); emit(E.var ‘:=‘ E1.var op E2.var) } E ➞ - E1 { E.var := freshVar(); emit(E.var ‘:=‘ ‘uminus’ E1.var) } E ➞ ( E1) { E.var := E1.var } E ➞ id { p:= lookup(id.name); if p ≠ null then E.var :=p else error }
13
Boolean Expressions 13 productionsemantic action E ➞ E1 op E2 { E.var := freshVar(); emit(E.var ‘:=‘ E1.var op E2.var) } E ➞ not E1 { E.var := freshVar(); emit(E.var ‘:=‘ ‘not’ E1.var) } E ➞ ( E1) { E.var := E1.var } E ➞ true { E.var := freshVar(); emit(E.var ‘:=‘ ‘1’) } E ➞ false { E.var := freshVar(); emit(E.var ‘:=‘ ‘0’) } Represent true as 1, false as 0 Wasteful representation, creating variables for true/false
14
Boolean expressions via jumps 14 productionsemantic action E ➞ id1 op id2 { E.var := freshVar(); emit(‘if’ id1.var relop id2.var ‘goto’ nextStmt+2); emit( E.var ‘:=‘ ‘0’); emit(‘goto ‘ nextStmt + 1); emit(E.var ‘:=‘ ‘1’) } This is useful for short circuit evaluation. Let us start with an example.
15
Boolean Expressions: an Example 15 E E E E E E a a < < b b or E E c c < < d d E E e e < < f f and
16
Boolean Expressions: an Example 16 E E E E E E a a < < b b or E E c c < < d d E E e e < < f f and if a < b goto 103100: T 1 := 0101: goto 104102: T 1 := 1103:
17
Boolean Expressions: an Example 17 E E E E E E a a < < b b or E E c c < < d d E E e e < < f f and if a < b goto 103100: T 1 := 0101: goto 104102: T 1 := 1103: if c < d goto 107104: T 2 := 0105: goto 108106: T 2 := 1107:
18
Boolean Expressions: an Example 18 E E E E E E a a < < b b or E E c c < < d d E E e e < < f f and if a < b goto 103100: T 1 := 0101: goto 104102: T 1 := 1103: if c < d goto 107104: T 2 := 0105: goto 108106: T 2 := 1107: if e < f goto 111108: T 3 := 0109: goto 112110: T 3 := 1111: 112:
19
Boolean Expressions: an Example 19 E E E E E E a a < < b b or E E c c < < d d E E e e < < f f and if a < b goto 103100: T 1 := 0101: goto 104102: T 1 := 1103: if c < d goto 107104: T 2 := 0105: goto 108106: T 2 := 1107: if e < f goto 111108: T 3 := 0109: goto 112110: T 3 := 1111: 112: 113: T 4 := T 2 and T 3
20
Boolean Expressions: an Example 20 E E E E E E a a < < b b or E E c c < < d d E E e e < < f f and if a < b goto 103100: T 1 := 0101: goto 104102: T 1 := 1103: if c < d goto 107104: T 2 := 0105: goto 108106: T 2 := 1107: if e < f goto 111108: T 3 := 0109: goto 112110: T 3 := 1111: 112: 113: T 4 := T 2 and T 3 T 5 := T 1 or T 4
21
Short circuit evaluation Second argument of a boolean operator is only evaluated if the first argument does not already determine the outcome (x and y) is equivalent to: “if x then y else false”; (x or y) is equivalent to: “if x then true else y”; Is short circuit evaluation equivalent to standard evaluation? We use it if the language definition dictates its use. 21
22
Our previous example 22 a < b or (c<d and e<f) 100: if a < b goto 103 101: T 1 := 0 102: goto 104 103: T 1 := 1 104: if c < d goto 107 105: T 2 := 0 106: goto 108 107: T 2 := 1 108: if e < f goto 111 109: T 3 := 0 110: goto 112 111: T 3 := 1 112: T 4 := T 2 and T 3 113: T 5 := T 1 and T 4 100: if a < b goto 105 101: if !(c < d) goto 103 102: if e < f goto 105 103: T := 0 104: goto 106 105: T := 1 106: naiveShort circuit evaluation
23
Control Structure: if, else, while. Consider the conditional jumps: One option is to create code for B, create code for S, and then create a jump to the beginning of S or the end of S according to B’s value. But it would be more efficient to create code that while executing B will jump to the correct location immediately when the value of B is discovered. if B then S 1 if B then S 1 else S 2 while B do S 1 S → |
24
Control Structure: if, else, while. The problem is that we do not know where to jump to… While parsing B’s tree for “if B then S”, we don’t know S’s code and where it starts or ends. Yet, we must generate jumps to these locations. Solution: for each expression B keep labels: B.trueLabel and B.falseLabel. We jump to these labels when B’s value is true or false correspondingly. Also, for each statement S, we have a label S.nextLabel which keeps the address of the code that follows the statement S. S S if B B then S S The semantic equation will be B.falseLabel = S.next. For B.true, we create a new label between B’s code and S’s code.
25
התכונה next While parsing statement S, generate the code with its next label. The attribute S.next is inherited: the child gets it. The attribute code is generated: the parent gets it. The label S.next is symbolic. The address will only be known after we parse S. productionsemantic action P SS.next = freshLabel(); P.code = S.code || label(S.next) S S1 S2S1.next = freshLabel(); S2.next = S.next; S.code = S1.code || label(S1.next) || S2.code
26
Control Structures: conditional 26 productionsemantic action S if B then S1B.trueLabel = freshLabel(); B.falseLabel = S.next; S1.next = S.next; S.code = B.code || gen (B.trueLabel ‘:’) || S1.code Are S1.next, B.falseLabel inherited or synthesized? Is S.code inherited or synthesized?
27
Control Structures: conditional 27 productionsemantic action S if B then S1 else S2 B.trueLabel = freshLabel(); B.falseLabel = freshLabel(); S1.next = S.next; S2.next = S.next; S.code = B.code || gen(B.trueLabel ‘:’) || S1.code || gen(‘goto’ S.next) || gen(B.falseLabel ‘:’) || S2.code B.trueLabel and B.falseLabel considered inherited
28
S if B then S1 else S2 28 B.code S1.code goto S.next S2.code … B.trueLabel: B.falseLabel: S.next: B.trueLabel = freshLabel(); B.falseLabel = freshLabel(); S1.next = S.next; S2.next = S.next; S.code = B.code || gen(B.trueLabel ‘:’) || S1.code || gen(‘goto’ S.next) || gen(B.falseLabel ‘:’) || S2.code
29
Boolean expressions 29 productionsemantic action B B1 or B2B1.trueLabel = B.trueLabel; B1.falseLabel = freshLabel(); B2.trueLabel = B.trueLabel; B2.falseLabel = B.falseLabel; B.code = B1.code || gen (B1.falseLabel ‘:’) || B2.code B B1 and B2B1.trueLabel = freshLabel(); B1.falseLabel = B.falseLabel; B2.trueLabel = B.trueLabel; B2.falseLabel = B.falseLabel; B.code = B1.code || gen (B1.trueLabel ‘:’) || B2.code B not B1B1.trueLabel = B.falseLabel; B1.falseLabel = B.trueLabel; B.code = B1.code; B (B1)B1.trueLabel = B.trueLabel; B1.falseLabel = B.falseLabel; B.code = B1.code; B id1 relop id2B.code=gen (‘if’ id1.var relop id2.var ‘goto’ B.trueLabel)||gen(‘goto’ B.falseLabel); B trueB.code = gen(‘goto’ B.trueLabel) B falseB.code = gen(‘goto’ B.falseLabel); Generate code that jumps to B.trueLabel if B’s value is true and to B.falseLabel if B’s value is false.
30
Boolean expressions How can we determine the address of B1.falseLabel? Only possible after we know the code of B1 and all the code preceding B1 30 productionsemantic action B B1 or B2B1.trueLabel = B.trueLabel; B1.falseLabel = freshLabel(); B2.trueLabel = B.trueLabel; B.falseLabel = B.falseLabel; B.code = B1.code || gen (B1.falseLabel ‘:’) || B2.code
31
Example 31 S S if B B then S1 B1 B2 and false true B1.trueLabel = freshLabel(); B1.falseLabel = B.falseLabel; B2.trueLabel = B.trueLabel; B2.falseLabel = B.falseLabel; B.code = B1.code || gen (B1.trueLabel ‘:’) || B2.code B.code = gen(‘goto’ B1.trueLabel) B.code = gen(‘goto’ B2.falseLabel)
32
Example 32 S S if B B then S1 B1 B2 and false true B.trueLabel = freshLabel(); B.falseLabel = S.next; S1.next = S.next; S.code = B.code || gen (B.trueLabel ‘:’) || S1.code B1.trueLabel = freshLabel(); B1.falseLabel = B.falseLabel; B2.trueLabel = B.trueLabel; B2.falseLabel = B.falseLabel; B.code = B1.code || gen (B1.trueLabel ‘:’) || B2.code B.code = gen(‘goto’ B1.trueLabel) B.code = gen(‘goto’ B2.falseLabel)
33
Computing the labels We can build the code while parsing the tree bottom- up, leaving the jump targets vacant. We can compute the values for the labels in a second traversal of the AST Can we do it in a single pass? 33
34
So far… Three address code. Intermediate code generation is executed with parsing (via semantic actions). Creating code for Boolean expressions and for control statements is more involved. We typically use short circuit evaluation, value of expression is implicit in control location. We need to compute the branching addresses. Option 1: compute them in a second AST pass. 34
35
Backpatching ( תיקון לאחור ) Goal: generate code in a single pass Generate code as we did before, but manage labels differently Keep targets of jumps empty until values are known, and then back-patch them New synthesized attributes for B – B.truelist – list of jump instructions that eventually get the label where B goes when B is true. – B.falselist – list of jump instructions that eventually get the label where B goes when B is false. 35
36
Backpatching For every label, maintain a list of instructions that jump to this label When the address of the label is known, go over the list and update the address of the label Previous solutions do not guarantee a single pass – The attribute grammar we had before is not S-attributed (e.g., next is inherited), and is not L-attributed (because the inherited attributes are not necessarily from left siblings). 36
37
Why is it difficult? Think of an if-then-else statement. To compute S1.next, we need to know the code of all blocks S1, B, S2, in order to compute the address that follows them. On the other hand, to compute the code of S1, we need to know S1.next, or S.next, which is not known before S1 is computed. So we cannot compute S1’s code with all jump targets, but we can compute it up to “leaving space” for future insertion of S1.next. Using backpatching we maintain a list of all empty spaces that need to jump to S1.next. When we know S1.next, we go over this list and update the jump targets with S1.next’s value. if B S thenelse S1S1 S2S2
38
Functions for list handling makelist(addr) – create a list of instructions containing addr merge(p1,p2) – concatenate the lists pointed to by p1 and p2, returns a pointer to the new list backpatch(p,addr) – inserts i as the target label for each of the instructions in the list pointed to by p 38
39
Accumulating the code Assume a bottom-up parsing so that the code is created in the correct order from left to right, bottom-up. The semantic analysis is actually executed during parsing and the code is accumulated. Recall that we associated two labels B.trueLabel and B.falseLabel for each expression B, which are the labels to which we should jump if B turns out true or false. We will now replace these with two lists B.truelist and B.falselist, which record all instructions that should be updated with the address of B.trueLabel or B.falseLabel when we discover their values. In addition, we also replace S.next with S.nextlist, maintaining all instructions that should jump to S.next, when we know what it is.
40
Accumulating Code (cont’d) B.truelist and B.falselist are generated attributes: the descendants tell the parent where there is code to fix. When ascending to the parent of B, we know what are the relevant addresses and we can backpatch them into all the instruction locations that are in the list. Similarly for S.nextlist. We will need a function nextinstr() that retruns the address of the next instruction.
41
Computing Boolean expressions Recall the code jumps to B.true ot B.false according to B’s value. Previously: Now with backpatching: { B.code := gen ( ' if ' id 1.var relop.op id 2.var ' goto ' B.true ) || gen ( ' goto ' B.false ) } B → id 1 relop id 2 { B.code := gen ( ' goto ' B.true ) } B → true { B.code := gen ( ' goto ' B.false ) } B → false { B.truelist := makelist ( nextinstr ) ; B.falselist := makelist ( nextinstr + 1 ) ; emit ( ' if ' id 1.var relop.op id 2.var ' goto_ ' ) || emit ( ' goto_ ' ) } B → id 1 relop id 2 { B.truelist := makelist ( nextinstr ) ; emit ( ' goto_ ' ) } B → true { B.falselist := makelist ( nextinstr ) ; emit ( ' goto_ ' ) } B → false
42
Computing Boolean expressions Recall the code jumps to B.true ot B.false according to B’s value. Previously: Now with backpatching: { B 1.true := B.false ; B 1.false := B.true ; B.code := B 1.code } B → not B 1 { B 1.true := B.true ; B 1.false := B.false ; B.code := B 1.code } B → ( B 1 ) { B.truelist := B 1.falselist ; B.falselist := B 1.truelist } B → not B 1 { B.truelist := B 1.truelist ; B.falselist := B 1.falselist } B → ( B 1 )
43
Computing Boolean expressions Recall the code jumps to B.true ot B.false according to B’s value. Previously: Now with backpatching: { B 1.true := B.true ; B 1.false := newlable() ; B 2.true := B.true ; B 2.false := B.false ; B.code := B 1.code || gen ( B 1.false ' : ') || B 2.code } B → B 1 or B 2 { B 1.true := newlabel (); B 1.false := B.false ; B 2.true := B.true ; B 2.false := B.false ; B.code := B 1.code || gen ( B 1.true ' : (' || B 2.code } B → B 1 and B 2 { backpatch ( B 1.falselist, M.instr ) ; B.truelist := merge ( B 1.truelist, B 2.truelist ) ; B.falselist := B 2.falselist } B → B 1 or M B 2 { backpatch ( B 1.truelist, M.instr ) ; B.truelist := B 2.truelist ; B.falselist := merge ( B 1.falselist, B 2.falselist ) } B → B 1 and M B 2 { M.instr := nextinstr } M →
44
Backpatching Boolean expressions 44 productionsemantic action B B1 or M B2backpatch(B1.falseList,M.instr); B.trueList = merge(B1.trueList,B2.trueList); B.falseList = B2.falseList; B B1 and M B2backpatch(B1.trueList,M.instr); B.trueList = B2.trueList; B.falseList = merge(B1.falseList,B2.falseList); B not B1B.trueList = B1.falseList; B.falseList = B1.trueList; B (B1)B.trueList = B1.trueList; B.falseList = B1.falseList; B id1 relop id2B.trueList = makeList(nextInstr); B.falseList = makeList(nextInstr+1); emit (‘if’ id1.var relop id2.var ‘goto _’) || emit(‘goto _’); B trueB.trueList = makeList(nextInstr); emit (‘goto _’); B falseB.falseList = makeList(nextInstr); emit (‘goto _’); M M M.instr = nextinstr;
45
Using a marker { M.instr = nextinstr;} Use M to obtain the address just before B2 code starts being generated 45 B1 or B B M M B2 Example:
46
Example 46 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto _ B id1 relop id2B.trueList = makeList(nextInstr); B.falseList = makeList(nextInstr+1); emit (‘if’ id1.var relop id2.var ‘goto _’) || emit(‘goto _’); B.t = {100} B.f = {101}
47
Example 47 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto _ B.t = {100} B.f = {101} M M M.instr = nextinstr; M.i = 102
48
Example 48 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto _ B.t = {100} B.f = {101} M.i = 102 B id1 relop id2B.trueList = makeList(nextInstr); B.falseList = makeList(nextInstr+1); emit (‘if’ id1.var relop id2.var ‘goto _’) || emit(‘goto _’); 102: if x> 200 goto _ 103: goto _ B.t = {102} B.f = {103}
49
Example 49 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto _ B.t = {100} B.f = {101} M.i = 102 102: if x> 200 goto _ 103: goto _ B.t = {102} B.f = {103} M M M.instr = nextinstr; M.i = 104
50
Example 50 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto _ B.t = {100} B.f = {101} M.i = 102 102: if x> 200 goto _ 103: goto _ B.t = {102} B.f = {103} M.i = 104 B id1 relop id2B.trueList = makeList(nextInstr); B.falseList = makeList(nextInstr+1); emit (‘if’ id1.var relop id2.var ‘goto _’) || emit(‘goto _’); 104: if x!=y goto _ 105: goto _ B.t = {104} B.f = {105}
51
Example 51 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto _ B.t = {100} B.f = {101} M.i = 102 102: if x> 200 goto 104 103: goto _ B.t = {102} B.f = {103} M.i = 104 104: if x!=y goto _ 105: goto _ B.t = {104} B.f = {105} B B1 and M B2backpatch(B1.trueList,M.instr); B.trueList = B2.trueList; B.falseList = merge(B1.falseList,B2.falseList); B.t = {104} B.f = {103,105}
52
Example 52 X 200 and x != y B B B B x x < < 100 B B x x > > 200 B B x x != y y B B and or M M M M 100: if x< 100 goto _ 101: goto 102 B.t = {100} B.f = {101} M.i = 102 102: if x> 200 goto 104 103: goto _ B.t = {102} B.f = {103} M.i = 104 104: if x!=y goto _ 105: goto _ B.t = {104} B.f = {105} B.t = {104} B.f = {103,105} B B1 or M B2backpatch(B1.falseList,M.instr); B.trueList = merge(B1.trueList,B2.trueList); B.falseList = B2.falseList; B.t = {100,104} B.f = {103,105}
53
Example 53 100: if x<100 goto _ 101: goto _ 102: if x>200 goto _ 103: goto _ 104: if x!=y goto _ 105: goto _ 100: if x<100 goto _ 101: goto _ 102: if x>200 goto 104 103: goto _ 104: if x!=y goto _ 105: goto _ 100: if x<100 goto _ 101: goto 102 102: if x>200 goto 104 103: goto _ 104: if x!=y goto _ 105: goto _ Before backpatching After backpatching by the production B B1 and M B2 After backpatching by the production B B1 or M B2
54
Backpatching for statements 54 productionsemantic action S if (B) M S1backpatch(B.trueList,M.instr); S.nextList = merge(B.falseList,S1.nextList); S if (B) M1 S1 N else M2 S2 backpatch(B.trueList,M1.instr); backpatch(B.falseList,M2.instr); temp = merge(S1.nextList,N.nextList); S.nextList = merge(temp,S2.nextList); S while M1 (B) M2 S1 backpatch(S1.nextList,M1.instr); backpatch(B.trueList,M2.instr); S.nextList = B.falseList; emit(‘goto’ M1.instr); S { L }S.nextList = L.nextList; S AS.nextList = null; M M M.instr = nextinstr; N N N.nextList = makeList(nextInstr); emit(‘goto _’); L L1 M Sbackpatch(L1.nextList,M.instr); L.nextList = S.nextList; L SL.nextList = S.nextList
55
Generate code for procedures we will see handling of procedure calls in much more detail later 55 n = f(a[i]); t1 = i * 4 t2 = a[t1] // could have expanded this as well param t2 t3 = call f, 1 n = t3
56
Extend grammar for procedures type checking – function type: return type, type of formal parameters – within an expression function treated like any other operator symbol table – parameter names 56 D define T id (F) { S } F | T id, F S return E; | … E id (A) | … A | E, A expressions statements
57
Summary Three address code is our IR. Intermediate code generation is executed while parsing (via semantic actions). Creating code for Boolean expressions and for control statements is more involved. We typically use short circuit evaluation, value of an expression is implicit in control location. We need to compute the branching addresses. Option 1: compute them in a second AST pass. Option 2: backpatching (a single pass): maintain lists of incomplete jumps, where all jumps in a list have the same target. When the target becomes known, all instructions on its list are “filled in”. 57
58
Recap Lexical analysis – regular expressions identify tokens (“words”) Syntax analysis – context-free grammars identify the structure of the program (“sentences”) Contextual (semantic) analysis – type checking defined via typing judgements – can be encoded via attribute grammars Syntax directed translation – attribute grammars Intermediate representation – many possible IRs – generation of intermediate representation – 3AC 58
59
Journey inside a compiler 59 Lexical Analysis Syntax Analysis Sem. Analysis Inter. Rep. Code Gen. float position; float initial; float rate; position = initial + rate * 60 Token Stream
60
Journey inside a compiler 60 Lexical Analysis Syntax Analysis Sem. Analysis Inter. Rep. Code Gen. 60 = + * AST idsymboltypedata 1positionfloat… 2initialfloat… 3ratefloat… symbol table S ID = E E ID | E + E | E * E | NUM
61
Journey inside a compiler 61 Lexical Analysis Syntax Analysis Sem. Analysis Inter. Rep. Code Gen. 60 = + * inttofloat 60 = + * AST coercion: automatic conversion from int to float inserted by the compiler idsymboltype 1positionfloat 2initialfloat 3ratefloat symbol table
62
Journey inside a compiler 62 Lexical Analysis Syntax Analysis Sem. Analysis Inter. Rep. Code Gen. t1 = inttofloat(60) t2 = id3 * t1 t3 = id2 + t2 id1 = t3 3AC 60 = + * inttofloat productionsemantic rule S id = ES.code := E. code || gen(id.var ‘:=‘ E.var) E E1 op E2E.var := freshVar(); E.code = E1.code || E2.code || gen(E.var ‘:=‘ E1.var ‘op’ E2.var) E inttofloat(num)E.var := freshVar(); E.code = gen(E.var ‘:=‘ inttofloat(num)) E idE.var := id.var; E.code = ‘’ t1 = inttofloat(60) t2 = id3 * t1 t3 = id2 * t2 id1 = t3 (for brevity, bubbles show only code generated by the node and not all accumulated “code” attribute) note the structure: translate E1 translate E2 handle operator
63
Journey inside a compiler 63 Inter. Rep. Code Gen. Lexical Analysis Syntax Analysis Sem. Analysis 3AC Optimized t1 = inttofloat(60) t2 = id3 * t1 t3 = id2 + t2 id1 = t3 t1 = id3 * 60.0 id1 = id2 + t1 value known at compile time can generate code with converted value eliminated temporary t3
64
Journey inside a compiler 64 Inter. Rep. Code Gen. Lexical Analysis Syntax Analysis Sem. Analysis Optimized t1 = id3 * 60.0 id1 = id2 + t1 Code Gen LDF R2, id3 MULF R2, R2, #60.0 LDF R1, id2 ADDF R1,R1,R2 STF id1,R1
65
Problem 3.8 from [Appel] A simple left-recursive grammar: E E + id E id A simple right-recursive grammar accepting the same language: E id + E E id Which has better behavior for shift-reduce parsing? 65
66
Answer The stack never has more than three items on it. In general, with LR-parsing of left-recursive grammars, an input string of length O(n) requires only O(1) space on the stack. 66 E E + id E id Input id+id+id+id+id id (reduce) E E + E + id (reduce) E E + E + id (reduce) E E + E + id (reduce) E E + E + id (reduce) E stack left recursive
67
Answer The stack grows as large as the input string. In general, with LR-parsing of right-recursive grammars, an input string of length O(n) requires O(n) space on the stack. 67 E id + E E id Input id+id+id+id+id id id + id + id id + id + id + id + id id + id + id + id id + id + id + id + id + id + id + id + id (reduce) id + id + id + id + E (reduce) id + id + id + E (reduce) id + id + E (reduce) id + E (reduce) E stack right recursive
68
Next… Runtime Environments 68
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.