Available expressions and common sub-expression elimination
Available expressions analysis: when an expression is available, e_gen and e_kill, the forward intersection equations with the universal set as initial value, the worked solution on a flow graph, and global common sub-expression elimination with a temporary.
Why it exists
Local common subexpression elimination finds a repeated computation inside one block. But a value computed in an earlier block may still be valid when a later block computes it again: if on every path to that later computation the expression has already been evaluated and its operands have not changed since, the second computation is redundant and can use the saved result. Available expressions analysis finds exactly these expressions.
Prerequisites
You need the data flow framework (data flow framework), with intersection as the meet.
Precise definitions
Available expression. An expression is available at a point if every path from the entry to evaluates , and after the last such evaluation on the path there is no assignment to or . It is a must property: all paths, not just one. If an expression is available where it is computed again, the new computation can reuse the earlier value.
Generating and killing.
- A block generates (e_gen) an expression if it evaluates it and does not redefine any of its operands afterwards in the block.
- A block kills (e_kill) an expression if it assigns to an operand of the expression and does not subsequently re-evaluate it (in the same block, after the assignment).
Within a block the instructions are taken in order: evaluating x + y adds the expression; assigning to x or y removes every expression that uses that variable. An instruction x = x + y evaluates x + y but also assigns x, so the expression is not generated (its operand changes).
Equations (forward, intersection).
The initial value of OUT[] for every block other than the entry is the set of all expressions of the program (the identity of intersection). The iteration then removes the expressions that are not really available; it converges to the largest solution. Starting from the empty set would wrongly give an empty answer in loops.
Global common subexpression elimination. For an evaluation of at a point where it is available:
- Find the evaluations of the expression that reach this point (the ones that make it available: going backwards along every path, the nearest evaluation).
- Introduce a new temporary ; replace each of those evaluations
u = x op ybys = x op y; u = s. - Replace the redundant evaluation
v = x op ybyv = s.
This is safe when the expression is available at the redundant evaluation and the temporary is not otherwise changed. It saves a computation at the cost of a copy, which a later copy-propagation pass may remove.
Difference from reaching definitions. Reaching definitions is a may analysis (union, one path is enough); available expressions is a must analysis (intersection, every path). Both are forward; the transfer functions have the same gen-kill form.
How it works
The program is t = a + b; u = c * d; if p > 0 goto B3 (block B1), a = 1; v = c * d; goto B4 (B2), w = c * d; e = 0 (B3, which falls through into B4) and z = a + b; y = c * d (B4). The animation solves the equations. After B1, OUT = {a + b, c * d}. B2 kills a + b (it assigns a), and c * d is evaluated after that assignment: OUT[B2] = {c * d}. B3 changes nothing that matters: OUT[B3] = {a + b, c * d}. At the join, IN[B4] = OUT[B2] ∩ OUT[B3] = {c * d}: c * d is available on both paths, a + b only on one. So in B4 the evaluation y = c * d is redundant, but z = a + b is not.
Available expressions: the iterative algorithm: start
Computed by a script, not typed- 11: t = a + b
- 22: u = c * d
- 33: if p > 0 goto 7
- 44: a = 1
- 55: v = c * d
- 66: goto 9
- 77: w = c * d
- 88: e = 0
- 99: z = a + b
- 1010: y = c * d
| block | gen | kill | IN | OUT |
|---|---|---|---|---|
| B1 | {a + b, c * d} | ∅ | ||
| B2 | {c * d} | {a + b} | ||
| B3 | {c * d} | ∅ | ||
| B4 | {a + b, c * d} | ∅ |
initial values
Step 0
Available expressions is a forward problem with intersection as the meet: an expression is available at a point if on every path from the entry it has been computed and its operands have not been changed since. IN of the entry is empty; the OUT of all other blocks starts as the set of all expressions (the identity of intersection) and shrinks. Table columns: gen, kill, IN, OUT.
Worked examples
EX 1e_gen and e_kill of a block
EasyBlock x = a + b; a = 1; y = a + b; c = c + d. Expressions of the program: a + b, c + d. Scan: x = a + b evaluates a + b (and x is not an operand): add it. a = 1 assigns a: remove a + b, and it is in kill. y = a + b evaluates it again: add a + b. c = c + d evaluates c + d but assigns c, an operand: c + d is not added; and c + d is killed. Result: e_gen = {a + b}; e_kill = {a + b, c + d} (a + b is killed by the assignment a = 1 and generated again afterwards, and because gen is added after kill, it is available at the end of the block).
EX 2Why intersection?
EasyAn expression is available at a join only if it is available on all incoming paths. If one path kills it, the value on that path may be out of date, so the second computation is not redundant. In the animation a + b is lost at B4 because the path through B2 assigns a.
EX 3Global common subexpression elimination
MediumFor the example program: c * d is evaluated in B1 (u = c * d) and available everywhere afterwards (no assignment to c or d). The evaluations in B2 (v = c * d), in B3 (w = c * d) and in B4 (y = c * d) are all redundant. Introduce s: B1: s = c * d; u = s; B2: v = s; B3: w = s; B4: y = s. The three redundant computations become copies. z = a + b is kept as it is, because a + b is not available there. (The transformed program was run against the original on random inputs and gives the same values.)
EX 4Loops: why the initial value is U
MediumB1: t = a + b; B2 (loop header): u = a + b; ... goto B2. IN[B2] = OUT[B1] ∩ OUT[B2]. If OUT[B2] started as ∅ the intersection would be ∅ and a + b would never be found available in the loop. Starting OUT[B2] = gives IN[B2] = {a + b}; then OUT[B2] = {a + b} (generated again), and the value is stable: u = a + b in the loop is redundant (its value is the one computed before the loop) provided nothing in the loop assigns a or b. If the loop body assigned a, OUT[B2] would lose a + b and IN[B2] too.
EX 5Which statements are true?
GATE level(a) "Available expressions is a forward data flow problem": true. (b) "The meet operator is union": false, intersection. (c) "OUT of every non-entry block is initialised to the empty set": false, to the universal set. (d) "An expression is killed by a definition of any variable in it": true. (e) "x = x + 1 generates the expression x + 1": false: its operand x is overwritten by the same instruction. (f) "If an expression is available at the start of a block, its first evaluation in that block (if its operands are unchanged before it) is redundant": true.
Cheat box
Cheat box
- Available at a point: on every path from the entry the expression is evaluated, with no assignment to its operands afterwards (must analysis).
- Forward, intersection. IN[entry] = ; OUT of all other blocks starts as (all expressions).
- OUT = e_gen (IN e_kill). e_gen: evaluated and not redefined later in the block; e_kill: an operand is assigned.
x = x + ydoes not generatex + y.- Global CSE: replace the redundant evaluation by a temporary computed where the value is generated.
- RD is union (some path), AE is intersection (all paths).
Traps and GATE patterns
Connections
Builds on
Across subjects
No cross-subject links yet.
Within a single block the same redundancy is found by value numbering or by a DAG (local optimisation); available expressions extends it across blocks. The machinery is the data flow framework with intersection as the meet; the other forward problem, reaching definitions, uses union, and liveness runs backwards.
PYQs
2 previous year questions from your book. I re-solved each one myself (1 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider the control flow graph shown in the figure. Which one of the following options correctly lists the set of redundant expressions (common subexpressions) in the basic blocks B4 and B5? Note: All the variables are integers.
- (a)B4: { b + i } B5: { c + m }
- (b)B4: { g * k } B5: { c + m }
- (c)B4: { g * k, b + i } B5: { }
- (d)B4: { g * k } B5: { }
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
Self-check
Question 1
Which expressions does the block x = a * b; a = 2; y = a * b generate and kill?
Show answerHide
gen = {a * b} (the second evaluation, after the assignment, is the one that survives); kill: a * b is killed by a = 2, but being generated again later it is available at the end of the block.
Question 2
At a join block with predecessors whose OUT sets are {a + b, c * d} and {c * d}, what is IN?
Show answerHide
The intersection: {c * d}.
Question 3
Why is x = x + y not an evaluation that makes x + y available?
Show answerHide
The instruction overwrites x, so after it the value of x + y no longer corresponds to the current x.
Question 4
How does global CSE avoid recomputing an available expression?
Show answerHide
It saves the value in a new temporary where the expression is generated and replaces the redundant evaluations by a copy of that temporary.
Revision card
One-screen revision
- Available = on every path evaluated, operands unchanged since. Forward, , IN[entry] = , others start at .
- e_gen: evaluated and operands not changed after in the block; e_kill: an operand assigned.
x = x + ydoes not generatex + y.- Global CSE: temporary at the generating sites, copies at the redundant ones.
References
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (2nd edition), Chapter 9 Machine-Independent Optimizations (9.2.6 available expressions; 9.1.4 global common subexpressions)
- ·Cooper and Torczon, Engineering a Compiler, Chapter 9 Data-Flow Analysis (available expressions); Chapter 10 Scalar Optimizations
Verification
Available expressions are computed by the forward intersection algorithm (scripts/cd_dfa.py) and, for 222 random programs, recomputed by enumerating the paths from the entry (each block at most twice) and intersecting what is available at the end of each path (scripts/enum-checks/cd_dfa.py): no difference. The global common sub-expression transformation of the page example (three redundant evaluations of c * d replaced by a temporary, a + b correctly kept) was executed against the original on 300 random inputs. Partial redundancy elimination and expressions with side effects, arrays or calls are outside this page. 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)