Efficient Field-Sensitive Pointer Analysis of C
Efficient Field-Sensitive Pointer Analysis of C
DAVID J. PEARCE
Victoria University of Wellington, Wellington, New Zealand
and
PAUL H.J. KELLY and CHRIS HANKIN
Imperial College, London, United Kingdom
The subject of this article is flow- and context-insensitive pointer analysis. We present a novel ap-
proach for precisely modelling struct variables and indirect function calls. Our method emphasises
efficiency and simplicity and is based on a simple language of set constraints. We obtain an O(v4 )
bound on the time needed to solve a set of constraints from this language, where v is the number of
constraint variables. This gives, for the first time, some insight into the hardness of performing field-
sensitive pointer analysis of C. Furthermore, we experimentally evaluate the time versus precision 4
trade-off for our method by comparing against the field-insensitive equivalent. Our benchmark
suite consists of 11 common C programs ranging in size from 15,000 to 200,000 lines of code. Our
results indicate the field-sensitive analysis is more expensive to compute, but yields significantly
better precision. In addition, our technique has been integrated into the latest release (version 4.1)
of the GNU Compiler GCC. Finally, we identify several previously unknown issues with an alter-
native and less precise approach to modelling struct variables, known as field-based analysis.
Categories and Subject Descriptors: F.3.2 [Logics a Meanings of Programs]: Semantics of Pro-
gramm in Languages—program analysis
General Terms: Algorithms, Theory, Languages, Verification
Additional Key Words and Phrases: Set-constraints, pointer analysis
ACM Reference Format:
Pearce, D. J., Kelly, P. H. J., and Hankin, C. 2007. Efficient field-sensitive pointer analysis
of C. ACM Trans. Program. Lang. Syst. 30, 1, Article 4 (November 2007), 42 pages. DOI =
10.1145/1290520.1290524 http://doi.acm.org/10.1145/1290520.1290524
This work was supported by the UK Engineering and Physical Sciences Research Council (EPSRC)
through a Ph.D. studentship and grant number GR/R15566.
A preliminary version of this article was presented at the 2004 International Workshop on Program
Analysis for Software Tools and Engineering (PASTE’04) [Pearce et at. 2004a].
This article provides a more detailed examination of the subject material, including a larger exper-
imental study, more examples and a complexity analysis of our algorithm.
Authors’ addresses: D. J. Pearce, School of Mathematics, Statistics and Computer Science, Victoria
University of Wellington, Wellington, New Zealand; email: [email protected]; P. H. J.
Kelly and C. Hankin, Department of Commputing, 180 Queen’s Gate, South Kensington Campus,
Imperial College London, SW7 2AZ, United Kingdom; email: {p.kelly, c.hankin}@imperial.ac.uk.
Permission to make digital or hard copies of part or all of this work for personal or classroom use is
granted without fee provided that copies are not made or distributed for profit or direct commercial
advantage and that copies show this notice on the first page or initial screen of a display along
with the full citation. Copyrights for components of this work owned by others than ACM must be
honored. Abstracting with credit is permitted. To copy otherwise, to republish, to post on servers,
to redistribute to lists, or to use any component of this work in other works requires prior specific
permission and/or a fee. Permissions may be requested from Publications Dept., ACM, Inc., 2 Penn
Plaza, Suite 701, New York, NY 10121-0701 USA, fax +1 (212) 869-0481, or [email protected].
C 2007 ACM 0164-0925/2007/11-ART4 $5.00 DOI 10.1145/1290520.1290524 http://doi.acm.org/
10.1145/1290520.1290524
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:2 • D. J. Pearce et al.
1. INTRODUCTION
Pointer analysis is the problem of determining statically what the pointer vari-
ables in a program may target. Consider the following C program:
void foo() {
int *p,*q,a,b;
p = &a;
if(...) q = p;
else q = &b;
/* point P1 */
...
}
Here a pointer analysis concludes that, during any execution of the pro-
gram, the following will hold at P1: p points-to a and q points-to a or b. We write
p→ {a} ∧ q → {a, b} to state this formally, where {a} and {a, b} are the target sets
of p and q respectively. A solution is sound if the target set obtained for each
variable contains all its actual runtime targets. Thus, q → {a} is an unsound
solution for the above because q can also point to b. A solution is imprecise if
an inferred target set is larger than necessary and the superfluous targets are
called spurious. So, for the above example, an imprecise (but sound) solution
for p is p → {a, b}. In general, obtaining a perfectly precise and sound solution
is undecidable [Landi 1992b; Ramalingam 1994] and, in practice, even rela-
tively imprecise information is expensive. The applications of pointer analysis
are many, but perhaps the most important uses today are in Compilers and
Software Engineering.
1.1 Compilers
Modern superscalar and VLIW processors require sufficient Instruction Level
Parallelism (ILP) to reach peak utilisation. For this reason, exposing ILP
through instruction scheduling and register allocation is a crucial role of the
compiler. This task is complicated by the presence of instructions which in-
directly reference memory, since their data dependencies are not known. For
languages such as C/C++, this problem is particularly acute because pointer
variables (the main source of indirect memory references) can target practi-
cally every memory location without restriction. Therefore, to achieve maxi-
mum pipeline throughput, the compiler must rely on pointer analysis to dis-
ambiguate indirect memory references.
Automatic parallelization is another example of how the compiler can achieve
a speedup by exposing parallelism within the program. This type of transforma-
tion is performed at a higher level than those for ILP and, hence, larger gains are
possible. Indeed, much success has been achieved through automatic paralleli-
sation of numerical FORTRAN programs (e.g., Padua et al. [1980], Wolfe [1982],
Padua and Wolfe [1986], McKinley [1994], So et al. [1998]). However, similar
results have yet to be seen on programs written in C/C++ or Java. The main
reason for this is simply that, without precise information about pointer targets,
compilers for these languages cannot perform automatic parallelisation safely.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:3
Finally, pointer analysis finds many other important uses within the com-
piler. In particular, it often enables traditional optimisations (e.g. common
subexpression elimination) to be applied at places which would otherwise be
deemed unsafe.
1.3 Overview
The focus of this work is on developing efficient algorithms for field-sensitive
pointer analysis of C. The basic idea is that the precision of a pointer analysis
can be improved by distinguishing the fields of struct variables. Almost all
previous pointer analyses for C fail to do this properly for a variety of reasons:
either they treat fields of a struct variable as one (e.g., Fähndrich et al. [1998],
Hasti and Horwitz [1998], Hind and Pioli [1998], and Shapiro and Horwitz
[1997]); or, they distinguish fields, but lose the ability to differentiate separate
instances of a struct (e.g., Heintze and Tardieu [2001b], Andersen [1994], and
Ghiya et al. [2001]); or, they are unable to analyze all but the simplest of pro-
grams (e.g., Wilson and Lam [1995], Emami et al. [1994], and Landi [1992a]).
The main contributions of this work are:
(2) For the first time, an O(v4 ) bound on the time needed for field-sensitive
pointer analysis of C is obtained, where v is the number of nodes in the
constraint graph.
(3) The largest experimental investigation into the trade-offs in time and preci-
sion of field-insensitive and -sensitive analyses for C. Our benchmark suite
contains 11 common C programs, ranging in size from 15,000 to 200,000
lines of code.
(4) The identification of several previously unknown problems with a similar
approach, known as field-based pointer analysis, when applied to the anal-
ysis of C programs.
Our technique is not the first field-sensitive, constraint-based pointer analysis
for C—previous work has covered this (see, Yong et al. [1999] and Chandra
and Reps [1999a]). Our claim then, is that we go beyond their initial treatment
by considering efficient implementation and some important algorithmic issues
not adequately addressed before. In particular, our technique is designed specif-
ically to work with the points-to or solution sets implemented as integer sets.
This permits the use of data structures supporting efficient set union, such as
bit vectors or sorted arrays, which are necessary for scalable pointer analysis.
Furthermore, we are the first to obtain a complexity bound on the time needed
to solve this problem. In doing this, we find that the field-sensitive pointer anal-
ysis problem is fundamentally harder for C than for Java. Indeed, while several
previous field-sensitive pointer analyses for Java are known (see, e.g., Rountev
et al. [2001], Lhoták and Hendren [2003], and Whaley and Lam [2002]), these
turn out to be insufficient when analyzing C.
is fast but imprecise. Set constraints lie somewhere in the middle—they are
more precise than unification, but still capable of analysing programs with a
hundred thousand lines of code or more (e.g., Fähndrich et al. [1998], Heintze
and Tardieu [2001b], Lhoták and Hendren [2003], and Pearce et al. [2004b]).
We now present our set-constraint formulation of the pointer analysis prob-
lem, which is based upon the following language.
p ⊇ q | p ⊇ {q} | p ⊇ ∗q | ∗ p ⊇ q
Here, p and q are constraint variables and ∗ is the usual dereference operator.
We can think of each constraint variable as containing the set of variables it
points to. Thus, p ⊇ {x} states p may point to x. Those involving “∗” are referred
to as complex constraints. Those knowledgeable about set constraints will notice
a lack of general constructors and projection. Essentially, we have simplified
the traditional set-constraint system by specialising it to our problem domain.
To perform the analysis we first translate the source program into the set-
constraint language, by mapping each source variable to a unique constraint
variable and converting assignments to constraints. Then, we solve the con-
straints to find a least solution, which can be formalised as deriving all possible
facts under the inference system of Figure 1. An example program, along with
its translation and derived solution, is shown in Figure 2. Regarding the hard-
ness of this problem, it is wellknown that a set of t constraints can be solved in
O(t 3 ) time [Melski and Reps 1997; Heintze 1994]. For completeness, we provide
a similar proof here:
LEMMA 2.1. A set of t constraints can be solved in O(t 3 ) time under the
inference system of Figure 1.
PROOF. This result stems from two facts: firstly, the total number of trivial
constraints generated (i.e. those of the form p ⊇ {q}) is bounded by O(v2 ),
where v is the number of variables; secondly, at most O(v2 ) simple constraints
(i.e. those of the form p ⊇ q) are possible. From these it follows that, in the
worse case, the trans rule must be applied O(v3 ) times, since at most v trivial
constraints can be propagated across each simple constraint. Note, the deref
rules need only be applied O(tv) times, since each dereferenced variable has
O(v) targets and there are O(t) complex constraints. Since v is O(t), we obtain
an O(t 3 ) bound on worst-case solving time.
Fig. 2. An example illustrating how a simple program is translated into constraints and then
solved. The initial constraint set (shown above the line) is generated directly from the program
source. The inference rules of Figure 1 are then applied to obtain the complete derivation shown
below the line. We assume for simplicity that variable names are unique, which can easily be
achieved in practice by augmenting with the name of the enclosing method. Also, f • represents
the return value of f . The final solution for each variable is the smallest set satisfying the fully
derived constraint system. Thus, in the example, constraints 19 + 20 imply the smallest solution
for q is {x, y}. Therefore, our analysis concludes q → {x, y} holds at all points in the program. The
key point here is that we must derive all facts in order to make a sound conclusion. This is because
we cannot be sure that the solution for a given variable is complete until all facts are known. A
number of demand-driven systems have also been proposed (e.g. Fähndrich et al. [2000], Heintze
and Tardieu [2001a], Vivien and Rinard [2001], Sridharan et al. [2005], Saha and Ramakrishnan
[2005], and Kodumal and Aiken [2005]) which can often avoid deriving all facts (although not in
the worst case). For the purposes of this work, however, we take the more traditional approach of
assuming that all facts must be derived.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:7
In the graph, we have placed Sol(n) for each variable n below its correspond-
ing node. This initially contains x iff n ⊇ {x} is in the initial constraint set.
The dashed edge, known as a complex edge, represents the complex constraint
v ⊇ ∗t, with the circle indicating which end is dereferenced. At this point,
the constraints can be solved by repeatedly selecting a (noncomplex) edge
x → y and merging Sol(x) into Sol( y) until no change is observed. This is often
referred to as converging or reaching a fixpoint. During this process, new edges
arising from the complex constraints must be added to the graph. To see why,
recall that our example contained the complex constraint v ⊇ ∗t. We know that,
initially Sol(t) = ∅, but at some point during the analysis Sol(t) ⊇ { p}. Clearly
then, there is a dependence from p to v and this could not have been known
at graph construction time. Therefore, the edge p → v must be added as the
solution for t becomes available. Thus, solving the above constraint graph gives:
Thus, a new edge has been added because of the constraint v ⊇ ∗t. When
reading the constraint graph, we can determine what edges will be added by
examining the solution at the circle end of a complex edge: edges will be added
between the nodes in that solution and the node at the other end of the complex
edge.
A useful observation is that nodes in the same cycle (when ignoring complex
edges) always end up with the same solution [Fähndrich et al. 1998; Heintze
and Tardieu 2001b]. So, in our example, nodes v, f • and q have the same final
solution. Therefore, we can simplify the graph by collapsing them into a single
representative, giving:
The gain from this simplification comes from time saved by not propa-
gating targets between internal nodes. However, identifying these cycles is
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:8 • D. J. Pearce et al.
complicated by the dynamic nature of the graph as edges added during solving
may introduce new cycles. Therefore, to exploit all such simplification oppor-
tunities we must be able to efficiently determine when a newly added edge
introduces a cycle.
In a similar vein, a technique we refer to as subsumed node compaction
can also help simplify the constraint graph. The idea, originally suggested by
Rountev and Chandra [2000], is illustrated by the following:
Here, x, y, z must have the same solution and, hence, can be collapsed into
one. Note, we assume here that y and z have not had their address taken
and are not targeted by a constraint such as y ⊇ ∗ p. Rountev and Chandra
[2000] provided a linear time algorithm for detecting such opportunities in
the constraint graph. Note, unlike with cycle detection, new opportunities for
applying this optimisation cannot arise during the analysis.
The approach to solving set constraints we have presented is sometimes
called Standard Form (SF) [Aiken and Wimmers 1993]. An alternative to this,
known as Inductive Form (IF), is often described in the literature as a sparser
and more efficient representation [Su et al. 2000; Rountev et al. 2001]. In gen-
eral, we find there is little evidence to support this claim that IF is more efficient
than SF: the only experimental study is Fähndrich et al. [1998]. This appears to
show that inductive form has an advantage over standard form. Unfortunately,
this result remains inconclusive because the cycle detection algorithm used
did not identify and collapse all cycles for efficiency reasons. Thus, it happens
that under inductive form the algorithm consistently collapses more cycles,
giving it an apparent advantage. However, we have since developed more effi-
cient cycle detection algorithms which can collapse all cycles under standard
form, thereby eliminating this distinction between them [Pearce et al. 2003,
2004b]. Therefore, we cannot draw concrete conclusions about the relative ef-
ficiency of either approach and, in general, equal success has been achieved
(e.g. Heintze and Tardieu [2001b] and Lhoták and Hendren [2003] versus
Rountev et al. [2001] and Fähndrich et al. [1998]). For the purposes of this
article, we are concerned only with Standard Form and, in the remainder, it is
assumed.
3.1 Field-Sensitivity
So far, we have not indicated how struct variables should be handled by
our analysis and there are three approaches: field-insensitive, where field
information is discarded by modelling each aggregate with a single constraint
variable; field-based, where one constraint variable models all instances of a
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:9
field; and finally, field-sensitive, where a unique variable models each field of
an aggregate. The following example aims to clarify this:
typedef struct { int *f1; int *f2; } aggr;
int *c,d,e,f;
a.f1 = &d; a ⊇ {d } f 1 ⊇ {d } a f 1 ⊇ {d }
a.f2 = &f; a ⊇{f} f2⊇{f} af 2 ⊇ { f }
b.f1 = &e; b ⊇ {e} f 1 ⊇ {e} b f 1 ⊇ {e}
c = a.f1; c⊇a c⊇ f1 c ⊇ af 1
Conclude c → {d , f } c → {d , e} c → {d }
∗ p(w1 , . . . , wn )
p ⊇ {lamv (v1 , . . . , vn )}
[func]
∀1 ≤ i ≤ n. vi ⊇ wi
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:10 • D. J. Pearce et al.
Here, we see that constraints are introduced on-the-fly between the actual
parameters and their caller values. The main issue here is the implementation
of lam. Certainly, we don’t wish to sacrifice the ability to implement solutions
as integer sets. One approach is to place the lam constructs into a table, so they
are identified by index. Thus, if care is taken to avoid clashes with the variable
identifiers, the two element types can co-exist in the same solution set. However,
this is inelegant as we must litter our algorithm with special type checks. For
example, when dealing with ∗ p ⊇ q, we must check for lam values in Sol( p).
In the next section, we present a simple and elegant solution for dealing with
indirect function calls which forms part of our mechanism for field-sensitivity.
For each variable, end(·) determines where the enclosing block of consec-
utively allocated variables ends. Without constraints on end(·), the inference
rules of Figure 3 would derive a ⊇ b from ∗( p+1) ⊇ b above as idx(a) = id x(q)+1.
While it remains unclear how best to model this, it certainly does not make sense
to propagate information into the parameters of g(). Therefore, we provide
end(·) information to prevent this. Furthermore, while the above example re-
sults from programming error, similar examples can be constructed which arise
solely from the inaccuracies inherent in flow- and context-insensitive analysis.
Note, attempting to read a return value where none exists is also prevented by
the end(·) information. This is because the return slot(s) are considered part
of the block of consecutively allocated variables for a function definition (see
Figure 4 for more on return slots).
Finally, the C language supports functions with variable length argument
lists (varags). These can be supported quite easily (albeit conservatively) by
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:12 • D. J. Pearce et al.
Fig. 4. This example illustrates how the analysis deals with parameter passing through function
pointers. Constraint 8 is the key as &f is translated into q—the first parameter of f—allowing
us access to r through the offset notation in (11) above. In fact, return values can be modelled
using this mechanism if we allocate the corresponding variable (e.g. f • ) to the slot following the
last parameter. Thus, we can always determine the offset of the return value from the type of the
function pointer being dereferenced. Note, allocating the return slot before the parameter slot(s)
causes problems when the function is incorrectly typed. This commonly occurs in C, when a function
has multiple prototypes of which some incorrectly assign a void return type. The problem is that
code that uses the function through an incorrect prototype will still compile and run correctly
(so long as the return value is not needed). However, our analysis would fail (if return slot was
allocated first) for calls to the function in the presence of an invalid prototype. This is because the
first argument of that call will be written into the first parameter slot, which is actually the return
value! Of course, this can be overcome by simply allocating a return slot for all functions regardless
of their return type; however, this will not work for the field-sensitive formulation of Section 4.1,
where multiple slots may be required for functions which return aggregates by value.
providing a special constraint variable at the end of the parameter block (before
the return value) for a function to capture those extra parameters passed to that
function.
et al. 2001; Whaley and Lam 2002; Lhoták and Hendren 2003]. However, Java
presents a simpler problem than C in this respect, since it does not permit the
address of a field to be taken. Indeed, it turns out the language of the previous
section is sufficient for field-sensitive analysis of Java. This works by using
blocks of constraint variables, as we did for functions, to represent aggregates.
For example, in the following, a block of two constraint variables (one for each
field) is used to represent “aggr a”:
Here, the address of a is modelled by the address of its first field. In this way,
the fields of a can be accessed via b without problem.
To analyze C, however, we must also be able to translate “q=&(b->f2);.”
This is a problem since we want to load the index of a. f 2 into Sol(q), but
there is no mechanism for this. Therefore, we extend the language to permit
the translation: q ⊇ b + 1, meaning load Sol(b) into a temporary, add 1 to
each element and merge into Sol(q). Note the inference rule in Figure 5. This
form can be represented by turning the constraint graph into a weighted
k
multigraph, where weight determines increment—so p ⊇ q + k gives q → p.
One difficulty with this new form is the Positive Weight Cycle (PWC) problem.
For example:
This is legal and well-defined C code. Here, the cycle arises from flow-
insensitivity, but other forms of imprecision can also cause them. Figure 6
provides a (somewhat contrived) example of a positive-weight cycle arising
from imprecision in the heap model. So, it seems that any formulation of a
field-sensitive analysis for C will necessarily have to deal with positive weight
cycles. This is supported by the work of Chandra and Reps [1999a, 1999b] who
encounter the same issue with a similar field-sensitive analysis. In general, the
problem is that cycles describe infinite derivations. To overcome this, we use
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:14 • D. J. Pearce et al.
Fig. 6. An example illustrating how a positive-weight cycle can arise from imprecision in the heap
model. We have included both the constraint derivation and the corresponding graph representation
for clarity. The key observation is that a malloc wrapper (i.e., f(int)) is used to allocate storage. This
means that, since a static heap model is being used where all objects returned by a particular call to
malloc are represented by one constraint variable, the local variables p and q end up containing the
same target constraint variable (i.e., HEAP0). Therefore, HEAP0 actually represents two distinct
heap objects in the program and we have carefully used this to construct a positive-weight cycle
involving HEAP0, a and i. A static heap model is the most common heap model since the alternative,
a dynamic heap model, can be very costly [Nystrom et al. 2004b].
loops. The following example demonstrates this, where unlabelled edges are
assumed to have zero weight:
Here, only one constraint variable is created to model every object allocated
by f(int) and this legitimately ends up representing multiple objects of dif-
ferent type (i.e. HEAP0 represents an aggr1 object and an array of ints). In-
deed, it is often impossible to determine the types that a heap variable will
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:16 • D. J. Pearce et al.
Fig. 7. Algorithm PW, a worklist-style algorithm supporting field-sensitive pointer analysis with
function pointers. The algorithm assumes that Sol( p) has been initialised with all trivial constraints
of the form p ⊇ {q}. The set C(n) contains all complex constraints involving “∗(n+k).” Notice that the
code for collapsing cycles has been omitted for brevity. This simply collapses all cycles which have
a zero-weight path between their nodes (see Section 4.1 for more on this); it has no other effect on
the constraint graph. The algorithm uses a topological iteration strategy to improve performance.
Computing the topological sort necessary for this can often be done for free by the cycle detector.
Finally, the set (n) holds the change in Sol(n) since the last time n was visited. This ensures a
node z is a member of δ for at most one visit of each node.
by removing a node n and visiting it. In doing this, any (immediate) successor of
n whose solution has changed is placed back onto the worklist. This continues
until the worklist is empty.
A well-known complication is that the order in which nodes are visited (i.e.
taken off the worklist) can greatly affect performance [Horwitz et al. 1987;
Burke 1990; Bourdoncle 1993b; Chen and Harrison 1994; Schön 1995; Fecht
and Seidl 1996; Nielson et al. 1999; Pearce et al. 2004b; Pearce 2005]. A good
choice is to visit nodes in topological order (also known as reverse postorder).
For some types of program analysis this approach is considered optimal (see
Horwitz et al. [1987] and Bourdoncle [1993b]). Unfortunately, this is not the case
for the pointer analysis problem being studied here [Pearce 2005]. Nevertheless,
it remains an excellent choice, especially considering that no better alternative
is known. Therefore, this strategy is used in PW although, to implement this
efficiently, a worklist in the true sense of the word is not actually used. Instead,
all nodes are sorted topologically at the beginning of each round and then visited
in sequence. For each node n, the algorithm first determines whether its solution
has changed since it was last visited. This is done by checking whether (n),
which records the change in n’s solution between visits, is empty. If not, the
algorithm processes all complex constraints involving ∗(n + k) and propagates
(n) to all successors. Note, (n) is loaded into δ to ensure that (n) can be
cleared before propagation. This is necessary to properly deal with (positive
weight) self loops.
The use of (·) is crucial to obtaining an optimal complexity bound on the
run-time of PW, because it ensures a node z is a member of δ for at most one
visit of each node. This technique has been previously referred to in the litera-
ture as difference propagation [Fecht and Seidl 1998; Pearce et al. 2004b] and
incremental sets [Lhoták and Hendren 2003; Berndl et al. 2003]. Another im-
portant point about PW is that we have omitted details of which cycle detection
algorithm to use. The standard approach is to employ Tarjan’s well-known algo-
rithm for detecting strongly connected components (see Tarjan [1972], Nuutila
and Soisalon-Soininen [1994], and Gabow [2000]). This algorithm always visits
every node and traverses every edge when called. However, as with difference
propagation, it is only necessary to examine those parts of the graph which have
changed since the last round. Therefore, several dynamic cycle detection algo-
rithms have been proposed that aim to perform an amount of work proportional
to the size of the graph change (see, e.g., Shmueli [1983] and Fähndrich et al.
[1998]). In fact, we have elsewhere developed the fastest known solutions for
this problem [Pearce et al. 2004b; Pearce 2005]. These came out of our work on
dynamic topological sort (see Pearce and Kelly [2004, 2006]) and, it turns out,
the two problems are closely related. In spite of the advantages of these more
advanced algorithms we choose, in the experimental comparison which follows,
to use Tarjan’s algorithm for detecting cycles. The reason for this is to simplify
our experimental results and to aid comparisons with previous works (which
did not have such cycle detection algorithms available to them). In fact, we
find in practice that Tarjan’s algorithm is surprisingly competitive (compared
with these alternatives) and experimental data looking at this can be found in
Pearce [2005].
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:19
time and, thus, we implement (n) with an array (note, this can be unsorted
since the algorithm guarantees no element is added to (n) more than once).
Also, δ follows the implementation of (n).
An interesting question is what a worst-case input for algorithm PW looks
like and the following provides a simple example:
LEMMA 4.2. A set of t constraints can be solved in O(t 3 ) time under the
inference system of Figures 1, 3 and 5.
The O(t 3 ) result tells us that the underlying hardness of constraint solving
is the same as for a standard field-insensitive analysis (recall from the end
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:21
5. EXPERIMENTAL STUDY
In this section, we provide empirical data over a range of benchmarks comparing
our field-insensitive and -sensitive systems. Table I provides information on
our benchmark suite. With two exceptions, all are available under open source
licenses and can be obtained online (e.g., http://www.gnu.org). Note that cc1
is the C compiler component of gcc, while named is distributed in the BIND
package. While both 147.vortex and 126.gcc are not available under open
source licences, they form part of the SPEC95 benchmark suite and have been
included to aid comparison with previous work.
The SUIF 2.0 research compiler from Stanford [SUIF2] was deployed as
the frontend for generating constraint sets. In all cases, we were able to com-
pile the benchmarks with only superficial modifications, such as adding extra
“#include” directives for missing standard library headers or updating function
prototypes with the correct return type. The constraint generator operates on
the full C language and a few points must be made about this:
— Heap model. The static model discussed briefly in Section 4.1 was used. Recall
that this uses a single constraint variable to represent all heap objects created
from a particular call to malloc and other related heap allocation functions.
— Arrays. These are treated by ignoring the index expression and, hence, rep-
resenting all elements of an array with one constraint variable.
— String Constants. A single constraint variable was used to represent all string
constants. In other words, we consider the right hand side of p="foo" and
q="bar" as referring to the same object.
— External Library Functions. These, almost entirely, came from the GNU C
library and were modelled using hand crafted summary functions, capturing
only aspects relevant to pointer analysis.
— Variable Length Argument Lists. These were dealt with using the technique
outlined in Section 4, where a single constraint variable is used to represent
every possible parameter in the vararg list of a method.
Our experimental framework contained a direct implementation of algorithm
PW and its field-insensitive variant, henceforth referred to as PWFI. The latter
was almost identical to PW, except those parts relating to field-sensitivity were
removed. Furthermore, our constraint generator produced field-sensitive con-
straints (see Figures 3–5) for PW, but field-insensitive constraints (see Figure 1)
for PWFI. Cycle detection and subsumed node compaction (recall Section 3)
were always applied to the initial constraint set, although projection merging
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:22 • D. J. Pearce et al.
(see Su et al. [2000]) was not as we found this degraded performance.1 Note, our
reported measurements do not include the time needed for generating the ini-
tial constraint sets and performing these simple optimizations. To implement
Sol(n) and (n), both implementations used bit vectors and arrays respectively.
They also employed the hash-based duplicate set compaction scheme of Heintze
and Tardieu [2001b] to ensure identical solution sets are shared. This turns out
to be necessary for solving the largest benchmark (ghostscript), which without
compaction needs well over 1GB of memory to complete. To validate our solvers
we manually inspected the output produced on a test suite of small programs
and also by ensuring that each algorithm produced the same output. The full
source code for our solving algorithms and constraint generator is available on-
line at http://www.mcs.vuw.ac.nz/∼djp. Finally, our experimental machine was
a 900-Mhz Athlon with 1GB of main memory, running Mandrake 10.2. The
executables were compiled using gcc 3.4.3, with compiler switches “-O3” and
“-fomit-frame-pointer.” Timing was performed using the gettimeofday func-
tion (which offers microsecond resolution on x86 Linux platforms) and averaged
over ten runs—this was sufficient to generate data with a variation coefficient
of ≤ 0.05, indicating low variance between runs. To reduce interference, exper-
iments were performed with all nonessential system daemons/services (e.g. X
windows, crond) disabled and no other user-level programs running. The code
itself was in C++, making extensive use of the Standard Template Library and
Boost Library (version 1.30.2).
Table II provides a comparison of some interesting differences between the
constraint sets generated for the field-sensitive analysis and those used for
the insensitive analysis. In particular, we see that the sensitive analysis al-
ways uses more constraint variables, which is expected as each field is now
1 Projection merging was originally designed for use with inductive form, which we do not use. Thus,
we conclude that the technique is simply not suited for use with standard form.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:23
Table II. Data comparing the field-insensitive and -sensitive constraint sets.
Constraint Variables
Total Addr Heap # PWC
uucp 3,319 / 5,208 199 / 1,929 20 / 940 1
make 4,786 / 6,933 259 / 2,396 69 / 1,794 0
gawk 7,303 / 10,140 333 / 3,137 96 / 2,496 0
147.vortex 11,929 / 16,019 2,201 / 5,943 21 / 2,310 0
bash 10,852 / 13,130 699 / 2,881 36 / 936 0
sendmail 10,276 / 12,428 682 / 2,726 13 / 546 1
emacs 17,972 / 38,181 3,844 / 23,618 172 / 12,900 0
126.gcc 27,895 / 50,654 1,113 / 23,774 231 / 22,407 0
cc1 43,888 / 76,383 1,492 / 33,856 258 / 31,992 1
named 34,778 / 47,230 4,278 / 15,764 24 / 1,704 1
gs 65,338 / 105,114 8,754 / 34,683 18 / 2,412 2
The breakdown of constraint variables shows the total count, the number of address-
taken and the number modelling the heap. In all cases, the two values given apply to
the insensitive and sensitive constraint sets (in that order). #PWC reports the number
of Positive Weight Cycles in the final (i.e., solved) constraint graph.
modelled with a separate variable. Another interesting point is that named and
ghostscript have noticeably fewer constraint variables modelling the heap
than others of similar size. In fact, ghostscript uses a malloc wrapper (a custom
memory allocator which wraps malloc) called png_malloc. This explains the
small heap variable count, since it implies there will be fewer direct invoca-
tions of malloc. The named benchmark does something similar, in that it uses
a memory pool (based on methods such as isc_buffer_allocate) to allocate
memory in many cases. Finally, the “# PWC” metric shows the number of pos-
itive weight cycles in the final graph. It is important to realise that this count
may be higher during solving, because some cycles could end up being combined
in the final graph. Note that, if at least one positive weight cycle is created then
“# PWC” will report a count greater than zero. This is because cycle detec-
tion cannot eliminate positive weight cycles—it can only reduce them to self
loops.
Figure 8 looks at the effect of field-sensitivity on solving time. It shows clearly
that the field-sensitive analysis is more expensive to compute. Furthermore,
with the exception of emacs, those benchmarks which have positive weight cy-
cles are significantly more expensive, relatively speaking, than the others.
Comments. Figure 9 gives some indication why the field-sensitive analysis
is more costly as it shows a reasonably clear correlation between performance
and visit count. Recall that, for the field-sensitive analysis, there are generally
more constraint variables (recall Table II) and these correspond to nodes
in the graph which must be visited. However, Figure 10 indicates that, in
many cases, the cost of performing a set union is actually lower. This suggests
that the increased precision offered by field-sensitivity could actually lead to
improved performance. Indeed, this idea is not new and others have made
such observations before (see, e.g., Lhoták and Hendren [2003], Rountev
et al. [2001], Whaley and Lam [2002], and Heintze and Tardieu [2001b]). An
interesting point here is that (with the exception of gs) average set size is
always lower for benchmarks which don’t have positive weight cycles, whilst it
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:24 • D. J. Pearce et al.
Fig. 8. A chart of our experimental data investigating the effect of field-sensitivity on the perfor-
mance of algorithm PW. This is given relative to the field-insensitive implementation (PWFI) to
allow data for different benchmarks to be shown on the same chart. Below each benchmark, the
absolute time taken by PWFI is shown for reference (Table III provides absolute values for both
implementations). Benchmarks containing positive weight cycles are marked with an asterisk.
Table III. Actual data values for the four metrics shown in Figures 8, 9, 10 and 11.
Benchmark Time Visit Avg Set Avg Deref
(s) Count Size Size
uucp PWFI 0.031 937.0 9.6 298.1
PW 0.17 5,916.0 15.6 125.1
make PWFI 0.075 1,271.0 22.2 341.2
PW 0.156 1,785.0 20.4 17.44
gawk PWFI 0.12 2,080.0 43.0 643.4
PW 0.26 2,796.0 32.3 22.72
147.vortex PWFI 0.31 5,412.0 19.2 384.1
PW 0.36 5,331.0 4.78 8.6
bash PWFI 0.5 3,479.0 130.0 544.0
PW 0.5 3,878.0 61.5 86.5
sendmail PWFI 0.31 2,959.0 90.7 498.8
PW 0.77 10,650.0 112.0 194.7
emacs PWFI 0.51 4,171.0 13.7 79.34
PW 2.1 6,904.0 5.18 5.41
126.gcc PWFI 1.18 12,972.0 17.2 1,044.0
PW 0.61 4,352.0 1.68 1.36
cc1 PWFI 7.56 23,723.0 47.0 3,069.0
PW 42.2 154,051.0 555.0 1,572.0
named PWFI 28.42 31,118.0 644.0 2,869.0
PW 95.4 61,973.0 1,590.0 2,172.0
gs PWFI 251.0 131,460.0 350.0 8,026.0
PW 2,130.0 186,757.0 29.8 7,387.0
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:25
Fig. 9. A chart of our experimental data investigating the effect of field-sensitivity on visit count for
algorithm PW. It shows the number of nodes visited by the field-insensitive (PWFI) and -sensitive
(PW) algorithms. This is given relative to PWFI implementation and, below each benchmark, the
exact number of nodes visited by PWFI is provided (Table III provides absolute values for both
implementations). All experimental parameters are the same as for Figure 8 and benchmarks
containing positive weight cycles are marked with an asterisk.
Fig. 10. A chart of our experimental data investigating the effect of field-sensitivity on average
set size for algorithm PW. It shows the average set size across all set union operations for the
field-insensitive (PWFI) and -sensitive (PW) implementations. This is given relative to the PWFI
implementation and, below each benchmark, exact values for PWFI are provided (Table III provides
absolute values for both implementations). All experimental parameters are the same as for Figure
8 and benchmarks containing positive weight cycles are marked with an asterisk.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:26 • D. J. Pearce et al.
Fig. 11. A chart of our experimental data investigating the effect of field-sensitivity on the Average
Deref metric. This is shown for the field-insensitive (PWFI) and -sensitive (PW) implementations
and is given relative to PW. Below each benchmark, the exact figures for PW are given for reference
(Table III provides absolute values for both implementations). All experimental parameters remain
the same as for Figure 8 and benchmarks containing positive weight cycles are marked with an
asterisk.
is always higher on those that do. This suggests that positive weight cycles are
a major expense and that eliminating them would be beneficial.
Figure 11 looks at the effect on precision of field-sensitivity but, before any
discussion, we must first understand exactly what is being shown. The chart
reports the number of possible targets for a dereference site, averaged across
all dereference sites. This is called the “Average Deref ” metric. It gives a more
useful measure of precision, compared with the average set size of all pointer
variables, since only dereference sites are of interest to client analyses. To facil-
itate a meaningful comparison (in terms of precision) between the sensitive and
insensitive analyses, we must normalize the value. To understand why, consider
a pointer p targeting the first field of variable “struct {int f1; int f2;} a.”
For the insensitive analysis, we have the solution p ⊇ {a}, whilst the sensitive
analysis gives p ⊇ {a. f 1}. Thus, the two appear to offer the same level of pre-
cision, since their solution sets are of equal size. However, this is misleading
because the insensitive analysis actually concludes that p may point to any
field of a. Therefore, we normalise the insensitive solution by counting each
aggregate by the number of fields it contains. In other words, we count p ⊇ {a}
as though it was p⊇ {a. f 1, a. f 2}.
The main observation from Figure 11 is that field-sensitivity gives more
precise results across the board. However, we again find there are significant
differences between those benchmarks which have positive weight cycles and
those which do not. In particular, those without always show a significantly
greater increase in precision from field-sensitivity. Figures 12 and 13 break up
the Average Deref metric to show its distribution for each benchmark. They
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:27
Fig. 12. Charts of our experimental data showing a breakdown of the average points-to set size
at dereference sites for each benchmark. Each bar indicates how many dereference sites (as a
percentage of the total) have points-to sets of size X , where X lies between the left boundary
and up to, but not including, the right boundary. For example, the second bar in each chart gives
the number of dereference sites with points-to sets containing exactly one element. Benchmarks
containing positive weight cycles are marked with an asterisk. Note, zero sized sets arise from
an artifact of our linker (see the discussion for more on this). The exact percentages for both
implementations are provided in Table IV.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:28 • D. J. Pearce et al.
Table IV. Actual percentages for the precision data shown in Figures 12 and 13.
Dereference Sites (% of normalised total)
0 1 2 3 10 100 1000+
uucp PWFI 18.0 18.0 2.5 1.5 2.5 58.0 0.0
PW 15.0 16.0 7.7 5.1 0.22 56.0 0.0
make PWFI 5.5 7.0 0.74 5.0 15.0 67.0 0.0
PW 5.7 19.0 6.3 4.1 65.0 0.0 0.0
gawk PWFI 6.2 7.7 6.3 4.2 26.0 0.92 49.0
PW 7.0 23.0 21.0 6.9 42.0 0.0 0.0
147.vortex PWFI 12.0 14.0 1.2 6.8 7.9 58.0 0.61
PW 13.0 19.0 2.6 56.0 8.6 1.0 0.0
bash PWFI 5.5 7.6 2.7 3.3 19.0 62.0 0.0
PW 5.5 24.0 6.1 2.9 5.7 56.0 0.0
sendmail PWFI 4.5 16.0 2.0 4.7 8.7 64.0 0.0
PW 4.4 22.0 3.7 4.3 4.4 61.0 0.0
emacs PWFI 24.0 17.0 27.0 2.7 7.9 21.0 0.53
PW 25.0 25.0 32.0 10.0 7.6 0.37 0.03
126.gcc PWFI 6.9 16.0 1.7 2.2 9.8 0.89 62.0
PW 33.0 28.0 25.0 13.0 0.44 0.004 0.0
cc1 PWFI 5.6 11.0 0.68 2.3 1.8 10.0 68.0
PW 6.6 19.0 3.5 4.4 0.5 0.0082 66.0
named PWFI 3.4 3.6 3.9 28.0 1.4 1.2 58.0
PW 5.1 8.4 1.3 28.0 2.2 2.7 52.0
gs PWFI 33.0 2.7 1.2 3.1 3.7 0.82 55.0
PW 35.0 7.2 3.0 2.1 0.25 0.3 52.0
Each column indicates how many dereference sites (as a percentage of the total) have points-to sets of size X ,
where X lies between the column’s boundary and up to, but not including, the next boundary.
Fig. 13. More charts of our experimental data showing a breakdown of the average points-to set
size at dereference sites for each benchmark. Each bar indicates how many dereference sites (as
a percentage of the total) have points-to sets of size X , where X lies between the left boundary
and up to, but not including, the right boundary. For example, the second bar in each chart gives
the number of dereference sites with points-to sets containing exactly one element. Benchmarks
containing positive weight cycles are marked with an asterisk. Note, zero sized sets arise from
an artifact of our linker (see the discussion for more on this). The exact percentages for both
implementations are provided in Table IV.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:30 • D. J. Pearce et al.
6. RELATED WORK
Flow- and context-insensitive pointer analysis has been studied extensively
in the literature (see, e.g., Pearce et al. [2004b], Lhoták and Hendren [2003],
Fähndrich et al. [1998], Heintze and Tardieu [2001b], Rountev and Chandra
[2000], Andersen [1994], Steensgaard [1996], and Das [2000]). These works
can, for the most part, be placed into two camps: extensions of either Andersen’s
[1994] or Steensgaard’s [1996] algorithm. The former use inclusion constraints
(i.e., set-constraints) and are more precise but slower, while the latter adopt
unification systems and sacrifice precision in favour of speed. Thus, new devel-
opments tend to be focused either on speeding up Andersen’s algorithm (e.g.,
Heintze and Tardieu [2001b], Fähndrich et al. [1998], Rountev and Chandra
[2000], and Pearce et al. [2004b]) or on improving the precision of Steensgaard’s
(e.g., Das [2000], Das et al. [2001], and Liang and Harrold [1999]). Further-
more, there have been numerous studies on the relative precision of these two
approaches (see, e.g., Liang et al. [2001], Foster et al. [2000], Hind and Pioli
[2000], Shapiro and Horwitz [1997], Das [2000], and Das et al. [2001], with the
results confirming that set-constraints offer useful improvements in precision.
Much work has also been done on context-sensitive analyses which, unlike the
analysis studied in this article, consider a function separately for each calling
context (see, e.g., Wilson [1997], Chatterjee et al. [1999], Cheng and Hwu
[2000], Foster et al. [2000], Fähndrich et al. [2000], Das et al. [2001], Liang
et al. [2001], Whaley and Lam [2004], and Nystrom et al. [2004b]). While this
can greatly improve precision, it is equivalent to fully inlining each function
before performing the analysis and is, generally speaking, impractical for
analysing large programs [Whaley and Lam 2004; Nystrom et al. 2004a]. Like-
wise, previous work has explored flow-sensitive analyses which, by taking into
account the order of program statements, can also increase precision (see, e.g.,
Hasti and Horwitz [1998], Hind and Pioli [1998], and Hind et al. [1999]). We
refer the reader to Hind [2001] for a more thorough survey of pointer analysis.
Several studies have looked at the relative merits of the three approaches to
modelling aggregates, with the conclusion that field-sensitive analyses are con-
siderably more precise than their field-based or field-insensitive counterparts
[Yong et al. 1999; Diwan et al. 1998; Liang et al. 2001; Rountev et al. 2001;
Lhoták and Hendren 2003]. However, it is important to realise that, since the
problem differs between Java and C, it does not necessarily make sense to com-
pare studies of Java with those for C. For example, previous results show that
of the three, field-sensitive analyses are generally fastest when analyzing Java
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:31
[Lhoták and Hendren 2003; Rountev et al. 2001; Whaley and Lam 2002], but
slowest when analyzing C [Yong et al. 1999; Pearce et al. 2004a]. The main rea-
son for this is that in C we can take the address of a field, whereas in Java we
cannot. Thus, for C, using a field-sensitive analysis increases the number of po-
tential pointer targets (often dramatically), leading to an increase in average set
size (as shown in Section 5). For Java, however, the number of potential targets
cannot go up with field-sensitivity—thus, average set size can only go down.
For the analysis of C programs, there is little data available on the relative
precision of the three methods. In Yong et al. [1999], a field-sensitive analysis
is shown to offer twice the precision of an insensitive analysis, although their
benchmarks were much smaller than those used in this work. Nevertheless, our
results from Section 5 do concur with this to some extent, although they also
indicate the pay-off decreases with benchmark size. For field-based analyses,
Heintze and Tardieu [2001b] present data that appears to show a field-based
analysis gives more precise results compared with an insensitive one. However,
their data is described as “preliminary” and, in particular, we find their metric
for comparison unsatisfactory because it has not been properly normalized (see
the discussion of Figure 11 for more on this). Thus, the only real conclusion we
draw from this work is that field-based analyses are faster than their insensitive
counterparts. Unfortunately, we must also caution that we believe field-based
analysis of C may be unsafe and this is discussed further in Section 6.1.
For Java, several studies show field-sensitive analyses are faster and more
precise than the alternatives [Rountev et al. 2001; Lhoták and Hendren 2003;
Whaley and Lam 2002]. As mentioned already, average set size might be one ex-
planation for this. Another might be the proliferation of indirect function calls
(due to virtual functions) in Java. This is relevant because a less precise anal-
ysis will identify more targets for an indirect call, thus introducing more con-
straints. Furthermore, these constraints are considerably more expensive than
those for dereferencing a data pointer, since they cause nontrivial value flow.
Unfortunately, the overall picture is complicated by a study showing little dif-
ference in precision between a field-based and field-sensitive analysis, with the
latter also running slower [Liang et al. 2001]. They argue that this should be
expected from the strong encapsulation supported by Java. Indeed, this has
some merit, if we consider that most fields in Java programs are read/written
through get/set methods. Thus, context-insensitivity combines all distinct ac-
cesses to a particular field, yielding the same effect as the field-based approach.
An example is given in Figure 14 and it seems that some simple strategies (such
as inlining these get/set methods) would be very beneficial here. In fact, at least
one analysis attempts something along these lines [Milanova et al. 2002], with
promising results. Still, it seems unclear why other studies (e.g., Lhoták and
Hendren [2003]) of field-sensitivity have not encountered this problem and we
can only speculate that they use some hidden technique to overcome it.
We now return to consider the relationship between our system and the
comparable previous works. The most important of these, due to Yong et al.
[1999], is a framework covering a spectrum of analyses from complete field-
insensitivity through various levels of field-sensitivity. The main difference
from our work is the approach taken to modelling field-addresses where,
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:32 • D. J. Pearce et al.
Fig. 14. Illustrating how get/set methods affect field-sensitivity. Notice that the this variable
is passed explicitly to each member function, reflecting what actually happens in practice. By
combining information at function boundaries we are losing the advantages of field-sensitivity.
(4) p ⊇ a. f 2 (fdref 1 , 2 + 3)
(5) p ⊇ {c} (trans, 1 + 4)
Here, the || operator can be thought of essentially as string concatenation,
such that {a}||b ⇒ a.b and (∗a)||b ⇒ c.b, if a ⊇ {c}. Hence, the corresponding
inference rules are:
q ⊇ (∗ p)|| f p ⊇ {a} (∗ p)|| f ⊇ q p ⊇ {a}
[fdref 1 ] q ⊇ a. f [fdref 2 ] a. f ⊇ q
Thus, we see that p ⊇ (∗b)||x replaces p ⊇ ∗(b+ k) from our system. While
this difference appears trivial, there are hidden complications in dealing with
certain uses of casting—even when such uses are defined as portable within
the ISO/ANSI C standard. The relevant points from the standard can be sum-
marised as follows:
(1) A pointer to a structure also points to the first field of that structure [ISO90
1990, 6.5.2.1]. As a result, the first field of a structure must be at offset 0.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:33
(2) Accessing a union member after the last store was to a different member
gives implementation-defined behaviour [ISO90 1990, 6.3.2.3]. Suppose we
have “union{int a;float b;} x.” Now, we can safely write and then read
x.a, but we cannot safely write to x.b and then read x.a.
(3) As an exception to the above, if a union contains several structures whose
initial members have compatible types, then it is permitted to access the
common initial sequence of any of them [ISO90 1990, 6.3.2.3]. Note, it is
sufficient for us to simply take compatible types to mean identical types,
although the actual definition is more subtle. To understand the meaning
of this point, suppose we have “union {T1 a;T2; b} x”, where T1 and T2
are two struct’s whose first N members have identical types. Furthermore,
suppose we assign to x.a. At this point, we may read any of the first N
members of x.b and, as expected, they will have the same values as the
first N members of x.a. This contrasts with the previous rule, which stated
we may only read from x.a.
The first point above is fairly straightforward and the following exam-
ple demonstrates that the string concatenation approach cannot model it
correctly:
Fig. 15. This example illustrates an issue with the string concatenation approach to field-
sensitivity. The problem arises because the type of “a” determines which field names are used
in the concatenation, leading to constraints involving non-existing variables b. f 1 and b. f 2. An
interesting point here is that, strictly speaking, this code has implementation-defined behaviour
under the ISO/ANSI C standard. This is because the two struct’s must be wrapped in a union in
order to be well-defined under the standard (see summary point 3 in Section 6). We have not done
this purely to simplify the example.
Again, it is impossible to conclude p→ {c} from here. The real problem is that
individual locations can have multiple names (e.g. &a and &a.f1 above) and a
system based solely on unique strings cannot easily deal with this. Another
example where this issue arises is given in Figure 15, where a different aspect
of the ISO/ANSI standard is exploited (points 2 + 3 from the above summary). In
this case, it is the ability to access the common initial sequence of two structures
interchangeably which causes the trouble.
To overcome the issues involved with string concatenation, Yong et al. [1999]
introduce three functions, normalise, lookup and resolve, whose purpose is to
bridge the gap between different names representing the same location. This
makes their system significantly more complicated and less elegant than our
approach, which avoids these issues entirely. However, an important feature
of their framework is the ability to describe both portable and non-portable
analyses. The latter can be used to support commonly found, but undefined
C coding practices which rely on implementation-specific information, such as
type size and alignment. In contrast, our system as described cannot safely
handle such practices. However, this could be done with only minor modification
(i.e., using actual offsets instead of field numbers) and, in fact, Nystrom et al.
[2004b] claim to have done just this, although they do not discuss exact details.
Yong et al. [1999] do not discuss the positive weight cycle problem, per-
haps because it is only relevant to particular instances of their framework.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:35
Looking at the graph representation on the right, we can more easily under-
stand the problem. When r’s solution is propagated into q’s, a new edge will
be added from the temporary node t to HEAP0 (due to the constraint ∗q ⊇ t).
However, s reads from the special variable aggr.f1 (which represents the corre-
sponding field across all aggr instances), rather than from HEAP0. Thus, there
will be no path from t to s, when in practice there should be, and this leads to the
unsound conclusion that s → {a}. The problem stems from the fact that, under
the ISO/ANSI standard, a pointer to a struct can be used interchangeably with
a pointer to its first field (see summary point 1 on page 32). We have carefully
constructed the above example to exploit this, leading to two constraint vari-
ables (aggr. f 1 and HEAP0) representing the same physical object. To resolve
this we must ensure updates to one are reflected in the other. That is, we must
coalesce the two variables together. A good point to do this is when a struct
pointer is assigned a value of a different type (e.g., at the statement “p = r”
above).
Another difficulty arises when the same object represents different struct’s.
For example:
typedef struct { int *f1; int *f2; double x;} aggr1;
typedef struct { int *f3; int *f4; int y;} aggr2;
typedef union { aggr1 a; aggr2 b; } aggr;
aggr x;
int c,d,*p;
x.a.f1 = &c; a g gr1. f 1 ⊇ {c}
p = x.b.f3; p⊇ a g gr2. f 3
The problem here is that the field-based analysis does not conclude p → {c}.
Again, this arises because two constraint variables (aggr1. f 1 and aggr2. f 3)
represent the same physical object. As before, this can only be resolved by en-
suring updates to one are reflected in the other. A reasonable solution might be
to coalesce constraint variables representing struct’s which appear in the same
union. Recall that, strictly speaking, the union is required for the above code to
be considered properly ANSI compliant (see summary point 3 on page 33). If this
were not the case, field-based analysis of C would (most likely) be completely
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:37
7. CONCLUSION
We have presented a novel approach to field-sensitive pointer analysis of C.
While this is not the first solution to this problem, we argue it is the simplest and
most elegant and have provided numerous examples to support this. We have
developed an algorithm which implements our ideas and provided a complexity
analysis which demonstrates, for the first time, that the problem of performing
field-sensitive pointer analysis for C can be solved in O(v4 ) worse-case time. We
have performed the largest experimental study to-date evaluating the trade-
offs in performance versus precision of using field-sensitivity when analysing
C programs. The results of this, which are reported here, demonstrate that
field-sensitivity can offer a significant improvement in precision, albeit at some
considerable computational cost.
While the overall conclusions of our experiments are positive, they also high-
light a significant issue—namely that positive weight cycles are a major hin-
drance to efficient and precise field-sensitive analysis. Therefore, we feel that
future work should consider this issue further and, hopefully, a satisfactory
solution will be found. Another area of interest would be to investigate the
effect on solving time of using the difference propagation technique with the
field-sensitive analysis, which due to time constraints we have been unable
to do.
Finally, we are pleased to say that Dan Berlin has independently inte-
grated our technique for field-sensitive pointer analysis into the latest release
(version 4.1) of the GNU Compiler GCC [Berlin 2005]. While his implementa-
tion differs a little from what we have presented here (e.g., it is intra-procedural
and does not employ cycle detection), it is still the same fundamental algorithm
underneath.
ACKNOWLEDGMENTS
We would like to thank Dan Berlin for useful feedback on our algorithm and the
anonymous TOPLAS referees for insightful and valuable comments on earlier
drafts of this article.
REFERENCES
AIKEN, A. 1994. Set constraints: Results, applications, and future directions. In Proceedings of
the Workshop on Principles and Practice of Constraint Programming (PPCP). Lecture Notes in
Computer Science, vol. 874. Springer-Verlag, New York, 326–335.
AIKEN, A. 1999. Introduction to set constraint-based program analysis. Sci. Comput. Prog. 35,
2–3, 79–111.
AIKEN, A., AND WIMMERS, E. L. 1992. Solving systems of set constraints. In Proceedings of the IEEE
Symposium on Logic in Computer Science (LICS). IEEE Computer Society Press, Los Alamitos,
CA, 329–340.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:38 • D. J. Pearce et al.
AIKEN, A. AND WIMMERS, E. L. 1993. Type inclusion constraints and type inference. In Proceed-
ings of the ACM Conference on Functional Programming Languages and Computer Architecture
(FPCA). ACM, New York, 31–41.
ALUR, R., HENZINGER, T. A., MANG, F. Y. C., QADEER, S., RAJAMANI, S. K., AND TASIRAN, S. 1998.
MOCHA: Modularity in model checking. In Proceedings of the Conference on Computer Aided
Verification (CAV). Lecture Notes in Computer Science, vol. 1427. Springer-Verlag, New York,
521–525.
ANDERSEN, L. O. 1994. Program analysis and specialization for the C programming language.
Ph.D. dissertation. DIKU, University of Copenhagen.
BALL, T. and HORWITZ, S. 1993. Slicing programs with arbitrary control-flow. In Proceedings of the
Workshop on Automated and Algorithmic Debugging (AADEBUG). Lecture Notes in Computer
Science, vol. 749. Springer-Verlag, New York, 206–222.
BERLIN, D. 2005. Structure aliasing in GCC. In Proceedings of the GCC Developers Summit.
25–36.
BERNDL, M., LHOTÁK, O., QIAN, F., HENDREN, L. J., AND UMANEE, N. 2003. Points-to analysis using
BDDs. In Proceedings of the ACM Conference on Programming Language Design and Implemen-
tation (PLDI). ACM, New York, 196–207.
BINKLEY, D. 1998. The application of program slicing to regression testing. Inf. Softw. Tech. 40, 11–
12, 583–594.
BLANCHET, B., COUSOT, P., COUSOT, R., FERET, J., MAUBORGNE, L., MINÉ, A., MONNIAUX, D., AND RIVAL, X.
2002. Design and implementation of a special-purpose static program analyzer for safety-
critical real-time embedded software. In The Essence of Computation: Complexity, Analysis,
Transformation. Lecture Notes in Computer Science, vol. 2566. Springer-Verlag, New York, 85–
108.
BLANCHET, B., COUSOT, P., COUSOT, R., FERET, J., MAUBORGNE, L., MINÉ, A., MONNIAUX, D., AND RIVAL, X.
2003. A static analyzer for large safety-critical software. In Proceedings of the ACM Confer-
ence on Programming Language Design and Implementation (PLDI). ACM, New York, 196–
207.
BOURDONCLE, F. 1993a. Abstract debugging of higher-order imperative languages. In Proceedings
of the ACM Conference on Programming Language Design and Implementation (PLDI). ACM,
New York, 46–55.
BOURDONCLE, F. 1993b. Efficient chaotic iteration strategies with widenings. In Proceedings of
the Conference on Formal Methods in Programming and Their Applications. Lecture Notes in
Computer Science, vol. 735. Springer-Verlag, New York, 128–141.
BURKE, M. 1990. An interval-based approach to exhaustive and incremental interprocedural
data-flow analysis. ACM Trans. Prog. Lang. Syst. (TOPLAS) 12, 3, 341–395.
CHANDRA, S., AND REPS, T. 1999a. Physical type checking for C. In Proceedings of the ACM Work-
shop on Program Analysis for Software Tools and Engineering (PASTE). ACM, New York, 66–
75.
CHANDRA, S. AND REPS, T. 1999b. Physical type checking for C. Technical Report BL0113590-
990302-04, Lucent Technologies, Bell Laboratories.
CHATTERJEE, R., RYDER, B. G., AND LANDI, W. A. 1999. Relevant context inference. In Proceedings
of the ACM Symposium on Principles of Programming Languages (POPL). ACM, New York,
133–146.
CHEN, L.-L. AND HARRISON, W. L. 1994. An efficient approach to computing fixpoints for complex
program analysis. In Proceedings of the ACM Supercomputing Conference (SC). ACM, New York,
98–106.
CHENG, B.-C. AND HWU, W.-M. W. 2000. Modular interprocedural pointer analysis using access
paths: design, implementation, and evaluation. In Proceedings of the ACM conference on Pro-
gramming Language Design and Implementation (PLDI). ACM, New York, 57–69.
DAS, M. 2000. Unification-based pointer analysis with directional assignments. In Proceedings
of the ACM Conference on Programming Language Design and Implementation (PLDI). ACM,
New York, 35–46.
DAS, M., LIBLIT, B., FÄHNDRICH, M., AND REHOF, J. 2001. Estimating the impact of scalable pointer
analysis on optimization. In Proceedings of the Static Analysis Symposium (SAS). Lecture Notes
in Computer Science, vol. 2126. Springer-Verlag, New York, 260–278.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:39
DIWAN, A., MCKINLEY, K. S., AND MOSS, J. E. B. 1998. Type-based alias analysis. In Proceedings
of the ACM Conference on Programming Language Design and Implementation (PLDI). ACM,
New York, 106–117.
DOR, N., RODEH, M., AND SAGIV, M. 2003. CSSV: Towards a realistic tool for statically detecting all
buffer overflows in C. In Proceedings of the ACM conference on Programming Language Design
and Implementation (PLDI). ACM, New York, 155–167.
EICHIN, M. W. AND ROCHLIS, J. A. 1989. With microscope and tweezers: An analysis of the internet
virus of November 1988. In Proceedings of the IEEE Symposium on Research in Security and
Privacy. IEEE Computer Society Press, Los Alamitos, CA, 326–343.
EMAMI, M., GHIYA, R., AND HENDREN, L. J. 1994. Context-sensitive interprocedural points-to anal-
ysis in the presence of function pointers. In Proceedings of the ACM Conference on Programming
Language Design and Implementation (PLDI). ACM, New York, 242–256.
FÄHNDRICH, M., FOSTER, J. S., SU, Z., AND AIKEN, A. 1998. Partial online cycle elimination in inclu-
sion constraint graphs. In Proceedings of the ACM Conference on Programming Language Design
and Implementation (PLDI). ACM, New York, 85–96.
FÄHNDRICH, M., REHOF, J., AND DAS, M. 2000. Scalable context-sensitive flow analysis using instan-
tiation constraints. In Proceedings of the ACM Conference on Programming Language Design and
Implementation (PLDI). ACM, New York, 253–263.
FECHT, C. AND SEIDL, H. 1996. An even faster solver for general systems of equations. In Pro-
ceedings of the Static Analysis Symposium (SAS). Lecture Notes in Computer Science, vol. 1145.
Springer-Verlag, New York, 189–204.
FECHT, C. AND SEIDL, H. 1998. Propagating differences: An efficient new fixpoint algorithm
for distributive constraint systems. In Proceedings of the European Symposium on Program-
ming (ESOP). Lecture Notes in Computer Science, vol. 1381. Springer-Verlag, New York, 90–
104.
FLANAGAN, C. 1997. Effective static debugging via componential set-based analysis. Ph.D. disser-
tation. Rice University.
FLANAGAN, C., LEINO, K. R. M., LILLIBRIDGE, M., NELSON, G., SAXE, J. B., AND STATA, R. 2002. Extended
static checking for Java. In Proceedings of the ACM conference on Programming Language Design
and Implementation (PLDI). ACM, New York, 234–245.
FOSTER, J. S., FÄHNDRICH, M., AND AIKEN, A. 1997. Flow-insensitive points-to analysis with term
and set constraints. Technical Report CSD-97-964, University of California, Berkeley, CA.
FOSTER, J. S., FÄHNDRICH, M., AND AIKEN, A. 1999. A theory of type qualifiers. In Proceedings of
the ACM Conference on Programming Language Design and Implementation (PLDI). ACM, New
York, 192–203.
FOSTER, J. S., FÄHNDRICH, M., AND AIKEN, A. 2000. Polymorphic versus monomorphic flow-
insensitive points-to analysis for C. In Proceedings of the Static Analysis Symposium (SAS).
Lecture Notes in Computer Science, vol. 1824. Springer-Verlag, New York, 175–198.
GABOW, H. N. 2000. Path-based depth-first search for strong and biconnected components. Inf.
Proc. Lett. 74, 3–4 (May), 107–114.
GHIYA, R., LAVERY, D., AND SEHR, D. 2001. On the importance of points-to analysis and other
memory disambiguation methods for C programs. In Proceedings of the ACM Conference on
Programming Language Design and Implementation (PLDI). ACM, New York, 47–58.
GODEFROID, P. 1997. VeriSoft: A tool for the automatic analysis of concurrent reactive software. In
Proceedings of the Conference on Computer Aided Verification (CAV). Lecture Notes in Computer
Science, vol. 1254. Springer-Verlag, New York, 476–479.
HARMAN, M., BINKLEY, D., AND DANICIC, S. 2003. Amorphous program slicing. J. Syst. Softw.
(JSS) 68, 1, 45–64.
HASTI, R. AND HORWITZ, S. 1998. Using static single assignment form to improve flow-insensitive
pointer analysis. In Proceedings of the ACM Conference on Programming Language Design and
Implementation (PLDI). ACM, New York, 97–105.
HEINTZE, N. 1994. Set-based analysis of ML programs. In Proceedings of the ACM Conference on
Lisp and Functional Programming (LFP). ACM, New York, 306–317.
HEINTZE, N. AND MCALLESTER, D. 1997a. Linear-time subtransitive control flow analysis. In Pro-
ceedings of the ACM Conference on Programming Language Design and Implementation (PLDI).
ACM, New York, 261–272.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:40 • D. J. Pearce et al.
HEINTZE, N. AND MCALLESTER, D. A. 1997b. On the cubic bottleneck in subtyping and flow analysis.
In Proceedings of the IEEE Symposium on Logic in Computer Science (LICS). IEEE Computer
Society Press, Los Alamitos, CA, 342–351.
HEINTZE, N. AND TARDIEU, O. 2001a. Demand-driven pointer analysis. In Proceedings of the ACM
Conference on Programming Language Design and Implementation (PLDI). ACM, New York,
24–34.
HEINTZE, N. AND TARDIEU, O. 2001b. Ultra-fast aliasing analysis using CLA: A million lines of C
code in a second. In Proceedings of the ACM Conference on Programming Language Design and
Implementation (PLDI). ACM, New York, 254–263.
HENZINGER, T. A., JHALA, R., MAJUMDAR, R., AND SUTRE, G. 2003. Software verification with Blast.
In Proceedings of the Workshop on Model Checking Software. Lecture Notes in Computer Science,
vol. 2648. Springer-Verlag, New York, 235–239.
HIND, M. 2001. Pointer analysis: Haven’t we solved this problem yet? In Proceedings of the 2001
ACM workshop on Program Analysis for Software Tools and Engineering (PASTE). ACM, New
York, 54–61.
HIND, M., BURKE, M., CARINI, P., AND CHOI, J.-D. 1999. Interprocedural pointer alias analysis. ACM
Trans. Prog. Lang. Syst. (TOPLAS) 21, 4, 848–894.
HIND, M. AND PIOLI, A. 1998. Assessing the effects of flow-sensitivity on pointer alias analyses.
In Proceedings of the Static Analysis Symposium (SAS). Lecture Notes in Computer Science, vol.
1503. Springer-Verlag, New York, 57–81.
HIND, M. AND PIOLI, A. 2000. Which pointer analysis should I use? In Proceedings of the ACM
International Symposium on Software Testing and Analysis (ISSTA). ACM, New York, 113–123.
HOLZMANN, G. J. 1997. The Spin model checker. IEEE Trans. Softw. Engin. 23, 5, 279–95.
HORWITZ, S., DEMERS, A. J., AND TEITELBAUM, T. 1987. An efficient general iterative algorithm for
dataflow analysis. Acta Inf. 24, 6, 679–694.
ISO90 1990. ISO/IEC. international standard ISO/IEC 9899, programming languages - C.
JOHNSON, R. AND WAGNER, D. 2004. Finding user/kernel pointer bugs with type inference. In Pro-
ceedings of the USENIX Security Symposium. USENIX, 119–134.
JONES, J. A., HARROLD, M. J., AND STASKO, J. 2002. Visualization of test information to assist fault
localization. In Proceedings of the International Conference on Software Engineering (ICSE).
ACM, New York, 467–477.
JONES, N. D. AND MUCHNICK, S. S. 1981. Flow analysis and optimization of lisp-like structures. In
Program Flow Analysis: Theory and Applications, S. S. Muchnick and N. D. Jones, Eds. Prentice-
Hall, Englewood Cliff, 102–131.
KODUMAL, J. AND AIKEN, A. 2005. Banshee: A scalable constraint-based analysis toolkit. In Pro-
ceedings of the Static Analysis Symposium, SAS. Lecture Notes in Computer Science, vol. 3672.
Springer-Verlag, New York, 218–234.
LANDI, W. 1992a. Interprocedural aliasing in the presence of pointers. Ph.D. dissertation, Rutgers
University, NJ.
LANDI, W. 1992b. Undecidability of static analysis. ACM Lett. Prog. Lang. Syst. 1, 4, 323–337.
LHOTAK, O. AND HENDREN, L. J. 2003. Scaling Java points-to analysis using SPARK. In Proceedings
of the Conference on Compiler Construction (CC). Lecture Notes in Computer Science, vol. 2622.
Springer-Verlag, New York, 153–169.
LIANG, D. AND HARROLD, M. J. 1999. Efficient points-to analysis for whole-program analysis. In Pro-
ceedings of the European Software Engineering Confrence (ESEC) and ACM Foundations of Soft-
ware Engineering (FSE). Lecture Notes in Computer Science, vol. 1687. Springer-Verlag/ACM,
New York, 199–215.
LIANG, D., PENNINGS, M., AND HARROLD, M. J. 2001. Extending and evaluating flow-insensitive and
context-insensitive points-to analyses for Java. In Proceedings of the ACM Workshop on Program
Analyses for Software Tools and Engineering (PASTE). ACM, New York, 73–79.
MCKINLEY, K. S. 1994. Evaluating automatic parallelization for efficient execution on shared-
memory multiprocessors. In Proceedings of the IEEE/ACM Supercomputing Conference (SC).
ACM, New York, 54–63.
MELSKI, D. AND REPS, T. 1997. Interconvertibility of set constraints and context-free language
reachability. In Proceedings of the ACM Workshop on Partial Evaluation and Program Manipu-
lation (PEPM). ACM, New York, 74–88.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
Efficient Field-Sensitive Pointer Analysis of C • 4:41
MILANOVA, A., ROUNTEV, A., AND RYDER, B. 2002. Parameterized object sensitivity for points-to and
side-effect analyses for Java. In Proceedings of the ACM International Symposium on Software
Testing and Analysis (ISSTA). ACM, New York, 1–11.
MYERS, B. A. 1986. Visual programming, programming by example, and program visualization;
A taxonomy. In Proceedings of the ACM Conference on Human Factors in Computing Systems
(CHI). ACM, New York, 59–66.
NIELSON, F., NIELSON, H. R., AND HANKIN, C. L. 1999. Principles of Program Analysis. Springer-
Verlag, New York.
NUUTILA, E. AND SOISALON-SOININEN, E. 1994. On finding the strongly connected components in a
directed graph. Inf. Proc. Lett. 49, 1 (Jan.), 9–14.
NYSTROM, E. M., KIM, H.-S., AND HWU, W.-M. W. 2004a. Bottom-up and top-down context-sensitive
summary-based pointer analysis. In Proceedings of the Static Analysis Symposium (SAS). Lecture
Notes in Computer Science, vol. 3148. Springer-Verlag, New York, 165–180.
NYSTROM, E. M., KIM, H.-S., AND HWU, W.-M. W. 2004b. Importance of heap specialization in
pointer analysis. In Proceedings of the ACM Workshop on Program Analysis for Software Tools
and Engineering (PASTE). ACM, New York, 43–48.
PADUA, D. A., KUCK, D. J., AND LAWRIE, D. H. 1980. High-speed multiprocessors and compilation
techniques. IEEE Trans. Comput. C-29, 9 (Sept.), 763–776.
PADUA, D. A. AND WOLFE, M. J. 1986. Advanced compiler optimizations for supercomputers. Com-
mun. ACM 29, 12, 1184–1201.
PEARCE, D. J. 2005. Some directed graph algorithms and their application to pointer analysis (on-
line version available at http://www.mcs.vuw.ac.nz/djp). Ph.D. dissertation, Imperial College,
London, United Kingdom.
PEARCE, D. J. AND KELLY, P. H. J. 2004. A dynamic algorithm for topologically sorting directed
acyclic graphs. In Proceedings of the Workshop on Efficient and Experimental Algorithms (WEA).
Lecture Notes in Computer Science, vol. 3059. Springer-Verlag, New York, 383–398.
PEARCE, D. J. AND KELLY, P. H. J. 2006. A dynamic topological sort algorithm for directed acyclic
graphs. ACM J. Experim. Alg. 11, 1.7.
PEARCE, D. J., KELLY, P. H. J., AND HANKIN, C. 2003. Online cycle detection and difference propa-
gation for pointer analysis. In Proceedings of the IEEE Workshop on Source Code Analysis and
Manipulation (SCAM). IEEE Computer Society Press, Los Alamitos, CA, 3–12.
PEARCE, D. J., KELLY, P. H. J., AND HANKIN, C. 2004a. Efficient field-sensitive pointer analysis for
C. In Proceedings of the ACM Workshop on Program Analysis for Software Tools and Engineering
(PASTE). ACM, New York, 37–42.
PEARCE, D. J., KELLY, P. H. J., AND HANKIN, C. 2004b. Online cycle detection and difference propa-
gation: Applications to pointer analysis. Softw. Qual. J. 12, 4, 309–335.
RAMALINGAM, G. 1994. The undecidability of aliasing. ACM Trans. Prog. Lang. Syst.
(TOPLAS) 16, 5, 1467–1471.
REISS, S. P. 1997. Cacti: A front end for program visualization. In Proceedings of the IEEE sym-
posium on Information Visualization (InfoVis). IEEE Computer Society Press, Los Alamitos, CA,
46–50.
REPS, T. AND TURNIDGE, T. 1996. Program specialization via program slicing. In Selected Papers
from the International Seminar on Partial Evaluation. Lecture Notes in Computer Science, vol.
1110. Springer-Verlag, New York, 409–429.
REYNOLDS, J. C. 1969. Automatic computation of data set definitions. In Proceedings of the Infor-
mation Processing Congress (IFIP). Vol. 1. North-Holland, Amsterdam, The Netherlands, 456–
461.
ROUNTEV, A. AND CHANDRA, S. 2000. Off-line variable substitution for scaling points-to analysis.
In Proceedings of the ACM Conference on Programming Language Design and Implementation
(PLDI). ACM, New York, 47–56.
ROUNTEV, A., MILANOVA, A., AND RYDER, B. G. 2001. Points-to analysis for Java using annotated
constraints. In Proceedings of the ACM Conference on Object Oriented Programming Systems,
Languages and Applications (OOPSLA). ACM, New York, 43–55.
SAHA, D. AND RAMAKRISHNAN, C. R. 2005. Incremental and demand-driven points-to analysis using
logic programming. In Proceedings of the ACM Conference on Principles and Practice of Declar-
ative Programming (PPDP). ACM, New York, 117–128.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.
4:42 • D. J. Pearce et al.
SCHON, E. 1995. On the computation of fixpoints in static program analysis with an application
to AKL. Technical Report R95-06, Swedish Institute of Computer Science. Nov.
SHAPIRO, M. AND HORWITZ, S. 1997. Fast and accurate flow-insensitive points-to analysis. In Pro-
ceedings of the Symposium on Principles of Programming Languages (POPL). ACM, New York,
1–14.
SHMUELI, O. 1983. Dynamic cycle detection. Inf. Proc. Lett. 17, 4 (Nov.), 185–188.
SO, B., MOON, S., AND HALL, M. W. 1998. Measuring the effectiveness of automatic parallelization
in SUIF. In Proceedings of the ACM/IEEE Supercomputing Conference (SC). ACM, New York,
212–219.
SRIDHARAN, M., GOPAN, D., SHAN, L., AND BODIK, R. 2005. Demand-driven points-to analysis for
Java. In Proceedings of the ACM Conference on Object Oriented Programming, Systems, Lan-
guages, and Applications (OOPSLA). ACM, New York, 59–76.
STEENSGAARD, B. 1996. Points-to analysis in almost linear time. In Proceedings of the ACM Sym-
posium on Principles of Programming Languages (POPL). ACM, New York, 32–41.
SU, Z., FÄHNDRICH, M., AND AIKEN, A. 2000. Projection merging: Reducing redundancies in inclu-
sion constraint graphs. In Proceedings of the Symposium on Principles of Programming Lan-
guages (POPL). ACM, New York, 81–95.
SUIF2. The SUIF 2 research compiler Stanford University, Stanford, CA, http://suif.stanford.edu.
SYSTA, T., YU, P., AND MÜLLER, H. 2000. Analyzing Java software by combining metrics and pro-
gram visualization. In Proceedings of the IEEE Conference on Software Maintenance and Reengi-
neering (CSMR). IEEE Computer Society Press, Los Alamitos, CA, 199–208.
TARJAN, R. E. 1972. Depth-first search and linear graph algorithms. SIAM J. Comput. 1, 2, 146–
160.
THE VIS GROUP. 1996. VIS: A system for verification and synthesis. In Proceedings of the Con-
ference on Computer Aided Verification (CAV). Lecture Notes in Computer Science, vol. 1102.
Springer-Verlag, New York, 428–432.
VIVIEN, F. AND RINARD, M. 2001. Incrementalized pointer and escape analysis. In Proceedings of
the ACM Conference on Programming Language Design and Implementation (PLDI). ACM, New
York, 35–46.
WAGNER, D., FOSTER, J. S., BREWER, E. A., AND AIKEN, A. 2000. A first step towards automated
detection of buffer overrun vulnerabilities. In Proceedings of the Network and Distributed System
Security Symposium (NDSS). The Internet Society, 3–17.
WHALEY, J. AND LAM, M. S. 2002. An efficient inclusion-based points-to analysis for strictly-typed
languages. In Proceedings of the Symposium on Static Analysis (SAS). Lecture Notes in Computer
Science, vol. 2477. Springer-Verlag, New York, 180–195.
WHALEY, J. AND LAM, M. S. 2004. Cloning-based context-sensitive pointer alias analysis using
Binary Decision Diagrams. In Proceedings of the ACM Conference on Programming Language
Design and Implementation (PLDI). ACM, New York, 131–144.
WILSON, R. P. 1997. Efficient context-sensitive pointer analysis for C programs. Ph.D. disserta-
tion. Stanford University, Stanford, CA.
WILSON, R. P. AND LAM, M. S. 1995. Efficient context-sensitive pointer analysis for C programs.
In Proceedings of the ACM Conference on Programming Language Design and Implementation
(PLDI). ACM, New York, 1–12.
WOLFE, M. J. 1982. Optimizing supercompilers for supercomputers. Ph.D. dissertation. Deptart-
ment of Computer Science, University of Illinois at Urbana-Champaign, Urbana, IL.
YONG, S. H., HORWITZ, S., AND REPS, T. 1999. Pointer analysis for programs with structures and
casting. In Proceedings of the ACM Conference on Programming Language Design and Imple-
mentation (PLDI). ACM, New York, 91–103.
ACM Transactions on Programming Languages and Systems, Vol. 30, No. 1, Article 4, Publication date: Nov. 2007.