composing dataflow analyses and transformations
Post on 11-Feb-2016
31 Views
Preview:
DESCRIPTION
TRANSCRIPT
Composing Dataflow Analyses and Transformations
Sorin Lerner (University of Washington)David Grove (IBM T.J. Watson)
Craig Chambers (University of Washington)
x := 11;if (x == 11) { DoSomething();}else { DoSomethingElse(); x := x + 1;}y := x; // value of y?
Phase ordering problem
• Optimizations can interact in mutually beneficial ways, and no order exploits all of these interactions.
• Classic example: constant propagation and unreachable code elimination.
x := 11;DoSomething();y := x;// value of y?
x := 11;DoSomething();y := 11;
const prop followedby unreachablecode elimination
const propagain
true
One known solution: Iterate individual analyses until the results don’t change
x := 11;do { if (x == 11) { DoSomething(); } else { DoSomethingElse(); x := x + 1; }} while (...)y := x; // value of y?
•Compiler is slow.•In the presence of loops in the source program, might not yield best possible results.
Another known solution: hand writtensuper-analysis
Lose modularity:– difficult to write,
reuse, and extend such analyses
Examples:– conditional constant
propagation [Wegman and Zadeck 91]
– class analysis, splitting and inlining [Chambers and Ungar 90]
– const prop and pointer analysis [Pioli and Hind 99]
MonolithicSuper-Analysis
Ideally...
• ... we want to:– Write analyses modularly– Exploit mutually beneficial interactions– Have a fast compiler
• We present a framework that achieves this.
Composition Framework
The key to modular composition
• Traditionally, optimizations are defined in two parts:1. A dataflow analysis.2. Rules for transforming the program representation
after the analysis is solved.
• The key insight is to merge these two parts:– Dataflow functions return either a dataflow value OR
a replacement graph with which to replace the current statement.
Roadmap
• Several small examples that show how flow functions work
• One large example that shows how modular analyses are automatically composed together
• Overview of the theory behind the framework• Experimental validation
Flow function returning a dataflow value
y := 5
Flow function returning a dataflow value
y := 5
[ ... ]
[ ..., y → 5]
PROPAGATE
Flow function returning a replacement graph
y := x+2
[x → 3]
Flow function returning a replacement graph
y := x+2
[x → 3]
REPLACEy := 5
Replacement
graph
Step 1: Initialize input edges with dataflow information
Flow function returning a replacement graph
y := x+2y := x+2
[x [x →→ 3] 3]
y := 5
[x → 3]
PROPAGATE
[x → 3, y → 5]
Step 2: Perform recursive dataflow analysis on the replacement graph
Step 1: Initialize Step 1: Initialize input edges with input edges with dataflow dataflow informationinformation
Flow function returning a replacement graph
y := x+2y := x+2
[x [x →→ 3] 3]
y := 5
[x → 3]
PROPAGATE
[x → 3, y → 5][x → 3, y → 5]
Step 2: Perform Step 2: Perform recursive dataflow recursive dataflow analysis on the analysis on the replacement replacement graphgraph
Step 1: Initialize Step 1: Initialize input edges with input edges with dataflow dataflow informationinformation
Step 3: Propagate dataflow information from output edges.
Flow function returning a replacement graph
y := x+2
[x → 3]
[x → 3, y → 5]
Replacement graphs:– used to compute
outgoing dataflow information for the current statement.
Replacement graphs:– used to compute
outgoing dataflow information for the current statement.
– a convenient way of specifying what might otherwise be a complicated flow function.
Flow function returning a replacement graph
y := x+2
[x → 3]
[x → 3, y → 5]
Soundness requirement:– Replacement graph must
have the same concrete semantics as the original statement, but only on concrete inputs that are consistent with the current dataflow facts.
Flow function returning a replacement graph
y := x+2
[x → 3]
[x → 3, y → 5]
Let’s assume we’ve reached a fixed point.
Flow function returning a replacement graph
y := x+2
[x → 3]
[x → 3, y → 5]
y := 5
Let’s assume we’ve reached a fixed point.
Flow function returning a replacement graph
y := 5
[x → 3]
[x → 3, y → 5]
Replacement graphs:– used to transform the
program once a fixed point has been reached.
Let’s assume we’ve reached a fixed point.
Iterative analysis example
y := x+2
[x → 3, y → 5]
[x → 3] [x → T]
Now, let’s assume we haven’t reached a fixed point.
Iterative analysis example
y := x+2
[x → 3, y → 5]
PROPAGATE
[x → 3] [x → T]
[x → T, y → T]
Now, let’s assume we haven’t reached a fixed point.
Branch folding example
if (x == 11)
F T
Branch folding example
if (x == 11)REPLACE
[x → 11][x → 11]
F T
Branch folding example
if (x == 11)if (x == 11)
[x [x →→ 11] 11] [x → 11]
[x → 11]FF TT [x → 11]
Branch folding example
if (x == 11)
[x → 11]
F T [x → 11]
Composing several analyses
x := new C;do { b := x instanceof C; if (b) { x := x.foo(); } else { x := new D; }} while (...)
class A { A foo() { return new A; }};class C extends A { A foo() { return self; }};class D extends A {};
Constant PropagationClass Analysis
Inlining
Unreachable code elimination
x := new C
merge
b := x instanceof C
x := new D x := x.foo()
merge
while(…)
if (b)
TF
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
PROPAGATE
while(…)
PROPAGATE PROPAGATE
[x → T] [x → {C}] T
merge
merge
TF
PROPAGATE
T
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
PROPAGATE
while(…)
PROPAGATE PROPAGATE
[x → T] [x → {C}] T([x → T], [x → {C}], T, T)
merge
merge
PROPAGATE
TF
T
PROPAGATE
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
PROPAGATE
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
while(…)
merge
merge
TF
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
while(…)
PROPAGATE
[x → T, b → T]
merge
merge
TF
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
([x → T], [x → {C}], T, T)
REPLACE
b := true
while(…)
[x → T, b → T]
merge
merge
TF
([x → T], [x → {C}], T, T)
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
b := true
([x → T], [x → {C}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
PROPAGATE
while(…)while(…)
mergemerge
mergemerge
TTFF
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)b := true
([x → T], [x → {C}], T, T)
while(…)while(…)
mergemerge
mergemerge
TTFF
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
• Replacement graph is analyzed by composed analysis.
• When one analysis chooses a replacement graph, other analyses see it immediately.
• Analyses communicate implicitly through graph transformations
while(…)
merge
merge
TF
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
REPLACE
σ
while(…)
merge
merge
TF
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T) σσ
while(…)
merge
merge
TF
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT)) σσσσ σ
σ
while(…)while(…)
mergemerge
mergemerge
TTFF( , , , )
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
σ
σσ
while(…)while(…)
mergemerge
mergemerge
TTFF
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT)) σσ
( , , , )( , , , )
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
while(…)
merge
merge
TF
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
while(…)
merge
merge
TF
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
REPLACE
( , , , )
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
while(…)while(…)
mergemerge
mergemerge
TTFF
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
( , ,( , , , ), )
( , , , )
( , , , )( , , , )
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
while(…)
merge
merge
TF
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
( , , , )
σ
x := new C
b := x instanceof C
x := new D x := x.foo()
if (b)([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
REPLACE
x := C::foo(x)
while(…)
merge
merge
T( , , , )
( , , , )
F
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
σ
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
x := C::foo(x)
σREPLA
CE
x := x
σ
class C extends A { A foo() { return self; }}
while(…)while(…)
mergemerge
mergemerge
FF TT σσ( , ,( , , , ), )
( , ,( , , , ), )
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
x := C::foo(x)x := C::foo(x)
σσ
x := x
σ
σPROPAGATE
while(…)while(…)
mergemerge
mergemerge
FF TT([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT)) σσ( , ,( , , , ), )
( , ,( , , , ), )
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
x := C::foo(x)x := C::foo(x)
σσ
x := x
σ
σσ
while(…)while(…)
mergemerge
mergemerge
FF TT([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT)) σσ( , ,( , , , ), )
( , ,( , , , ), )
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
x := C::foo(x)
σ
σσ
while(…)while(…)
mergemerge
mergemerge
FF TT
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT)) σσ( , ,( , , , ), )
( , ,( , , , ), )
x := new Cx := new C
b := x instanceof Cb := x instanceof C
x := new Dx := new D x := x.foo()x := x.foo()
if (b)if (b)
x := C::foo(x)
σ
σ
while(…)while(…)
mergemerge
mergemerge
FF TT([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT)) σσ( , ,( , , , ), )
( , ,( , , , ), )
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ TT], [x ], [x →→ {C}], {C}], TT, , TT))
([x ([x →→ T, T, b b →→ true], true],
[x [x →→ {C}, b {C}, b →→ {Bool}], {Bool}], TT, , TT))
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
x := new C
b := x instanceof C
x := x.foo()
if (b)
while(…)
merge
merge
T([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
( , , , )
x := new D
F
x := new C
b := x instanceof C
x := x.foo()
if (b)
PROPAGATE([x → T, b → true],
[x → {C}, b → {Bool}], T , T)
while(…)
merge
merge
T
x := new D
F
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
( , , , )
x := new C
b := x instanceof C
x := x.foo()
if (b)
PROPAGATE
([x → T], [x → {C}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
while(…)
merge
merge
T
x := new D
F
([x → T, b → true],
[x → {C}, b → {Bool}], T , T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
( , , , )
x := new C
b := x instanceof C
x := x.foo()
if (b)
while(…)
merge
merge
T
x := new D
F
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T , T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
([x → T, b → true],
[x → {C}, b → {Bool}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
([x → T], [x → {C}], T, T)
( , , , )
([x → T], [x → {C}], T, T)
x := new C
b := x instanceof C
x := x.foo()
if (b)
x := x
b := true
while(…)
merge
merge
T
x := new D
F
x := new C
b := true
x := x
x := new C;do { b := x instanceof C; if (b) { x := x.foo(); } else { x := new D; }} while (...)
x := new C;do { b := true; x := x;} while (...)while(…)
merge
merge
x := new C;do { b := x instanceof C; if (b) { x := x.foo(); } else { x := new D; }} while (...)
x := new C;do { b := true; x := x;} while (...)
•Analyses are defined modularly and separately.
•Combining them achieves the results of a monolithic analysis.
• If the analyses were run separately in any order any number of times, no optimizations could be performed.
Theoretical foundation
• Definition: used abstract interpretation to define precisely how the framework composes analyses.
• Soundness theorem: if the individual analyses are sound, the composed analysis is sound.
• Termination theorem: composed analyses are guaranteed to terminate, under reasonable conditions.
• Precision theorem: if the composed flow function is monotonic, the composed analysis is guaranteed to produce results as precise as running the individual analyses in sequence, any number of times, in any order.
Experimental validation
• Implemented and used our framework in the Vortex and Whirlwind compilers for 5+ years.– composed: class analysis, splitting, inlining, const
prop, CSE, removal of redundant loads and stores, symbolic assertion prop
• Compiled a range of big programs in Vortex.– largest benchmark: Vortex compiler with ~60,000 lines
• Our framework vs. iteration:– compiles at least 5 times faster
• Our framework vs. monolithic super-analysis:– same precision– compiles at most 20% slower
Conclusions
• We developed and implemented a new approach for defining dataflow analyses.
• Our approach allows analyses to be written modularly:– easier to write and reuse analyses.
• Our approach allows analyses to be automatically combined into a monolithic analysis.
top related