Introduction

An occurrence of an expression xopyx \mathbin{op} y is a common subexpression if the same expression was computed earlier and neither xx nor yy has changed since. Recomputing it wastes time; reusing the earlier value is common subexpression elimination (CSE). Within a basic block, the DAG finds such reuse directly. Across blocks, the compiler needs to know that the expression was computed on every path to the current point, which is the available expressions analysis.

This note covers the global case: the definition of availability, the e_gene\_gen and e_kille\_kill sets, the forward-intersection equations and their initialisation, and the transformation that introduces a temporary. Questions ask whether an expression is available at a point, for the IN and OUT sets of a small flow graph, or for the code after global CSE.

Prerequisites

Core concepts

  • Available expression. x+yx + y is available at point pp if every path from the entry to pp evaluates x+yx + y, and after the last such evaluation on each path there is no assignment to xx or yy.
  • Generates. A block generates x+yx + y if it evaluates x+yx + y and does not afterwards redefine xx or yy.
  • Kills. A block kills x+yx + y if it assigns xx or yy and does not afterwards recompute x+yx + y.
  • Universe UU. All expressions appearing on the right of instructions in the program.
  • Equations.
OUT[entry]=∅,IN[B]=⋂P∈pred(B)OUT[P],OUT[B]=e_genB∪(IN[B]−e_killB)OUT[\text{entry}] = \emptyset, \qquad IN[B] = \bigcap_{P \in pred(B)} OUT[P], \qquad OUT[B] = e\_gen_B \cup (IN[B] - e\_kill_B)
  • Initialisation. OUT[B]=UOUT[B] = U for every BB other than the entry. Starting at UU and shrinking finds the largest solution.

How it works

Computing e_gene\_gen and e_kille\_kill within a block

Scan the block forward. For each instruction z=xopyz = x \mathbin{op} y:

  1. Add xopyx \mathbin{op} y to e_gene\_gen (and remove it from e_kille\_kill).
  2. Remove from e_gene\_gen every expression that has zz as an operand, and add those expressions (from UU) to e_kille\_kill.

The order matters: in x = x + y the expression x+yx + y is generated and then killed by the assignment to xx, so the block does not generate it.

Example 1: one expression available, one not

Text
B1: a = x + y
    p = x * y
B2: if a > 0 goto B4        (B1 -> B2; B2 -> B3; B2 -> B4)
B3: x = a - 1
    q = x * y               (B3 -> B4)
B4: c = x + y
    e = x * y

Take U={x+y, x∗y, a−1}U = \{x + y,\ x * y,\ a - 1\}.

Blocke_gene\_gene_kille\_kill
B1B_1x+y, x∗yx + y,\ x * ya−1a - 1
B2B_2nonenone
B3B_3a−1, x∗ya - 1,\ x * yx+yx + y
B4B_4x+y, x∗yx + y,\ x * ynone

In B3B_3, x = a - 1 kills both expressions over xx, but q = x * y then generates x∗yx * y again; x+yx + y stays killed.

Solving (one pass already gives the fixed point here, a second confirms it):

BlockININOUTOUT
B1B_1∅\emptysetx+y, x∗yx + y,\ x * y
B2B_2x+y, x∗yx + y,\ x * yx+y, x∗yx + y,\ x * y
B3B_3x+y, x∗yx + y,\ x * ya−1, x∗ya - 1,\ x * y
B4B_4OUT[B2]∩OUT[B3]=x∗yOUT[B_2] \cap OUT[B_3] = x * yx+y, x∗yx + y,\ x * y

So in B4B_4, x∗yx * y is available but x+yx + y is not: along the path through B3B_3, xx changes after x+yx + y was computed. Note that x∗yx * y holds different values on the two paths (it was recomputed in B3B_3 after xx changed); availability only requires that the most recent evaluation on each path is still valid.

Available expressions and global CSE on the four-block example
Figure 1. Available expressions on the four-block example: x + y and x * y are available after B1, B3 kills x + y but recomputes x * y, so at the entry of B4 only x * y is available, and global CSE replaces e = x * y in B4 by e = u1, with u1 set in both B1 and B3.

The global CSE transformation

