Download presentation
Presentation is loading. Please wait.
Published byOla Rød Modified over 6 years ago
1
Fall 2016-2017 Compiler Principles Lecture 6: Dataflow & Optimizations 1
Roman Manevich Ben-Gurion University of the Negev
2
Tentative syllabus mid-term exam Front End Intermediate Representation
Scanning Top-down Parsing (LL) Bottom-up Parsing (LR) Intermediate Representation Operational Semantics Lowering Optimizations Dataflow Analysis Loop Optimizations Code Generation Register Allocation Energy Optimization Instruction Selection mid-term exam
3
Previously The need for Intermediate Representations
Three-Address Code Lowering AST in While to IL Operational semantics Equivalence Correctness of lowering
4
agenda Introduction to optimizations Formalisms for program analysis
Basic blocks Control flow graphs Dataflow analyses and optimizations Live variables dead code elimination In recitation: Available expressions common sub-expression elimination + copy propagation
5
Introduction to optimizations
6
Optimization points today and next week source code Front end
Code generator target code IR User profile program change algorithm Compiler apply IR optimizations Compiler register allocation instruction selection peephole transformations today and next week
7
Overview of IR optimization
Formalisms and Terminology Control-flow graphs Basic blocks Local optimizations (won’t cover this year) Optimizing small pieces of a function Global optimizations Optimizing functions as a whole The dataflow framework Defining and implementing a wide class of optimizations
8
Semantics-preserving optimizations
An optimization is semantics-preserving if it does not alter the semantics of the original program Examples: Eliminating unnecessary statements Computing values that are known statically at compile-time instead of runtime Evaluating constant expressions outside of a loop instead of inside Non-examples: Reordering side-effecting computations Replacing bubble sort with quicksort (why?) The optimizations we will consider in this class are all semantics-preserving How can we find opportunities for optimizations?
9
Program analysis In order to optimize a program, the compiler has to be able to reason about the properties of that program An analysis is called sound if it never asserts an incorrect fact about a program All the analyses we will discuss in this class are sound (Why?)
10
Soundness if (y < 5) x := 137; else x := 42; Print(x);
“At this point in the program, x holds some integer value”
11
Soundness if (y < 5) x := 137; else x := 42; Print(x);
“At this point in the program, x is either 137 or 42”
12
Soundness if (y < 5) x := 137; else x := 42; Print(x);
“At this point in the program, x is 137”
13
Soundness if (y < 5) x := 137; else x := 42; Print(x);
“At this point in the program, x is either 137, 42, or 271”
14
Control flow graphs
15
Visualizing IR main: t0 := Call ReadInteger() a := t0
b := t1 L0: t2 := 0 t3 := b == t2 t4 := 0 t5 := t3 = t4 IfZ t5 Goto L1 c := a a := b t6 := c % a b := t6 Goto L0 L1: Call PrintInt()
16
Visualizing IR main: t0 := Call ReadInteger() a := t0
b := t1 L0: t2 := 0 t3 := b == t2 t4 := 0 t5 := t3 = t4 IfZ t5 Goto L1 c := a a := b t6 := c % a b := t6 Goto L0 L1: Call PrintInt()
17
Visualizing IR start end main: t0 := Call ReadInteger() a := t0
b := t1 L0: t2 := 0 t3 := b == t2 t4 := 0 t5 := t3 = t4 IfZ t5 Goto L1 c := a a := b t6 := c % a b := t6 Goto L0 L1: Call PrintInt() t0 := Call ReadInteger() a := t0 t1 := Call ReadInteger() b := t1 L0: t2 := 0 t3 := b == t2 t4 := 0 t5 := t3 = t4 IfZ t5 Goto L1 c := a a := b t6 := c % a b := t6 Goto L0 Call PrintInt
18
Basic blocks A basic block is a maximal sequence of IR instructions where There is exactly one label where control enters the sequence, which must be at the start of the sequence There is exactly one label where control leaves the sequence, which must be at the end of the sequence Informally: a sequence of instructions that always execute as a group
19
Control-flow graphs A control-flow graph (CFG) is a graph of the basic blocks in a function From here on CFG stands for “control-flow graph” and not “context free grammar” Each edge from one basic block to another indicates that control can flow from the end of the first block to the start of the second block Dedicated nodes for the start and end of a function Program executions correspond to paths the executions of the program
20
Scope of optimizations
An optimization is local if it works on just a single basic block An optimization is global if it works on an entire control-flow graph An optimization is interprocedural if it works across the control-flow graphs of multiple functions We won't talk about this in this course
21
Optimizations and analyses
Most optimizations are only possible given some analysis of the program's behavior In order to implement an optimization, we will talk about the corresponding program analyses Program analysis = algorithm that processes program and infers facts Sound facts = facts that hold for all program executions Sound analysis = program analysis that infers only sound facts
22
dead code elimination (DCE)
23
Definition An assignment to a variable v is called dead if the value of that assignment is never read anywhere Dead code elimination removes dead assignments from IR Determining whether an assignment is dead depends on assignments succeding it
24
Dead code elimination example
Can we remove this statement? a := b Can we remove this statement? c := a Can we remove this statement? d := a + b Can we remove this statement? e := d Can we remove this statement? d := a Can we remove this statement? f := e Can we remove this statement? Print(d)
25
Live variables The analysis corresponding to dead code elimination is called liveness analysis A variable is live at a point in a program if later in the program its value will be read before it is written to again
26
Optimizing via liveness analysis
Dead code elimination works by computing liveness for each variable, then eliminating assignments to dead variables Dead code elimination If x := R {v1,…,vk} And x {v1,…,vk} and R has no side-effects (not a function call) We can eliminate x := R (if R has side-effects, we can transform the assignment to just R)
27
Live variable analysis
28
Plan Define liveness analysis on single statements
Define liveness analysis on basic blocks Define liveness analysis on acyclic CFGs Define liveness on arbitrary CFGs
29
The goal Given an IL command C and set of live variables LVout after C executes, compute the set of live variables LVin before C executes We will call this function the transformer of C and write it as LVin = F[C](LVout) LVin { ... } lab: C LVout { ... }
30
DEF/USE For an IL statement C, we define
DEF – the variables possibly modified by C, USE – the variables read by C Command Type DEF USE skip {} x := R {x} Vars(R) Goto l’ IfZ x Goto l’ IfNZ x Goto l’ Call f(x1,…, xn) {x1,…, xn} Ret x
31
Liveness transformer Given an IL command C and set of live variables LVout after C executes, compute the set of live variables LVin before C executes We define the transformer of C: LVin = (LVout \ DEF(C)) USE(C) LVin { ... } lab: C LVout { ... } x := x+1
32
Analyzing and optimizing basic blocks
33
Analyzing basic blocks
Let P = C1,…, Cn be a basic block and LVn+1 be the initial set of live variables after Cn To compute LV1,…, LVn simply apply F[Ci] for i=n to 1 LV1 C1 LV2 C2 LV3 … LVn Cn LVn+1
34
Liveness example Which statements are dead? { b } a := b; { a, b }
c := a; { a, b } Which statements are dead? d := a + b; { a, b, d } e := d; { a, b, e } d := a; { b, d, e } f := e; { b, d } Foo(b, d) { }
35
Dead code elimination Can we find more dead assignments now? { b }
a := b; { a, b } c := a; { a, b } Can we find more dead assignments now? d := a + b; { a, b, d } e := d; { a, b, e } d := a; { b, d, e } f := e; { b, d } Foo(b, d) { }
36
Second liveness analysis
{ b } a := b; { a, b } Which statements are dead? d := a + b; { a, b, d } e := d; { a, b } d := a; { b, d } Foo(b, d) { }
37
Second dead code elimination
{ b } a := b; { a, b } Can we find more dead assignments now? d := a + b; { a, b, d } e := d; { a, b } d := a; { b, d } Foo(b, d) { }
38
Third liveness analysis
{ b } a := b; { a, b } Which statements are dead? d := a + b; { a, b } d := a; { b, d } Foo(b, d) { }
39
Third dead code elimination
{ b } a := b; { a, b } Can we find more dead assignments now? d := a + b; { a, b } d := a; { b, d } Foo(b, d) { }
40
Fourth liveness analysis
{ b } We can eliminate the two remaining assignments via copy propagation a := b; No more dead statements { a, b } d := a; { b, d } Foo(b, d) { }
41
Global liveness analysis
42
Global analysis technical challenges
Need to be able to handle multiple predecessors/successors for a basic block Join operator Need to be able to handle multiple paths through the control-flow graph may need to iterate multiple times to compute final value but the analysis still needs to terminate! Chaotic iteration (fixed point iteration) Need to be able to assign each basic block a reasonable default value for before we've analyzed it Bottom value
43
Global analysis technical challenges
Need to be able to handle multiple predecessors/successors for a basic block Join operator Need to be able to handle multiple paths through the control-flow graph may need to iterate multiple times to compute final value but the analysis still needs to terminate! Chaotic iteration (fixed point iteration) Need to be able to assign each basic block a reasonable default value for before we've analyzed it Bottom value
44
Global dead code elimination
Local dead code elimination needed to know what variables were live on exit from a basic block This information can only be computed as part of a global analysis How do we extend our liveness analysis to handle a CFG?
45
Global liveness analysis: CFGs without loops
46
CFGs without loops b := c + d; e := c + d; Start
x := c + d; a := b + c; y := a + b; x := a + b; y := c + d; Foo(x,y) End
47
CFGs without loops ? {a, c, d} b := c + d; e := c + d; Start
Which variables may be live on some execution path? Start {a, b, c, d} ? x := c + d; a := b + c; {b, c, d} {a, b, c, d} y := a + b; {a, b, c, d} {a, b, c, d} x := a + b; y := c + d; {a, b, c, d} {x, y} Foo(x,y) {x, y} End
48
Which assignments are redundant?
{a, c, d} b := c + d; e := c + d; Start {a, b, c, d} x := c + d; a := b + c; {b, c, d} {a, b, c, d} y := a + b; {a, b, c, d} {a, b, c, d} x := a + b; y := c + d; {a, b, c, d} {x, y} Foo(x,y) {x, y} End
49
CFGs without loops {a, c, d} b := c + d; e := c + d; Start
{a, b, c, d} x := c + d; a := b + c; {b, c, d} {a, b, c, d} y := a + b; {a, b, c, d} {a, b, c, d} x := a + b; y := c + d; {a, b, c, d} {x, y} Foo(x,y) {x, y} End
50
CFGs without loops b := c + d; Start a := b + c;
Dead code elimination can lead to more optimizations a := b + c; x := a + b; y := c + d; Foo(x,y) {x, y} End
51
CFGs without loops b := c + d; Start a := b + c;
x := a + b; y := c + d; Foo(x,y) {x, y} End
52
Major changes – part 1 In a local analysis (analysis of one basic block), each statement has exactly one predecessor In a global analysis, each statement may have multiple predecessors A global analysis must have some means of combining information from all predecessors of a basic block
53
Combining values b := c + d; e := c + d; {c, d} Start {b, c, d}
Need to combine currently-computed value with new value Start {b, c, d} x := c + d; a := b + c; {b, c, d} {a, b, c, d} y := a + b; {a, b, c, d} {a, b, c, d} x := a + b; y := c + d; {a, b, c, d} {x, y} Foo(x,y) {x, y} End
54
Combining values b := c + d; e := c + d; {c, d} Start {a, b, c, d}
x := c + d; a := b + c; {b, c, d} y := a + b; {a, b, c, d} {a, b, c, d} {a, b, c, d} x := a + b; y := c + d; {a, b, c, d} {x, y} Foo(x,y) {x, y} End
55
Combining values b := c + d; e := c + d; {a, c, d} Start {a, b, c, d}
x := c + d; a := b + c; {b, c, d} y := a + b; {a, b, c, d} {a, b, c, d} {a, b, c, d} x := a + b; y := c + d; {a, b, c, d} {x, y} Foo(x,y) {x, y} End
56
Global liveness analysis: handling loops
57
Major changes – part 2 In a local analysis, there is only one possible path through a basic block In a global analysis, there may be many paths through a CFG May need to recompute values multiple times as more information becomes available Need to be careful when doing this not to loop infinitely! (More on that later)
58
CFGs with loops b := c + d; c := c + d; IfZ ... Start
a := b + c; d := a + c; c := a + b; a := a + b; d := b + c; Ret a End
59
How do we use join here? b := c + d; c := c + d; IfZ ... Start
a := b + c; d := a + c; c := a + b; a := a + b; d := b + c; ? Ret a {a} End
60
Major changes – part 3 In a local analysis, there is always a well defined “first” statement to begin processing In a global analysis with loops, every basic block might depend on every other basic block To fix this, we need to assign initial values to all of the blocks in the CFG
61
CFGs with loops - initialization
{} b := c + d; c := c + d; Start {} a := b + c; d := a + c; {} c := a + b; a := a + b; d := b + c; {} Ret a {a} End
62
CFGs with loops - iteration
{} b := c + d; c := c + d; Start {} a := b + c; d := a + c; {} c := a + b; a := a + b; d := b + c; {} {a} Ret a {a} End
63
CFGs with loops - iteration
{} b := c + d; c := c + d; Start {} a := b + c; d := a + c; {} c := a + b; a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
64
CFGs with loops - iteration
{} b := c + d; c := c + d; Start {} a := b + c; d := a + c; c := a + b; {} {a, b, c} a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
65
CFGs with loops - iteration
{} b := c + d; c := c + d; Start {b, c} a := b + c; d := a + c; c := a + b; {} {a, b, c} a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
66
CFGs with loops - iteration
b := c + d; c := c + d; {} Start {b, c} {b, c} a := b + c; d := a + c; {} c := a + b; {a, b, c} a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
67
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
68
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
69
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a} Ret a {a} End
70
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
71
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
72
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
73
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
74
CFGs with loops - iteration
b := c + d; c := c + d; {c, d} Start {a, b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
75
CFGs with loops - iteration
b := c + d; c := c + d; {a, c, d} Start {a, b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
76
CFGs with loops – fixed point
b := c + d; c := c + d; {a, c, d} Start {a, b, c} a := b + c; d := a + c; {b, c} {a, b} c := a + b; {a, b, c} {a, b, c} a := a + b; d := b + c; {a, b, c} {a, c, d} Ret a {a} End
77
formalizing Global liveness analysis
78
Global liveness analysis
Initially, set IN[C] = { } for each command C Set IN[end] to the set of variables known to be live on exit ({} unless special assumptions) Language-specific knowledge Repeat until no changes occur: For each command C in any order you'd like: Set OUT[C] to be the union of IN[Cnext] for each successor Cnext of C Set IN[C] to (OUT[C] \ DEF(C)) USE(C) Yet another fixed-point iteration!
79
Global liveness analysis
a:=b+c IN[s]=(OUT[s] – {a}) {b, c} OUT[s]=IN[s2] IN[s3] IN[s2] IN[s3] s2 s3
80
Why does this work? To show correctness, we need to show that
The algorithm eventually terminates, and When it terminates, it has a sound answer
81
Termination argument …?
82
Termination argument Once a variable is discovered to be live during some point of the analysis, it always stays live Only finitely many variables and finitely many places where a variable can become live
83
Soundness argument (sketch)
Each individual rule, applied to some set, correctly updates liveness in that set When computing the union of the set of live variables, a variable is only live if it was live on some path leaving the statement So, locally, every step in the algorithm is correct Does local correctness imply global correctness? Yes under some conditions Monotone dataflow
84
Optimization correctness
85
Revisiting semantic equivalence
Recall our definition of semantic equivalence: for every pair of IL programs P and P’ – they are equivalent if for every input state m, P m = P’ m Does it work for P and P’=DCE(P) ? P P’ a := b c := a d := a + b e := d d := a f := e Print(d) a := b d := a Print(d) DCE
86
Refining semantic equivalence
Definition: For each IL program P, let V=ObservableVars(P) be the variables appear in Ret and Call commands Definition: For every pair of IL programs P and P’ we say that they are observably equivalent if for every input state m, (P m)|ObservableVars(P) = (P’ m)|ObservableVars(P’)
87
Semantic equivalence for DCE
Lemma: Let DCE(P)=P’ be a dead code elimination optimization applied to program P then the following holds: ObservableVars(P) = ObservableVars(P’) Theorem: P and DCE(P) are observably equivalent
88
Example P P’ For m = [pc1, a0 ,b1 , c2 , d3 , e4 , f5]
a := b c := a d := a + b e := d d := a f := e Print(d) a := b d := a Print(d) ObservableVars(P)={d} For m = [pc1, a0 ,b1 , c2 , d3 , e4 , f5] At the function call for P we have [pc6, a1 ,b1 , c1 , d1 , e2 , f2]|{d} = [d1] for P’ we have [pc3, a1 ,b1 , c2 , d1 , e4 , f5] {d} = [d1] At the output: P m = [pc7, a1 ,b1 , c1 , d1 , e2 , f2] P’ m = [pc4, a1 ,b1 , c2 , d1 , e4 , f5] (P m)|{d} = (P’ m)|{d} = [d1]
89
Next lecture: Dataflow Analysis Framework
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.