500 likes | 743 Views
SSA: Static Single-Assignment Form. Compiler Baojian Hua bjhua@ustc.edu.cn. Middle End. translation. AST. IR1. translation. IR2. other IR and translation. asm. Optimizations. opt. opt. translation. AST. IR1. opt. translation. IR2. opt. opt. other IR and translation. asm.
E N D
SSA: Static Single-Assignment Form Compiler Baojian Hua bjhua@ustc.edu.cn
Middle End translation AST IR1 translation IR2 other IR and translation asm
Optimizations opt opt translation AST IR1 opt translation IR2 opt opt other IR and translation asm
SSA History • Optimization is IR-dependent: • We’ve discussed optimizations on the AST, TAC or CFG • Key problem: is there a best IR for optimization? • In late 1980’, researchers at IBM watson research center developed a new IR: SSA • with many fancy properties • great for optimizations • Later, SSA is used in many optimizing compilers: • GCC, MS VC, Jikes, MLton, LLVM, hotspot, … • As a result, SSA becomes a de-factor standard IR for optimizing compilers
Values ≠ Locations int i, sum1=0, sum2=0; for (i=0; i<10; i++) sum1 += i; for (i=0; i<100; i++) sum2 += i;
Use-Def Chains & Def-Use Chains int i, sum1=0, sum2=0; for (i=0; i<10; i++) sum1 += i; for (i=0; i<100; i++) sum2 += i;
UD Chains & DU Chains can be very Expensive int i, j, x, y; switch (i){ case 1: x = 1; break; case 2: x = 2; break; case 3: x = 3; break; case 4: x = 4; break; default: x = 5; break; } switch (j){ case 1: y = x+1; break; case 2: y = x-2; break; case 3: y = x*8; break; default: y = x/3; break; } For M uses and N defs: O(M *N) space and time.
UD Chains & DU Chains can be Expensive int i, j, x, y, z; switch (i){ case 1: x = 1; break; case 2: x = 2; break; case 3: x = 3; break; case 4: x = 4; break; default: x = 5; break; } z = x; switch (j){ case 1: y = z+1; break; case 2: y = z-2; break; case 3: y = z*8; break; default: y = z/3; break; } A solution to this is to limit each variable to have just one definition site.
SSA • SSA is an IR in which every variable is assigned at most once (in text) x = 2 x = 1 x = 2 x = 1 y = x
Advantages of SSA • Make DU and UD chains explicit • each definition knows its use • each use knows its unique definition • Make optimizations: • easier • faster • For most optimizations, also reduce the time/space requirements
Example: Constant propagation on SSA x = 3 … Each variable has just one definition, so we need not do reaching definition analysis. … a = x Can we replace this “x” with constant “3”?
Example: Copy propagation on SSA x = y … Each variable has just one definition, so we need not do reaching definition analysis. … a = x Can we replace this “x” with the variable “y”?
Example: Dead code elimination (DCE) on SSA x = v … Can we eliminate this assignment? … If “x” is not used anywhere, then one can eliminate this. (No liveness analysis!) …
Example: Common Sub-expression Elimination (CSE) on SSA x = a+b … Both a and b should have been assigned before this definition! SSA: each variable assigned at most once! If x dominates y, then one can do this substitution! (Why?) … y = a+b Can we replace this “a+b” with the variable “x”? No available expression analysis and reaching expression analysis!
Converting to SSA • Easy for a basic block • rename each definition • and rewrite each use to the recent def. a = x+y b = a+x a = b+2 c = y+1 a = c+a a1 = x+y b1 = a1+x a2 = b1+2 c1 = y+1 a3 = c1+a2
CFG c = 1 c1 = 1 a = x + y b = a + x a = b + 2 c = y + 1 a1 = x + y b1 = a1 +x a2 = b +2 c2 = y +1 a = c + a a3 = c? + a? So how to rewrite such kind of uses? Use a fiction of Φfunction.
Merging at joins with Φ function c = 1 c1 = 1 a = x + y b = a + x a = b + 2 c = y + 1 a1 = x + y b1 = a1 +x a2 = b +2 c2 = y +1 a = c + a a3 = Φ(a1, a2) b2 = Φ(b1, ?) c3 = Φ(c1, c2) a4 = c3 + a3 Φfunction indicates which definition to use.
The Φ function • Φ merges multiple definitions along multiple control paths into a single definition • For a basic block b with p predessors: • x = Φ (x1, x2, …, xp) • How does Φ know which xi to choose? • don’t care for analysis • can eliminate Φ before execution
Eliminating Φ function c1 = 1 c1 = 1 a1 = x + y b1 = a1 +x a2 = b +2 c2 = y +1 a1 = x + y b1 = a1 +x a3 = a1 c3 = c1 a2 = b +2 c2 = y +1 a3 = a2 c3 = c2 a3 = Φ(a1, a2) c3 = Φ(c1, c2) a4 = c3 + a3 a4 = c3 + a3
Trivial SSA Construction • At each join point, insert Φ for every live variable x = 1 x1 = 1 y = 2 y = 3 y1 = 2 y2 = 3 z = x + y x2 =Φ(x1, x1) y3 =Φ(y1, y2) z1 = x2 + y3 This Φ function is unnecessary!
Minimal SSA Construction • At each join point, insert Φ for every variable with different outstanding definitions x = 1 x1 = 1 y = 2 y = 3 y1 = 2 y2 = 3 z = x + y y3 =Φ(y1, y2) z1 = x2 + y3
Where to insert Φ? 1 Suppose there is a definition of “x” in block 4, which node needs a Φ? 2 3 4 Alternatively, which node does not need a Φ? 5 6 8 7 9 11 10 12
Where to insert Φ? 1 1 2 2 3 4 4 3 5 5 6 6 7 12 8 7 8 11 9 11 9 10 12 10
Dominance Frontier (DF) 1 1 2 2 3 4 4 3 5 5 6 6 7 12 8 7 8 11 9 11 9 10 12 10
Example: DF for node 5 1 1 2 2 3 4 4 3 5 5 6 6 7 12 8 7 8 11 9 11 9 10 12 10
Using DF to Place Φ • If there is a definition in node n, then there should be a Φ in DF(n) • for the definition “x=v”, insert • x=Φ(x, …, x) • where the number of arguments equal with the number of predecessors of n • One can use a worklist algorithm
Placing Φ place_phi () compute defsites(x) for every variable x; for (each variable x) for (each defsite d in defsites(x)) for (each node n in DF(d)) if we haven’t place Φ, then put one for x of the form x = Φ(x, …, x); if (n not in defsites(x)) defsites(x) \/= {n};
Example 1: {} 2: {2} 3: {2} 4: {} 5: {7} 6: {7} 7: {2} i = 1 j = 1 k = 0 1 1 2 3 4 k < 100? 2 3 4 5 6 7 j < 20? return j 5 6 j = i k = k+1 j = k k = k+2 7
Placing Φ 1: {} 2: {2} 3: {2} 4: {} 5: {7} 6: {7} 7: {2} Var i: w = defsites(i) = {1} i = 1 j = 1 k = 0 1 k < 100? 2 3 4 j < 20? return j 5 6 j = i k = k+1 j = k k = k+2 7
Placing Φ 1: {} 2: {2} 3: {2} 4: {} 5: {7} 6: {7} 7: {2} Var j: w = defsites(j) = {1,5,6} i = 1 j = 1 k = 0 1 n = 1, w={5,6} n = 5, w={6} k < 100? 2 3 4 j < 20? return j 5 6 j = i k = k+1 j = k k = k+2 7 {j}
Placing Φ i = 1 j = 1 k = 0 1: {} 2: {2} 3: {2} 4: {} 5: {7} 6: {7} 7: {2} Var j: w = defsites(j) = {1,5,6} 1 n = 1, w={5,6} {j} n = 5, w={6} 2 k < 100? n = 5, w={6, 7} n = 6, w={7} 3 j < 20? 4 return j n = 7, w={} 5 j = i k = k+1 j = k k = k+2 6 7 {j} j = Φ(j, j)
Placing Φ i = 1 j = 1 k = 0 1: {} 2: {2} 3: {2} 4: {} 5: {7} 6: {7} 7: {2} Var j: w = defsites(j) = {1,5,6} 1 n = 1, w={5,6} n = 5, w={6} {j} 2 j = Φ(j, j)k < 100? n = 5, w={6, 7} n = 6, w={7} n = 7, w={} 3 j < 20? 4 return j n = 7, w={2} n = 2, w={} 5 j = i k = k+1 j = k k = k+2 6 {j} 7 j = Φ(j, j)
Placing Φ i = 1 j = 1 k = 0 1: {} 2: {2} 3: {2} 4: {} 5: {7} 6: {7} 7: {2} Similar for k. Leave it to you. 1 {j, k} 2 j = Φ(j, j)k = Φ(k, k) k < 100? 3 4 j < 20? return j 5 6 j = i k = k+1 j = k k = k+2 {j, k} j = Φ(j, j) k = Φ(k, k) 7
Renaming variables • Walk the dominator tree, renaming variable as you go • Replace each variable use with most recent renamed definition • easy for a basic block • for branches, use the most recent definition above the dominator tree nodes • that is, a pre-order tree walk
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 1 3 4 1 3 4 5 6 7 j < 20? return j 5 6 j = i k = k + 1 j = k k = k + 2 1 Walk the dominator tree (in pre-order). 3 2 j = Φ(j , j ) k = Φ(k , k ) 7
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 2 1 3 4 2 1 2 3 4 5 6 7 j < 20? return j 5 6 j = i k = k + 1 j = k k = k + 2 Walk the dominator tree (in pre-order). j = Φ(j , j ) k = Φ(k , k ) 7
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 2 1 3 4 2 1 2 3 4 5 6 7 j < 20? return j 2 5 6 j = i k = k + 1 j = k k = k + 2 Walk the dominator tree (in pre-order). j = Φ(j , j ) k = Φ(k , k ) 7
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 2 1 3 4 2 1 2 3 4 5 6 7 j < 20? return j 2 5 6 j = i k = k + 1 j = k k = k + 2 3 1 Walk the dominator tree (in pre-order). 3 2 j = Φ(j , j ) k = Φ(k , k ) 7 3 3
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 2 1 3 4 2 1 2 3 4 5 6 7 j < 20? return j 2 5 6 j = i k = k + 1 j = k k = k + 2 3 1 2 Walk the dominator tree (in pre-order). 4 3 2 4 2 j = Φ(j , j ) k = Φ(k , k ) 7 3 4 3 4
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 2 1 5 3 4 2 1 5 2 3 4 5 6 7 j < 20? return j 2 5 6 j = i k = k + 1 j = k k = k + 2 3 1 2 Walk the dominator tree (in pre-order). 4 3 2 4 2 j = Φ(j , j ) k = Φ(k , k ) 7 5 3 4 5 3 4
Rename Variables i = 1 j = 1 k = 0 1 1 1 1 1 2 2 j = Φ(j , j )k = Φ(k , k ) k < 100? 2 1 5 3 4 2 1 5 2 3 4 5 6 7 j < 20? return j 2 2 5 6 j = i k = k + 1 j = k k = k + 2 3 1 2 Walk the dominator tree (in pre-order). 4 3 2 4 2 j = Φ(j , j ) k = Φ(k , k ) 7 5 3 4 5 3 4
Summary • SSA is an intermediate representation • static single definition for every variable • invariant maintained by fake Φ functions • Conversion to SSA can be very fast • linear-time algorithm exists • SSA is very compact • linear size of the original program • Optimizations on SSA is very easy • no need to do data-flow analysis • more on this next time