A Practical Framework For Demand-Driven Interprocedural Data Flow Analysis
A Practical Framework For Demand-Driven Interprocedural Data Flow Analysis
The high cost and growing importance of interprocedural data flow analysis have led to an in-
creased interest in demand-driven algorithms. In this article, we present a general framework
for developing demand-driven interprocedural data flow analyzers and report our experience in
evaluating the performance of this approach. A demand for data flow information is modeled as
a set of queries. The framework includes a generic demand-driven algorithm that determines the
response to a query by iteratively applying a system of query propagation rules. The propaga-
tion rules yield precise responses for the class of distributive finite data flow problems. We also
describe a two-phase framework variation to accurately handle nondistributive problems. A perfor-
mance evaluation of our demand-driven approach is presented for two data flow problems, namely,
reaching-definitions and copy constant propagation. Our experiments show that demand-driven
analysis performs well in practice, reducing both time and space requirements when compared
with exhaustive analysis.
Categories and Subject Descriptors: D.3.4 [Programming Languages]: Processors—compil-
ers;optimization; D.2.2 [Software Engineering]: Tools and Techniques; H.3.4 [Information
Storage and Retrieval]: Systems and Software—question-answering
General Terms: Algorithms, Performance, Experimentation
Additional Key Words and Phrases: Copy constant propagation, data flow analysis, def-use chains,
demand-driven algorithms, distributive data flow frameworks, interprocedural data flow analysis,
program optimizations
1. INTRODUCTION
Data flow analysis has become a mandatory component of today’s optimizing and
parallelizing compilers. In addition, data flow analysis is increasingly used to im-
prove the capabilities and performance of software development tools such as editors
[Reps et al. 1983], debuggers [Weiser 1984], and testing tools [Duesterwald et al.
1992; Frankl and Weyuker 1988]. Along with the growing use of data flow anal-
This work was supported in part by National Science Foundation Presidential Young Investigator
Award CCR-9157371 and grant CCR-9402226 to the University of Pittsburgh.
A preliminary version of the demand-driven framework was presented in ACM SIGPLAN-SIGACT
Symposium on Principles of Programming Languages, 1995.
Authors’ addresses: E. Duesterwald, Hewlett-Packard Laboratories, 1 Main Street, Cambridge,
MA 02142; email: [email protected]; R. Gupta and M.L. Soffa, Department of Computer
Science, University of Pittsburgh, Pittsburgh, PA 15260; email: {gupta; soffa}@cs.pitt.edu.
Permission to make digital/hard copy of all or part of this material without fee is granted
provided that the copies are not made or distributed for profit or commercial advantage, the
ACM copyright/server notice, the title of the publication, and its date appear, and notice is given
that copying is by permission of the Association for Computing Machinery, Inc. (ACM). To copy
otherwise, to republish, to post on servers, or to redistribute to lists requires prior specific
permission and/or a fee.
c 1997 ACM 0164-0925/97/1100-0992 $03.50
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997, Pages 992–1030.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 993
ysis comes an increased concern about the high time and space requirements of
computing and maintaining the data flow information that is needed. Computing
data flow solutions, especially if interprocedural analysis is involved, can be very
costly [Griswold and Notkin 1993]. Moreover, costly analysis phases may have to
be applied more than once for several reasons:
Multiple Optimizations. Optimizing compilers typically perform a number of in-
dependent optimizations, and each optimization may require a distinct data flow
analysis to be performed.
Invalidated Information. If code transformations are applied to a program, the
data flow in the program changes, and previously computed data flow solutions
may no longer be valid. As a consequence, data flow must be either updated or
recomputed following the application of code transformations.
User Edits. Data flow information may be invalidated through program edits
by the user. During program development, program edits are to be expected and
should be efficiently handled. The respective analysis may have to be repeated to
provide the new data flow solution.
Although the need for efficient data flow analysis algorithms continues to increase,
current data flow applications still rely on exhaustive algorithms. Phrased in the
traditional framework [Kam and Ullman 1977], the solution to a data flow problem
is expressed as the fixed point of a system of data flow equations. Each equation
expresses the solution at one program point in terms of the solutions at immediately
preceding (or succeeding) points. As a result, data flow solutions are computed in
an inherently exhaustive fashion: information is computed at all program points.
Such an exhaustive solution definition is likely to result in very large equation
systems limiting both the time and space efficiency of even the fastest fixed-point
evaluation algorithm.
This article presents an alternative approach to data flow analysis that avoids
the costly computation of exhaustive solutions through the demand-driven retrieval
of information. Demand-driven analysis provides a promising approach to improve
the performance of data flow analyses for several reasons:
Selective Data Flow Requirements. Several code transformations in optimizing
compilers are applicable to only certain components of a program, such as loops.
Even if optimizations are applicable everywhere in the program, the compilation
time overhead can be reduced by optimizing only the most frequently executed
regions of the program (e.g., frequently called procedures or inner loops). Demand-
driven analysis provides an efficient way for computing all relevant information
that affects the code inside the selected code region without having to analyze the
complete program.
User Queries in Interactive Tools. In an interactive tool, the user issues specific
information requests with respect to selected program points rather than inquiring
about the entire program. For example, when debugging, a user may want to
know what statements have an impact on the value of a variable at a certain point.
The extent of data flow information requested by the user is not fixed before the
debugging tool executes, but may vary depending on the user and the program.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
994 · Evelyn Duesterwald et al.
Section 4 discusses the framework variation for handling nondistributive data flow
problems. Section 5 describes instances of the demand-driven analysis framework
for reaching-definitions and copy constant propagation. An experimental evalua-
tion of the performance of the demand-driven analyzers is presented in Section 6.
Section 7 discusses related work, and concluding remarks are given in Section 8.
2. BACKGROUND
A program consisting of a set of possibly recursive procedures p1 , . . . , pk is repre-
sented by an interprocedural control flow graph (ICFG). An ICFG is a collection
of distinct control flow graphs G1 , . . . , Gk where Gi = (Ni , Ei ) represents proce-
dure pi . The nodes in Ni represent the statements in procedure pi , and the edges
in Ei represent the transfer of control among the statements in pi . Two distin-
guished nodes entryi and exiti represent the unique entry and exit nodes of pi .
The set E = ∪ { Ei | 1 ≤ i ≤ k} denotes the set of all edges in the ICFG, and
N = ∪ {Ni | 1 ≤ i ≤ k} denotes the set of all nodes. The complexity analysis of
our algorithms assumes that |E| = O(|N |).1 The sets pred(n) = {m|(m, n) ∈ Ei }
and succ(n) = {m|(n, m) ∈ Ei } contain the immediate predecessors and successors
of node n, respectively. For a call site node s, call(s) denotes the procedure called
from s.
We assume that each procedure is reachable from a series of calls starting from
the main procedure. Furthermore, we assume the program contains no infinite
loops and no interprocedural branching other than procedure calls and returns. A
sample program and its ICFG are shown in Figure 1.
An execution path is a sequence of nodes π = n1 . . . nk such that for 1 ≤ i < k (i)
(ni , ni+1 ) ∈ E, where ni is not a call site (intraprocedural control), (ii) call(ni ) = p
for some procedure p and ni+1 = entryp (procedure invocation), or (iii) ni = exitp
for some procedure p, and there exists an m ∈ pred(ni+1 ) such that call(m) = p
(procedure return). An execution path that has correctly nested call and return
nodes is called a valid execution path. To be a valid execution path, a procedure
that returns must return to the site of its most recent call. Consider the example
in Figure 1. The path 1, 2, 10, 11, 12, 13, 7, 8, 9 that returns to the incorrect call site
after the execution of procedure p is not valid. Invalid paths violate the calling
context of procedures and may lead to imprecise information if considered dur-
ing the analysis. The demand-driven analysis described in this article propagates
information only along valid execution paths.
A finite data flow framework is a pair D = (L, F ), where
—(L, v, ⊥, >) is a finite lattice. The finite set L represents the universe of program
facts with a partial order v, a least bottom element ⊥, and a greatest top element
>. The partial order v defines a meet operator u and a dual join operator t as
the greatest lower bound and the least upper bound, respectively.
—F ⊆ {f : L 7→ L} is a set of monotone flow functions (i.e., x v y =⇒ f (x) v f (y))
that contains the identity function and that is closed under composition and
pointwise meet (i.e., f, g ∈ F =⇒ f · g ∈ F and f u g ∈ F ).
1 This assumes that switch statements have been transformed into a sequence of conditionals. A
similar transformation is assumed for indirect function invocation through function variables.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
996 · Evelyn Duesterwald et al.
procedure main
1
entry procedure p
10
2 entry
call p
3 a:=0 11 read(a,b)
if (b=0)
T F
4 6 12
b:=a call p c:=1
5 7
c:=b a:=b+c
13
exit
8
write(b)
9
exit
fm (X(m))
For nonentry nodes n: X(n) = u
m∈pred(n) φ(entry
if m not a call site
q ,exitq )
(X(m)) if call(m)=q
(a)
q ,exitq )
· φ(entryp ,m) (x) if call(m)=q
(b)
grams with local variables and procedures with parameters. We briefly review the
Sharir and Pnueli approach in the remainder of this section.
Sharir and Pnueli present a two-phase functional approach to interprocedural
analysis that ensures that the calling context of each procedure is preserved. During
the first phase each procedure is analyzed independently of its calling context. The
results of this phase are procedure summary functions as defined by the equation
system in Figure 2(b). The summary function φ(entryp ,exitp ) : L 7→ L for procedure
p maps data flow facts from the entry node entryp to the corresponding set of facts
that hold upon procedure exit. The summary functions are defined inductively by
computing for each node n in p the function φ(entryp ,n) such that if x ∈ L holds
upon procedure entry, the corresponding element φ(entryp ,n) (x) ∈ L holds on entry
to node n.
The actual calling context of a procedure is propagated during the second phase,
based on the summary functions. The data flow solution X(n) at a node n expresses
the set of facts that holds on entry to node n. X(n) is computed by mapping
the solution X(entryp ), which holds on entry to p, to node n using the summary
function φ(entryp ,n) . Figure 2(a) shows the equation system that defines the solution
X(n).
For finite lattices, Sharir and Pnueli propose a work list algorithm to solve the
equation system in Figure 2 in two phases. The algorithm requires O(MaxCall ×
height(L) × |L| × |N |) time, where height(L) denotes the height2 of the lattice L
and where MaxCall is the maximal number of call sites calling a single procedure.
2 The height of a lattice L denotes the number of elements in the longest chain in L.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
998 · Evelyn Duesterwald et al.
−−
| undefined
|
−− any integer
involves the repeated application of the flow functions to the current solution vector
values. Note that during a forward propagation all available information must be
collected, since prior to reaching node 8, information at a preceding node cannot
be ruled out as irrelevant. In particular, when encountering a call to procedure p
(at nodes 2 and 6) the procedure must be fully analyzed in order to ensure that
the complete information that may reach node 8 has been collected.
Now consider how a demand-driven analysis determines whether variable b is a
copy constant at node 8. Unlike exhaustive analysis, demand-driven analysis is
goal-directed. A solution to the question is determined by a partial search that
is started at node 8 and proceeds backward along each path that in the forward
direction leads to node 8. Note that this direction is the reverse of the direction
of the exhaustive analysis. During this backward search only information that is
actually relevant for the current query is collected. The search terminates as soon as
the gathered information implies a solution to the initial query. In this case, upon
encountering the read statement at node 11 (via the call at node 6), the backward
search will establish that b cannot be a copy constant at node 8 and terminate.
Generally, there are three ways in which demand-driven analysis avoids unneces-
sary computations that must be performed in the exhaustive analysis. First, early
termination is achieved by terminating the demand-driven analysis algorithms as
soon as the relevant information has been obtained, possibly leaving large portions
of the program that are not needed to obtain the demanded solution unvisited.
Second, when visiting a node, the information that is not needed to resolve the
current demand at that node is not collected. Finally, nodes/procedures are ana-
lyzed only if needed, i.e., only if it has been determined that information from the
node/procedure may affect the demanded solution. For example, in Figure 1, the
demand-driven analysis computes summary information for procedure q only with
respect to the call site at node 6. Summary information with respect to the call
site at node 2, which is never reached during the search, is not considered.
The demand-driven analysis framework provides a generalization of the preceding
backward search that formally defines the search characteristics. Such generaliza-
tion is obtained by providing answers to the following questions:
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1000 · Evelyn Duesterwald et al.
q = hy, ni
that denotes the truth value of the term: y v X(n). In other words, query q
raises the question as to whether a specific set of data flow facts y is implied by
the exhaustive solution at a selected program node n. For example, the question
as to whether variable b in Figure 1 is a copy constant at node 8 corresponds to
the query q = h[b], 8i.
—q = hy, entryp i for some procedure p (procedure entry node) Query q raises the
question as to whether y holds on entry to every invocation of procedure p. It
follows that q can be translated into the Boolean conjunction of queries hy, mi for
every call site m calling procedure p. If p is the main program then q evaluates
to true if y = ⊥, since by definition X(entrymain ) = ⊥. Otherwise, q evaluates
to false.
—q = hy, ni, where node n is some arbitrary nonentry node For simplicity, assume
first that n has a single predecessor m. The exhaustive equation system from
Figure 2(a) shows that y v X(n) if and only if y v h(X(m)), where h is either a
node flow function or a summary function. In either case h is monotone such that
h(⊥) v h(X(m)) v h(>), and the following two special cases result for query q:
If neither of these two cases applies, the query q translates into a new query
q 0 = hz, mi for node m. The lattice element z to be queried on entry to node m
should be the least element z (i.e., smallest set of facts), such that z v X(m)
implies y v h(X(m)). The appropriate query element z for the new query
q 0 can be determined using the function hr which is the reverse of function h
[Hughes and Launchbury 1992].
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1001
Example. Consider the reverse flow function fnr in CCP for a node n. By
the distributivity of the reverse functions, it is sufficient to define fnr only for
base elements.3 The reverse function value fnr ([vi =const]) denotes the least lattice
element, if one exists, that must hold on entry to node n in order for variable vi to
have the constant value const on exit of n. If fnr ([vi =const]) = ⊥, the trivial value
⊥ is sufficient on entry to node n (i.e., variable vi always has value const on exit).
For example, consider node 3 in Figure 1 with the assignment a := 0. The reverse
function f3r is defined such that f3r ([a = 0]) = ⊥, indicating that a always has the
value 0 on exit of node 3, and f3r ([a = 1]) = >, indicating that there exists no
entry value which would cause variable a to have the value 1 on exit. For a variable
v not equal to a and any constant value c, the reverse flow function is simply the
identity f3r ([v = c]) = [v = c].
If the function h is distributive, the following inequalities hold and establish a Ga-
lois connection between the function h and its reverse hr [Cousot 1981; Hughes and
Launchbury 1992]:
(h r · h)(x) v x and (h · h r )(x) w x (1)
We now show how the relationship between a flow function and its reverse can be
exploited during query propagation. First, consider the following properties of the
3 For each element [v1 = c1 , . . . , vl = cl ] that is obtained as a finite join over base elements, the
reverse function is f r ([v1 = c1 , . . . , vl = cl ]) = f r ([v1 = c1 ]) t . . . t f r ([vl = cl ]).
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1002 · Evelyn Duesterwald et al.
function reversal. It can be easily shown that the distributivity of h with respect
to the meet u implies the distributivity of the reverse function hr with respect to
the join t:
hr (x t y) = hr (x) t hr (y) (2)
Furthermore, the following properties hold with respect to the composition, the
meet, and the join of functions [Hughes and Launchbury 1992]:
(g · h)r = hr · g r
(3)
(g u h)r = g r t hr
Using the reverse functions, the complete set of query propagation rules can be
established as shown in Figure 4. The operator ∧ denotes Boolean conjunction.
If node m is not a call site, the reverse function fmr can be determined by locally
inspecting the flow function fm . Otherwise, if node m calls procedure p, the reverse
summary function φr(entryp ,exitp ) is determined.
Procedure Query(y, n)
input: a lattice element y ∈ L and a node n
output: the answer true or false to the query hy, ni
begin:
1. for each m ∈ N do query[m] ← ⊥
2. query[n] ← y; worklist← {n};
3. while worklist 6= ∅ do
4. remove a node m from worklist;
5. case m = entrymain :
6. if query[m] = ⊥ return(false);
7. case m = entryq for some procedure q:
8. for each call site m0 such that call(m0 ) = q do
9. query[m0 ] ← query[m0 ] t query[m];
10. if query[m0 ] changed then add m0 to worklist;
11. endfor;
12. otherwise:
13. for each m0 ∈ pred(m) do
(
r (query[m])
fm0 if m0 is not a call site
14. new ←
φr(entry (query[m]) if call(m0 )=q
q ,exitq )
15. if (new = >) then return( false )
16. else
17. query[m0 ] ← query[m0 ] t new;
18. if query[m0 ] changed then add m0 to worklist;
19. endif;
20. endfor;
21. endwhile;
22. return(true);
end
each step a node n is removed from the work list, and the query hquery[n], ni
is translated according to the propagation rule that applies to node n. The new
queries resulting from this translation are merged with the previous queries at the
respective nodes. A node n from a newly generated query is added to the work list
unless the newly generated query was previously raised at node n (lines 9,10,17, and
18). Note that procedure Query terminates immediately after a query evaluates to
false. If a query evaluates to false, it is not necessary to evaluate all remaining
queries in the work list, since the overall answer to the input query must also be
false. Thus, procedure Query can terminate early, and the remaining contents of
the work list are simply discarded. Otherwise, procedure Query terminates with
the answer true when the work list is exhausted and all queries have evaluated to
true.
To determine the complexity of the query algorithm the number of join operations
and reverse function applications is considered. A join/reverse function application
is performed at a node n in lines 9, 14, and 17 only if the query at a successor of n
has changed (or at the entry node of a procedure p if n is a call site of p), which
can happen at most O(height(L)) times. Hence, procedure Query requires in the
worst case O(height(L) × |N |) join operations and/or reverse function applications.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1004 · Evelyn Duesterwald et al.
If the program under analysis consists of only a single procedure (i.e., intra-
procedural analysis), procedure Query provides a complete implementation of the
demand-driven data flow analysis. The interprocedural case requires an efficient
algorithm to compute the reverse summary functions. The algorithm to compute
reverse summary function values is obtained as a variation of the Sharir and Pnueli
work list algorithm for computing the forward summary functions in the exhaustive
framework presented in Section 2. However, here summary functions are computed
in the reverse direction. Assuming that (1) the asymptotic cost of a meet and a
join are same and that (2) the asymptotic cost of flow function application and
of reverse flow function application are the same, the algorithm presented in this
section has the same worst-case complexity as Sharir and Pnueli’s algorithm for the
original summary functions.
Figure 6 shows procedure Computeφr which is invoked with a pair (p, y) specifying
a procedure p and a lattice element y. Procedure Computeφr returns the summary
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1005
function value φr(entryp ,exitp ) (y) after evaluating the necessary subsystem of the
reverse equation system (4). Individual function values are stored in a table M :
N ×L 7→ L such that M [n, y] = φr(n,exitq ) (y), where q is the procedure that contains
node n. The table is initialized with the value ⊥, and its contents are assumed to
be preserved between subsequent calls to procedure Computeφr . Thus, results of
previous calls are reused, and the table is incrementally computed during a sequence
of calls. After calling Computeφr with a pair (p, y), the work list is initialized with
the pair (exitp , y). The contents of the work list indicate the table entries whose
values have changed but whose new values have not yet been propagated. During
each step a pair is removed from the work list; its new value is determined, and all
other entries whose values might have changed as a result are added to the work
list.
Consider the cost of k calls to Computeφr . Storing the table M requires space
for |N | × |L| lattice elements. To determine the time complexity consider the
number of join operations (in procedure Propagate) and of reverse flow function
applications (at the call to Propagate in line 16). The loop in lines 4–17 is executed
O(height(L) × |L| × |N |) times, which is the maximal number of times the lattice
value of a table entry can be raised, i.e., the maximal number of additions to
the work list. In the worst case, the currently inspected node n is a procedure
entry node. Processing a procedure entry node results in calls to Propagate for
each predecessor of a call site for that procedure. Thus, the k calls to Computeφr
require in the worst case O(max(k, MaxCall × height(L) × |L| × |N |)) join and/or
reverse function applications, where MaxCall is the maximal number of call sites
calling a single procedure. Procedure Computeφr requires O(|N | × |L|) space to
store lattice elements.
3.6 Caching
Processing a sequence of k queries requires k separate invocations of procedure
Query, which may result in the repeated evaluations of the same intermediate
queries. Repeated query evaluation can be avoided by maintaining a cache. En-
hancing procedure Query to include caching requires only minor extensions. The
cache consists of entries cache[n, y] for each node n and lattice element y. Each
entry contains the previous result, if any, of evaluating the query hy, ni. The query
propagation is modified such that each time before a newly generated query q is
added to the work list, the cache is consulted. The query q is added to the work
list only if the answer for q is not found in the cache.
Entries are added to the cache after the termination of a query evaluation in
a single pass over the visited nodes. The inclusion of caching has the effect of
incrementally building the data flow solution during a sequence of calls to Query.
Caching does not increase the asymptotic time or space complexity of procedure
Query. Storing the cache requires O(|N |×|L|) space, and updating the cache can at
most double the amount of work performed during the query evaluation. Moreover,
the asymptotic worst-case complexity of k invocations of Query, if caching is used,
is the same for any k distinct queries.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1006 · Evelyn Duesterwald et al.
Local variables in the caller that are passed by value or that are passed as part
of an expression to a value parameter are not bound to any variable in the callee.
Hence bs ({v}) = ∅ if v 6∈ GV and if v is not passed to a reference parameter at s.
The bindings for a set V of variables are determined as the union of the bindings
for each variable in V :
bs (V ) = {v ∈ V | bs ({v})}
In addition we need to describe the data flow effects on global and local variables
that result after returning from a called procedure. These data flow effects have
previously been described by specific return functions [Knoop and Steffen 1992].
Similarly, we define for our analysis the reverse binding b−1s that binds variables
from the called procedure to the corresponding variables in the calling procedure.
Specifically, let fp be a formal reference parameter of the callee that was passed at
call site s as the actual parameter ap from the address space of the caller. Then we
have
b−1
s ({f p}) = {ap}.
Finally, for variables v local to the callee, including formal value parameters we
define
b−1
s ({v}) = ∅.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1007
Binding functions are defined over sets of variables. However, data flow analysis
requires the binding of lattice elements at call sites. Thus, for each data flow prob-
lem it is assumed that two functions b̃s and b̃−1
s are defined to be the corresponding
counterparts of bs and b−1s that are applicable to the lattice elements in the data
flow problem.
Example. We illustrate the use of the binding functions in CCP for the program
fragment shown in Figure 7. Consider the query that raises the question as to
whether variable f is a copy constant at the write statement. How this query is
propagated across the recursive call depends upon whether f is a value parameter
and thus local to the caller or whether it is a reference parameter for which a binding
relation to a variable in the called procedure exists.
First assume that f is a value parameter and thus that bs ({f }) = ∅, expressing
that no summary information for the callee is needed and that the query can simply
be copied across the call. At the read statement the query can then be falsified,
establishing that f is not a copy constant.
Now assume that f is a reference parameter. In this case bs ({fcaller )} = {fcallee }.
Note that this equation refers to two distinct instances of variable f as noted by
the subscripts. This binding relation indicates that summary information concern-
ing the formal parameter f is required. Thus, a procedure summary computation
is triggered to determine the procedure entry query that results when querying
whether the formal f is a copy constant on procedure exit. The summary compu-
tation determines along one branch of the if statement that the query resolves to
true with the value 5. This value will eventually stabilize at the procedure entry
node indicating that the formal parameter f will always have value 5 on procedure
exit independently of the procedure entry value of f . Using this summary informa-
tion at the recursive call causes the initial query to resolve to true indicating that
every instance of the formal f always has constant value 5 at the write statement.
3.8 Aliasing
The presence of reference parameters causes an additional complication for data
flow analysis by introducing the potential of aliasing. As for standard exhaustive
analysis, ignoring the potential of aliasing may lead to unsafe information during
query propagation.
Two variables x and y are aliases in a procedure p if x and y may refer to the same
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1008 · Evelyn Duesterwald et al.
| , 2, 3)
(−− | , 1, 4)
(−−
a := b +c
| , −−
(−− | , −−
| )
shown in Figure 8. Unlike CCP, regular constant propagation (CP) includes the
evaluation of arithmetic expressions. Consider the flow function for the node in
Figure 8. Each lattice element is a triple (xa , xb , xc ) with one component for each
of the three variables a, b, and c. The flow function fcp for the assignment is of the
form:
(xb + xc , xb , xc ) if both xb and xc denote constant values
fcp (xa , xb , xc ) =
(⊥, xb , xc ) otherwise
We first show that fcp is not distributive, and then we show that relationship (1)
r
does not hold for fcp and its reverse fcp .
Claim 1: Function fcp is Not Distributive. Consider the situation where el-
ement (⊥, 2, 3) is propagated to the node along the left incoming branch and
element (⊥, 1, 4) is propagated along the right incoming branch. Applying the
flow function fcp to each incoming value in isolation yields fcp (⊥, 2, 3, ) = (5, 2, 3)
and fcp (⊥, 1, 4, ) = (5, 1, 4). Thus, with respect to each branch, the lattice value
on exit of the node indicates correctly that variable a has the constant value 5:
fcp (⊥, 2, 3, ) u fcp (⊥, 1, 4, ) = (5, ⊥, ⊥). However, if the information that reaches
the node along the two incoming paths is merged prior to applying the flow func-
tion, it will not be discovered that variable a has value 5: fcp ((⊥, 2, 3, ) u (⊥, 1, 4, ))
= fcp (⊥, ⊥, ⊥) = (⊥, ⊥, ⊥). Hence, fcp is not distributive.
−r
Claim 2: Relationship (1) is Violated for fcp and Its Reverse fcp . Consider the
r
reverse function fcp , and assume it is applied to the lattice element [a = 5] that
r
denotes that variable a has value 5. By definition fcp ([a = 5]) is the meet over all
elements (xa , xb , xc ) such that fcp (xa , xb , xc ) w [a = 5]. There are infinitely many
values for variables b and c that would result in a constant value 5 for variable
a when executing the assignment a := b + c. Since the meet over this infinite
set of possible constant values is bottom, it follows that fcp r
([a = 5]) = (⊥, ⊥, ⊥),
incorrectly suggesting that the value (⊥, ⊥, ⊥) on node entry is sufficient for a to
have value 5 on node exit. Thus, the relationship fcp · fcp r
(x) w x does not hold for
the nondistributive function fcp .
With only an implication in the query translation the rules no longer provide reliable
answers. The preceding implication still ensures that if query q = hy, ni evaluates
to false, then it must be that y 6v X(n). However, if the propagation rules yield a
true answer nothing can be said. If appropriate worst-case assumptions are made
for true responses in the query propagation, the query algorithm may still be used
to provide approximate information in the presence of nondistributive functions.
ond phase only propagates the subset of the constants needed to resolve previously
guessed queries by considering only the marked portion of the program.
The preceding strategy of using a preparatory backward analysis in order to
reduce the analysis effort of the original forward analysis is not limited to the
problem of constant propagation. The preparatory marking phase can be thought
of as a filter that is applied in order to identify some portions of the solution that
are guaranteed to be irrelevant and therefore can be ignored during the actual
analysis (second phase). However, in general, it cannot be guaranteed that the
preparatory phase actually leads to a reduction in the second phase. In the worst
case, the entire program is marked during the first phase, in which case the complete
exhaustive original analysis would be performed during the second phase. Thus, if
the data flow problem is distributive, the demand-driven approach of choice is the
reversal-based analysis framework developed in Section 3.
5. APPLICATIONS
We presented a framework for demand-driven data flow analysis that includes a
generic demand-driven algorithm. It remains to be shown that demand-driven
algorithms have efficient implementations in practice. Since the generic algorithm is
expressed in general terms, a straightforward implementation may not be the most
efficient one for a given data flow problem. This section considers two analysis
problems—namely, reaching-definition analysis and copy constant propagation—
and shows that it is possible to efficiently implement the generic algorithm by
exploiting the specific properties of these data flow problems.
5.1 Demand-Driven Reaching-Definition Analysis
Reaching-definition analysis is one of the classical bit vector problems. The alge-
braically simple definition of bit vector problems allows for efficient implementa-
tions of the lattice elements and lattice operations using Boolean operations on bit
vectors.
A definition of a variable v is any statement that assigns a value to a. A definition
d is a reaching-definition at a program point n if there exists a valid execution path
from definition d to n along which the defined variable is not redefined. The lattice
elements in reaching-definition analysis are subsets of the set Def of definitions in
the program, and the meet operator is set union (∪). We use Def (v) to denote the
definitions of variable v.
A specialized instance of the general framework for computing reaching-definit-
ions is obtained by specializing the individual components of the framework: (1)
the query definition, (2) the query propagation rules, and (3) the generic analysis
algorithm. The specialization presented here assumes C-style programs with local
and global scoping and procedures with value parameters. Extensions for handling
reference parameters are straightforward and based upon the handling of procedure
parameters as described in Section 3.7.
5.2 Specialized Queries and Propagation Rules
The generic framework from Section 3 assumes queries in a true/false format. While
it is generally possible to gather reaching-definition information using true/false
queries only, it may not be the most efficient way. Consider, for example, the
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1013
(a)
r
Pres(exit (v) = true
p ,exitp )
r
Pres(n,exit (v) =
p)
( )
_ r
Pres(entry (bm (v)) ∧ Pres(m,exit
r
(v) if m ∈ call(q)
q ,exitq ) p)
{Action: r
if Pres(m,exit )
(v) = true then Def pr (v) = Def pr (v) ∪ Defm (v)}
p
(b)
Fig. 9. (a) Specialized propagation rules and (b) summary computation for reaching-definitions.
node n. Note that unless node n is a call site, Defn (v) is a singleton.
Figure 9(a) shows the resulting propagation rules. Note that the specialized query
format hv, ni describes a set of queries and therefore no longer represents a single
truth value. To accommodate the new format the propagation rules in Figure 9 are
displayed using set notation. During the propagation of a query hv, ni all definitions
that are encountered are collected in the solution set.
To determine Presn (v) and Defn (v) if node n contains a call site we define the
specialized instances of the procedure summary functions. Corresponding to the
general summary function φr(entryp ,exitp ) from Section 3, a Boolean summary func-
r
tion Pres(entry p ,exitp )
is defined for each procedure p as shown in Figure 9(b) such
that
r
—Pres(n,exitp)
(v) = true if there exists a path in procedure p from node n to pro-
cedure exit that preserves the definitions of variable v, i.e., a path that does not
contain a definition of variable v.
While computing these reverse summary functions, the definitions that are encoun-
tered and that reach procedure exit are collected in a set Defpr (v) as stated in the
equation system in Figure 9(b). Thus, Defpr (v) contains the definitions of variable
v that are generated along some path from the entry to the exit of procedure p
without being subsequently redefined. Note that Defpr (v) not only contains the
definitions directly contained in p but may also contain definitions that are gener-
ated by procedures subsequently called from p. Based on the summary functions,
the query propagation rules can be extended to include the propagation across
a call site s ∈ call(p) by setting the variables Press (v) and Defs (v) as follows:
r
Press (v) = Pres(entry p ,exitp )
(v) and Defs (v) = Defpr (v). To illustrate the definit-
ions of these variables, we show in Table II the definition sets for the program
example from Figure 1.
the general case in which reverse summary computations may be triggered for each
lattice element, the specialized summary computation from Figure 9 only considers
summary computation with respect to individual variables. Thus, at most |GV | ×
|N | entries may be generated, and each entry may require the inspection of at most
MaxCall other entries. The overall cost of k summary requests is O(max(k, |GV | ×
|N |) × MaxCall).4 It follows that the total worst-case time for processing k queries
is O(max(k, MaxVar × MaxCall × |N |)).
4 In programs that contain reference parameters, summary information is needed for global vari-
ables and formal reference parameters. The asymptotic complexity for k requests changes to
O(max(k, (|GV | + M axF P ) × |N | × MaxCall)) where M axF P is the maximal number of formal
parameters in any procedure.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1016 · Evelyn Duesterwald et al.
(b)
Fig. 10. (a) Specialized propagation rules and (b) reverse summary functions for CCP.
(b). The reverse summary function provides the necessary information to determine
the sets Vals (v) at call sites s ∈ call(p) such that
Valpr (v) if φ(entryp ,exitp ) ([v]) 6= >
Vals (v) =
⊥ otherwise.
The reverse flow functions and summary functions are illustrated in Tables III
and IV for the program example from Figure 1.
5 In programs that contain reference parameters, summary information is needed for both global
variables and formal reference parameters resulting in O((|GV | + MaxFP) × |N |) entries, where
MaxFP is the maximal number of formal parameters in any procedure.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1018 · Evelyn Duesterwald et al.
2 6 10
b:=a−c call proc3 c:=a+b
3 7 11
call proc2 write(x) exit
4 8
exit exit
5.7 Optimization
This section describes a simple but effective optimization of the query propagation
algorithm through query advancing. We illustrate query advancing using the ex-
ample of reaching-definitions in Figure 11. The same advancing optimizations may
be used for shortening the propagation paths of CCP queries.
There are two types of opportunities for query advancing:
—Advancing across call: Consider the query for reaching-definitions hx, 7i request-
ing the reaching-definitions of the global variable x at node 7 in Figure 11. Prop-
agating the query across the call site at node 6 would require the computation
of summary information about the called procedure. However, if it is known
that the called procedure p (and procedures subsequently called from p) does not
contain a node with a definition of variable x (or any of x’s aliases) no summary
computation is necessary, since all definitions of x must be preserved. Thus,
the summary computation can be skipped, and the query hx, 7i can directly be
forwarded across the procedure call as shown by the dashed arrow.
—Advancing to entry: Consider the propagation of the query hx, 5i from the entry
of procedure proc2 to the call site in procedure proc1. Based on the propagation
rules from Section 3 the query would be translated into a new query at node 3.
However, if it is known that procedure proc1 (and any procedure subsequently
called from proc1) does not contain a node with a definition of variable x (or any
of x’s aliases), it is not necessary to propagate the new query through procedure
proc1. Instead the query can be directly forwarded to the entry node of proc1 as
shown by the dashed arrow.
The additional information needed for query advancing are the flow-insensitive
procedure summary sets Mod(p) [Cooper and Kennedy 1988]. Mod(p) contains the
programs with reference parameters the overall time requirements are O(MaxCall ×
6 For
MaxVar3 × |N |).
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1019
variables that may be modified by the execution of procedure p because they are
modified either directly in p or in a procedure subsequently called by p. Cooper
and Kennedy presented a simple iterative work list algorithm that operates on the
program’s call graph to compute the Mod sets [Cooper and Kennedy 1988].
The summary information M od(p) is called flow-insensitive, since determining
this information does not require flow analysis and the control flow within each
procedure can be ignored. In contrast, the summary information expressed by the
reverse functions φ r is called flow-sensitive, since it does require the control flow
in each procedure to be analyzed.
6. PERFORMANCE EVALUATION
An experimental study was carried out to evaluate the practical benefits of the
demand-driven approach. The study’s primary objective was to compare the per-
formance of demand-driven analysis algorithms with that of standard exhaustive
algorithms. Additional experiments were carried out to evaluate the benefits of
caching. The study examined analyzers for two analysis problems:
(1) interprocedural def-use chains based on reaching-definitions and
(2) interprocedural copy constant propagation as described in Section 5.
Computing def-use chains is a fundamental problem in most compiler optimizations.
A def-use chain connects the definition of a variable with one of the uses of the
defined value. Using reaching-definitions information, def-use chains are computed
by pairing each use of a variable with each of the variable’s definitions that reach the
use. The demand-driven def-use chains analysis is based on the reaching-definitions
analyzer from Section 5.
For each analysis problem, three analyzer versions were implemented:
—a caching version of the demand-driven analysis algorithm,
—a noncaching version of the demand-driven analysis algorithm, and
—an exhaustive analysis algorithm. The exhaustive algorithms for reaching-definit-
ions and copy constant propagation are based on the functional approach to
interprocedural analysis by Sharir and Pnueli [1981]. Since the Sharir/Pnueli
framework also serves as the basis for the demand-driven analysis framework, it
provides a natural exhaustive counterpart to the demand-driven algorithms.
The three algorithms were implemented in C as part of the PDGCC compiler
project at the University of Pittsburgh. The PDGCC system contains a C front
end that provides statement-level control flow graphs. The implemented algorithms
assume programs that are free of pointer-induced aliasing. Pointer references in C
are handled by assuming that the address operator “&” destroys the value of the
variables to which it is applied. Future versions will incorporate separately com-
puted alias information into the analysis as described in Section 3 to safely handle
aliasing without having to make overly conservative worst-case assumptions. An
important aspect of the compiler front end that has direct implications on analy-
sis performance is the treatment of temporary variables. The PDGCC front end
generates single-assignment temporary variables. The use of single-assignment tem-
poraries avoids the creation of artificial data dependencies among statements which
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1020 · Evelyn Duesterwald et al.
may be beneficial for tasks such as register allocation. However, the generation of
single-assignment temporaries also increases the size of the address space. Single-
assignment temporaries are typically used in a fairly controlled way such that their
uses and definitions are in nearby statements. Thus, temporaries may not actually
require global analysis and could instead be analyzed locally. For example, it may
be possible to determine the def-use chains for a temporary variable immediately
after the temporary has been created. However, if the program changes, the lo-
cality property of references to temporaries may be destroyed, and a subsequent
reanalysis of the program may have to consider temporary variables. In order to
avoid a bias in the experimental results toward a particular strategy for handling
temporary variables, the experiments are conducted in two versions. One version
considers the complete address space in each procedure, including all compiler-
generated temporaries, and the other version considers only source-level variables
in the analysis.
The experiments were run on a SUN SPARCstation 5 with 32MB of RAM. Table
V lists the 14 C programs that were used during the study and shows for each
program the number of code lines, the number of control flow graph nodes, the
number of procedures and call sites, and the maximal number of variables in any
one procedure. Parentheses indicate the number of source-level variables. In the
following tables we use parentheses to indicate the results with respect to the source-
level variable space that excludes compiler-generated temporaries.
Except for the first three, the programs are core routines of Unix utility sources.
All reported analysis times are user CPU times in seconds determined using the
Unix library routine getrusage. The reported analysis times reflect the mean value
over five test runs. If query advancing was enabled in the demand-driven analyzer,
the measured analysis times include the time to compute the Mod sets. All reported
space measurements include only the amount of memory that is allocated for data
flow vectors, cache memory, and other auxiliary structures that are needed for
analysis purposes, such as the storage of the Mod sets if query advancing was
enabled.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1021
Table VII. Exhaustive Analysis Times for Copy Constant Propagation (CCP)
Table VIII. Demand-Driven Versus Exhaustive Analysis with Caching for Def-Use Chains
Table IX. Demand-Driven Versus Exhaustive Analysis with Caching for CCP
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1023
VIII and IX also show the cache fill, which is the percentage of the exhaustive
solution that has been accumulated in the cache at the end of the demand-driven
analysis. Thus, the cache fill indicates the portion of the exhaustive solution that
is actually needed to answer all queries. The cache fill values show that actually
only a small portion of the exhaustive solution is relevant. The remaining portion of
the solution consists of useless reaching-definitions or copy constant information for
variables that are no longer live in the current procedure. Demand-driven analysis
naturally suppresses the computation of the useless information of dead variables,
since this information is not queried.7
For the complete variable space including temporaries, the relevant portion ranges
from 7% to only 25%. As expected, when temporaries are excluded, the relevant
portion is higher, ranging from 14% to 51%. Temporary variables are likely to gen-
erate large portions of unneeded information, since temporary variables are usually
defined and used at nearby points and are dead in the remaining portion of the
containing procedure. However, Tables VIII and IX show that even after excluding
temporaries from the analysis, on an average more than half of the solution is not
needed.
opt
Tables VIII and IX also display the speedups (Tex /Tcache ) of the demand-driven
analyzer with caching over the exhaustive analyzer. The demand-driven analyzer
computes def-use chains faster than the exhaustive analyzer by factors ranging from
1.1 up to 11.4 in 10 out of 14 test programs. In CCP, the demand-driven analysis
outperforms the exhaustive analysis in all programs with speedup factors ranging
from 1.4 up to 44.3. The exclusion of temporaries causes a larger portion of the
exhaustive solution to be computed (i.e., a higher cache fill) and therefore results
in slightly lower speedups.
Tables VIII and IX also show the space savings of the demand-driven analyzer
as a percentage of the exhaustive space. The worst-case space requirements of
the demand-driven analysis are higher than for standard exhaustive analysis by
a small constant amount, since in addition to data flow solutions a “visited” flag
needs to be stored at every node in the graph. However, the tables show that in
practice demand-driven analysis requires less space in almost all programs. The
lower space requirements are expected, since demand-driven analysis computes less
information than exhaustive analysis. The space savings are primarily due to the
fact that demand-driven analysis permits the suppression of unnecessary procedure
summary computations.
7 Note that the same redundancies in the exhaustive solution would result if, instead of reaching-
definition analysis, the directional dual live-use analysis were used to compute the def-use chains.
In exhaustive live-use analysis, the uses of variables may be propagated past the points where the
respective variable is live. Avoiding the propagation of live-use information through the program
portion where the respective variables are dead would require dynamically changing the bit vector
sizes during the analysis each time a variable becomes dead. The overhead of changing bit vector
sizes is likely to quickly outweigh the savings that may result from avoiding the useless information
propagation.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1024 · Evelyn Duesterwald et al.
(with query advancing) was executed with the same set of queries as in the first
experiment. The accumulated analysis times T opt and space consumption S opt are
shown in Table X for def-use chains and in Table XI for CCP. The tables show
the accumulated analysis time T opt and the amount of space used (S opt ) for the
opt
noncaching analyzers. Tables X and XI also show the speedup (T opt /Tcache ) of
the demand-driven analyzer with caching over the demand-driven analyzer without
caching and the space usage of the caching demand-driven analyzer as a percentage
of the space used by the non-caching analyzer. Except for one of the short pro-
grams (queens) in the def-use chain analyzer, adding the caching capability resulted
in moderate speedup factors of up to 2.2. The analysis of program queens resulted
in too few cache hits, causing the savings to be less than the overhead of the cache
management.
are raised throughout the program. If fewer demands are raised, caching is likely
to be less beneficial, since there may not be enough cache hits to compensate for
the cache management overhead.
An additional inspection of the benchmark programs with the highest and lowest
speedups was carried out in order to identify the program characteristics that affect
the analyzers’ performance. In general, the speedups of the demand-driven analyzer
over the exhaustive analyzer are highest if the lengths of the propagation paths for
the individual queries are the shortest. The length of query propagation paths
depends primarily on reference locality properties. If variables are defined and
used in nearby statements, the propagation paths are short. A number of program
characteristics may have a direct impact on the length of propagation paths and
analyzers’ performance. These characteristics include program size, nesting depth
of control structures, number of global variables, number and size of procedures,
and the structure and density of the call graph. Exceptionally high speedups of
demand-driven analysis over exhaustive analysis result in programs that combine
several of these program characteristics.
7. RELATED WORK
Numerous analysis algorithms have been presented that utilize some form of delayed
or demand-driven evaluation to allow for efficient analysis implementations. Among
others, the concepts of deriving data flow information by backward propagation of
assertions was used in a debugging system for Pascal-like programs with higher-
order functions [Bourdoncle 1993] and in an analysis for discovering linked condi-
tions in programs [Stoyenko et al. 1993]. A types-based framework was developed
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1026 · Evelyn Duesterwald et al.
sion of the SSA form to include only the necessary alias information. Similar ideas
have also been implemented in the demand-based expansion algorithm of factored
def-def chains [Choi et al. 1992].
8. CONCLUDING REMARKS
We presented in this article a new demand-driven approach which has been devel-
oped through a general framework. The framework is applicable to the class of
distributive and finite data flow problems. To precisely handle the nondistributive
case, this work also outlines a two-phase framework variation. The practical benefits
of the demand-driven approach have been demonstrated through experimentation.
Using a demand-driven approach to data flow analysis in compilers and software
tools results in a considerable change in their overall design, since demand-driven
analysis does not obey the classical, strict, phased design of a compiler or software
tool. In the phased design, data flow analysis is performed in isolation indepen-
dently of its context and, in particular, independently of the application phase that
follows the analysis. While such a strict separation into phases may simplify the
overall design and implementation of a compiler or software tool, it also limits the
information available to each individual phase and may thereby render the phases
unnecessarily inefficient. Since nothing is known about the actual information de-
mands of the application, the analysis must consider all possibly relevant data flow
facts and is therefore necessarily exhaustive. In contrast, demand-driven analysis is
directly interleaved with the application such that analysis is performed only if trig-
gered by a demand. If caching is used, repeated invocations of the demand-driven
analyzer result in the subsequent accumulation of the data flow solution. Thus,
if exhaustively many demands are issued by the application, the demand-driven
analyzer eventually accumulates the complete exhaustive solution.
As an additional benefit, our demand-driven algorithms have a natural paral-
lelization. Individual queries can be propagated and resolved in parallel without
requiring a separate phase to explicitly uncover parallelism [Duesterwald 1996]. Fu-
ture investigation will further consider the utility of our demand-driven algorithms
for the parallelization of data flow analysis.
ACKNOWLEDGMENTS
The authors gratefully thank the anonymous referees for their detailed comments
and suggestions, which have helped improve both the contents and presentation of
this article.
REFERENCES
Babich, W. and Jazayeri, M. 1978. The method of attributes for data flow analysis: Part II.
Demand analysis. Acta Inf. 10, 3 (Oct.), 265–272.
Blume, W. and Eigenmann, R. 1995. Demand-driven symbolic range propagation. In Proceedings
of the Workshop on Languages and Compilers for Parallelism. Lecture Notes in Computer
Science, vol. 1033. Springer Verlag, Berlin, 141–160.
Bourdoncle, F. 1993. Abstract debugging of high-order imperative languages. In Proceedings
of the SIGPLAN 1993 Conference on Programming Language Design and Implementation.
ACM, New York, 46–55.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
A Practical Framework for Demand-Driven Interprocedural Data Flow Analysis · 1029
Burke, M. 1987. An interval analysis approach toward exhaustive and incremental data flow
analysis. Tech. Rep. RC 12702, IBM Thomas J. Watson Research Center, Yorktown Heights,
N.Y.
Choi, J., Cytron, R., and Ferrante, J. 1992. On the efficient engineering of ambitious program
analysis. IEEE Trans. Softw. Eng. 20, 2 (Feb.), 105–114.
Choi, J.-D., Burke, M., and Carini, P. 1993. Efficient flow-sensitive interprocedural computat-
ion of pointer-induced aliases and side effects. In Proceedings of the 20th ACM SIGACT-
SIGPLAN Symposium on Principles of Programming Languages. ACM, New York, 232–245.
Cooper, K. 1985. Analyzing aliases of reference formal parameters. In Proceedings of the 12th
ACM Symposium on Principles of Programming Languages. ACM, New York, 281–290.
Cooper, K., Hall, M., and Kennedy, K. 1992. Procedure cloning. In Proceedings of the IEEE
1992 International Conference on Computer Languages. IEEE, New York, 96–105.
Cooper, K. and Kennedy, K. 1988. Interprocedural side-effect analysis in linear time. In Pro-
ceedings of the SIGPLAN 1988 Symposium on Compiler Construction. ACM, New York, 57–66.
Cousot, P. 1981. Semantic foundations of program analysis. In Program Flow Analysis: Theory
and Applications, S. Muchnick and N. Jones, Eds. Prentice-Hall, Englewood Cliffs, N.J., 303–
342.
Cousot, P. and Cousot, R. 1978. Static determination of dynamic properties of recursive pro-
cedures. In Proceedings of the IFIP Conference on Programming Concepts, E. Neuhold, Ed.
North-Holland, Amsterdam, 237–277.
Cytron, R. and Gershbein, R. 1993. Efficient accommodation of may-alias information in SSA
form. In Proceedings of the SIGPLAN 1993 Conference on Programming Language Design
and Implementation. ACM, New York, 36–45.
Duesterwald, E. 1996. A demand-driven approach for efficient interprocedural data flow analysis.
Ph.D. thesis, Univ. of Pittsburgh, Pittsburgh, Pa.
Duesterwald, E., Gupta, R., and Soffa, M. 1992. Rigorous data flow testing through output
influences. In Proceedings of the 2nd Irvine Software Symposium. 131–145.
Duesterwald, E., Gupta, R., and Soffa, M. 1995. Demand-driven computation of interprocedu-
ral data flow. In Proceedings of the 22nd ACM Symposium on Principles of Programming
Languages. ACM, New York, 37–48.
Emami, M., Ghiya, R., and Hendren, L. 1994. Context-sensitive interprocedural points-to analy-
sis on the presence of function pointers. In Proceedings of the SIGPLAN 1994 Conference on
Programming Language Design and Implementation. ACM, New York, 242–256.
Frankl, P. and Weyuker, E. 1988. An applicable family of data flow testing criteria. IEEE
Trans. Softw. Eng. SE-14, 10 (Oct.), 1483–1498.
Griswold, W. and Notkin, D. 1993. Automated assistance for program restructuring. ACM
Trans. Softw. Eng. Methodol. 2, 3 (July), 228–269.
Hankin, C. and LeMetayer, D. 1994. A type-based framework for program analysis. In Pro-
ceedings of the 1st International Static Analysis Symposium. 380–394.
Horwitz, S., Reps, T., and Sagiv, M. 1995a. Demand interprocedural dataflow analysis. In Pro-
ceedings of the 3rd ACM SIGSOFT Symposium on the Foundations of Software Engineering.
ACM, New York, 104–115.
Horwitz, S., Reps, T., and Sagiv, M. 1995b. Demand interprocedural dataflow analysis. Tech.
Rep. 1283, Computer Science Dept., Univ. of Wisconsin, Madison, Wisc. Aug.
Hughes, J. and Launchbury, J. 1992. Reversing abstract interpretations. In Proceedings of
the 4th European Symposium on Programming. Lecture Notes in Computer Science, vol. 582.
Springer Verlag, Berlin, 269–286.
Jones, N. and Mycroft, A. 1986. Data flow analysis of applicative programs using minimal
function graphs. In Proceedings of the 13th ACM Symposium on Principles of Programming
Languages. ACM, New York, 296–306.
Kam, J. and Ullman, J. 1977. Monotone data flow analysis frameworks. Acta Inf. 7, 3 (July),
305–317.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.
1030 · Evelyn Duesterwald et al.
Knoop, J. and Steffen, B. 1992. The interprocedural coincidence theorem. In Proceedings of the
4th International Conference on Compiler Construction. Lecture Notes in Computer Science,
vol. 641. Springer Verlag, Berlin, 125–140.
Landi, W. and Ryder, B. 1992. A safe approximate algorithm for interprocedural pointer alias-
ing. In Proceedings of the SIGPLAN 1992 Conference on Programming Language Design and
Implementation. ACM, New York, 235–248.
Marlowe, T. and Ryder, B. 1990a. An efficient hybrid algorithm for incremental data flow
analysis. In Proceedings of the 17th ACM Symposium on Principles of Programming Lan-
guages. ACM, New York, 184–196.
Marlowe, T. and Ryder, B. 1990b. Properties of data flow frameworks, a unified model. Acta
Inf. 28, 2 (Dec.), 121–163.
Pollock, L. and Soffa, M. 1989. An incremental version of iterative data flow analysis. IEEE
Trans. Softw. Eng. 15, 12 (Dec.), 1537–1549.
Reps, T. 1994. Solving demand versions of interprocedural analysis problems. In Proceedings
of the 5th International Conference on Compiler Construction. Lecture Notes in Computer
Science, vol. 786. Springer Verlag, Berlin, 389–403.
Reps, T., Horwitz, S., and Sagiv, M. 1995. Precise interprocedural dataflow analysis via graph
reachability. In Proceedings of the 22nd ACM Symposium on Principles of Programming
Languages. ACM, New York, 49–61.
Reps, T., Teitelbaum, T., and Demers, A. 1983. Incremental context-dependent analysis for
language-based editors. ACM Trans. Program. Lang. Syst. 5, 3 (July), 449–477.
Rosen, B., Wegman, M., and Zadeck, F. 1988. Global value numbers and redundant computat-
ions. In the 15th ACM Symposium on Principles of Programming Languages. ACM, New York,
12–27.
Ryder, B. 1983. Incremental data flow analysis. In Proceedings of the 9th ACM Symposium on
Principles of Programming Languages. ACM, New York, 167–176.
Sagiv, M., Reps, T., and Horwitz, S. 1995. Precise interprocedural dataflow analysis with
applications to constant propagation. In FASE 95: Colloquium on Formal Approaches in
Software Engineering. Lecture Notes in Computer Science, vol. 915. Springer Verlag, Berlin,
651–665.
Sharir, M. and Pnueli, A. 1981. Two approaches to interprocedural data flow analysis. In
Program Flow Analysis: Theory and Applications, S. Muchnick and N. Jones, Eds. Prentice-
Hall, Englewood Cliffs, N.J., 189–234.
Steensgaard, B. 1996. Points-to analysis in almost linear time. In Proceedings of the 23rd ACM
Symposium on Principles of Programming Languages. ACM, New York, 32–41.
Stoyenko, A., Marlowe, T., Halang, W., and Younis, M. 1993. Enabling efficient schedulabil-
ity analysis through conditional linking and program transformations. Control Eng. Pract. 1, 1,
85–105.
Strom, R. and Yellin, D. 1993. Extending typestate checking using conditional liveness analysis.
IEEE Trans. Softw. Eng. 19, 5 (May), 478–485.
Weiser, M. 1984. Program slicing. IEEE Trans. Softw. Eng. Methodol. 10, 4 (July), 352–357.
Wilson, R. and Lam, M. 1995. Efficient context-sensitive pointer analysis for C programs. In
Proceedings of the SIGPLAN 1995 Conference on Programming Language Design and Imple-
mentation. ACM, New York, 1–12.
Zadeck, F. 1984. Incremental data flow analysis in a structured program editor. In Proceedings
of the 1984 SIGPLAN Symposium on Compiler Construction. ACM, New York, 132–143.
ACM Transactions on Programming Languages and Systems, Vol. 19, No. 6, November 1997.