In compiler theory, a reaching definition for a given instruction is an earlier instruction whose target variable can reach (be assigned to) the given one without an intervening assignment. For example, in the following code:
d1 : y := 3 d2 : x := y
d1
is a reaching definition for d2
. In the following, example, however:
d1 : y := 3 d2 : y := 4 d3 : x := y
d1
is no longer a reaching definition for d3
, because d2
kills its reach: the value defined in d1
is no longer available and cannot reach d3
.
The similarly named reaching definitions is a data-flow analysis which statically determines which definitions may reach a given point in the code. Because of its simplicity, it is often used as the canonical example of a data-flow analysis in textbooks. The data-flow confluence operator used is set union, and the analysis is forward flow. Reaching definitions are used to compute use-def chains.
S
{\rmREACH}\rm[S]=cupp{\rmREACH}\rm[p]
{\rmREACH}\rm[S]={\rmGEN}[S]\cup({\rmREACH}\rm[S]-{\rmKILL}[S])
In other words, the set of reaching definitions going into
S
S
pred[S]
pred[S]
S
S
S
S
For a generic instruction, we define the
{\rmGEN}
{\rmKILL}
{\rmGEN}[d:y\leftarrowf(x1, … ,xn)]=\{d\}
{\rmKILL}[d:y\leftarrowf(x1, … ,xn)]={\rmDEFS}[y]-\{d\}
where
{\rmDEFS}[y]
y
d
Reaching definition is usually calculated using an iterative worklist algorithm.
Input: control-flow graph CFG = (Nodes, Edges, Entry, Exit)
// put all nodes into the changed set// N is all nodes in graph,Changed = N;
// Iterate while (Changed != emptyset)