Liveness analysis
Live variable analysis: definition of liveness, use and def sets, the backward equations with union, the iterative solution on a flow graph, dead assignments, and the uses in dead-code elimination and register allocation.
Why it exists
A variable is only worth keeping if its current value will be read later. If it will not, the assignment that computed it is wasted (dead code), and the register or memory location can be reused for something else (register allocation). Liveness analysis finds, for every point of the program, the set of variables whose present value may still be used. It looks forward in the program but works backwards: whether is live now depends on what happens after.
Prerequisites
You need the data flow framework (data flow framework): gen-kill sets, meet, and iteration; liveness is its backward instance.
Precise definitions
Live variable. A variable is live at a point if the value of at may be used along some path starting at , that is, there is a path from to a use of on which is not redefined before that use. Otherwise it is dead at . (A may property: one path suffices.)
use and def of a block.
- : the variables whose value is read in before any assignment to them in .
- : the variables assigned in (whether or not they are used afterwards).
For a block a = b + c; b = 1; d = a: reading b and c before assigning, so (the a read in d = a was assigned earlier in the block, so it is not in use); .
Equations (backward, union).
IN[] is the set of variables live at the start of ; OUT[] the set live at its end. A variable live on exit from is live on entry unless assigns it first; a variable used in before being assigned is live on entry. The initial value of every set is , and the blocks are visited in the reverse of the order used for forward problems. (If some variables are live on exit from the whole program, such as the results returned, they are put into the boundary value.)
Applications.
- Dead-code elimination. An assignment
x = ...is dead if is not live immediately after it. Inside a block scan backwards from OUT[], keeping the live set: for each instruction, if its destination is not in the live set the instruction is dead (remove it, if it has no side effects); otherwise remove the destination from the set and add the operands. - Register allocation. The number of variables live at the same time is a lower bound on the registers needed; two variables that are never live together can share a register (the interference graph is built from liveness).
- Detecting uninitialised variables: a variable live on entry to the program is used before it is defined.
- The live-on-exit set of a block is what local optimisation needs to remove dead assignments inside the block.
How it works
The flow graph has four blocks: B1 (a = 1; b = 2; c = a + b; if c > 5 goto B3), B2 (d = c * 2; goto B4), B3 (d = a - b) and B4 (print d), with edges B1 → B2, B1 → B3, B2 → B4, B3 → B4. The animation computes use and def, then iterates backwards, B4 first. B4 reads d, so IN[B4] = {d} and OUT[B4] = ∅. B3 defines d and reads a and b: IN[B3] = {a, b} (d is defined in B3 so it is removed from OUT[B3] = {d}). B2 reads c and defines d: IN[B2] = {c}. B1's OUT is the union of the IN sets of B2 and B3, {c} ∪ {a, b}, and since B1 defines a, b, c they are all removed from IN[B1], which is empty: nothing is live on entry to the program (good: no variable is used before it is defined).
Live variable analysis: the iterative algorithm: start
Computed by a script, not typed- 11: a = 1
- 22: b = 2
- 33: c = a + b
- 44: if c > 5 goto 7
- 55: d = c * 2
- 66: goto 8
- 77: d = a - b
- 88: print d
| block | use | def | IN | OUT |
|---|---|---|---|---|
| B1 | ∅ | {a, b, c} | ||
| B2 | {c} | {d} | ||
| B3 | {a, b} | {d} | ||
| B4 | {d} | ∅ |
initial values
Step 0
Live variables is a backward problem with union as the meet: a variable is live at a point if some path from there uses it before redefining it. Each block has a use set (variables read before being written in it) and a def set (variables written in it). The equations run from the exits backwards, in the reverse order of the blocks. Table columns: use, def, IN, OUT.
Worked examples
EX 1use and def
EasyBlock B: x = y + 1; z = x * 2; y = 7. Reads: y (before any assignment to y), then x (assigned earlier in B). So and . If OUT[B] = {z}, then IN[B] = : only y is live on entry. The final y = 7 is dead (nothing reads y afterwards, since OUT[B] does not contain it).
EX 2Backward order
EasyFor a chain B1 → B2 → B3 where B3 reads v, B2 assigns v and B1 only reads w: start from B3: IN[B3] = {v}. OUT[B2] = {v}; B2 defines v, so IN[B2] = ∅ (the definition screens the use). OUT[B1] = ∅, IN[B1] = {w}. So v is live between B2 and B3, but not at the start of B2 or in B1. A forward pass would not give this answer; the sets flow from uses backwards.
EX 3A loop
MediumB1: s = 0; i = 0; B2 (header): if i >= n goto B4; B3: s = s + i; i = i + 1; goto B2; B4: print s. Live sets (backward): IN[B4] = {s}. B2: OUT = IN[B3] ∪ IN[B4]; IN[B3] = {s, i} (reads before writing): so OUT[B2] = {s, i}, and B2 reads i and n: IN[B2] = {i, n, s}. OUT[B3] = IN[B2] = {i, n, s}; IN[B3] = {s, i} ∪ ({i, n, s} − {s, i}) = {s, i, n}. B1: OUT = {i, n, s}; defines s, i: IN[B1] = {n}: only the input n is live on entry, as it should be. Note that n stays live all around the loop because the test reads it every time.
EX 4Find the dead assignments in a block
Mediumx = 1; y = 2; z = x + y; x = 3; print z with nothing live on exit. Scan backwards with live = ∅: print z: live = {z}. x = 3: x is not live: dead, remove (live unchanged). z = x + y: z live: keep; live = {x, y}. y = 2: y live: keep; live = {x}. x = 1: x live: keep. Only the fourth instruction is dead (checked by the program). After removing it, the block still computes z = 3.
EX 5Which variables are live?
GATE levelFor the flow graph of the animation: at the end of B1 the live variables are a, b (needed by B3's d = a - b) and c (needed by B2's d = c * 2). At the start of B3 they are a, b. Variable d is live at the end of B2, B3 (read by print d in B4) but dead at the start of B2, B3 (both assign it first). The maximum number of simultaneously live variables at any point is 3 (a, b, c after the first block), so three registers suffice for these variables.
Cheat box
Cheat box
- Live at a point: the current value may be read later on some path before being overwritten.
- use = read before written in the block; def = assigned in the block.
- Backward, union: OUT[B] = IN[successors]; IN[B] = use (OUT def). Start from ; visit blocks in reverse order.
- Dead assignment: destination not live immediately after the instruction.
- Within a block scan backwards: if dest live: dead; else live = (live dest) operands.
- Uses: dead-code elimination, register allocation, uninitialised variables.
Traps and GATE patterns
Connections
Builds on
Across subjects
No cross-subject links yet.
Liveness is the backward case of the data flow framework. It gives the live-on-exit sets that local optimisation needs to delete dead assignments, and the live ranges that decide register sharing in code generation. Reaching definitions asks the dual question about where a value came from (reaching definitions).
PYQs
5 previous year questions from your book. I re-solved each one myself (2 by running or simulating, 3 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider the control flow graph given below. Which one of the following options is the set of live variables at the exit point of each basic block?
- (a)B1:{a, b, c, e, f}, B2:{d, e}, B3:{b, c, e, f}, B4:∅
- (b)B1:∅, B2:{d, e}, B3:{a, c, f}, B4:∅
- (c)B1:{a, b, c, e, f}, B2:{d, e}, B3:{c, e, f}, B4:∅
- (d)B1:∅, B2:{d, e, f}, B3:{a, b, c, e, f}, B4:∅
Which ONE of the following techniques used in compiler code optimization uses live variable analysis?
- (a)Run-time function call management
- (b)Register assignment to variables
- (c)Strength reduction
- (d)Constant folding
For a statement S in a program, in the context of liveness analysis, the following sets are defined: USE (S) : the set of variables used in S IN (S) : the set of variables that are live at the entry of S OUT(S) : the set of variables that are live at the exit of S Consider a basic block that consists of two statements, S1 followed by S2. Which one of the following statements is correct?
- (a)OUT(S1) = IN(S1) ∪ USE(S1)
- (b)OUT(S1) = IN(S2) ∪ OUT(S2)
- (c)OUT(S1) = IN(S2)
- (d)OUT(S1) = USE(S1) ∪ IN(S2)
Which one of the following is FALSE?
- (a)A basic block is a sequence of instructions where control enters the sequence at the beginning and exits at the end.
- (b)Available expression analysis can be used for common subexpression elimination.
- (c)Live variable analysis can be used for dead code elimination.
- (d)x=4*5⇒x=20 is an example of common subexpression elimination
The program below uses six temporary variables a, b, c, d, e, f. a = 1 b = 10 c = 20 d = a+b e = c+d f = c+e b = c+e e = b+f d = 5+e return d+f Assuming that all operations take their operands from registers, what is the minimum number of registers needed to execute this program without spilling?
- (a)2
- (b)3
- (c)4
- (d)6
Self-check
Question 1
What are use and def of the block a = b + c; c = a * 2?
Show answerHide
use = {b, c} (c is read before it is assigned); def = {a, c}. (a is read after being assigned in the block, so it is not in use.)
Question 2
Block B has , and OUT[B] = {y, z}. What is IN[B]?
Show answerHide
IN[B] = {x} ({y, z} {y}) = {x, z}.
Question 3
When is the assignment t = e dead?
Show answerHide
When t is not live immediately after it.
Question 4
Why does liveness run backwards?
Show answerHide
Whether a value is live depends on later uses, so the information must flow from the uses back towards the definitions.
Revision card
One-screen revision
- Live = may be used later before redefinition. use (read first), def (assigned).
- Backward, union: OUT = IN[succ]; IN = use (OUT def).
- Dead assignment: destination not live after it; used for dead code and registers.
- Example sets: IN = ∅,
{c},{a, b},{d}for B1 to B4.
References
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (2nd edition), Chapter 9 Machine-Independent Optimizations (9.2.5 live-variable analysis); Chapter 8 (8.6.3 register allocation and liveness)
- ·Cooper and Torczon, Engineering a Compiler, Chapter 8 and 9 (live analysis)
Verification
The live-variable sets are computed by the backward iterative algorithm (scripts/cd_dfa.py) and, for 222 random programs, recomputed by a different method: a search over single instructions for a path from the point to a use with no redefinition in between (scripts/enum-checks/cd_dfa.py). The dead assignment of the page example was found by a backward scan within the block. Registers are mentioned only as an application; interference graphs are outside the syllabus items and are not developed here. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (token counts, FIRST and FOLLOW sets, LL(1) tables and moves, LR item sets, state counts and parses, operator-precedence values, attribute evaluation and printing schemes, activation-record counts and access links, parameter-passing results, three-address code, jumping and backpatched code, leaders and blocks, value numbering and DAGs, gen/kill and data flow sets, Sethi-Ullman labels and live ranges); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)