Phase ordering problem Optimizations can interact in mutually
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. true x : = 11; if (x == 11) { Do. Something(); } else { Do. Something. Else(); x : = x + 1; } y : = x; // value of y? const prop followed x : = 11; by unreachable Do. Something(); code elimination y : = x; // value of y? const prop again x : = 11; Do. Something(); y : = 11;
One known solution: Iterate individual analyses until the results don’t change • Compiler is slow. • In the presence of loops in the source program, might not yield best possible results. x : = 11; do { if (x == 11) { Do. Something(); } else { Do. Something. Else(); x : = x + 1; } } while (. . . ) y : = x; // value of y?
Another known solution: hand written super-analysis Monolithic Super-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]
Ideally. . . • . . . we want to: • We present a framework that achieves this. – Write analyses modularly – Exploit mutually beneficial interactions – Have a fast compiler 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.
Flow function returning a dataflow value y : = 5
Flow function returning a dataflow value [. . . ] y : = 5 PROPAGATE [. . . , y → 5]
Flow function returning a replacement graph y : = x+2
Flow function returning a replacement graph Step 1: Initialize input edges with dataflow information Replacement graph [x → 3] y : = x+2 REPLACE 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] y : = x+2 [x → 3] y : = 5 [x → 3, y → 5] PROPAGATE
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] y : = x+2 y : = 5 [x → 3, y → 5] Step 3: Propagate dataflow information from output edges. PROPAGATE
Flow function returning a replacement graph Replacement graphs: [x → 3] y : = x+2 [x → 3, y → 5] – 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 Soundness requirement: [x → 3] y : = x+2 [x → 3, y → 5] – 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 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 [x → 3, y → 5] y : = 5
Flow function returning a replacement graph Let’s assume we’ve reached a fixed point. [x → 3] y : = 5 [x → 3, y → 5] Replacement graphs: – used to transform the program once a fixed point has been reached.
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
Branch folding example [x → 11] if (x == 11) F T REPLACE
Branch folding example [x → 11] if (x == 11) F T [x → 11]
Branch folding example [x → 11] if (x == 11) F T [x → 11]
Composing several analyses Constant Propagation Class Analysis Inlining Unreachable code elimination 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 { };
x : = new C merge b : = x instanceof C if (b) F T x : = new D x : = x. foo() merge while(…)
x : = new C PROPAGATE [x → T] merge b : = x instanceof C if (b) F T x : = new D x : = x. foo() merge while(…) PROPAGATE [x → {C}] T T
x : = new C PROPAGATE ([x → T], T, →T){C}] [x [x →→ T] {C}], [x merge b : = x instanceof C if (b) F T x : = new D x : = x. foo() merge while(…) T T
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 [x → T, PROPAGATE b → 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, REP b → T] T x : = new D x : = x. foo() merge while(…) E b : = true if (b) F LAC
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) b : = true if (b) F ([x → T, T x : = new D x : = x. foo() merge while(…) PROPAGATE b → true], [x → {C}, b → {Bool}], T, T)
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) b : = true if (b) F ([x → T, T x : = new D x : = x. foo() merge while(…) b → true], [x → {C}, b → {Bool}], T, T)
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(…) • 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
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 REPLACE 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) ( , , , ) F T x : = new D ([x → T, [x → {C}, b → {Bool}], T, T) 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, b → true], [x → {C}, b → {Bool}], T, T) if (b) ( , , , ) F T x : = new D ([x → T, b → true], [x → {C}, b → {Bool}], T, T) x : = x. foo() REPLACE 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 → T, [x → {C}, b → {Bool}], T, T) x : = x. foo() , ) merge while(…) b → true],
x : = new C ([x → T], [x → {C}], T, T) merge ([x → T], [x → {C}], T, T) x : = C: : foo(x) b : = x instanceof C ([x → T, b → true], [x → {C}, b → {Bool}], T, T) if (b) ( , , , ) F T x : = new D ( , , ([x → T, [x → {C}, b → {Bool}], T, T) x : = x. foo() , ) merge while(…) b → true], σ REPLACE
x : = new C ([x → T], [x → {C}], T, T) class C extends A { A foo() { return self; } } merge σ ([x → T], [x → {C}], T, T) x : = C: : foo(x) b : = x instanceof C ([x → T, b → true], [x → {C}, b → {Bool}], T, T) ( , , , ) F T x : = new D ( , , ([x → T, b → true], [x → {C}, b → {Bool}], T, T) σ REPLACE if (b) x : = x. foo() , ) merge while(…) x : = x
x : = new C ([x → T], [x → {C}], T, T) merge σ ([x → T], [x → {C}], T, T) x : = C: : foo(x) b : = x instanceof C ([x → T, b → true], [x → {C}, b → {Bool}], T, T) if (b) ( , , , ) F T x : = new D ( , , ([x → T, b → true], [x → {C}, b → {Bool}], T, T) σ x : = x. foo() σ , ) merge while(…) PROPAGATE 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) ( , , , ) F T x : = new D ( , , ([x → T, b → true], [x → {C}, b → {Bool}], T, T) 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(…)
x : = new C merge b : = x instanceof C b : = true if (b) F T x : = new D x : = x. foo() merge while(…) x : = x
x : = new C; do { b : = x instanceof C; if (b) { x : = x. foo(); } else { x : = new D; } } while (. . . ) merge b : = true x : = x merge 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. 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 (. . . )
Analysis followed by transformations
Integrating analysis and transformations
Composing analyses and transformations
- Slides: 58