A second example: Liveness Analysis
A variable is *live* at a instruction if its current value will be used later in the computation.
Applications for liveness include register allocation (only live variables need
registers), and for dead code removal (if a variable is not live immediately after
an assignment is made to it, then the assignment is useless and can be removed).
Define liveness using dataflow analysis (on standard CFG, not SSA)
gen[n] = set of variables used by node n
kill[n] = set of variables defined by node n
s gen[s] kill[s]
t <- b bop c {b,c} {t}
t <- M[b] {b} {t}
M[a] <- b {a,b} {}
if a relop b then L {a,b} {}
t <- f(a1,...an) {a1,...an} {t}
Note that flow equations for this problem are *backwards*, i.e.,
data flows in reverse direction from control flow.
Let succ[n] = set of successors of node n in CFG.
in[n] = gen[n] U (out[n] - kill[n])
out[n] = union over all s in succ[n] of in[s]
In this case we want the fixed point solution having the *smallest* sets.
Example:
1 a <- 0
2 L: b <- a + 1
3 c <- c + b
4 a <- b * 2
5 if a < N goto L
6 f(c)
Assume that a,b,c are local variables not used after the termination of this code fragment.
Here's a solution. The control flow equations are solved as usual, but it is more efficient (takes
fewer iterations) to fill them in in (roughly) reverse execution order, computing out[] before in[].
That's because liveness is a "backwards" flow problem.
iteration 0 iteration 1 iteration 2 iteration 3
n succ[n] gen[n] kill[n] out[n] in[n] out[n] in[n] out[n] in[n] out[n] in[n]
6 - c - - - - c - c - c
5 2,6 a - - - c a,c a,c a,c a,c a,c
4 5 b a - - a,c b,c a,c b,c a,c b,c
3 4 b,c c - - b,c b,c b,c b,c b,c b,c
2 3 a b - - b,c a,c b,c a,c b,c a,c
1 2 - a - - a,c c a,c c a,c c
Note that this in example, no more than two of {a,b,c} are ever simultaneously live,
so two registers will suffice to hold these variables at all times.