For each instruction s:z=xopys: z = x \mathbin{op} y such that xopyx \mathbin{op} y is available at ss (and not killed earlier in ss's block):

  1. Search backward along the flow graph from ss to find the evaluations w=xopyw = x \mathbin{op} y that reach ss (stopping at each such evaluation).
  2. Create a new temporary uu.
  3. Replace each such evaluation w=xopyw = x \mathbin{op} y by u=xopyu = x \mathbin{op} y followed by w=uw = u.
  4. Replace ss by z=uz = u.

For Example 1, the evaluations of x∗yx * y reaching B4B_4 are in B1B_1 and B3B_3:

Text
B1: a  = x + y
    u1 = x * y
    p  = u1
B3: x  = a - 1
    u1 = x * y
    q  = u1
B4: c  = x + y
    e  = u1

One multiplication is saved on every execution of B4B_4. The copies p = u1 and q = u1 are candidates for copy propagation: if later uses of p and q can read u1 instead, the copies become dead and are removed. In B4B_4, c = x + y must stay: the expression is not available on the path through B3B_3.

Example 2: why the initial value is UU

Text
B1: a = x + y               (B1 -> B2)
B2: (loop header; no code)  (B2 -> B3, B2 -> B4)
B3: b = 2                   (B3 -> B2, the back edge)
B4: c = x + y

Nothing in the loop touches xx or yy, so x+yx + y is available at B4B_4. Starting from OUT[B]=UOUT[B] = U, the first pass gives IN[B2]={x+y}∩U={x+y}IN[B_2] = \{x + y\} \cap U = \{x + y\}, and the value survives every pass. Starting instead from OUT[B]=∅OUT[B] = \emptyset, IN[B2]={x+y}∩OUT[B3]=∅IN[B_2] = \{x + y\} \cap OUT[B_3] = \emptyset on the first pass and stays empty forever, because nothing in the loop regenerates x+yx + y. The empty start is safe but loses this common subexpression. Intersection problems start at the top, UU, so that back edges do not erase facts prematurely.

Example 3: a loop that redefines an operand

Text
B1: t = a + b               (B1 -> B2)
B2: c = a + b               (B2 -> B3, B2 -> B4)
B3: a = c + 1               (B3 -> B2, the back edge)
B4: d = a + b

Take U={a+b, c+1}U = \{a + b,\ c + 1\}. B1B_1 generates a+ba + b; B2B_2 generates a+ba + b and kills c+1c + 1; B3B_3 generates c+1c + 1 and kills a+ba + b.

BlockININOUTOUT
B1B_1∅\emptyseta+ba + b
B2B_2OUT[B1]∩OUT[B3]=∅OUT[B_1] \cap OUT[B_3] = \emptyseta+ba + b
B3B_3a+ba + bc+1c + 1
B4B_4a+ba + ba+ba + b
  • c = a + b in B2B_2 is not redundant, although B1B_1 computed a+ba + b just before: on the second and later iterations control arrives from B3B_3, where aa has changed.
  • d = a + b in B4B_4 is redundant: the only way into B4B_4 is from B2B_2, which has just evaluated a+ba + b with the current aa. The backward search from B4B_4 stops at B2B_2, so only that evaluation gets the temporary: B2B_2 becomes u = a + b; c = u, and B4B_4 becomes d = u. B1B_1 is unchanged.

What else kills an expression

  • Commutativity is not assumed. a+ba + b and b+ab + a are different expressions for the basic analysis; a compiler that normalises operand order (for commutative operators only) can treat them as one.
  • Stores through pointers and procedure calls. *p = w may assign any variable p can point to, and a call may assign any variable the callee can reach. Without alias information, such an instruction kills every expression whose operands might be affected.
  • Array element assignments kill expressions that read the same array, as in local CSE.

Local and global CSE together

Local CSE (the DAG of a block) handles repetitions inside a block; global CSE handles expressions whose earlier evaluation is in another block. A compiler usually runs local CSE first, so that each block evaluates each expression at most once before its operands change, then global CSE, then copy propagation and dead-code elimination to clean up the copies.

Related material. Partial-redundancy elimination handles expressions available on some paths but not all, as x+yx + y at B4B_4 in Example 1, by inserting evaluations on the paths that lack them (here, after x = a - 1 in B3B_3) so that the expression becomes fully redundant. It also subsumes loop-invariant code motion. It is the subject of Dragon Book Section 9.5 (lazy code motion).

Important properties and observations

  • Forward and intersection: an expression must be available along all paths; one killing path suffices to make it unavailable.
  • Initialise to UU (except at the entry). Initialising to ∅\emptyset is safe but can lose availability around loops (Example 2).
  • Availability is about expressions, not values: x∗yx * y at B4B_4 has different values on the two paths, and the temporary handles that.
  • Kill by operand assignment: any assignment to xx or yy kills every expression over xx or yy unless it is recomputed later in the block.
  • Order inside a block matters for e_gene\_gen and e_kille\_kill, as in x = x + y.
  • CSE introduces copies, which copy propagation and dead-code elimination then remove.

Common misconceptions.

  • "Available means computed on some path." That is a may property; availability is must.
  • "An expression whose operands have the same values is available." Availability is defined by the absence of reassignment, not by equal values.
  • "Global CSE replaces the later evaluation by the variable of the earlier one." That is wrong if that variable is reassigned in between; a fresh temporary is used.

GATE examination perspective

  • Is xopyx \mathbin{op} y available at a given point? Check every path for an evaluation not followed by an operand assignment.
  • Compute e_gene\_gen, e_kille\_kill, IN and OUT. Work on a small flow graph, intersecting at joins and initialising to UU.
  • Which expressions can be eliminated. Also consider what the code looks like after introducing a temporary.
  • Loops that redefine an operand. An evaluation just before the loop does not make the same expression available at the loop header if the loop body changes an operand (Example 3).
  • Hidden kills. Calls, pointer stores and array stores can kill an expression without naming its operands.
  • Local versus global. DAG for one block; available expressions for the flow graph.
  • Distinguish the classic analyses. Available expressions (forward, intersection) versus reaching definitions (forward, union) versus liveness (backward, union).

Summary

Sources

  1. A. V. Aho, M. S. Lam, R. Sethi, J. D. Ullman, Compilers, Principles, Techniques, and Tools, 2nd ed., Pearson Addison-Wesley, 2007: Section 9.1.4 (global common subexpressions), Section 9.1.5 (copy propagation), Section 9.1.6 (dead-code elimination), Section 9.2.6 (available expressions), Section 9.5 (partial-redundancy elimination, related material only).

Next steps