Professional Documents
Culture Documents
CIS 461 Compiler Design and Construction Fall 2012 Lecture-Module 17
CIS 461 Compiler Design and Construction Fall 2012 Lecture-Module 17
Lecture-Module 17
slides derived from Tevfik Bultan, Keith Cooper, and Linda
Torczon
1
Structure of a Compiler
Semantic Intermediate
Scanner Parser Code
Analysis
Generation
IR
2
Intermediate Representations
3
Abstract Syntax Trees (ASTs)
if (x < y)
Statements
x = 5*y + 5*y/3;
else
y = 5; IfStmt AssignStmt
x = x+y;
AssignStmt AssignStmt x +
<
x y x + y x y
5
* /
5 y 3
*
5 y
4
Directed Acyclic Graphs (DAGs)
Use directed acyclic graphs to represent expressions
Use a unique node for each expression
Statements
if (x < y)
x = 5*y + 5*y/3; IfStmt AssignStmt
else
y = 5;
x = x+y; AssignStmt AssignStmt
<
x y +
/
*
3
5
5
Control Flow Graphs (CFGs)
Nodes in the control flow graph are basic blocks
A basic block is a sequence of statements always entered at the
beginning of the block and exited at the end
Edges in the control flow graph represent the control flow
if (x < y)
x = 5*y + 5*y/3; B0
if (x < y) goto B1 else goto B2
else
y = 5;
x = x+y; B1 B2
x = 5*y + 5*y/3 y = 5
B3
Each block has a sequence of statements x = x+y
No jump from or to the middle of the block
Once a block starts executing, it will execute till the end
6
Stack Machine Code pushes the value
at the location x to
if (x < y) the stack
load x
x = 5*y + 5*y/3; load y
else iflt L1 pops the top
goto L2 two elements and
y = 5; compares them
L1: push 5
x = x+y;
load y
multiply pops the top two
push 5 elements, multiplies
JVM: A stack machine them, and pushes the
load y
In the project we generate assembly code for JVM result back to the stack
multiply
which is converted to bytecode by Jasmin assembler
push 3
JVM interpreter executes the bytecode on different
divide
machines
add
JVM has an operand stack which we use to evaluate stores the value at the
store x top of the stack to the
expressions
goto L3 location x
JVM provides 65535 local variables for each method
L2: push 5
The local variables are like registers so we do not have
store y
to worry about register allocation
L3: load x
Each local variable in JVM is denoted by a number
load y
between 0 and 65535 (x and y in the example will be
add
assigned unique numbers)
store x
7
Three-Address Code
Each instruction can have at most three operands
Assignments
x := y
x := y op z op: binary arithmetic or logical operators
x := op y op: unary operators (minus, negation, integer to
float conversion)
Branch
goto L Execute the statement with labeled L next
Conditional Branch
if x relop y goto L relop: <, =, <=, >=, ==, !=
if the condition holds we execute statement labeled L next
if the condition does not hold we execute the statement following this
statement next
8
Three-Address Code
if (x < y) Variables can be represented with
x = 5*y + 5*y/3; their locations in the symbol table
else
y = 5; if x < y goto L1
goto L2
x = x+y; L1: t1 := 5 * y
t2 := 5 * y
t3 := t2 / 3
Temporaries: temporaries correspond x := t1 + t2
to the internal nodes of the syntax tree goto L3
L2: y := 5
L3: x := x + y
9
Three-Address Code Generation for a Simple
Grammar
Attributes: E.place: location that holds the value of expression E
E.code: sequence of instructions that are generated for E
Procedures: newtemp(): Returns a new temporary each time it is called
gen(): Generates instruction (have to call it with appropriate arguments)
lookup(id.name): Returns the location of id from the symbol table
11
Code Generation for Boolean Expressions
Two approaches
Numerical representation
Implicit representation
Numerical representation
Use 1 to represent true, use 0 to represent false
For three-address code store this result in a temporary
For stack machine code store this result in the stack
Implicit representation
For the boolean expressions which are used in flow-of-control statements
(such as if-statements, while-statements etc.) boolean expressions do not
have to explicitly compute a value, they just need to branch to the right
instruction
Generate code for boolean expressions which branch to the appropriate
instruction based on the result of the boolean expression
12
Boolean Expressions: Numerical Representation
Attributes : E.place: location that holds the value of expression E
E.code: sequence of instructions that are generated for E
id.place: location for id
Global variable: nextstat: Returns the location of the next instruction to be generated
(each call to gen() increments nextstat by 1)
13
Boolean Expressions: Implicit Representation
Attributes : E.code: sequence of instructions that are generated for E
E.false: instruction to branch to if E evaluates to false
E.true: instruction to branch to if E evaluates to true
(E.code is synthesized whereas E.true and E.false are inherited)
id.place: location for id
Pre-test
Loops
Evaluate condition before loop (if needed)
Loop body
Evaluate condition after loop
Branch back to the top if condition holds
Post-test
Merges test with last block of loop body
Next block
While, for, do, and until all fit this basic model
16
Flow-of-Control Statements: Code Structure
17
Flow-of-Control Statements
Attributes : S.code: sequence of instructions that are generated for S
S.next: label of the instruction that will be executed immediately after S
(S.next is an inherited attribute)
Productions Semantic Rules
S if E then S1 else S2 E.true newlabel();
E.false newlabel();
S1.next S. next;
S2.next S. next;
S.code E.code || gen(E.true :) || S1.code
|| gen(goto S.next) || gen(E.false :) || S2.code ;
S S1 ; S2 S1.next newlabel();
S2.next S.next; 18
S.code S .code || gen(S .next :) || S .code
Example
19
Backpatching
E.true, E.false, S.next may not be computed in a single pass (they are
inherited attributes)
Backpatching is a technique for generating labels for E.true, E.false,
S.next and inserting them to the appropriate locations
Basic idea
Keep lists E.truelist, E.falselist, S.nextlist
E.truelist: the list of instructions where the label for E.true
have to be inserted when it becomes available
S.nextlist: the list of instructions where the label for S.next
have to be inserted when it becomes available
When labels E.true, E.false, S.next are computed these labels are
inserted to the instructions in these lists
20
Flow-of-Control Statements: Case Statements
Case Statements
1 Evaluate the controlling expression
2 Branch to the selected case
3 Execute the code for that case
4 Branch to the statement after the case
Part 2 is the key
Strategies
Linear search (nested if-then-else constructs)
Build a table of case values & binary search it
Directly compute an address (requires dense case set)
Use an array of labels that is addressed by the case value
21
Type Conversions
Mixed-type expressions
Insert conversions as needed from conversion table
i2f r1, r2 (convert the integer value in register r 1 to float, and store
the result in register r2)
Most languages have symmetric conversion tables
Typical
Addition
Table
22
Memory Layout
Placing run time data structures
S G
S
Stack and heap share free
C t l H space
t
o a&o e
d t b a
a Fixed-size areas together
c
e i a p
k For compiler, this is the
c l entire
0 high picture
23
Memory Allocation
PD
D D; D
D id : T
T char | int | float | array[num] of T | pointer T
Enter the variables to the symbol table with their type and memory location
Set the type attribute T.type
Layout the storage for variables
Calculate the offset for each local variable and enter it to the symbol table
Offset can be offset from a static data area or from the beginning of the local
data area in the activation record
24
A Translation Scheme for Memory Allocation
P {offset 0;} D
D D; D
D id : T {enter(id.name,T.type, offset); offset offset + T.width; }
T char { T.type char; T.width 4; }
| int { T.type integer; T.width 14; }
| float { T.type float; T.width 8; }
| array[num] of T1 { T.type array(num.val, T1.type);
T.width num.val * T1.width; }
| pointer T { T.type pointer(T1.type); T.width 8; }
PD
D D; D
D id : T | proc id; D; S
26
Memory Allocation for Nested Procedures
P {t mktable(nil); push(t,tblptr); push(0, offset)}
D
{addwidth(top(tblptr), top(offset)); pop(tblptr); pop(offset);}
D D; D
D proc id ;
{ t mktable(top(tblptr)); push(t,tblptr); push(0,offset); }
D;S
{ t top(tblptr); addwidth(t, top(offset)); pop(tblptr);
pop(offset);
D id : T
{enter(top(tblptr), id.name, T.type, top(offset));
top(offset) top(offset) + T.width; }
27
Array access:
How does the compiler handle A[ i ][ j ] ?
First, must agree on a storage scheme
Row-major order (most languages)
Lay out as a sequence of consecutive rows
Rightmost subscript varies fastest
A[1,1], A[1,2], A[1,3], A[2,1], A[2,2], A[2,3]
Column-major order (Fortran)
Lay out as a sequence of columns
Leftmost subscript varies fastest
A[1,1], A[2,1], A[1,2], A[2,2], A[1,3], A[2,3]
Indirection vectors (Java)
Vector of pointers to pointers to to values
Takes more space
Trades indirection for arithmetic (in modern processors memory access is
more costly than arithmetic)
Locality may not be good if indirection vectors are used
28
Laying Out Arrays
The Concept
1,1 1,2 1,3 1,4
A
2,1 2,2 2,3 2,4
Row-major order
A 1,1 1,2 1,3 1,4 2,1 2,2 2,3 2,4
Column-major order
A 1,1 2,1 1,2 2,2 1,3 2,3 1,4 2,4
Indirection vectors
These have distinct &
1,1 1,2 1,3 1,4 different cache behavior
A The order of traversal of
2,1 2,2 2,3 2,4 an array can effect the
performance
29
Base of A (starting Almost always a power of 2, known at
address of the array) compile-time use a shift for speed
Expensive computation!
What about A[i1][i2] ? Lots of +, -, x operations
30
Optimizing Address Calculation for A[ i ][ j ]
In row-major order
@A + (i-low1)(high2-low2+1) sizeof(A[1][1]) + (j - low2) sizeof(A[1][1])
Which can be factored into
@A + i (high2-low2+1) sizeof(A[1][1]) + j sizeof(A[1][1])
- (low1 (high2-low2+1) sizeof(A[1][1]) + low2 sizeof(A[1][1]))
If lowi, highi, and sizeof(A[1][1]) are known, the last term is a constant
Define @A0 as
@A - (low1 (high2-low2+1) sizeof(A[1][1]) + low2 sizeof(A[1][1]))
And len2 as (high2-low2+1)
31
Array References
What about arrays as actual parameters?
@A
low1
Whole arrays, as call-by-reference parameters
high1
Need dimension information build a dope vector
low2
Store the values in the calling sequence high2
Pass the address of the dope vector in the parameter slot
Generate complete address polynomial at each reference
32
Structure of a Compiler
Semantic Intermediate
Scanner Parser Code
Analysis
Generation
33