PPTX

Fall 2014-2015 Compiler Principles
Lecture 8: Intermediate Representation
Roman Manevich
Ben-Gurion University
Tentative syllabus
Front
End
Intermediate
Representation
Optimizations
Code
Generation
Local
Optimizations
Register
Allocation
Top-down
Parsing (LL)
Dataflow
Analysis
Instruction
Selection
Bottom-up
Parsing (LR)
Loop
Optimizations
Scanning
mid-term
Lowering
exam
2
Previously
• Why compilers need Intermediate
Representations (IR)
• Translating from abstract syntax (AST) to IR
– Three-Address Code
• Local optimizations
– Sethi-Ullman algorithm for efficient code
generation
3
cgen for basic expressions
cgen(k) = { // k is a constant
Choose a new temporary t
Emit( t := k )
Return t
{
cgen(id) = { // id is an identifier
Choose a new temporary t
Emit( t := id )
Return t
{
4
Naive cgen for binary expressions
• Maintain a counter for temporaries in c
• Initially: c = 0
• cgen(e1 op e2) = {
Let A = cgen(e1)
c=c+1
The translation emits code
Let B = cgen(e2)
to evaluate e before e .
c=c+1
Why is that?
Emit( _tc := A op B; )
Return _tc
}
1
2
5
Example: cgen for binary expressions
cgen( (a*b)-d)
6
Example: cgen for binary expressions
c=0
cgen( (a*b)-d)
7
Example: cgen for binary expressions
c=0
cgen( (a*b)-d) = {
Let A = cgen(a*b)
c=c+1
Let B = cgen(d)
c=c+1
Emit( _tc := A - B; )
Return _tc
}
8
Example: cgen for binary expressions
c=0
cgen( (a*b)-d) = {
Let A = {
Let A = cgen(a)
c=c+1
Let B = cgen(b)
c=c+1
Emit( _tc := A * B; )
Return tc
}
c=c+1
Let B = cgen(d)
c=c+1
Emit( _tc := A - B; )
Return _tc
}
9
Example: cgen for binary expressions
c=0
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
10
Example: cgen for binary expressions
c=0
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
_t0:=a;
11
Example: cgen for binary expressions
c=0
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
12
Example: cgen for binary expressions
c=0
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t2:=_t0*_t1
13
Example: cgen for binary expressions
c=0
here A=_t2
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t2:=_t0*_t1
14
Example: cgen for binary expressions
c=0
here A=_t2
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t2:=_t0*_t1
_t3:=d;
15
Example: cgen for binary expressions
c=0
here A=_t2
cgen( (a*b)-d) = {
here A=_t0
Let A = {
Let A = { Emit(_tc := a;), return _tc }
c=c+1
Let B = { Emit(_tc := b;), return _tc }
c=c+1
Emit( _tc := A * B; )
Return _tc
}
c=c+1
Let B = { Emit(_tc := d;), return _tc }
c=c+1
Emit( _tc := A - B; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t2:=_t0*_t1
_t3:=d;
_t4:=_t2-_t3
16
Naïve translation
• cgen translation shown so far very inefficient
– Generates (too) many temporaries – one per subexpression
– Generates many instructions – at least one per
sub-expression
• Expensive in terms of running time and space
• Code bloat
• We can do much better
17
agenda
• Lowering optimizations
– Sethi-Ullman algorithm for efficient code
generation
18
Lowering optimization
19
Improving cgen for expressions
1. Improve translation for leaves
2. Reuse temporaries
3. Weighted register allocation for expression
trees
– Reorder computations
20
Optimization 1: leaves
21
Improving cgen for leaves cases
• Observation – naïve translation needlessly
generates temporaries for leaf expressions
• Solution: treat leaves as special cases by
returning them immediately without storing
them in temporaries
22
Optimization 2: reusing
temporaries
23
cgen with temporaries recycling
• Observation – temporaries used exactly once
– Once a temporary has been read it can be reused
for another sub-expression
• Want to implement cgen(x := e1 op e2) with
small number of temporaries
How can we make
• cgen(x := e1 op e2) = {
this more efficient?
c=0
Emit(x := cgen(e1 op e2))
}
24
Improved cgen
• c=0
cgen(e1 op e2) = {
cgen(e1)
c=c+1
cgen(e2)
c=c-1
Emit( _tc := _tc op _tc+1; )
}
25
Example
c=0
c=0
cgen( (a*b)-d) = {
{
{ Emit(_tc := a;), return _tc }
c=c+1
{ Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
}
c=c+1
{ Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
}
Code
26
Example
c=1
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
27
Example
c=1
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
28
Example
c=0
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
29
Example
c=0
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t0:=_t0*_t1
30
Example
c=1
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t0:=_t0*_t1;
31
Example
c=1
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t0:=_t0*_t1;
_t1:=d;
32
Example
c=0
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t0:=_t0*_t1;
_t1:=d;
33
Example
c=0
c=0
cgen( (a*b)-d) = {
Let _tc = {
Let _tc = { Emit(_tc := a;), return _tc }
c=c+1
Let _tc = { Emit(_tc := b;), return _tc }
c=c-1
Emit( _tc := _tc * _tc+1; )
Return _tc
}
c=c+1
Let _tc = { Emit(_tc := d;), return _tc }
c=c-1
Emit( _tc := _tc - _tc+1; )
Return _tc
}
Code
_t0:=a;
_t1:=b;
_t0:=_t0*_t1;
_t1:=d;
_t0:=_t0-_t1;
34
Example 2
_t0 := cgen( ((c*d)-(e*f))+(a*b) )
c = 0
_t0 := c*d
c = c + 1
_t0 := cgen(c*d)-(e*f))
_t1 := e*f
c = c - 1
_t0 := _t0 -_t1
c = c + 1
_t1 := a*b
c = c - 1
_t0 := _t0 + _t1
35
Optimization 3: weighted
register allocation
36
Sethi-Ullman translation
• Algorithm by Ravi Sethi and Jeffrey D. Ullman
to emit optimal TAC
– Minimizes number of temporaries
– (Minimizes number of instructions by reducing spills)
– A kind of local register allocation (within single
expressions)
• Uses two ideas
1. Reusing temporaries
2. Choosing order of translation
37
Weighted register allocation
• Suppose we have expression e1 op e2
– e1, e2 without side-effects
• That is, no function calls, memory accesses, ++x
– Does order of translation matter? … Yes
• Sethi & Ullman’s algorithm translates heavier
sub-tree first
– Optimal local (per-statement) allocation for sideeffect-free statements
38
Example 1 (assuming leaves require temporaries)
_t0 := cgen( a+(b+(c*d)) )
left child first
_t0 +
_t0 a
right child first
_t0 +
_t1 a
_t1 +
_t1 b
_t1 b
_t2 *
_t2 c
4 temporaries
_t0 +
d _t3
_t0 *
_t1 c _t0 d
2 temporaries
39
Sethi-Ullman: weighted register allocation
• Can save registers by re-ordering subtree
computations
• Phase 1: check that no side-effects exist and
otherwise revert to translation following pre-defined
order of computation
• Phase 2: assign weight to each node
– Weight = number of registers needed
– Leaf weight known
– Internal node weight
• w(left) > w(right) then w = left
• w(right) > w(left) then w = right
• w(right) = w(left) then w = left + 1
• Phase 3: translate by following heavier child first
40
Sethi-Ullman algorithm example
cgen( g := (a+b)+((c+d)+(e+f)))
w=3
w=2
w=1
a
+
+
w=1
w=3
b
w=2
w=1
c
+
+
w=1
w=2
d
w=1
e
+
w=1
f
Code
_t0 := c;
_t1 := d;
_t0 := _t0 * _t1;
_t1 := e;
_t2 := f;
_t1 := _t1 * _t2;
_t0 := _t0 - _t1
_t1 := a;
_t2 := b;
_t1 := _t1 -_t2
_t0 := _t1 - _t0
g := _t0
41
Example with side effects
_t0 := cgen( a+b[5*c] )
Phase 1: check absence of side-effects in expression tree
Phase 2: assign weight to each AST node
+
w=1
a
w=2
array access
base
b
w=2
index
*
w=1
w=1
5
w=2
c
w=1
42
Example with side effects
_t0 := cgen( a+b[5*c] )
Phase 2: use weights to decide on order of translation
_t0 +
w=1
a _t1
_t0 := c
_t1 := 5
w=2
Heavier sub-tree
_t0 array access
base
_t1 b
w=2
Heavier sub-tree
index
_t0 *
w=1
W=2
w=2
_t0 := _t1 * _t0
_t1 := b
_t0 := _t1[_t0]
w=1
5 _t1 c
w=1
_t0
_t1 := a
_t0 := _t1 + _t0
43
Note on weighted register allocation
• The algorithm is local – works for a single expression
• Must reset temporaries counter after every
statement:
– x := y; y := z; should not be translated to
_t0 := y;
x := _t0;
_t1 := z;
y := _t1;
– But rather to
_t0 := y;
x := _t0; # Finished translating statement. Set c=0
_t0 := z;
y := _t0;
44
Going beyond the basic SU algorithm
• There exist advanced extensions of SU
– Take into account semantics of operators
– Take into account latency
45
Can we do better than basic SU here?
cgen( g := (a+b)+((c+d)+(e+f)))
What if we are allowed to
rewrite expressions?
w=3
w=2
w=1
a
+
+
w=1
w=3
b
w=2
w=1
c
+
+
w=1
w=2
d
w=1
e
+
w=1
f
Code
_t0 := c;
_t1 := d;
_t0 := _t0 + _t1;
_t1 := e;
_t2 := f;
_t1 := _t1 + _t2;
_t0 := _t0 + _t1
_t1 := a;
_t2 := b;
_t1 := _t1 +_t2
_t0 := _t1 + _t0
g := _t0
46
Can we do better than basic SU here?
cgen( g := (a+b)+((c+d)+(e+f))) =
cgen( g := a + (b + (c + (d + (e + f)))))
+
w=1
a
w=1
Can rewrite since + is
associative and
commutative
w=2
+
b
w=1
w=2
+
w=2
c
w=1
+
d
w=1
w=2
+
e
w=2
w=1
f
Code
_t0 := e;
_t1 := f;
_t0 := _t0 + _t1;
_t1 := d;
_t0 := _t0 + _t1;
_t1 := c;
_t0 := _t0 + _t1;
_t1 := b;
_t0 := _t0 + _t1;
_t1 := a;
_t0 := _t0 + _t1;
g := _t0
47
Next lecture:
Local Optimizations