580 likes | 597 Views
Learn about the composition framework for modularly writing and optimizing dataflow analyses, with examples and experimental validation.
E N D
Composing Dataflow Analyses and Transformations Sorin Lerner (University of Washington) David Grove (IBM T.J. Watson) Craig Chambers (University of Washington)
const prop followed by unreachable code elimination const prop again 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; if (x == 11) { DoSomething(); } else { DoSomethingElse(); x := x + 1; } y := x; // value of y? true x := 11; DoSomething(); y := 11;
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.
Monolithic Super-Analysis Another known solution: hand writtensuper-analysis • 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] • Lose modularity: • difficult to write, reuse, and extend such analyses
Composition Framework Ideally... • ... we want to: • Write analyses modularly • Exploit mutually beneficial interactions • Have a fast compiler • We present a framework that achieves this.
The key to modular composition • Traditionally, optimizations are defined in two parts: • A dataflow analysis. • 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 PROPAGATE [ ..., y → 5]
y := 5 REPLACE Flow function returning a replacement graph Step 1: Initialize input edges with dataflow information Replacement graph [x → 3] [x → 3] y := x+2
y := 5 Flow function returning a replacement graph Step 2: Perform recursive dataflow analysis on the replacement graph Step 1: Initialize input edges with dataflow information [x → 3] [x → 3] y := x+2 PROPAGATE [x → 3,y → 5]
y := 5 Flow function returning a replacement graph Step 2: Perform recursive dataflow analysis on the replacement graph Step 1: Initialize input edges with dataflow information [x → 3] [x → 3] y := x+2 PROPAGATE [x → 3,y → 5] [x → 3,y → 5] Step 3: Propagate dataflow information from output edges.
Flow function returning a replacement graph • 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. • Replacement graphs: • used to compute outgoing dataflow information for the current statement. [x → 3] y := x+2 [x → 3,y → 5]
Flow function returning a replacement graph • 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. [x → 3] y := x+2 [x → 3,y → 5]
Flow function returning a replacement graph Let’s assume we’ve reached a fixed point. [x → 3] y := x+2 [x → 3,y → 5]
Flow function returning a replacement graph Let’s assume we’ve reached a fixed point. [x → 3] y := x+2 y := 5 [x → 3,y → 5]
Flow function returning a replacement graph Let’s assume we’ve reached a fixed point. • Replacement graphs: • used to transform the program once a fixed point has been reached. [x → 3] y := 5 [x → 3,y → 5]
Iterative analysis example Now, let’s assume we haven’t reached a fixed point. [x → 3] [x →T] y := x+2 [x → 3,y → 5]
Iterative analysis example Now, let’s assume we haven’t reached a fixed point. [x → 3] [x →T] y := x+2 PROPAGATE [x → 3,y → 5] [x →T,y →T]
Branch folding example if (x == 11) F T
REPLACE Branch folding example [x → 11] [x → 11] if (x == 11) F T
Branch folding example [x → 11] [x → 11] if (x == 11) [x → 11] [x → 11] F T
Branch folding example [x → 11] if (x == 11) [x → 11] F T
Constant Propagation 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 { }; Class Analysis Inlining Unreachable code elimination Composing several analyses
x := new C merge b := x instanceof C if (b) F T x := new D x := x.foo() merge while(…)
x := new C PROPAGATE PROPAGATE PROPAGATE PROPAGATE [x → {C}] [x →T] T T merge b := x instanceof C if (b) F T x := new D x := x.foo() merge while(…)
x := new C PROPAGATE PROPAGATE PROPAGATE PROPAGATE PROPAGATE ([x →T], [x → {C}], T, T) [x → {C}] [x →T] T T merge b := x instanceof C if (b) F T x := new D x := x.foo() merge while(…)
x := new C ([x →T], [x → {C}], T, T) merge PROPAGATE ([x →T], [x → {C}], T, T) b := x instanceof C if (b) F T x := new D x := x.foo() merge while(…)
x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C PROPAGATE [x →T, b →T] if (b) F T x := new D x := x.foo() merge while(…)
b := true REPLACE x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) ([x →T], [x → {C}], T, T) b := x instanceof C [x →T, b →T] if (b) F T x := new D x := x.foo() merge while(…)
b := true x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T], [x → {C}], T, T) PROPAGATE if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() merge while(…)
b := true x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T], [x → {C}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() merge while(…)
x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) • 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 b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) F T x := new D x := x.foo() merge while(…)
REPLACE x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ if (b) F T x := new D x := x.foo() merge while(…)
x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ σ if (b) F T x := new D x := x.foo() merge while(…)
( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ σ σ if (b) σ F T x := new D x := x.foo() merge while(…)
( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ σ if (b) σ σ F T x := new D x := x.foo() merge while(…)
( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() merge while(…)
REPLACE ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() merge while(…)
( , , , ) ( , , , ) ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() merge while(…)
( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() merge while(…)
x := C::foo(x) REPLACE ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ σ F T x := new D x := x.foo() merge while(…)
x := C::foo(x) x := x ( , , , ) ( , , , ) REPLACE x := new C class C extends A { A foo() { return self; } } ([x →T], [x → {C}], T, T) merge σ σ ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ F T x := new D x := x.foo() merge while(…)
x := C::foo(x) x := x ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge σ ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ F T x := new D x := x.foo() σ PROPAGATE merge σ while(…)
x := C::foo(x) x := x ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge σ ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ F T x := new D x := x.foo() σ merge σ σ while(…)
x := C::foo(x) ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge σ ([x →T], [x → {C}], T, T) b := x instanceof C σ σ ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ F T x := new D x := x.foo() merge while(…)
x := C::foo(x) ( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge σ ([x →T], [x → {C}], T, T) b := x instanceof C σ ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) σ F T x := new D x := x.foo() ([x →T, b → true], [x → {C}, b →{Bool}], T, T) merge while(…)
( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() ([x →T, b → true], [x → {C}, b →{Bool}], T, T) merge while(…)
( , , , ) ( , , , ) x := new C ([x →T], [x → {C}], T, T) merge ([x →T], [x → {C}], T, T) b := x instanceof C ([x →T, b → true], [x → {C}, b →{Bool}], T, T) if (b) ([x →T, b → true], [x → {C}, b →{Bool}], T, T) F T x := new D x := x.foo() ([x →T, b → true], [x → {C}, b →{Bool}], T, T) merge PROPAGATE ([x →T, b → true], [x → {C}, b →{Bool}], T , T) while(…)