Professional Documents
Culture Documents
Lecture Notes in Computer Science: Edited by G. Goos and J. Hartmanis
Lecture Notes in Computer Science: Edited by G. Goos and J. Hartmanis
Springer-Verlag
Berlin Heidelberg NewYork
London Paris Tokyo
Hong Kong Barcelona
Budapest
Series Editors
Gerhard Goos Juris Hartmanis
Universit~it Karlsruhe Cornell University
Postfach 69 80 Department of Computer Science
Vincenz-Priessnitz-Stra6e 1 4130 Upson Hall
W-7500 Karlsruhe, FRG Ithaca, NY 14853, USA
Author
Anne Mulkers
Department of Computer Science, K.U. Leuven
Celestijnenlaan 200 A, B-3001 Heverlee, Belgium
This work is subject to copyright. All rights are reserved, whether the whole or part
of the material is concerned, specifically the rights of translation, reprinting, re-use
of illustrations, recitation, broadcasting, reproduction on microfilms or in any other
way, and storage in data banks. Duplication of this publication or parts thereof is
permitted only under the provisions of the German Copyright Law of September 9,
1965, in its current version, and permission for use must always be obtained from
Springer-Verlag. Violations are liable for prosecution under the German Copyright
Law.
9 Springer-Verlag Berlin Heidelberg 1993
Printed in Germany
Typesetting: Camera ready by author/editor
45/3140-543210 - Printed on acid-free paper
Preface
first layer, consisting of the type and mode analysis, basically supplies the logical
terms to which variables can be bound. The two subsequent layers of the analysis
heavily rely on these descriptions of term values. The sharing analysis derives
how the representation of logical terms as structures in memory can be shared,
and the liveness analysis uses the sharing information to determine when a term
structure in memory can be live.
Acknowledgments
This book is based on my Ph.D. dissertation [59] conducted at the Department
of Computer Science of the K.U.Leuven, Belgium. The research presented has
been carried out as part of the RFO/AI/02 project of the Diensten voor de
programmable van he~ wetenschapsbeleid, which started in November 1987 and
was aimed at the study of implementation aspects of logic programming: 'Logic
as a basis for artificial intelligence: control and efficiency of deductive inferencing
and parallelism'.
I am indebted to Professor Maurice Bruynooghe, my supervisor, for giving
me the opportunity to work on the project and introducing me to the domain
of abstract interpretation, for sharing his experience in logic programming~ his
invaluable insights and guidance. I wish to thank Will Winsborough for many
helpful discussions, for his advice on the design of the abstract domain and
safety proofs and his generous support; Gerda Janssens for her encouragement
and support, and for allowing the use of the prototype for type analysis as the
starting point for implementing the liveness analysis; Professors Yves Willems
and Bart Demoen, for managing the RFO/AI/02 project and providing me with
optimal working facilities; Professor Marc Gobin, my second supervisor, and
Professors Baudouin Le Charlier and Danny De Schreye, for their interest and
helpful comments, and for serving on my Ph.D. thesis committee. I also want to
thank my family, friends and colleagues for their support and companionship.
Introduction 1
Abstract Interpretation 5
2.1 Basic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Abstract Interpretation Framework . . . . . . . . . . . . . . . 7
2.2.1 Overview of the F r a m e w o r k . . . . . . . . . . . . . . . . . . . . 8
2.2.2 Concrete a n d A b s t r a c t D o m a i n s of S u b s t i t u t i o n s . . . . . . . . 10
2.2.3 Primitive Operations ....................... 11
2.2.4 A b s t r a c t I n t e r p r e t a t i o n Procedure . . . . . . . . . . . . . . . . 14
2.3 E x a m p l e : I n t e g r a t e d T y p e a n d Mode Inference . . . . . . . . . 16
2.3.1 Rigid a n d I n t e g r a t e d T y p e G r a p h s . . . . . . . . . . . . . . . . 16
2.3.2 Type-graph Environments ..................... 23
2.3.3 P r i m i t i v e O p e r a t i o n s for T y p e - g r a p h E n v i r o n m e n t s . . . . . . 25
3 Related Work 31
3.1 Aliasing a n d P o i n t e r A n a l y s i s . . . . . . . . . . . . . . . . . . . 31
3.2 Reference C o u n t i n g a n d Liveness Analysis . . . . . . . . . . . . 38
3.3 Code O p t i m i z a t i o n . . . . . . . . . . . . . . . . . . . . . . . . . 41
4 Sharing Analysis 47
4.1 Sharing Environments . . . . . . . . . . . . . . . . . . . . . . . 47
4.1.1 Concrete R e p r e s e n t a t i o n of Shared S t r u c t u r e . . . . . . . . . . 48
4.1.2 A b s t r a c t R e p r e s e n t a t i o n of Shared S t r u c t u r e . . . . . . . . . . 55
4.1.3 T h e Concrete a n d A b s t r a c t D o m a i n s . . . . . . . . . . . . . . . 62
4.1.4 Order R e l a t i o n a n d U p p e r b o u n d O p e r a t i o n . . . . . . . . . . . 66
4.2 Primitive Operations ....................... 68
4.2.1 Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.2.1.1 Xi : Xj . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.2.1.2 Xi : f(Xil,...,Xij) . . . . . . . . . . . . . . . . . . . . . . . 85
4.2.2 Procedure E n t r y . . . . . . . . . . . . . . . . . . . . . . . . . . 93
4.2.3 Procedure Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
4.3.1 Example: i n s e r t / 3 ........................ 111
4.3.2 Relevance of S h a r i n g Edges . . . . . . . . . . . . . . . . . . . . 114
v lll CONTENTS
LivenessAnalysis 127
5.1 Liveness E n v i r o n m e n t s . . . . . . . . . . . . . . . . . . . . . . 127
5.1.1 Concrete R e p r e s e n t a t i o n of Liveness I n f o r m a t i o n . . . . . . . . 128
5.1.2 A b s t r a c t R e p r e s e n t a t i o n of Liveness I n f o r m a t i o n . . . . . . . . 133
5.1.3 T h e Concrete a n d A b s t r a c t D o m a i n s . . . . . . . . . . . . . . . 141
5.1.4 Order R e l a t i o n a n d U p p e r b o u n d O p e r a t i o n . . . . . . . . . . . 145
5.2 Primitive Operations . . . . . . . . . . . . . . . . . . . . . . . 147
5.2.1 Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.2.1.1 Xi = X j . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.2.1.2 X i : f ( X i , , . . . , X i j ) . . . . . . . . . . . . . . . . . . . . . . . 153
5.2.2 Procedure E n t r y . . . . . . . . . . . . . . . . . . . . . . . . . . 154
5.2.3 Procedure Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
5.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
5.3.1 Example: q s o r t / 3 . . . . . . . . . . . . . . . . . . . . . . . . . 165
5.3.2 Precision of the Liveness Analysis . . . . . . . . . . . . . . . . 168
5.3.3 T h e Practical Usefulness of Liveness I n f o r m a t i o n . . . . . . . . 171
6 Conclusion 179
Bibliography 213
Chapter 1
Introduction
_1 I -J I _1 [
-, , I , -, , I , -~ , I i
Rev_L2
and on the soundness of the results that can be obtained, rather than on the
efficiency of the analysis. Due to imprecision that is inherent to the global anal-
ysis algorithms, not all garbage cells can be detected in arbitrary cases. We will
extensively discuss the strength of the analyses that are proposed.
The study of code optimization schemes that explicitly reclaim or reuse
garbage cells is beyond the scope of the present book. In [52], Mari~n et al.
discussed some preliminary experiments on code optimization based on liveness
information. Only opportunities for local reuse of storage cells are considered,
i.e. reuse within the same clause where a cell is turned into garbage. Non-local
reuse would require extra run-time data areas to keep track of the free space.
Although possible in principle, non-local reuse therefore will be less beneficial for
code optimization. The reuse of storage also introduces some new requirements
on the trailing mechanism of standard Prolog implementations that will affect
the performance. We will briefly discuss these issues in Section 3.3 and 5.3.3.
Chapter 2
Abstract Interpretation
In this chapter, we first set forth the basic principles of abstract interpretation.
Then, we sketch one framework in particular, as it will be used in the remainder
of the book. Finally, we describe an application, namely type and mode analysis,
which constitutes the first layer of the global flow analysis to derive the liveness
of data structures. A more detailed introduction to abstract interpretation of
declarative languages can be found in [1].
2.1 Basic C o n c e p t s
Static p r o g r a m analysis is a general technique for deriving properties of the
run-time behavior of a program. The information obtained in this way can be
used to drive the optimization phase of a compiler, or to guide source level
program transformation and program development tools (e.g. for debugging).
Often, program analysis can be viewed as executing the program over a symbolic
or abstract domain of d a t a descriptions, instead of over the normal concrete
d a t a domain, and therefore it is called abstract interpretation. To mimic the
concrete execution of a program, the basic operations defined on the standard
domain are replaced by abstract operations defined on the abstract domain. The
resulting flow analysis produces a so-cMled abstract semantics: for each possible
point of control in the program, it gives a finite description of the set of data
states that the program could be in when execution passes through that point.
The program properties that compiler optimizations are based on are usually
undecidable. Since static analyses are expected to be finitely computable, the
data descriptions will be imprecise in general. The abstract interpretation is
said to be sound if the d a t a descriptions computed for each program point give
upper approximations of the set of concrete data states that can occur during
program execution.
Patrick and Radhia Cousot [24] provided a general framework for data-flow
analysis problems of imperative languages, and they defined conditions which
ensure the soundness of an abstraction. Based on that work, a variety of abstract
6 CHAPTER 2. A B S T R A C T INTERPRETATION
interpretation frameworks have been developed for the specification and verifica-
tion of analyses for logic programs [11, 25, 27, 43, 48, 49, 51, 55, 57, 65, 81, 82].
A framework is a formally based, generic construction for program analysis that
provides a basis for sound optimizations. To this end, it includes theorems that
ensure the safety and termination of the analysis if the application dependent do-
mains and operations supplied to complete the construction obey certain safety
requirements.
The standard theory of lattices provides the conceptual framework for pro-
gram analysis. In order to apply the method for a new application, the space
of properties (the abstract domain) capturing the information of interest should
be a complete lattice (i.e. a set with a partial ordering such that each subset
has a least upper bound and a greatest lower bound), and the functions used
should be monotone or order-preserving on the lattice. If these conditions are
satisfied, then the Tarski-Knaster Fixpoint Theorem [71] guarantees the exis-
tence of a solution for the fixpoint problem posed by the abstract semantics of
the program.
Let (C, E) be the concrete domain, and (A, _<) the domain of descriptions. In
the context of logic programs, the concrete domain C will typically be the power-
set of the set of substitutions. A substitution is a set a - {X1 +-- K:I,..., Xc *---
ice}, where the Xi are distinct variables and the /Ci are terms. The set D
= { X 1 , . . . , X~} is called the domain of the substitution a (denoted as dom(a)).
An interesting application of program analysis for logic programming languages
is mode analysis. Significant performance improvements can be achieved in inter-
preters if it is known how the logical variables are used in a relation (i.e. as input
or output or a mixture of the two). For this application, the abstract domain
A will describe the instantiation state of the terms assigned to the variables by
the concrete substitutions that are possible at run time. A simple mode analysis
can be based on the set (a, g, f}, partially ordered such that f < a and g < a.
The modes f, g and a are abbreviations for respectively free, ground and any.
An element of the abstract domain A is either • or an abstract substitution of
the form {Xi +-- rn~ [ Xi E D & rn/ e {f, g, a}} that assigns a mode to each
variable of some domain D.
The semantics of the descriptions is given by a concretization function "), :
A ---* C, such that ~(z) is the set of concrete objects described by z. Conversely,
given a collection of concrete objects, the abstraction function a : C ---* A yields
the best overall description of the objects. Suppose that the normal interpreta-
tion of a program is defined in terms of an operator F c , which has as abstract
counterpart FA (e.g. for the execution of logic programs, unification is the cen-
tral operation). The traditional abstract interpretation scheme requires that the
following conditions are satisfied.
1 . (C, _ ) and (A, <) are complete lattices,
2. q, : A --4 C and ot : C ~ A are monotonic, i.e.
3. z = a ( 7 ( z ) ) for all z e A,
4. 9 E_ for a l l . C,
6. E for a l l . A.
Conditions 2, 3 and 4 ensure that C and A have a similar structure. The last
condition is called the safely or soundness requirement, and ensures that the
abstract operation mimics the concrete operation such that no false conclusion
can be drawn about the concrete operation's behavior. The framework described
in the next section requires a set of similar but slightly weaker conditions.
Two basic classes of abstract interpretation of logic programs can be distin-
guished. The top-down analyses are based on the standard operational semantics
of Prolog, i.e. SLD-resolution with a left-to-right computation rule which always
resolves the leftmost goal in the current resolvent. In order to derive the states
in which clauses and their calls can be reached, a query specification is provided
that characterizes the entry uses of the program. The bottom-up analyses [18, 53]
are based on the fixpoint semantics of logic programs. Because the evaluation
of a b o t t o m - u p semantics does not correspond to the operational behavior of a
program, it does not readily provide information about call patterns describing
how the clauses of a program will be used in actual computations. Furthermore,
the b o t t o m - u p computations tend to derive a lot of redundant facts not actually
needed to answer a specific query. To overcome both deficiencies, the normal
top-down Prolog execution is simulated in recent work by first transforming the
program and the query according to the magic templates method and then ex-
ecuting the new program b o t t o m up [18, 66]. This method is adopted from
the field of deductive databases, where magic templates are used to provide for
efficient b o t t o m - u p evaluation of database queries. The resulting b o t t o m - u p
abstract interpretation method seems to be equally suited for all sorts of appli-
cations as the corresponding top-down methods using the same abstract domain
and operations.
~0 a p p ( _ l l , ~ 2 , _ . i 3 ) /~s
/%
~I /=nil B2 _Y=_Z ~s ~4 _X=V.._U~5 app(_U,_g,_w) ,Be _Z=-E._W ~T
1
Figure 2.1: AND-OR-graph for a query ?- a p p ( _ l l , -12, _3.3).
: AbstrSubD --~ 2 c ~
Although the framework does not refer explicitly to the abstraction function
a : 2 C~ --~ AbstrSubD,
we assume such a function is given and allows the user to abstract the initial set
of queries.
The set AbstrSubD is not required to be finite, nor does it need to be a
complete lattice; the abstract interpretation framework of [11] imposes a slightly
weaker algebraic structure on AbstrSubD that still guarantees termination of the
abstract interpretation procedure. There must exist a binary relation < that is
reflexive and transitive (a preordering), and that satisfies the following property
The relation <_ can also be used modulo this equivalence relation, which yields
a partial order for the set of equivalence classes. The existence of a uniquely
determined representative for each equivalence class may be convenient, but is
not required by the framework.
Further requisites for the framework are
We will use a generalization of the Upo operation for a finite set {/31,... ,/3,) of
abstract substitutions, which is defined in the obvious way.
Note that for the sake of precision of the analysis, Upp should return a value as
small as possible (e.g. the least upperbound if AbstrSubD is a lattice).
H 1 ,., H p
9., ~n t -o,
programs. Below, the most general unifier of two terms K1,/C2 is denoted by
mgu(/C1, ~2). We denote the set of variables that occur in a particular term/C
as Vars(/C).
. Procedure-Entry(/~t=, P ) : { ~ . , . . . , / ~ . }
Applied to a call P with abstract call-substitution ~i,=, the procedure-entry
operation extends the abstract AND-OR-graph at the 'Or'-node repre-
senting the call P, by adding a branch for each clause Hj :- BJ , . . . , B ~
(1 < j _ p) defining the predicate P (see Figure 2.2), and it computes the
abstract substitutions 3~,=,...,~,~. The domain of the abstract substitu-
tion ~',~ is the set of variables Dj in the jth clause. The computation of the
abstract substitutions is clone in two steps out of convenience. First, the
abstract call-substitution/~i,~is restricted by the call AbstrRestrict(~i,,,P)
to a substitution ~.~tr E F-AbstrSubDp, only referringto the domain Dp of
the call p6. This step is independent of the set of clauses defining P. In the
second step, /~.,t,is renamed for the domain of variables for each of the
clauses, and the local variables of the clause are initialized.For instance,
for the application of mode analysis mentioned above, the local variables
of the clause (i.e.the variables not occurring in the head) are initializedto
the mode f (free).
The safety requirements that are sufficient for the correctness of the two
steps of the procedure-entry operation are respectively:
6 T h i s i s n o t e s s e n t i a l , b u t i t i m p r o v e s t h e p e r f o r m a n c e ; we a s s u m e t h a t t h e p r o c e d u r e - e n t r y
o p e r a t i o n u s e s e l e m e n t s of F - A b s t r S U b D p for t h e r e c u r s l v e p r e d i c a t e s .
2.2. A B S T R A C T I N T E R P R E T A T I O N F R A M E W O R K 13
CI B~,, p w',t,
I't o ~ t #o,,t C~
HI ... n p
B~ ... 1 1
B,~,Bo,, B~ 9.. v P
B,,~,flo,,,
Due to the normal form of the source programs, proving the safety of the
second step is rather straightforward for most applications. The first step
may be more involved, especially for the application of liveness analysis.
For the example of Figure 2.1, procedure entry computes/31 and f14 from
the call app(_11,__12,__13), represented by the root node, and the call-
substitution/30.
2. Procedure-Exit(/31,, 1
{#o,,,, ...,/3o,,,}, P ) = ,fi'o,,,
Procedure-exit is illustrated in Figure 2.3. It can be applied when the
final abstract success-substitution ~J,t has been derived for each clause
defining P. The initial step computes an abstract substitution arst; that
has as domain Dp, the set of variables of the call P. We have a,,t~ P'o~t =
Upp({/31,...,13.r}), where /~ (1 < j < p) is the renaming to Dp of
AbstrRestrict(/3sout, HJ). In the second step, the eztensio~ operation
AbstrExtend reintroduces the other variables in the domain DP.nv of the
calling environment (note: Dp C_ DBnv, where DEn, is the domain of/3i,
and the calling clause C :- C1,... ,P,... ,Co), and makes explicit the effect on
those variables of the execution of the procedure: /3ou, = AbstrExtend(/3i,,
#,,,,
O~t '
p).
14 CHAPTER 2. A B S T R A C T INTERPRETATION
The safety requirements that are sufficient for the correctness of the two
steps of the procedure-exit operation are respectively:
(a) If for some j such that 1 < j < p, a~,~ E 7(~/~,~) and aj E 7 ( ~ , t ) ,
and (3a over Vars(Pai,~) : Pai,.,a = HJaj), with HJ the head of the
properly renamed jo, clause defining P, and the only variables of
(C :- C1,...,P,...,Cc)a~r, occurring in a also occur in Pa~,~, then
{ f'~ratr ~
o'inO'[D p E 3"k~'out/"
{f4vstr~J, and
(b) If a ~ 9 3 " ( ~ ) and (3a over Vars(Pa~)) : a ~ a l D p 9 3"x~-o~t
the only variables of (C :- C 1 , . . . , P , . . . , C c ) a i n occurring in a also
occur in Pa~,~, then a~,:[Dzn" 9 7(Dour).
Proving the safety of the first step follows almost immediately from the
properties of the Upp operation and the monotonicity of the concretization
function 3'. We will illustrate this in Section 2.3.3.
For the example of Figure 2.1, procedure exit computes ~s from the-call
represented by the root node, a p p ( - 2 1 , - / 2 , - 2 3 ) , the abstract call-substi-
tution/~0, and the final abstract success-substitutions of the two clauses,
/~3 and ~7.
. AbstrUnify( ~,,, P ) -- ~o~,t
For programs in normal form, the unification call P can have either of two
forms: X~ - Xj or X~ = f(X~,,...,Xi~). Let DBnv be the domain of ~i,~.
Th e safety requirement that is sufficient for the correctness of the abstract
unification operation is:
For the example of Figure 2.1, abstract unification computes/35 from the
call ~=.E. _U, and the call-substitution ~4.
A l g o r i t h m 2.1 Abstrlnterpretation(/3i,~,P)
9 call Procedure-Entry(/3i.,P);
9 call Abstrlnterpretation for each of the calls in the bodies of each of the
clauses HJ :- B~,..., B ~ , ( 1 < j _< p) added to the AND-OR-graph by
Procedure-Entry in the previous step, always working on a call whose
abstract call-substitution is already computed;
9 call erocedure-Exit(/3i., {/3so,,t I 1 _ j < p}, P).
C a s e 4: If P has an ancestor node with a call P' to the same predicate, but the
abstract call-substitutions are not equivalent (/3~'~t~ ~ ~i,, l~rStrl up to renaming
of variables), then if ~-i,,A~'t~<_ ~-,,,Br't~',the computation proceeds as in C a s e
3, otherwise, the subgraph of P' is recomputed using n(Upp(/3~.~,t~,/3..,,t~' ))
as new value for/3[,~t~'.7
Note that the iterations in Case 3 and Case 4 terminate because there does
not exist an ascending chain in F-AbstrSUbDp. For proofs of the correctness and
termination of the above procedure, as well as for further details, we refer the
reader to [11].
Definition 2.3.1
A rigid type graph is an ordered triple, T = (NodesT-, ForwardArcsT-, BackArcsT-),
2.3. E X A M P L E : I N T E G R A T E D T Y P E A N D M O D E I N F E R E N C E 17
TT~.~ T2 Or T30f~ M
nil fa C / ~ g ax
lnt O ~ lit
a b
Recall that a tree is a directed acyclic graph (DAG) satisfying the following
properties:
There is exactly one node, called the root, with indegree 0.
Every node except the root has indegree 1.
There is a path from the root to each node.
The root of the tree Yt,~ (and of the type graph 7-) is denoted Root(T). The
label of a node n C NodesT is denoted Label(n). The i th child ofa functor node n
(with respect to ForwardArcs~r U BackArcs:r) is denoted Child(i, n). The elements
of ForwardArcsT are called forward arcs, the elements of BackArcs:r backward
arcs. The information described by a type graph can also be expressed by a
context free grammar. We use the convention that (non-terminal or primitive)
type symbols start with a capital letter, and that the other functor node labels
consist of small letters only.
E x a m p l e : Figure 2.4 gives the graphical representation of the following type
graphs:
T1 ::= nil ] '.'( Int, T1),
T2::=a[f(a[b[W2)[g(Int),
T3 ::= f(T3) [ Max.
In the figure, the nodes of a type graph are represented by their label. We do not
explicitly indicate the direction of the forward arcs. We use the convention that
S A unique natural n u m b e r n is associated with each function symbol f and referred to as
the arityof the symbol. A function symbol of arlty 0 is referred to as a constant. T h e outdegree
of a node labeled with a function symbol is equal to the arity of the function symbol.
18 C H A P T E R 2. A B S T R A C T I N T E R P R E T A T I O N
forward arcs are drawn downwards, while backward arcs are drawn upwards.
The root of the type graph is the topmost node.
There exist several approaches to specify the meaning of a type graph, i.e.
the set of concrete terms that it represents. It is the set of terms recognized by
the type graph considered as a finite automaton, or the language accepted by
the context free grammar that can be derived from the type graph. Here, it is
understood that the nodes labeled 'Int' represent the set of integers, the nodes
labeled 'Real' the set of real numbers. The node labeled _1_represents the empty
set of terms. The node labeled 'Max' represents the set of all terms, including
variables and partially instantiated terms.
Another approach is related to the scheme presented by Yardeni and Shapiro
in [85] where a subclass of logic programs, the Regular Unary Logic (RUb)
programs, is used as a specification language for a class of regular tl/pes. A R U L
program is a logic program satisfying certain syntactic rules: every predicate
is unary, no two head arguments of clauses of the same predicate are top-level
unifiable, every body goal is of the form p(_X) with p a predicate name and _.X
a variable, every variable in a clause occurs exactly once in its head and once
in its body. Given a type graph as defined above, a unary Prolog predicate can
be associated with every node of the type graph, and a set of defining clauses
can be constructed according to the structure of the type graph. Consider for
example the type graph T1 of Figure 2.4, and number the nodes in a depth-first
left-to-right manner, assigning number i to the root node. If we associate with
node i the predicate Pi, then we get the following Prolog program.
pI(-X) : - ( p ~ ( _ x ) ; p s ( - x ) ) .
p2 (nil).
p z ( ' . ' ( - X , -Y)) : - integer(1), PI(-Y).
pl ( n i l ) .
pl('.'(-X, -Y)) : - i n t e g e r ( _ X ) , PI(-Y).
P2 (nil).
pS('.'(-X, -Y)) : - i n t e g e r ( _ X ) , pI(-Y).
The predicate associated with a node labeled .J_ would be 'fail',and a node
labeled 'Max' would bc associated with a predicate generating all the terms that
can be constructed from the functors and constants in the source program, the
variables, and the terms in the primitive types (e.g. Int, Real).
The meaning or denotation of the type graph T1 coincides with the meaning
of the predicate p l / 1 , namely, the set of all lists of integer elements. A formal
definition of the denotation of a type graph based on fixpoint semantics can
also be given directly, i.e. without the intermediate step of associating a Prolog
program with the type graph. A related approach is taken for instance by
Mishra [58], although in a different formalism, to define the interpretation of
the regular tree8 that form the basis of his type system. Let Vars be the set of
variables that can occur in the terms, and SMa m the set of all the terms that can
2.3. E X A M P L E : I N T E G R A T E D T Y P E A N D M O D E I N F E R E N C E 19
Equipped with the pointwise defined operations tO and N, and the subset ordering
C_, the set of interpretations of a type graph 7- forms a complete lattice.
T h e e m p t y interpretation, defined as
is the b o t t o m element.
To define the m e a n i n g of a type graph using fixpoint concepts, we need
a m o n o t o n i c operator on the complete lattice of interpretations for this type
graph. The next definition first introduces an auxiliary function: given a node
of the type g r a p h and an interpretation I, it associates a set of terms with the
node depending on its label and the set of terms associated with its child nodes
by the given interpretation I.
if Label(n) = l then r
else if Label(n) -= lnt then SInt
else if Label(n) = Real then SReat
else if Label(n) = Maz then S Maz
E)7-(n, I) = else if Label(n) = Or then U { I ( m ) I m is a child of n}
f(Q,...,tk)
[ }
tj E I(Child(j,n)),
else if Label(n) = f then
for all j : l <_j <_ k, "
k the outdegree of n
9Note that the Herbrand universe Up of a program P only contains ground terms, so
Up # SMax.
20 C H A P T E R 2. A B S T R A C T I N T E R P R E T A T I O N
We now use this operator in a way that is similar to the use of the immediate
consequence operator Tp when defining the meaning of logic programs [50]. The
operator ~gT can be shown to be monotonic and continuous (i.e. it preserves the
least upperbound of each increasing sequence). The powers of the operator are
defined as usual.
~97- T 0(I) -= I,
~9~r T i(I) = ~9:r(~gcr T (i - l)(I)),
T ,,,(I) = T i(I).
Here, w is the first infinite ordinal and U is the pointwise operation mentioned
above. According to the Fixpoint Theorem of Knaster and Tarski, the operator
has a least fixpoint, and because it is continuous, the least fixpoint is given by
~9~r T w(0). Note that K)~- T w(0)(n), called the denotation of the node n, can
be an infinite set of finite terms if the type graph T contains backward arcs.
When using rigid types, it is not possible to distinguish the set of free variables
from the universe of all terms: both are represented by a node labeled 'Max'.
Consequently, when the value of a term is changed because of the instantiation
of some free variable subterm, then the old type is still a correct description
for the new term value. This property significantly simplifies the definition and
the correctness proofs of the operations for rigid type graphs. For instance, the
unification of two type graphs can be stated in terms of the intersection of the
underlying finite automata, for which well-known algorithms exist.
represent the set of all lists of integer elements. This causes the algorithms that
check for equality or inclusion of type graphs (relations which are needed by the
abstract interpretation algorithm), to be quite complex and inefficient.
2.3. E X A M P L E : I N T E G R A T E D T Y P E AND MODE I N F E R E N C E 21
Note that the compactness of the type graph assures that neither rnl nor m2
are labeled 'Max'. The principal label restriction limits the expressive power
of the type graphs, but it makes them deterministic as recognizers of terms.
For instance, the set of terms {f(a, b), f(c, d)} cannot be represented; it will be
approximated by the tuple-dis~ributive set {f(a, b), f(c, d), f(a, d), f(c, b)} [58,
85]. Compact type graphs satisfying the principal label restriction are called
normal type graphs.
In the examples, we will also refer to the depth restriction for type graphs.
For every function symbol, the depth restriction indicates the maximum number
of nodes having that function symbol as label that can occur on a path in the
underlying tree Tt~,, of the type graph and that starts from the root node. The
restriction is required in order to get a finite subdomain of type-graph environ-
ments, and to guarantee termination of the abstract interpretation procedure.
The depth bound will also turn out to be an important parameter to tune the
precision of the sharing and liveness analysis. The particular value of the bound
can be fixed for the whole program, or chosen on a per clause and functor basis
according to some heuristic. Normal type graphs satisfying the depth restriction
are called restricted type graphs. An algorithm to construct a restricted type
graph with a denotation that contains at least the denotation of some given
normal type graph, can be found in [39]. The report also presents a formal
correctness proof for the algorithm.
Unfortunately, rigid types are not sufficiently precise to deal with partially
instantiated terms. For instance, in Figure 2.5, T1 is a rigid type graph rep-
resenting open-ended trees; an empty open-ended tree is a free variable, so its
22 CHAPTER 2. A B S T R A C T INTERPRETATION
T2 Max T3 Or_
lnt lnt
type is 'Max'. However, T1 is not a compact type graph. The compaction al-
gorithm transforms the type graph T1 into the type graph T2, and causes all
structure information to be lost. The compact representation, corresponding to
mode a (any), does not provide any useful information for code optimization.
Therefore, rigid types are generalized to integrated types which are defined in
the same way as rigid types, except that nodes with the label 'V' are allowed
additionally (see type graph T3 in Figure 2.5). The symbol 'V' represents the
set of all variables (i.e. if Labd(n)= V, then /Dr(n, I) = Vars). In general, in-
tegrated types are not closed under substitution, and the operations become
more complex. Information capturing the dependencies between the values of
variables is needed to obtain sufficiently precise operations. We return to this
topic in Section 2.3.2, and in the sequel, by type we will mean an integrated type,
unless stated otherwise.
The structural properties of compactness and depth restriction are also im-
posed on integrated type graphs. In spite of these properties, there are still
different restricted type graphs representing the same set of terms. For instance,
there exist several alternative representations for the set of all terms.
"TMazI ::-----Max
TMaz2 ::---- V l i n t I Real [ a [f(TMaz 2) [ . . . I t(T~=x 2, TMaz2)
Here a/0, f / l , ..., t/2 enumerates all the functor labels that occur in the source
program. Also, the set of restricted type graphs ordered by set inclusion of the
concretizations does not form a lattice, because there does not exist a least up-
perbound operation. Fortunately, the set of normal type graphs has the algebraic
structure required by the framework of abstract interpretation. There exists a
preorder relation < for normal type graphs satisfying
We also assume that the type graphs have as their root a node that is not labeled
'Or'. Each type graph that we use to represent an execution environment will
have its root labeled by either the tupling functor, /), or _1_.
The type graphs are augmented with sets of constraints expressing dependencies
between variables. The constraints play an i m p o r t a n t role in the precision that
can be achieved by the primitive abstract operations. The abstract domains
that we propose for the sharing and liveness analysis in Chapter 4 and 5, do not
rely on any particular representation for the variable dependencies. In order to
give the reader some intuition about the issues involved, we describe the sets of
constraints as they are used in [40] for the domain of integrated types. Another
approach can be found in [83].
First, we introduce some additional notational conventions to select nodes in
a type graph.
If there are no 'Or'-nodes on the path between n and the selected node, then
the selector ( f 1 , i l ) . . . ( f p - l , i v - x ) . (fp, iv) can be abbreviated as i l . . . i p - l . i v,
and is called a determinate selector. If there are 'Or'-nodes on the path, then
the selector is called non-determinate and cannot be abbreviated. To select a
subterm in a particular concrete t e r m /C, an abbreviated determinate selector
can be used, e.g. for ~: = f(g(a, b)), the expression ~ / 1 . 2 selects b. We only use
selectors that are well defined for the type graph (or term) considered.
A type-graph environment 7- e E AbstrSubD, i.e. an abstract substitution over
a domain D = { X ~ , . . . , X,}, is a tuple (7-, SVaI~-, NUniT-, PShrT-) such that
9 The Type component 7- is a normal integrated type graph with its root
labeled by either the tupling functor () of arity c, or _1_. In the former case,
the value of a variable Xi in a concrete substitution represented by 7-' is
m e a n t to belong to the denotation of the node Child(i, Root(7.)). In the
latter case, the e m p t y set of substitutions is represented.
D e f i n i t i o n 2.3.7 ( C o n c r e t l z a t l o n o f a t y p e - g r a p h e n v i r o n m e n t ) Given
a domain D - { X I , . . . , Xc}, and a type-graph environment 7 . e - (7", SValT",
NUni~-, PShr~r) E AbstrSubD,
TGEnvConc(7.') =
8 = {X~ ~--/C~ I 1 < i < c} for/C -- t/C1,... ,/Cc) ]
0 E ConcrSubD E TGConc(7.) and 8 satisfies the SValT-, NUniT I "
and PShr~ constraint8
The fully formalized definitions can be found in [39]. Here, we restrict ourselves
to some examples of type-graph environments over the domain D = {X1, X2}.
Examples:
1. 7.~ -- (7-1, 0, 0, 0) with 7-1 ::= (f(alb, alc), g(alb, Intlv)). Although the
SVal-constraint does not enforce any dependencies between the values of
11An algorithm for constructing the selected subgraph is in [40]. Ln some cases, nodes are
duplicated.
2.3. EXAMPLE: I N T E G R A T E D T Y P E AND MODE INFERENCE 25
X1 and X2, identical subterms may occur; e.g. the following substitutions
belong to TGEnvConc(T~):
01 = ( x l ~- f(a, a), x 2 ~- g@, 3)},
02 = ( x l ~- I(~, c), x2 ~- g(~, v)}.
2. T~ = (T1, {{X1/1, X2/1}}, g, g)9 The SVal-constraint enforces the first ar-
guments of the values of X1 and X2 to be identical, so 02 E TGEnvConc(T~),
but 0x r TGEnvConc(T~)9
Note: We use the symbol ::-- if we specify a type graph by means of a grammar
rule instead of a triple as in Definition 2.3.1.
In general, if b o t h {Xi/$1,Xj/82} and {Xi/819 , Xj/$2.$} belong to some
SVal-constraint set, then the latter is in fact redundant. Similarly, if the SVal-
constraint set enforces the sharing of free variables, it may be possible to sim-
plify the NUni- or PShr-constraint sets9 A normal form of the type-graph en-
vironments can be computed such that most of the redundant components are
removed, while the denotation is left unchanged. It is beyond the present scope
to discuss the issue in detail (see [39]). Also in [39], it is proved that the algebraic
structure imposed by the framework is satisfied for the domain of type-graph en-
vironments. For instance, a preorder relation _<Ta and an upperbound operation
TGOpp are defined, and the following property is established.
we summarize some of the basic results. The operations do not deal explicitly
with the trivial case of T~, because the result of any abstract operation on the
bottom element is again 7"~.. The bottom element represents that the program
point it decorates is never reached during program execution.
W e firstfix the notation for concrete substitutions that will be used in the re-
mainder of the book. For a fixed enumeration of program variables, Xx,..., X~,
a concrete term environment is a term K = I]~1, .... E n ) , which represents the
concrete substitution 0 = ~XI *'- El, ..., X,~ ~-- En}. The subterm Ei is also
represented as Eft.
Recall that a unification call P can have either of two forms: Xi = Xj or
Xi = f(Xil,...,Xi#). Therefore, AbstrUnify is defined as a function that is
polymorphic in its third argument:
TGUnify(T/n', i, j): returns the type-graph environment 7-o,f resulting from ab-
stract unification of the i th and jth component of T/n. This corresponds to
the basic operation X i = X j .
TGUnify(~n e, i,f(Q,..., ij)): returns the type-graph environment To~te result-
ing from abstract unification of the ith component of T/n and a type graph
having root node labeled f and as sons the i~h,..., i~h components of T/,~.
This corresponds to the basic operation 3 ( / = f ( X i l , . . . , X i , ) .
The unification of type-graph environments is a rather complex operation where
the constraint sets play an important role. We do not present any details here,
but we will need the following safety results for type-graph environments when
defining the abstract operations for sharing and liveness analysis.
T h e o r e m 2.3.9 ( S a f e t y of TGOnify(-, i, j) [38]) Let Tin ~ be a type-graph en-
vironment and ICin E TGEnvConc(T/ne). Let I < i < j <_ arity(Label(Root(Tin))).
I r a = m g u ( E i n / i , E i n / j ) i~ not fail, E o . t = Ein~ r, and 7"o.t' = TGUnify(T/n',
i , j ) , then Eont E TGEnvConc(Tont').
T h e o r e m 2.3.10 ( S a f e t y o f T G U n i f y ( - , i , f ( i t , . . . , i j ) ) [38]) Let 7"i,~~ be a
type-graph environment and Ein E TGEnvConc(T/ne). Let 1 <_ i, i t , . . . , i j
arit~Label(Root(Tin))) such that i, it, . . . . ij are pairwise distinct, and f is a
functor of arity j . I f a = rngu(ICi,/i, f ( E i n / i l , . . . , E i n / / j ) ) is not fail, Eo~t =
Ei,~ a, and To,,,e =TGUnify(T/n e, i, f ( it, . . . , ij ) ), then Eo=tE rGEnvConc(To,,te).
Before considering the Procedure-Entry and Procedure-Exit operations, we intro-
duce an auxiliary function. Recall that all Prolog programs are assumed to be
in normal form, so the arguments of a call (or of the head of a clause), can
be given as a subset (Xil,...,Xi..} of the domain of the clause environment
{Xz,..., X,~}, and an injection i: {I,..., m } --, {1,..., n}: k ~ i~ can be used
to formally define the restriction of the clause environment to the arguments of
the call (or the head of the clause).
In the second step of procedure entry, T~,t~ ~ is renamed for the domain of vari-
ables for each of the clauses matching with the call P and the local variables
of those clauses are initialized. Assume that T~,t,~ = (T,,t,, SValTrstr, NUniT, str,
PShrT- t,), and T~,t~ ::= (Tt . . . . , T,~). Assume that the cai~ is P ( X 1 , . . . , X,~,),
and the jth clause P(Z{ , .. . g ~ ) : .- s t , . .~ BJq j '. with domain
9 . , {Z~, , Z,~,J Z,,,+I,
. . . , Z ~ , } . Then T~ = (7-j, SVal~rj, NUni~-j, PShrT-j), where Tj ::= (Tx,... ,Tin,
V, .. ., V), which reflects that the local variables {Z,,~+x,..., J Z~,} are free vari-
ables on procedure entry. Also the constraint sets are renamed for the domain
of variables of the clause.
component and for a concrete term environment the renaming is done implicitly
by the injection.
The next theorem reformulates the safety requirement as introduced in Sec-
tion 2.2.3 in terms of type-graph environments and concrete term environments.
It states that if P is called with a concrete substitution represented by the term
environment /Ci,, E TGEnvConc(Ti,i e) and is solved by means of the k th defin-
ing clause for P which returns the term environment/C E TGFnvConc(7-ont~),
then the projection of the latter term environment on the arguments of the head
of that clause is described by the abstract type-graph environment T~~ e. The
safety of the first step of procedure exit follows from the safety of TGRestrict, the
properties of the TGUpp operation and the monotonicity of the concretization
function TGEnvConc.
In the second step of procedure exit, the extension operation TGExtend, reintro-
duces the other variables of the domain of the calling environment and makes
explicit the effect on those variables of the execution of the procedure: 7-o,f -
TGExtend(7~,, e, ~,t~', it=n). Due to sharing existing prior to the call, the value
of variables not involved in the call can be changed too. Hence, the extension
operation has to derive how their type component and the constraint sets are
affected by executing the call. We do not present any details here.
The safety requirement of the second step is stated in the following theorem.
For the development of an abstract domain for sharing and liveness analysis
(Chapters 4 and 5), we regard the set of type-graph environments and the op-
erations TGUnify, TGRestrict and TGExtend as an abstract data type and its
operations. The safety of the abstract operations for procedure entry and pro-
cedure exit will be based on the safety results for type-graph environments.
Chapter 3
Related Work
In this chapter, we review some of the papers that inspired our design of the
abstract domain. In the first section, we consider several approaches for 8hating
analysia, the prerequisite for liveness analysis. In the second section, we summa-
rize related research on determining the likeness of data structures, both in the
context of logic programming languages and for functional languages. The last
section discusses experimental results of code optimization based on run-time
properties derived by abstract interpretation.
ther discussion). Plaisted [68] described a general method for detecting places
in a Prolog program where structures with loops might be created and proposed
a preprocessor for adding tests at such places that cause subgoals to fail if they
return infinite terms. W e discuss Plaisted's method more extensively than oth-
ers because it first introduced the idea of alternating paths (a closure operation
for propagating the sharing relation without full transitive closure). This con-
cept inspired our definitions of the primitive operations (Section 4.1.2) for the
derivation of sharing information that is sufficiently precise for the application
of liveness analysis.
The abstract domain used by Plaisted is based on the so-called Binary
Term/Literal Schemata. In essence, these schemata provide a finite way to
represent infinite sets of terms/literals. The schemata retain partial information
about term instances: some of their subterms are represented by term locators r~,
causing information about substructures and the multiplicity of variables to be
lost. A given term locator may occur more than once in a binary term schema,
indicating more than one occurrences of some subterm. The information that is
relevant for detecting possible infinite loops, namely the pairs of positions in the
subterms that contain common variables, is represented by means of functions
returning (an approximation of) the multiplicity of variable occurrences.
9 Is(ri): returns the number of isolated variables of term locator r~, that is,
variables that occur only in r~ and nowhere else.
r4 rl
/ #l
## # r3'
## /
#
## 1#
II I r3"
I
# I
r5 - - r2
and second step consist of several rules to adjust the Rep, Is and VC functions
reflecting their behavior under unification. For example, when a term locator
is unified against a ground term, its variable count VC is set to zero. This
in turn will influence other Rep relations. When unifying the example binary
literal schemata given above, Rep(rT, rl), Is(rT) and VC(rT) will be set to zero,
because the term locator'r7 is unified against the constant 'c'. We do not discuss
these rules in further detail as there is no direct relationship with our work. The
last step of the algorithm proceeds by constructing a graph, the nodes of which
are t e r m locator occurrences of the term/literal schemata being unified, and
edges representing the Rep relation. Note that a term locator m a y label more
than one node in the graph if it has several occurrences in the term schemata
being unified. In the example, there are two occurrences of the term locator
r3; we distinguish them as r~ and r~'. The resulting graph is augmented with
unification edges (connecting nodes labeled with t e r m locators having non-zero
variable count VC, such that one of the locators occurs in a term that the other
locator is unified against) and label edges (connecting nodes labeled with the
same t e r m locator). Figure 3.1 shows the result of this step when unifying
the above example binary literal schemata. Unification edges are represented as
dashed edges, Rep edges as full lines, and label edges as full lines marked 'lb'. The
alternating cycles of Rep and label edges on the one hand and unification edges
on the other hand, indicate places where circular terms might be introduced
by the unification. For Figure 3.1, we have such a cycle r4 - r5 - r2 - rl -
r4. Indeed, this reflects the loop that is created when unifying the instances
P(g( X, U), X, Y, c) and P(g( h(V, Y), f( Z) ), V, Z, W) mentioned above. The loop
rl - r2 - r6 - r~' - r~ - rl is not an alternating cycle, but it could be interpreted
as indicating the possible creation of internal sharing in terms represented by
the t e r m locator rl. Unfortunately, Plaisted's paper does not address soundness
proofs for these operations; the intuition behind the graph transformation rules
is not always apparent.
We do not use the concept of term locators in our abstract domain, because it
does not provide a proper abstraction for representing sharing of term structures.
However, for the specifications of our abstract operations, we borrow the idea of
alternating paths to propagate the sharing relation. Roughly speaking, the Rep
and label edges correspond with the sharing (or old) edges in our application,
the unification edges to the binding (or new) edges (see Section 4.1.2).
34 CHAPTER 3. RELATED WORK
The paper of SCndergaard [70] is concerned with the same problem of de-
termining circumstances under which the occur check may be safely dispensed
with. His method is a simplification of Plaisted's within a framework of top-
down abstract interpretation for Prolog programs. He does not use the concept
of term locators. Informally, variables are aliased or share if in some execution
of the program they may be bound to terms which contain a common variable.
Abstract substitutions are pairs of the form A = ( G , R ) where G is a set of
variables and R is a binary relation on variables. The intended meaning is that
A describes the set of all substitutions, each of which makes (at least) all the
variables in G ground and has no more sharing than that specified by R. A pair
(X, Y) E R, where X ~ Y, allows X and Y to share a variable, while (X, X) 6 R
allows X to have some multiple variable constituent. Note that R is s y m m e t -
ric but not necessarily reflexive. No sharing of ground terms is represented (if
X 6 G then for any variable Y, (X, Y) ~ R). When restricted to a finite set of
variables (e.g. the variables of some program clause), the domain yields a lattice
of finite height under the lexical ordering based on set inclusion. Sondergaard
further introduces the concept of preunification. It is a kind of partial evalua-
tion of the unification of predicate arguments which derives templates for each
pair of subgoal and clause heading. The abstract unification algorithm is only
described informally via a series of examples.
Codish, D a m s and Yardeni [17, 19] use the same notion of abstract substi-
tution as defined by SOndergaard, but in a framework for b o t t o m up abstract
interpretation of logic programs. Their contribution provides a rigorous seman-
tics for the analysis and a soundness proof for the abstract unification algorithm.
We include some of the formal definitions here.
The set Sub represents the set of idempotent concrete substitutions, defined
in the usual way. The set Vats denotes the set of variables and PVar C_ Vats
denotes the set of variables that may occur in programs.
The set of abstract substitutions, ASub, is the set of pairs (G, R) G 2 pvar x
2 (PwrxPvar) for which
1. G is finite
2. R is symmetric
3. R n ( G x P V a r ) = O
Let ASub_l_ = ASub U {A_}. The abstraction function, a : 2 s~b ---, ASub• is
defined by a(0) = _1_,and for @ :/: 0, a ( | = (G, R) where
0co of a variable)
3.1. ALIASING AND POINTER ANALYSIS 35
The ordering E on ASub• is defined by A_ _E (G, R), and (G1, R1) _E (G2, R2)
iff G1 _D G2 and R1 C_ R2. The concretization function, q, : ASub• ---* 2 s~b, is
defined by -y(_l_) = 0, and for A r _k,
For example, let X , Y 9 PVar, U E Vats \ PVar, and consider the concrete
substitutions 0 = {X ~-- f(U, U), Y ~-- g(U)} and ~o = {X ~- b}, then
Note that the internal sharing in the variable X is represented by a reflexive pair
(X, X) in the sharing relation. Also, the sharing relation is not transitive; e.g.
an abstract substitution A = {(X, Y), (Y, Z)} specifies potential sharing between
X and Y and between Y and Z, but not between X and Z.
The core of the analysis is the abstract unification algorithm, which is a
transition system mimicking the concrete algorithm for finding the most general
solution of a set of equations. It is divided into two parts. The input is a set of
equations, together with an abstract substitution. The first step (called preuni-
fication in [70]) solves the concrete part of the equations and corresponds to the
standard Herbrand algorithm for solving a set of equations. The second step of
the algorithm solves the abstract part of the equations. The solution of a single
abstract equation cA, where e =_ tl = t~ and )~ = (G, R), is approximated by
considering the variable multiplicity of tile equation. For example, if one side of
the abstract equation is ground then the solution grounds all other variables and
causes no sharing; i.e. if vats(t1) C_ G or vats(t2) C G, then e,~ results in the
abstract substitution ,V = (G tJ vats(e), R \ vats(e)), where R \ S is a shorthand
for R \ ((S x PVar) tJ (PVar x S)). In this way, the propagation of groundness
is reflected. In the other cases, aliasing propagation is required. For example, if
e - P ( X , Y , Z ) = P(A, f(A, B), B) and A = (0,0), then we get the abstract sub-
stitution ,V = (0, R), where R = {(A, X), (A, Y), (B, Y), (B, Z), (X, Y), (Y, Z)}.
Note that no sharing between A and B is derived. Indeed, such sharing could
only be introduced by the unification if there was internal sharing in Y prior to
the unification. We refer to [17, 19] for the formal definitions of the abstract uni-
fication algorithm and its soundness proof. The concept of alternating sequences
(or mixed paths) is implicitly used in tile algorithm.
In the sharing analysis approach of Jacobs and Langen [37] and Muthukumar
and Hermenegildo [63], a concrete substitution 0 for the set of variables in a
clause, PVar, is abstracted by means of a set of sets of variables. First, each
variable U in the universe of all variables, UVar, is associated with the set of
variables through which it occurs in the given substitution 0.
occs(o, u) = { x I x 9 u e var4XO))
A: --, 2 :0 U) I V c UVar}.
36 CHAPTER 3. RELATED WORK
For example, the substitution 8 = {X *-- I(U, V), Y ~-- g(V), Z ,-- a} is repre-
sented by the abstract sharing A(8) -- {0, {X, Y}, {X}}. Intuitively, variables
that occur together in a set have a variable in common, variables that do not
occur in any set are ground. The abstraction function .4 is extended to sets of
substitutions as follows.
2 - , 2 Pv ' : e H L3,4(0).
060
The set of abstract sharings can be ordered by set inclusion C, and set union
U yields a safe least upperbound operation. The concretization function 7 maps
an abstract sharing to the set of all substitutions it approximates.
. . 7 : 2 2Pva ~ 2 : A H {0 I A(0) c_
The abstract substitution which makes all program variables in a clause ground
is {0}. The bottom element in the lattice is 0, representing failure. The top
element in the lattice is the powerset of all the clause variables.
In this abstract domain, internal sharing within a variable is not represented
explicitly. This causes a loss of precision in the unification algorithm, as was
pointed out by Codish et al. in [17]. Consider the unification X = f(Y, Z)
and an abstract sharing Ai,~ = {{X},{Y}, {Z}}, i.e. neither of the variables
is ground, nor shares any free variable with the others. A substitution such
as 8 = { X ~ f(U, U), Y *-- g(V), Z ~-- W}, belongs to the concretization of
Ai,~. Consequently, sharing between Y and Z has to be derived by the abstract
unification, even for those cases where no internal sharing in the variable X is
possible (Ao~,, = {{X, Y}, {X, Z}, {X, Y, Z}}).
A formalization of the abstract unification algorithm and the other prim-
itive operations required in the framework of Bruynooghe [11], is given by
Muthukumar and Hermenegildo in [64]. There they argue that the combined
determination of sharing and freeness information yields operations that are
more precise than if they were computed separately. Freeness information al-
lows to distinguish between variables that are just bound to another variable
and variables possibly bound to a complex term. For that purpose, they use
abstract substitutions consisting of two components: a sharing component as
described above and a freeness component given by means of a mapping from
PVar into the set {G, F, N F } , where G, F, N F represent respectively ground,
free, and potentially non-free. The second component thus provides the mode
information discussed in the previous chapter. The interaction between the two
components in the unification algorithm increases the accuracy of the analysis to
some extent. For instance, if X, Y, Z are known to be free before the unification
3.1. A L I A S I N G A N D P O I N T E R A N A L Y S I S 37
is not allowed because of the finiteness requirement for abstract domains. The
inference rules are transitive-like operations propagating the alias relation. For
example,
Comp('2"l, X) AL Y &
Comp(T2, X) AL Z & Comp(']-2, Y) AL Z.
T2 is a component type of T1
Such rules can cause imprecision because they do not fully exploit the structure
information available in type graphs for relating the (component) types. The
formalism is suited for liveness analysis if expressions of the form Live(X) and
Live(CompCT, X)) are added to represent the (parts of) instantiations that may
not be deallocate& The correctness of the method was argued only informally.
In the paper [60], we formalized an abstract domain based on type graphs,
and an abstract unification operation to analyze how the terms to which program
variables are bound, can share substructures at run time. Formal proofs of the
soundness of the operation are given in [61]. The abstract domain in the present
work is an enhanced version of the one given there, yielding more precise results
for the extension operation.
The problem of structure sharing is significantly different in the context of
functional or imperative languages, primarily because the sharing introduced by
unification is significantly more complicated to analyze at compile time than
parameter passing, term construction and subterm selection. Nevertheless, so-
lutions to related structured-data dependency problems have appeared in the
literature. For instance, in order to transform Lisp programs for concurrent
execution, Larus and Hilfinger [46] describe a technique for detecting conflicts
between pairs of read and write accesses to structures linked by pointers. Their
method is based on alias graphs that model a collection of linked structures
in memory: the nodes correspond to instances of a structure type; the directed
edges represent pointers between the structures; summary nodes are used to join
several nodes of the graph into a single node, thereby make it possible to repre-
sent unbounded chains of run-time objects. The alias graphs finitely represent
the potential sharing of structure, visible at any point in the program. Horwitz,
Pfeiffer and Reps [32] describe a similar technique to determine an approxima-
tion of the actual layouts of memory that can arise at each program point during
execution. Their method is also intended for languages that have pointer-valued
variables, heap-allocated storage and a destructive update operation.
The work of Chase, Wegman and Zadeck [15] is not directly applicable to logic
programming either. They look at liveness analysis for imperative languages,
where full unification is not an issue. Their approach summarizes the linked
data structures allocated in a heap by a 8forage shape graph, in which one node
corresponds to possibly many nodes in the heap. As in the approach of Janssens
and Bruynooghe [12, 38, 40], the authors avoid the loss of information incurred
in prior contributions [32, 46] by imposing a k-depth bound beyond which the
structure is collapsed to a single node. As for the type inferencing used as the first
layer of our liveness analysis, they rather seek to fold the structure into something
like a rational tree or tree automaton. The heuristical method they use to fold
the tree consists of superimposing only structure cells that are generated by the
same instruction. The approach of ]anssens and Bruynooghe has been to impose
a fixed bound on the depth beyond which folding is required. The approach of
Chase, Wegman and Zadeck in effect enables different bounds to be used for
different programs, while preserving the finite ascending chain property of the
abstract domain, needed to ensure termination. To derive information useful
for compile-time garbage collection, the storage shape analysis is followed by a
heap reference counting analysis, during which the nodes of the storage shape
graphs are annotated with reference counts from the lattice (0, 1, oo) similar to
the approach of Hudak [33]. An extended model of heap reference counting is
also suited to reason about acyclic data structures represented by cyclic storage
shape graphs.
CP, the (top of) heap pointer H, the (top of) trail pointer TR, the current envi-
ronment E, and the current choice-point B. A n environment corresponds to an
activation of some clause and contains information about bindings for the clause
variables and about how to continue the execution. For each call to a predicate
with more than one defining clause, a choice-point is created on the local stack,
containing the information neededon backtracking. In order to reduce the space
needed for an environment on the local stack, clause variables are classified ac-
cording to their local life-time in the clause: variables that occur only once in
the clause are called void; variables that have more than one occurrence, but
do not need to survive a call, are called temporary; all other variables are called
permanent. Only the bindings for the permanent variables have to be stored in
an environment on the local stack.
Prolog programs are compiled into intermediate code, approximately one in-
struction per Prolog symbol. The instruction set is classifiedinto get, put, unify,
procedural and indezing instructions. The put-instructions put the arguments of
a call into the argument registers. The indexing instructions determine the set
of clauses that possibly match with the call and create a choice-point if needed.
The procedural instructions deal with control transfer and environment han-
dling. The get-instructions take care of unifying a call and the head of some
clause. Components of compound terms are encoded by means of the unify in-
structions. Unify instructions operate in either of two modes: in write mode,
they create a new structure on the heap, and in read mode, they perform unifica-
tion with an existing term. The put_listand put_struct instructions always switch
to write mode, the get_Hst and get_struct instructions switch to write m o d e if
the corresponding argument register contains an uninstantiated variable. If the
corresponding argument register does not contain an uninstantiated variable,
then the get_listand get_struct instructions switch to read mode. For instance,
if the instruction unify_atom cte is executed in write mode, it allocates a new
value cell on the heap, initializesit with tag C O N S T and value field cte, and
increments the heap pointer H. Likewise, the instruction u~ify_val Xi copies the
dereferenced value cell determined by Xi on top of the heap if it is not a free
variable. If the dereferenced value cell represents a free variable, then a refer-
ence to that variable is copied on top of the heap. The instruction unify_var Xi
allocates an U N D E F value cell on top of the heap and sets Xi pointing to it.
Mari~n et al. [52] describe an experiment set up to assess the impact of
the information about modes, types, length of reference chains and liveness of
data structures on the extent of code optimizations that can be performed by a
compiler. The paper illustrates how m o d e information can be used to enhance
clause indexing and to specialize the unify instructions generated for arguments
known to be input or output (thus eliminating run-time checking). Type infor-
mation can be used to remove tag-checking instructions, for instance to improve
the processing of arithmetic expressions. Knowledge about the reference chain
length can be used to deal more efficiently with dereferencing. Liveness infor-
mation makes it possible to recognize at compile time whether storage occupied
by a data structure can be reused to create new data structures in the same
3.3. CODE OPTIMIZATION 43
procedure.
The experiments discussed consider only a restricted case of local reuse of
garbage cells, namely reuse in a construction operation within the same chunk
of the clause where the garbage is created. A chunk is defined as either a chunk-
without-head or a head immediately followed by a chunk-without-head. A chunk-
without-head is a number, possibly zero, of in-line calls followed by an out-of-line
call. In-line calls are calls which use only argument registers of the WAM (e.g.
built-in primitive predicates). Consider the following clause.
The clause consists of one chunk only, so the method will be applicable. However
for the recursive clause of n r e v / 2 , the selection and the construction operation
belong to different chunks because nrev(_U, _RID is an out-of-line call.
9 Before constructing the new term, set a register, D, to the saved structure.
D is the destructive overwrite register.
The best results are obtained when a term must be constructed with the same
functor as the garbage cell and which has m a n y arguments in common. Basically,
some additional move instructions are introduced a n d / o r replace instructions for
pushing new terms on the heap.
44 CHAPTER 3. RELATED W O R K
stricted, and suggests that further work should be done to enhance the abstract
domain.
In [72, 73, 74], Taylor describes an abstract domain suited for mode, reference
chain and choice-point analysis. The analyzer differentiates between different
categories of constants, list and compound term types, and keeps track of alias
information. The P a r m a compiler developed by Taylor is a native (machine)
code compiler for the MIPS RISC architecture; it is not based on the WAM
instruction set, but uses a similar memory model. The information derived by
global analysis is used to reduce the cost of dereferencing and trailing operations.
The statistics presented in the paper on performance improvement and code
size reduction are very promising and indicate that sophisticated native code
compilers using global data-flow analysis can lead to high-performance Prolog
implementations.
Chapter 4
Sharing Analysis
This chapter consists of three parts. In the first section, we motivate the de-
sign of the sharing environments by first identifying the concrete information
of interest and then constructing an appropriate abstraction. The second sec-
tion contains the formal definitions of the primitive operations for the domain
of sharing environments and their soundness proofs. The final section provides
a few examples and discusses the strength of the sharing analysis.
Definition 4.1.2 For a (possibh./ non-ground) term IC and s E O(IC), the sub-
term IC/s and functor or variable K[s] determined by s are defined as follows.
~c/s = if (8 = ~) then
cue ~:~/s', where (s = i.8') and (~: - f ( ~ : l , . . . , ~o)),
K:[s] = if (~:18 ~ va,s) then ~ / s cue f, where (~:/s - f ( # c , , . . . , SCo)).
TermShift is a closure operation on sets of pairs of occurrences. W e use it in
correspondence with the idea that if two terms occupy the same storage cells
in an implementation, then their corresponding subtcrrns also occupy the same
storage cells. (Note, ~:/r - ~:/, ~ (r.t e O(r) ~ ,.t e O(~:)).)
Definition 4.1.3 For K a term and P~z a set of unordered pairs (r,s) such
that r , s ~_ a(IC) and IClr - ICIa, we define VermShift(/C, Px:) = {(r.t,8.01
(r, s) (~ Px: & r.L s.t E O(/C)}.
Some subterms cannot share their representation, while in the implementations
for which our analysis is intended, multiple occurrences of the same free variable
always share their representation. So, we introduce two concepts to characterize
meaningful sets of sharing pairs for a given term/C.
4.1. S H A R I N G E N V I R O N M E N T S 49
D e f i n i t i o n 4.1.4 For a term IC, we call a set Pp: of unordered pairs (r, s) such
that r, s E O ( E ) , a pre-sharing component for K: if the following conditions are
met.
I. (r, s) E P~ :~ IC/r ___IC/s
Only identical subterms can share.
D e f i n i t i o n 4.1.5 For a term K:, we call a set P~ of unordered pairs (r, s) such
that r, s E O(IC), a concrete sharing component for K: if Px: is a pre-sharin#
component for E and it satisfies the following condition, which corresponds to the
property that multiple occurrences of a free variable share their representation.
D e f i n i t i o n 4.1.6 For a term IC and a set Ppc of unordered pairs (r, s) such that
r, s E O(IC), we define
O
E
h h h h Ihl
A A / ~ .... . / N
X f g Y g f g f
I IY.'"
Z
9 I ,
LI
Z ....... Z
(a) (b) (c) (d)
Skeletons Environments Skeletons Environments
I
Figure 4.1: Concrete input (a) and output (b) sharing environments for the Unify
operation, the DAGs in structure-copying implementations before (c) and after
(d) unification, and the representations (e), (f) in structure-sharing implemen-
tations.
unification. For the term part, this means applying the most general unifier of
ICi,,/i and ICi,~/j, defined for example as in [47] and denoted by mgu(lCi,Ji,ICi,~/j).
(Note that, from the definition of substitution application, we have the property,
lCo,. = Ic,,,~ =~ o g q ~ ) c_ OgCo.,).)
For the sharing part expressing the effect of unification means adding pairs
indicating positions where unification has introduced new sharing of subterms.
The language implementations that we address create new sharing when binding
a variable to an existing structure. The sharing existing in the input term prior
to the unification is preserved and extended downwards in the case that the
term is further instantiated by the unification (cfr. the TermShift operation).
Finally, the new sharing propagates through the old sharing because, for the
representations of terms that we consider, sharing is transitive.
For example, consider the following term and sharing component (see also
Figure 4.1.a).
4.1. SHARING E N V I R O N M E N T S 51
x,
o o o
h h h h h h
I I I "'--~'" I I I I ""k j I
g U U -: .~_-..- U : . . - U --_.-U --i-
-'~ V -~~- -__. - Y ~ _. .~- u' . ~ .~
Figure 4.2: The order in which unification subproblems are solved affects the
sharing created.
Suppose that the call to be executed is P(-X2,-X4), where the predicate P/2 is
defined by the single clause P(-Yx,-Y2) :- -YI=-Y2. Computing Restrict((K:i,,,
CSharing~:,,),P(_X2,_X~)) then results in the sharing environment (K:r,t,,
CSharing~:,,,,) of Figure 4.3.b.
o <> o o
f h Y h h h h h f h Z h
I XA
U Y.."
,.." gA Z
A
X Y g
A Z
A ..... A I
I/
Z..."
I/
Z..'
I :-. ...t..'.
z,..9.: ..... :. z. " M-':" :....z.'.
. . . . . . :: ."
Figure 4.3: Concrete input Ca) and output (b) sharing environments for the
Restrict operation. Concrete input (a), (c) and output (d) sharing environments
for the Extend operation.
Notice that selectors always determine nodes not labeled 'Or'. The type-graph
analogue of the TerrnShift operation is a closure operation on sets of pairs of
type-graph nodes.
D e f i n i t i o n 4.1.10 For T a type graph and PT" a set of pairs (r,s) such that
r, s E NodesT and Label(r) = Label(s) and Label(s) ~ 'Or', define
l if s= e then e
else let s = i.s I in
Sel(K:, s) = if ~ l i ~ Vars then (i, V)
else if IC/ i E S1.t then (i, Int)
else if IC/i E SR,=l then ( i, Rea 0
else (i, IC[i]).Sel( IC/ i, s').
The Sel function translates the term occurrence s for a term/C into a type-graph
selector, e.g.,
Sel((b,f(a, g(c))),2.2) = (2,f).(2,g).
Type graphs are the basis of our abstract term environment. We use type graphs
having their root labeled by the tupling functor 0 with arity corresponding to the
number of program variables in the domain of the abstract term environment.
As is explained in Section 2.3, for the purposes of a type inference system a type
graph is augmented with information about what positions in the denoted terms
can be occupied by the same variable.
The type graph 7" captures the structure of the terms in the set denoted by
7-'. The sharing of free variables within those terms is captured by the shar-
ing component VSharingT-. We do not require any particular representation for
VSharingT. We regard T e as an abstract data type and TGUnify, TGRestrict,
and TGExtend as its operations. This abstract data type hides the variable shar-
ing component but exports the type-graph environment, 7". For this abstract
data type, we assume an operation TGEnvConc, which maps T e to the subset
of TGConc(7-) that conforms to VSharingT-. In the sequel, we will also use the
following property.
The property can be proved by induction on the length of the path s, by using
the inclusion TGEnvConc(T') C TGConc(T) and the definition of TGConc.
In order to represent abstract sharing of structure, we borrow such an ex-
isting abstract term environment and instrument it by analogy to the concrete
sharing environment. The instrumentation needs the portion of the abstract
term environment that represents term structure. It does not need the portion
that represents variable sharing. The borrowed abstract term environment and
operations are used to infer what terms program variables can be bound to. The
instrumentation is used to infer shared structures in the representation of those
terms.
D e f i n i t i o n 4.1.14 For art abstract term environment T" corttainirt9 the type
graph 7- = (NodesT, ForwardArcs:T, BackArcsT>, we call a set of unordered pairs
(re, n) such that m , n E Nodes~r, Label(m)= Label(n), and Label(m) r ' O r ' art
abstract sharing component for T .
An abstract sharing component, ASharingcr, for a type graph T, constrains
the sharing that may be possible in the terms represented by the type graph T.
An abstract sharing environment, consisting of a type graph and an abstract
sharing component, characterizes the current binding environment in a Prolog-
9style computation: free variables in a binding must match variable nodes (nodes
labeled 'V') in the type graph; multiple occurrences of the same variable must
match type-graph nodes that are connected by a sharing edge (an element of
ASharing7-), since the language implementation is assumed to give them a shared
representation. Unlike concrete sharing components, which are TermSbift closed,
abstract sharing components need not be closed w.r.t. TGShift. The concretiza-
tion function, defined in Section 4.1.3 below, assures a correct interpretation by
first calling the TGShift operation.
Note that, while a concrete sharing component contains pairs of occurrences
indicating identical subterms, the definition of an abstract sharing component
requires only that sharing edges connect nodes having the same label. We opted
for the weaker condition in the case of abstract sharing components because it
simplifies the primitive operations and safety proofs, while retaining enough of
the precision for the liveness analysis. As an alternative, we could define shar-
ing edges to connect nodes determining subgraphs with non-empty intersection
(as defined in [38, 40]). An even stronger condition is described below in Sec-
tion 4.3.2. The weaker condition allows edges in an abstract sharing component
that may not be relevant with respect to the type graph they are associated
with. We will discuss the consequences for the precision of the sharing analysis
in Section 4.3.2.
While CSharings: does not contain reflexive pairs, ASharingT- possibly does.
Because of backarcs, a node in a type graph can correspond to several subterms
in a concrete term represented by the type graph. Therefore, self-edges are
significant in an abstract sharing component.
:::" V v V
''"-..= .... .o.-*~176"*..o.........-o~
(a) Co)
Figure 4.4: An abstract type graph and sharing component (a) and an abstract
sharing environment (b).
self-edge). In the type-graph representation, all the elements of the list are
represented by the same node. The following are among the concrete terms
represented: _0, [ I I _U], [_X, _I I -Y], [_3[,_Y, _Z,_Y I _W]. [_X I _X] is not.
Definition 4.1.15 For a type graph 7", and two sets B, C of unordered pairs
( R, S) such that R, S E Nodes:T, toe define
AlternatlngCIosure(B, C) =
4.1. SHARING E N V I R O N M E N T S 59
Abs~rUnify(OT,t(L,F,R))
Tin <> Tout <>
Or V V V t Or [st Or
Program 4.h i n s e r t / 3
(a) (b)
Figure 4.6i Abstract input (a) and output (b) sharing environments for the
AbstrOnify operation.
sion operations. The definitionof the abstract unificationis similarto the defini-
tion of the concrete unification. Let AbstrUnify((T/,,',ASharingT,,,),i,j) = {To,,t',
ASharing~- ,). For the type-graph part, we take To,,t" = TGUnify('T/,,',i,j),
where the function TGUnify returns the abstract term environment resulting
from abstract unificationof the ith and jth components of T. (See [38, 40, 83].)
Let us consider the sharing component. As in the concrete case, structures that
are (possibly) sharing before unificationremain (possibly) sharing after unifica-
tion. However, the abstract sharing component for T/n must be converted into an
abstract sharing component for To,,t.To this end, we will define (see Section 4.2)
a function Convert that takes the input sharing TGShift('T/,~,ASharingT-,,~),ex-
pressed in terms of the nodes of T/,,,and reexpresses it in terms of the nodes of
To,,t. Special care has to be taken with edges in TGShift(T/,,ASharingT-,,)that
involve nodes labeled 'V'. As the output type graph To,,treflects the effect of
unification,nodes labeled 'V' in 7~,~can correspond to nodes in Toat not labeled
'V', indicating variables that got bound to some structure.
As in the concrete unification, AbstrUnify introduces new sharing where a
variable is unifiedagainst some other structure. These new sharing edges are col-
lected and mapped onto the output type graph To,,tby a function BindingEdges.
This function returns an edge for every node in 7"o,,tcorresponding to a node
labeled 'V' in the gth or jth component of T/,~.
The main differencewith concrete unificationis that the abstract unification
uses AlternatingClosure,rather than TransitiveClosureto propagate the sharing
relation. The AlternatingClosureoperation adds only the pairs of nodes connected
by a sequence of edges in which the old and new edges alternate.
Example: Figure 4.6.a represents a type graph T and an abstract sharing com-
ponent ASharingT- -- {(ml, m8), (m9, m10)}. For the nodes of the type graph,
we show both their name (e.g.,m3) and label (e.g.,f = Label(m3)). Note that
(..X, f(_X,~(_g,a)), f(a,f(a,a)))and (_~, f(_Y,f(-X,a)), f(a,:f(a,a)))
are both represented by (T', ASharingT), but (l, f(_X,f(_X,a)), f(a,f(a,
a))) is not, because m8 is not self-sharing. To compute AbstrOnify((T',
ASharingT), 2, 3), we first use TGUnlfy(T t, 2,3) to construct the output type
graph T' (see Figure 4.6.b). Then we compute
4.1. SHARING ENVIRONMENTS 61
In Figure 4.6.b the elements of BindingEdges are represented as dashed lines and
the elements of Convert as dotted lines. Note that the sharing edge (ml, m8) in
T corresponds to two sharing edges (n5, n6), (n5, nl0) in T'.
The definition of the abstract restriction operation AbstrRestrict is also very
similar to the definition of the concrete restriction. Let AbstrRestrict((Ti, ",
ASharing~r~,,), c a l l ) = (Tt,tr', ASharing~r ,.). For the type-graph part, we take
T~~ ~ = TGRestrict(T~, ~, call), where the function TGRestrict returns the ab-
stract term environment resulting from the restriction of the term environment
T~,," to the variables of the call. (See [38, 40].) For the sharing component, we
need a function that takes the input sharing TGShift(Ti,~, AShafing~r~.,), expressed
in terms of the nodes of T~,, and reexpresses the edges pertaining to the call in
terms of the nodes of T~,tr. Figure 4.7 shows an example input (a) and output
(b) of the AbstrRestrict operation for some call p(_Y,_Z,_U).
Finally, we consider the call AbstrExtend((Ti,,',ASharingT-,~),(T~,t,',
ASharingT_,~), c a l l ) = (To~t ~, ASharingyo.,), for extending the abstract bind-
ing state (T~,t~', ASharing=r_,.), which is the result of the abstract interpreta-
tion of some call, for the variables of that call, to an abstract binding state
for the variables in the calling environment (Ti,', ASharingT~). For the type-
graph part, we take To,,t ~ = TGExtend(Ti,~,T,,t,',call), where the function
TGExtend returns the abstract term environment resulting from extending the
term environment T.~,tr" to the variables of the calling abstract term environ-
ment T~,,t. (See [38, 40].) The sharing component is obtained by propagating
the n e w sharing (resulting from executing the call), through the old sharing exist-
ing prior to the call, using the AlternatingCIosure operation. However, computing
the new sharing as in the concrete case, by translating the sharing component
ASharingT.,,. for the term environment T , , t , e to the term environment Tout',
introduces imprecision.
Or V V V V V V V V f or ,:~-.y 2 v...~-) f
/ \ l~ . ..' ,3 I
Iat V~4 ~ "~ Int V,.s ~ \ . . " a x ~/
%....~....-"
~ ~.,-
"".. 6 ..."
-..........9
Figure 4.7: Abstract input Ca) and output (b) sharing environments for the
AbstrRestrict operation. Abstract input Ca), (c) and output (d) sharing environ-
ments for the AbstrExtend operation.
4.1.3 T h e C o n c r e t e and A b s t r a c t D o m a i n s
Having introduced the basic concepts, we are now in the position to formally
define the concrete and abstract domains for the sharing analysis. We use a
twofold sharing c o m p o n e n t to m a i n t a i n detailed information a b o u t w h a t sharings
are passed down from a calling environment and w h a t sharings are created in
a local environment. T h e distinction plays a m a j o r role in the precision of the
extension operation, as was illustrated in Section 4.1.2.
4.1. SHARING E N V I R O N M E N T S 63
The AbsPairT,iC function maps a concrete sharing pair for a term K: into an
abstract sharing edge for the type graph 7- recognizing the term K. The con-
cretization function for abstract sharing environments can now be reformulated
as follows.
D e f i n i t i o n 4.1.20 ( R e f o r m u l a t i o n of D e f i n i t i o n 4.1.18) For an abstract
sharing environment (7-', (AShr~-, AShr,~)),
{
InstrEnvConc((7-', (AShr~-, AShr~-))) =
(K:,(CShr~:,CShr~:)) I JC 9 TGEnvConc(7-') ']
& CShr~, CShr~: are pre-sharing components for ]~
& TransitiveCIosure(CShr~c U CShr~:) is a concrete
sharing component for IC
& AbsPairT-,lc(CShr~c ) _C TGShift(7-, AShr~
& AbsPairT-,/c(CShr~) C_ TGShift(7-, AShr~)
The following lemma states that the alternating closure operation of abstract
sharing components safely approximates the transitive closure operation of con-
crete pre-sharing components.
L e m m a 4.1.21 ( S a f e t y o f AlternatingClosure) For an abstract term environ-
ment 7-e and a term /C E TGEnvConc(7-e), let Ppcl, Plc~ be two pre-sharin#
components for ]C. Then
AbsPairT-,/c(TransitiveClosure(Pjc 1 U Px:2)) C
AlternatingCIosure(AbsPairT-,/C (P~c1), AbsPairT-,/C (PJc~)).
Proof. An element of the first set has the form (7-[SeI(/C, r)], 7-[SeI(/C, s)]),
where r r s and (r, s) E TransitlveCIosure(P~:l U P~:~). We must prove that
(7-[Se=(~:, r)], 7-[Se=(~:, s)])
AlternatingClosu re(AbsPairT-,K~ (Px: 1), AbsPairT-,/C (PJc2)).
By the definition of TransitiveClosure, there exists a finite sequence E over P/cl U
P~c2 such that
E = (r~, s d , . . . , (r., s.) for some ~ C ~V,
r = rz, s = s,~, (4.2)
VlEM :I <l<n::~st =rt+l.
Given such a finite sequence, we can transform it into an "alternating" sequence
E' satisfying
E' = (r~, s~), . . . , (r~, s~) for some m 9 ~W,
!
r : r~,s : am,
VeeIV:l<e<m~slt: r l'+ 1 '
Vk 9 ( ( l <_2 k < r_n ' ,s~k
=:~(r2k ' ) 9 PJcl)& (4.3)
( l <_2 k + l < m _ ~ ( r = k' + l , s2~+l
' ) PJc2))V
/ /
((1 _< 2/~ < m ~ (r2k, s2~) 9 e~:2)&
( l <_ 2 k + l < m _ ::r ' ' 9 em))-
4.1. SHARING ENVIRONMENTS 65
We first introduce two reduction steps for finite sequences satisfying (4.2). At
least one of these reduction steps will be applicable if (4.3) is not satisfied.
Clearly, the reduction steps can only be applied a finite number of times and
lead to an alternating sequence E' = (r~, s t ) , . . . , (rim, S'm) satisfying (4.3). For
every k 9 such that 1 < k < m, let (Rk, Sk) = (T[Sel(]C, r~)], 7"[Sel(]C, s~)]).
From the definition of AbsPairT,/C , it follows that
Based on the previous result, we can now show that the full sharing relation
of some concrete environment described by an abstract sharing component can
be safely approximated by the AlternatingCIosure operation.
We derive that
66 C H A P T E R 4. SHARING A N A L Y S I S
AbsPairT,)C (CSharingK:)
-- AbsPair]-,/c(TransitiveCIosure(CShr~c U CShr~c)),
by the definition of CSharingK:,
C_AlternatlngCIosure(AbsPairT,lc(CShr~c), AbsPairT, lc(CShr~c)) ,
by Lemma 4.1.21 (the safety of AlternatingCIosure),
C AlternatingClosure(TGShift(~, AShr~-), TGShift('/', AShr~-)),
by (4.5),
and the monotonicity of AlternatingClosure,
C_ASharingT,
by the definition of ASharlngcr.
Summarizing, we have AbsPairT,ic(CSharingK: ) c_ ASharing~-, as desired. []
<re
&~ Translate(T1, AShr~-l,'/'2) C_ TGShift(T2, AShr~-2)
&~ Translate(7"1, AShr~z , 7"~) _C TGShift(T~, AShr~-~)
Note that in the case where (7"~ --- T~) 2 it follows from the definition that
(T~, (AShr~-l, AShr~-l) ) ~Sh (T~, (AShr.~2 ,Ashr~-2) )
(r, s) 9 CShr~c =:~ (T2[SeI(K~, r)], T2[SeI(K~, s)]) 9 Translate(T1, AShr~-l, T2),
(r,s) 9 CShr~c ::~ (T2[SeI(/C,r)I,T2[SeI(/C,s)]) E Translate(T1,AShr~-l, T2 ),
and by the definition of -<s~
The minimal element of the abstract domain of sharing environments is (T~L, (0,
O)), with T~_ as defined in Section 2.3.2. The maximal element of the abstract
domain is (T~-, (AShr~r,.,AShrPr,_)) , with T~- as defined in Section 2.3.2, and
AShr~- _, AShrPr T the maximal set of sharing edges, containing all pairs of func-
tion nodes of TT having the same label. The subdomain of sharing environments
based on restricted type graphs does not contain any infinite ascending chains,
because the domain of restricted type graph environments is finite, and for each
finite type graph, there are only finitely many different sharing components.
where
~
T = TGUpp(T~, T~),
AShr~r = Translate('Tl, AShr~l , T) U Translate(T2, AShr.~2, T),
AShr~- = Translate(T1, AShr.~l , 7") U Translate(T2, AShr~-~, T).
Proof. This follows from Property 2.3.8 of T G U p p and the definitions of <sh
and Upp. []
4.2.1 Unification
First we give the specifications of the concrete and abstract unification corre-
sponding to the basic operation X~ : Xy. Then we reformulate these specifi-
cations for the basic operation X~ : f(X~,...,X~#). For Prolog programs in
normal form, these are the only two forms of unification that can occur.
4.2. PRIMITIVE OPERATIONS 69
4.2.1.1 Xi = Xj
Proof. From the definitions of TermShift and BindingPairs, it is clear that New
is a set of unordered pairs of the form (r, s) where r, s E C0(~o~,t). We must
prove that New satisfies Conditions 4.1.4.1-4.
C o n d i t i o n 4.1.4.4 is satisfied by New" Given (r, s), (s, t) E New where r :fl
t, we must prove that (r,t) E New. From the definitions of TermShift and
BindingPairs, we know that (r, s ) a n d (s, t) are both of the form (i.p.q, j.p.q)
where ( i . p , j . p ) i s a member of BindingPairs(Ei,~,i,j). Suppose that s =
i.p.q, then r = j.p.q -- t, i.e. there are no two different consecutive pairs in
New.
Proof. From the definitions of Unify and its auxiliary functions, it is clear
that s AccNew, s and s are sets of unordered pairs
of the form (r, s) where r, s E O(/Co~t). We must prove that s AccNew,
s t and s satisfy Conditions 4.1.4.1-4.
For (rt, st) E AccNew ----TermShlft(/Co~,t, CShr~r it follows from the prop-
erties of TermShift that also (rt.t, st.t) E AccNew. For (rt, st) E New it
follows from Lemma 4.2.3 that (rt.t, st.t) 9 New.
Thus, there is a finite sequence in AccNew U New of the form (rl.L s l . t ) , 9 9
(r,~.t, s~.t) with rl -- r, s,~ -- s, st = rt+l for 1 <_ l < n. So by the definition
of TransltiveClosure, also (rA, s.t) 9 CShr~co.~.
Similarly, we can prove the property for the sharing component CSharingjCo.~
: TransitiveCIosure(CShr~c.. ' U CShr~o.,).
C o n d i t i o n 4.1.4.4 is s a t i s f i e d b y t h e sets CShr~co**, AccNew, CShr~c~ and
CSharingpCo.,:
Given (r, s), (s, t) E CShr~co.t (resp. AccNew, CShr~co.,, CSharing~c~
where r # t, we must prove that (r,t) 9 CShr~co.' (resp. AccNew, CShr~o.,,
CSharing~Co.,).
By the assumptions of the lemma, CShr~c~ and CShr~,. satisfy Condi-
tion 4.1.4.4.
For (r, s), (s, t ) 9 CShr~r = TermShift(/Co,a, CShr~q~) where r#~, 3r', #,
., r r m : (r, s) = (r r ~ % t) = (r r ~ (r r (r e') 9
CShr~,~ ~ ~'.~ # e'.m. We have r = s = r 9 0 ( ; o , , ) ~ r S II 9
O(/Ci,). Assume, without loss of generality, that # --- s'.p, hence p.n =
m & r' 9~ E'.p. From (d',E') 9 CShr~q. & s' = #'.p 9 O(/Ci,,), we
have (s".p,E'.p) 9 CShr~q~, because CShr~q., satisfies Condition 4.1.4.1
and 4.1.4.3. From (r',s'),(s".p,t".p) 9 CShr~c,,* & # = s".p & r' # t".p,
we have (r', t".p) 9 CShr~q,,, because CShr~q,, satisfies Condition 4.1.4.4.
Finally, (r, t) = (r'.n, t".m) = (r'.n, t".p.n) 9 CShrTc~ ' = TermShlft(/Co,,t,
CShr~q~), from the definition of TermShift.
In a similar way we can prove the property for AccNew = TermShift(/Co=t,
CShr~q~).
For (r, s), (s, t) 9 CShr~r = TransitiveCIosure(AccNew U New), let (rl, sl),
. . . , (r,~, s,~) be the finite sequence for (r, s) as specified in the definition
of TransitiveCIosure, and (r~, s ~ ) , . . . , ( r ' , s ' ) the finite sequence for (s, t).
Since s,, = s = rl, the finite sequence (r~, s~),..., (r,,, s,), (r'~, s'z),...,
(rk, s ' ) then assures that (r, t) 9 CShr~co.,.
Similarly, we can prove the property for the sharing component CSharinglc~
= TransitiveCIosure(CShr~co. ' U CShr~:o.,).
[]
The algorithm for finding a most general unifier in Definition 4.2.1, is the Solved
Form Algorithm for simplifying sets (systems) of equations given in [47]. Based
on Herbrand's original unification algorithm, this algorithm views unification as
a process of transforming a solvable equation set into an equivalent solved form
equation set. When applied to an unsolvable equation set, the algorithm halts
with failure.
4.2. P R I M I T I V E O P E R A T I O N S 73
i. f(t1,...,tn) = f(rl,...,rn)
replace by the equations tl = rl,..., t,~ = r,~
2. f ( t l , . . . . tn) : g ( r t , . . . , r m ) where f ~t g or n ~ m
---* halt with failure
3. X = X
---* delete the equation
The following lemma states that the set of concrete sharing environments is
closed under the Unify(-, i, j) operation.
From the relationship between mgu's and solved form equation sets [47] we know
that mgu(K:~,,/i, s can be obtained by transforming the solvable equation
set
Eo = { q li = P q . l j }
into its equivalent solved form (a is a most general unifier of Eo).
The Solved Form Algorithm throws away the upper structure of terms when
the latter does not occur in the variable bindings that solve the equation set. We
74 C H A P T E R 4. S H A R I N G A N A L Y S I S
wish to observe the effect of the algorithm on the terms in the environment as
it incrementally isolates and applies variable bindings. To facilitate this we add
an extra variable and auxiliary equation, the purpose of which is simply to force
the algorithm to leave the entire environment in the equation set. W e consider
the equation set
X = /Ci~ }
be the solved form of Ei,~. Since Ei~ and Eo~t are equivalent, they have the
same most general unifiers. Let 7 be one. Then it follows that
X = /C
E = tl = t2
Induction hypotheses:
(c) For each tt = t2 in E that is not of the auxiliary form X =/C and that has
not yet been the subject of Step 5, there exists a path s E O(/C/i)NO(/C/j)
such that either tl - IC/i.s,t2 =- IC/j.s or t2 - IC/i.s,ti - IC/j.s
Clearly, Condition (b) expressed for the stage given by the solved form equation
set E ~ t and the term/C gives the conclusion of the lemma.
Basis: The induction hypotheses hold for the stage given by Ei~ and/Cir.
Condition (a): The pairs (i.s, j.s) are in BindingPairs(/C~,,,i, j) by definition, so
from the the fact that TermShift is an increasing function, they are in New.
Condition (b): Because CSharingpc,~ = TransitiveCIosure(CShr~c~ U CShr~c~ ) is
a sharing component for /Ci,,, the pairs (r,q) are in CSharingjc~ . So, using
Lemma 4.2.2, they are in CSharingpc..,.
4.2. P R I M I T I V E O P E R A T I O N S 75
Condition (c) holds because the only equation in Ei,~ not of the auxiliary form
X =/C,,, is ICi,,/i = ICin/j, and e 9 O(IC,,,/i) (30(ICi,,/j).
S t e p : We consider each step of the Solved Form Algorithm and show that
its application preserves the induction hypotheses. We use Ebe! .... ICb,Ior , and
E~lt,r, ICMte~ to denote the two consecutive stages just before and after the
application of a step of the Solved Form Algorithm. That is, we assume that the
induction hypotheses hold for Eb,Jor,, ICb~lo~,. We must prove that they hold for
case 1: When Step 1 is applied,/Cb~lo~e ~ /C=#er, so Conditions (a) and (b) are
preserved. By Induction Assumption (c) there exists s E O(/Cb~lo~/i)0
O(ICb,lo~,/j) such that (without loss of generality) f ( t l , . . . , t , ~ ) =
JCbelo,.e/i.s, and f ( r l , . . . , rn) ~ ]CbeforJj.s. S o w e have for all g satisfying
l<g<n
& (,'L
case 4: Again, ICS~lo~, = tC~#~, preserving Conditions (a) and (b). Clearly, also
Condition (c) is preserved.
case 5: By assumption, the algorithm does not fail. Let Z = t be the selected
equation.
Consider a path s such that i.s,j.s E O(]Calte~) &
C o n d i t i o n (a):
(]C~tter/i.s E VarsV ICalttr/j.s E Vats). We must prove that (i.s,j.s) C
New. By the way /C~#er is constructed from /Cbelo~,, we know that
there exist paths p and r such that s = p.r & i.p, j.p E O(IC~,/o~,) &
(ICt,tlor,/i, p 9 Vats V ICt,,lo,.,/j.P 9 Vats). By the Induction Assump-
tion (a), (i.p,j.p) 9 New. Because 36:IC,,it,r = K:o~t, it follows from
the definition of substitution application that O(/Cajt,~) C_ O(/Co~t).
Because New is a pre-sharing component for/Co~t (Lemma 4.2.3), it
follows that (i.p.r,j.p.r)= (i.s,j.s) 9 New.
C o n d i t i o n (b):Consider paths r,q such that r,q 9 O(IC~lt,, ) & r #
q & /Calter/r -~ ICalttr/q & ICalter/r 9 Vars. We must prove that
(r,q) 9 CShatingt:o,,. Let IC,,#,~/r =_ IC,,]tt,./q =- Y . Since /Ca#,~
is constructed from ICt,~lo,., according to Step 5, we know that each
s 9 O(K:~/t,~) such that IC~lt~ds =_ Y must satisfy one of the following
(Figures 4.8.a and 4.8.b).
(s 9 a ---- (4.9)
76 C H A P T E R 4. S H A R I N G A N A L Y S I S
or
3s', s " : .
(.= s'." o(,) /
& I C b , l o , , / s ' -- Z & I C , ~ , ~ / s -- ~ / s " -- Y (4.10)
]
(r, q) E CSharingjc~
from (q, j.p.q"), (r, j.p.r") E CSharingpCo~,, which was shown above.
C o n d i t i o n (c): For each equation "1 ~elor~ : ~belor~
~ in Ebb/ore other than
the selected Z = t and not yet selected in a previous application of
Step 5, and not of the auxiliary form X = /Cb,lo~, there exists by
Induction Assumption (c), a path s such that i.s,j.s E O(IC~/o,~)
and (t~ el~ = IC~lo~/i.s ) & (t~ ~1~ -- IC~lor~/j.s ) (without loss of
generality). Since replacing Z by t throughout the system has the
same effect on ~~1belo~e' +
~2b~lort as it has on ICbtlo~e/ i.s, ICbelo~t/ j.s the
relationship (t~ lt~ = IC,jt~r/i.s) & (t~ ft'~ =__IC,jt~/j.s) holds in the
resulting system Ealt,~.
I-3
(a) i.,, j.s ~ o(pc) ~ (PCli.s ~ vats v Ic/j.s ~ Vats) = (i.s, j.,) ~ New
(b) Vr, q E O(/C): (/C/r ---- ]C/q & /C/r E Vars & r ?d q ::~ (r, q) E CSharingjc.,,.)
We are now in the position to define the abstract operation. The following
definition uses auxiliary functions Convert and BindingEdges, defined below.
where
To~,t" = TGUnlfy(Tin', i, j),
and
Recall that failure is represented by T~_ in the case of type graph environments
(Section 2.3.2), and by (T~_, (0, 0)) for the abstract domain of sharing environ-
ments.
We still have to define the auxiliary functions. The function Convert takes
an abstract sharing component for a type graph Ti,,and reexpresses it for a type
graph To,n. In particular, the function will be used for a type graph To,,tthat
describes a supersct of instantiations of terms represented by the type graph Ti,.
Convert(Ti., P:T~.,To.t) =
/ (7-o.,[R],To.,[s])13R,s e S(To.,):
([R, S 6 S(~,,) & (Ti,,[R], Ti,,[S]) 9 TGShlft(Ti,,, PT"~.)]
v
[3R',S',k,Z,/: R = R'.(k,/) ~ S=S'.(Z,:)
R'.(k, v), S'.(t, v) e s ( ~ , ) /
& f is a functor or a constant &
(T..[R'.(k, V)], ~.[S'.(I, V)]) E -I-CShift(~., P~r,..)])
The function BindingEdges i~ the type-graph analogue of the BindingPairs func-
tion for concrete terms.
BindingEdges(T~,~, To,,,, i, j) =
(7"o.,[R], 7-o,,,[S]) I 3R, S E S(To,,t), 3 f :
f is a functor, a constant or V &
([R = (i, f) a S = (j, f) a ((i, V) e S(~.) v (j, V) ~ S(~.))]
(either the i th or the jth component of Tin is itself a V-node)
V
[3g,T,l: R= (i,g).T.(l,f) & S=(j,g).T.(l,f) &
(<i,9).T.<l,v) e s(~) v <j,9).T.<Z,V) e S(~))]
(or there is a V-node inside the i 'h or jth component of 'Tin))
We now formulate the theorem stating the safety of the abstract unification.
Before proving the theorem itself, we first prove several lemmas that contribute
to its proof. Most of the propositions in the remainder of the section use the
relationships given by the following condition.
C o n d i t i o n 4.2.10
that is,
Fix arbitrary r,s,t such that (r,s) E P~ and r.t,s.t 9 O(/C). From Prop-
erty 4.1.13, it follows that SeI(/C, r.t), Sel(/C, s.t) 9 S ( T ) . Since/C/r = IC/s, for
T = Sel(/C/r, t), we have, by the definition of Sel,
Sel(~:,rt) = Sel(~,r) T,
selC~:,8.t) = Sei(~,s).T.
Therefore, we have
4.2. PRIMITIVE OPERATIONS 81
(7-o``t[Se[(/co``t,r)], 7-o``t[Sel(/co``hs)])
E Convert(~T/.,AbsPair~.,/c,,,(P~c,.), To.,).
From Definition 4.1.4, we have r, s 6 0 ( / C , , ) . From/Ci, 6 TGEnvConc(~,') and
Property 4.1.13, we have
by (4.16),
E AbsPairT~jCi~(Plq. ),
from (r, s) E PJc~., and the definition of AbsPairTi,./Ci,=,
c_ ZGShift(~., abseslrz.,iC~.(P~,.)) ,
by the definition of TGShift.
Summarizing, we have
The desired result is now obtained from (4.14), (4.15), (4.16), (4.17) and
an application of the first disjunct in the definition of Convert.
case 2: (/Ci,=/r, Kin/s E Vars). Here we have
for r ~,s~ E O(/Ci,), l,k E -~ such that r = r~.l, s = s~.k and f either V,
a functor or a constant, depending on whether the substitution a further
instantiates ICi,,/r = ICi,/s. If the variable is not instantiated by the
substitution, then Sel(/Co,,t, r) = Sel(}C/n , r / & Sel(/Cout, s) = Sel(lCin, s),
and the result is obtained as in case 1 above. Otherwise we observe that
(~.[SoKJC~.,r)],~.[S~I(IC~.,s)])
E AbsPair~,=,/Ci,=(Px:,.,),
from (r, s) E PK:.. and the definition of AbsPair~.,,/Ci,~,
C TGShift(~,,, AbsPairTo~,Ki,,(P~q.,)),
by the definition of TGShift.
Summarizing, we have
(~.[Sel(~:,.,r)],~.[SeI(~:,.,s)])
E TGShift(Tin, AbsPair~,~./Ci,~(P~) ). (4.20)
The desired result is now obtained from (4.141, (4.151, (4.18), (4.19), (4.201
and an application of the second disjunct in the definition of Convert.
[]
We are now in a position to prove the m a i n result of the section. We restate the
proposition.
Proof. Applying Conditions 4.2.10.5 and 4.2.10.6 and expanding the definitions
of Unify and AbstrUnify, we can rewrite 1. as
AbsPairTo.jCo.t(TransitiveClosure(AccNew LJ New))
C_AlternatingCIosure(C, B).
(4.24)
AbsPaira- /C (AccNew)
= AbsPairTo~t,/~o.t(TermShift(/Co.t, CShr~c,.)),
by the definition of AccNew,
C TGShift(To.t, AbsPairTo.t,/Co.t(CShr~c,~)),
by Lemma 4.2.12 (the safety of TGShift),
c_ TGShift(7"..,, Convert( ., AbsPa .),
by Lemma 4.2.13 (the safety of Convert),
and the monotonicity of TGShift,
C TGShift(To.t, Convert(~,,, TGShift(~,~, AShr~-), To.t)),
by Condition 4.2.10.3c,
and the monotonicity of TGShift and Convert,
= TGShift(To.,, Convert(~., AShr~-, To.t)),
by the definition of Convert,
and the idempotence of TGShift,
= C,
by the definition of C.
4.2. P R I M I T I V E O P E R A T I O N S 85
Summarizing, we have
AbsPairTo,,t,iCo~t(New) C B. (4.26)
Finally, we prove (4.24).
AbsPair']-o.t,ICo.t(TransitiveCIosure(AccNew U New))
C_ AlternatingCIosure(AbsPairTo~t,iCo~t(AccNew),
AbsPair To~t,IC o,,t ( New ) ),
by Lemma 4.1.21 (the safety of AlternatingClosure),
C_ AlternatingClosure(C, B),
by (4.25), (4.26),
and the monotonicity of AlternatingCIosure.
[]
4.2.1.2 Xi : f ( X i l , . . . , X i j )
The main difference with the basic operation Xi : Xj is the specification of
the set BindlngPairs describing places where new sharing is introduced by the
concrete unification. If the left-hand side Xi is a free variable, then the operation
is in fact a construction operation, otherwise we call it a selection operation. The
set BindingPairs is defined as the union of two sets; however, one of these sets will
be empty depending on whether the operation is a selection or a construction.
D e f i n i t i o n 4.2.15 For a concrete sharing environment QCi,~,(CShr~:,~,CShr~c,,.))
and 1 < i, i l , . . . , ij < arity(lCin[r such that i, i l , . . . , ij are pairwise distinct and
f is a functor of arity j,
Unify((/Cin, (CShr~c,. , CShr~,.)), i, f( il, . . . , ij ) ) =
if mgu(ICinli, f(ICinlil,...,ICinlij)) is fail
then fail
else (ICo,a, (CShr~co,,,, CShr~co,,,)),
86 C H A P T E R 4. S H A R I N G A N A L Y S I S
where
ICont ~ ICing, for a = mgu(ICinli, f(K.i,~lil,..., PC,,/i,)),
and
The next lemma, stating that the initial sharing is preserved, can be proved in
the same way as Lemma 4.2.2 for the basic operation Xi = X 1.
The proof that Definition 4.2.15 guarantees the concrete unification operation
Unify(-,/, f ( i l , . . . , i j ) ) to be well defined on the domain of concrete sharing
environments is almost identical to the proof of the well-definedness of the
Unify(-, i, j) operation. First we demonstrate that New = TermShift(/Cont,
BindingPairs(ici,,,i,f(il,...,ij))) is a pre-sharing component for Icont. Sec-
ond, using the previous result, we prove that CShr~:~ AccNew, CShr~o., and
CSharing~:.~, = TransitiveCIosure(CShr~:.., U CShr~n,) , are pre-sharing compo-
nents for/Cont. Third, we demonstrate that CSharinglco., is a concrete sharing
component for/Co~t. Using the fact that it is a pre-sharing component, we still
need to show that it satisfies Condition (4.1), concerning multiple occurrences
of a free variable in/Cont.
Proof. From the definitions of TermShift and BindingPairs, it is clear that New
is a set of unordered pairs of the form (r, s) where r, s 9 O(ICo,,). We must
prove that New satisfies Conditions 4.1.4.1-4.
that ]Co,,t/i.s - ]Co,,t/it.p for all s satisfying 1 < s < j and for all paths
p E O(]Co~,t/i.s The function TermShift preserves the property because
corresponding subterms of identical terms are identical.
[]
Proof. The proof of this lemma is identical to the proof of Lemma 4.2.4, except
that Lemma 4.2.17 is used instead of Lemma 4.2.3. r3
The following lemma states that the set of concrete sharing environments is
closed under the Unify(-, i, f ( i l , . . . , is) ) operation.
E= = { ~ . l i = f(~.li,,..., ~.li~)}
into its equivalent solved form (a is a most general unifier of Eo).
Again, we wish to observe the effect of the algorithm on the terms in the en-
vironment as it incrementally isolatesand applies variable bindings. W e consider
the equation set
J~o~t =
be the solved form of E~,~. Since Ei,~ and Eo,,e are equivalent, they have the
same most general unifiers. Let ")' be one. Then it follows that
/C = X 7 =/Ci,~cr (modulo renaming) =/Co~t.
From the fact that Unify(~/Ci,~,CSharingpc,.,), i, f ( i l , . . . , ij)) does not fail, it fol-
lows that two cases are possible.
case 1: lCi,Ji E Vars & ICi,~/i ~ Vars(f(ICi,JQ,...,ICi,Jij)).
case 2= ICi,~[i] = S, where f is a functor of arity j.
In case 1, assuming ICi,Ji = Z, step 5 of the Solved Form Algorithm is applica-
ble, replaces Z by f(ICi,~/Q,..., ICi,Jij) in the auxiliary equation X = /Ci,~,
and then halts. Given paths r,q such that r,q E O(/Co,,t) & r ~ q &
ICo,,t/v = ICo,,t/q & ICo,,t/T" ~ Vars, we must prove that (r, q) G CSharing~c~ Let
ICo,,t/r - ICo,,Jq = Y . Since/Co,,t is constructed from/Ci,~ according to Step 5,
we know that each s E O(/Co,,t) such that/Co,,t/s = Y must satisfy one of the
following.
or
~$1,~a, 8 II .
I 8 = 81.~a.a II 9' ~ o ( ~ . ) ~ ~.I~' = z ~ ~ (4.28)
l<_l,<_j 9" e o ( ~ . / i ~ . ) ~ ~o ~, / , = ~ . / i ~ . . , " - Y /
4.2. PRIMITIVE OPERATIONS 89
Note that for case (4.28), we have lCi,~/s' - Z = ]Ci,,/i. So, because CSharingjc, =
TransitiveCIosure(CShr~c,. ' U CShr~:,,) satisfies Condition (4.1), we have t h a t ei-
ther s' = i or (s I, i) E CSharing~ci.,. So, using L e m m a 4.2.18, and CSharingjci. C_
CShadng;c~ ( L e m m a 4.2.16), we have either s = i.g,.s" or (s,i.g,.s") E
CSharing,c..,. From ]Ci,,/i E Vats and the definition of Unify, we know t h a t
3g,, s" : 1 < ts <_ j & ]Ci,~/it,.s" ----Y & (s, it,.s") E CSharingjco., (4.29)
II II 7,11 ~ II\
V (it.. , l,.q ) E CSharingjq .
9
l.. = ll,.q
It follows from ~" :/: q, CSharing,c~ C CSharingjc .... and L e m m a 4.2.18, that
(r, q) E CSharing~:o~,.
Otherwise, one of r and q satisfies (4.27) (assume it is r, without loss of
generality), and the other (thus q) satisfies (4.29). From r satisfying (4.27) we
know K:~,,/r = Y, and from q satisfying (4.29) we know 3gq,q" : 1 <_ gq <
j & ]Ci,Jit,.q" =- Y & (q, it,.q") E CSharing;c..,. Because CShating,c,.. sat-
isfies Condition (4.1), we have either r = it,.q" or (r, i t , . q ' ) E CSharing,c,. C_
CSharing,:o.,. It follows from r # q , (q, it,.q") E CSharing,: .... and Lemma4.2.18,
t h a t (r, q) E CSharing;co.,.
In e a s e 2, step 1 of the Solved Form Algorithm is applicable, and results in
the set of equations
E~,~ =
X
ICin/i.1
= K:i,, }
=. ICi,~/il .
Similarly as in the proof of L e m m a 4.2.5, one can show t h a t the following in-
duction hypotheses hold at each stage of the Solved Form Algorithm given by
90 C H A P T E R 4. SHARING A N A L Y S I S
X = /C}
E = tl = t2 "
Induction hypotheses:
(~) i.t.s, i~.s ~ o(~c) & pc/i.l.s ~ w~s v ~:/i~.s E w~s) ~ (i.d.s, i~.s) ~ N ~
(b) Vr, q E O(/C): (IC/r =_ IC/q & /C/r E Vats & r :fi q => (r,q) E CSharlngjc..t)
(c) For each tx = t2 in E that is not of the auxiliary form X =/C and that has not
yet been the subject of Step 5, there exists a path s E O(Ig/i.l) n O(lC/it)
such that either tt = IC/i.s t2 =- IC/it.s or t2 = IC/i.Ls, tl =- IC/it.s
Clearly, Condition (b) expressed for the stage given by the solved form equation
set Eo~. and the term K; gives the conclusion of the lemma. []
AShr~-.., = Convert(Ti,,,AShr~-,To.t),
C = TGShift(To,,,, Convert(~n, AShr~%,Toat)),
B = TGShift(To,,, BindingEdges(T/,,, To,t, i, f ( i l , . . . , ij))),
TGShift(To.,, gShr~..,) = AlternatingClosure(C, B).
The following theorem gives the correctness condition for the AbstrUnify opera-
tion.
Condition 4.2.22
Proof. The proof of this theorem is identical to the proof of Theorem 4.2.11,
except that Theorem 2.3.10 is used instead of Theorem 2.3.9, and Lemma 4.2.24
instead of Lemma 4.2.14. []
92 CHAPTER 4. S H A R I N G ANALYSIS
AbsPairTo,,t,iCo,,t(BindingPairs(ICi,~ , i, f ( i l , . . ., ij )))
C_ BindingEdges(Ti,, To,,t, i, f ( i l , . . . , ij)).
or
and
else
Sel(~,., i) = (i, V) 6 S ( ~ . ) .
4.2. P R I M I T I V E OPERATIONS 93
From Condition 4.2.22.5 (the unification succeeds) and the definition of substi-
tution application, we have that i.g, it E O(ICo,,t), and
as desired.
[]
1. Proj(/C, i) a = Proj(/Ca, i)
e. Proj(/C,i)/k.r =_ JC/i(k).r
3. Proj(O(/C), i) = O(Proj(/C, i))
Proof. These equalities follow immediately from the definition of Proj and the
definition of substitution application. []
The arguments of the restriction operation (i.e. the first step of the procedure-
entry operation) comprise the abstract sharing environment at the call point and
the icau injection. The operation always succeeds, because of the normal form
of programs.
Proof. By the definition of Restrict, we have K:,,t, ---- Proj(K:i,~, i~,,n), CShr~..,. =
0 and CShr~c..,. = Proj(TransitiveCIosure(CShr~c~ U CShr~.),ic.u). The empty
set is a pre-sharing component for any term. Because (/Ci,,, (CShr~c,.,, CShr~,~))
is a concrete sharing environment, and because the function Proj preserves Con-
ditions 4.1.4 and (4.1), it follows that CShr~c.... is a concrete sharing component
for K:~ot~. Hence (]~r,t~, (CShr~: .... , CShr~.,.)) is a concrete sharing environment.
[]
5. ic~zz : { 1 , . . . , m }
--* { 1 , . . . , n } is an injection (for m <_ n) such that
i~,u({1,..., re}) = dom~,u
6. (]Cr, t. , (CShr~c..., CShr~c.,,.)) = Restrict((/Cin, (CShr~c.. , CShr~c.~)), ir
96 C H A P T E R 4. S H A R I N G A N A L Y S I S
AbsPair7-..K:.~ i,=n)) =
AbsPair.T ~-
9 ~$t!P~l~lPst!p~
(CShr~ P~st~l
~ C AbstrProj(~,~, ASharingT-i~, T~,t~, lean), (4.36)
--
For the procedure-exit operation (Section 4.2.3), a special version of the restric-
tion operation is needed that restricts the abstract sharing environment at the
last program point of some clause to the variables of the head. It differs from
the version needed on procedure entry, in that the new sharing edges created in
the current environment, are not joined with the old sharing edges to become
the set of old edges for a lower level of the proof tree (resp. AND-OR-graph
in the abstract case). Procedure exit is needed to move one level upwards in-
stead. The new edges of the current environment will also be considered as new
edges at that higher level. We omit the proofs of the well-definedness of the
concrete operation and the safety of the abstract operation as they are rather
straightforward.
sharing environment at the last program point of each applicable clause is re-
stricted (see Section 4.2.2) to the arguments of the head of the clause. Then
these restricted abstract sharing environments (all having the same arity) are
combined by the upperbound operation (i.e. an over approximation is computed,
see Section 4.1.4). In a second step, the extension operation regains the infor-
mation about the calling environment that was lost by the restriction operation
on procedure entry (it computes the effect of the call on the variables of the
environment that are not passed as arguments to the call). In this section, we
present only the extension operation that is needed in the second step of the
procedure-exit operation. Proving the safety of the first step is straightforward,
as was discussed in Section 2.3.3.
Again, we start with the instrumentation of the concrete extension operation
for the domain of concrete sharing environments. We need the following auxiliary
function.
where
~out -~ ]('in 0",
f o r the substitution a over Vars(Proj(/C~,,i,=z,)) such that Proj(/C/n,ir =_
]~rstrl and
TermShift
I
(CShr~:,..,
'
CShr~,.)
TermShift
Call
/
~..,)
TransitiveCIosure L
Head "N~Proj
~
Call I
. . .
Call/
. ..
(CShr~c.... , CShr~c..,.) q
Call0
Restrict2 I
(CShr~c, CShr~c) ,,,J
in the subgoal that is executed, or in the renamed clauses used during the sub-
derivation, but no other variables. The condition on the relationship between
the sharing components of ]Ci,, and ]C,.,t, further characterizes the implementa-
tions for which our analysis is intended, namely, we address implementations in
which sharing, once introduced between term representations, persists until final
success or failure of the computation.
Notes:
9 The specification of the Extend operation does not use the value of CShr~c.."
to construct the output sharing environment (see Figure 4.9). However,
the properties of CShr~:.... are crucial to the proof of Lemma 4.2.37.
9 The extend operation differs from the unification operation only in the
definition of New.
The following lemma states that the sharing derived by the extension operation
comprises both the initial sharing and the sharing that resulted from the call.
I. CSharingjc.. C CSharingjc.~,.
2. RevProj(CSharingx::...,icon) C CSharingjc.~..
4.2. P R I M I T I V E OPERATIONS 101
Proof. We first prove 1. From the properties of TermShift and the definition of
Extend, we have
3t~i :
Using Property 4.2.26.1 and the definition of Extend we have that for a substi-
tution over Vars(Proj(:,.,it..)),
Proj(/Co,,=, ir - Proj(/Ci,= a, it=u) = Proj(K;i., ir a -/Cr,t~. (4.43)
From ki.r;.ti, li.s;.ti E O(Kr,tr) and the definition of Proj, it then follows that
ir i~,,u(li).s~.ti E O(/Co,,t), and because of (4.42) also uj.~i,vj.ti E
O(/Coft) for all j satisfying 1 < j < q. Using the definition of TermShift, we ob-
tain a finite sequence (ut.ti, vx.ti),. 9 (% .ti, %.ti) over TermShift(/go=t, CShr~c,~)
UTermShift(/go,,t, CShr~,,,) such that ul.ti = ir &%.~ = ic=u(li).s~. From
the definition of Extend, CShr~c.. ' -- TermShift(/Co,,t, CShr~c,.,) and AccNew =
TermShift(/Co~,t, CShr~c,, ). Summarizing we have
s) =
E TransitiveCIosure(TransitiveCIosure(CShr~co. ' U AccNew) U New).
W e observe that
The proof that Definition 4.2.36 guarantees the concrete operation Extend to
be well defined on the domain of concrete sharing environments is analogous to
4.2. P R I M I T I V E O P E R A T I O N S 103
Proof. From the definition of RevProj, it is clear that New is a set of unordered
pairs of the form (r, s) where r, s E O(/C~ We must prove that New satisfies
Conditions 4.1.4.1-4.
[]
Proof. The proof of this lemma is identical to the proof of L e m m a 4.2.4, except
that L e m m a 4.2.38 is used instead of L e m m a 4.2.3. []
O~t
ICratr /
/
/
9/ m
_L.,I'\ I \ x
Y Y
or
3q', q" :
( q = ql.q,, & q, E O(ICi,~) & lCi,,/q' - X, E Vars(Proj(/Cin, i~,m)) '~ (4.50)
\ & Xq cr =_ t.q & ql, E O(tq) & ~out//q ~-- tq/q" = Y /
Note that
X, E Vars(Proj(K:in, icau))
g
3k,.q* G O(Proj(/Cin, ir k, E ( 1 , . . . , m} & IC.i,~/ic~u(kq).q* =- X , .
3k,,q*,q': ( (q&=icall(k')'q*'q'v(q'icall(k'
- Y)'q*'q' )lC,,tr/k,.q,.q. E CSharingx:.., ) (4.51)
Suppose r and s both satisfy (4.51), i.e., 3k,, r*, r", k,, s*, s":
(r = iea/l(k,).r*.r" V (r, icall(kr ).r* .r") E CSharingjco.,) &
(s = i,an(k,).s*.s" V ks,
' z~n~,
" ' s * 9s " ~) E CSharingx:..,) &
' k ,).
(/c,.,,,./k,.r
9
.r
II
- Y - ~,.,,,/~,,.s
*
.s
It
).
Because CSharingjc.,,. = TransitiveClosure(CShr~c., * U CShr~c.,,. ) satisfies Con-
dition (4.1), we have
kr.r* .r" = k,.s* .s" V (kr .r*.r", k,.s* .s") E CSharingR:.., .
It follows from RevProj(CSharing~c..,., i,~n) C_ CSharing~:.~, (Lemma 4.2.37), r
s, and Condition 4.1.4.4, proved above, that (r, s) E CSharingjc..,.
Otherwise, one of r and s satisfies (4.49) (assume it is r, without loss of gen-
erality) and the other (thus s) satisfies (4.51). From r satisfying (4.49) we know
/Ci,~/r -- Y E Vars(/C,,~), and from s satisfying (4.51) we know )C,.,t~/k,.s*.s" =
r e Wrs(~:.,,). Because W r s ( ~ : . , , ) n W r s ( ~ ) C_ Wrs(Proi(~:~, i . , ) ) , we have
3 k,, r ' : k, E { 1 , . . . , m} &/Ci,~/i,,n(k,).r' - IC,ot~/k,.r' - Y . From CSharing~c,~
and CSharingx: .... satisfying Condition (4.1) and s satisfying (4.51), we know
3kr, r t, ks, s*, 8" :
(r = i,an(kr).r' V (r, ic~n(kr).r') E CSharingx:,,.) &
(k~ .r I = k, .s* .s" V (k~.r I, k , . s * . s ' ) E CSharingjc.,,.) &
(s = i,~,(k,).s*.s" V (s,i,~n(k,).s*.s")E CSharing~c..,).
It follows from r :fi s, CSharingJc,,. C CSharlngjc.~,, RevProj(CSharlngx:..,., lean) C
CSharingjc.., (Lemma 4.2.37), and Condition 4.1.4.4, proved above, that (r, s) E
CSharingx:.~,. D
whePe
T.o,~t" = TGExtend (~,,~, ~,t~ ', i~.u),
and
AShr~ro., = Convert(~,~,AShr~-,To~t),
C = TGShift(2-o~t,Convert(~,~, AShr~-,]-o,,t)),
B = AbstrRevProj(Trstr, TGShift(Tr,t~, AShr~..,.), To,,t, icau),
TGShift(To,,t, AShr~-o.,) = AlternatingClosure(C, B).
Theorem 4.2.44, below, gives the correctness condition for the AbstrExtend op-
eration. It assumes the following condition.
C o n d i t i o n 4.2.43
1. ( i,~, <AShr~-~,AShr~- )) is an abstract sharing en~ironmen/.
~. (Jq., <CSh4c,~, CShr~,.>) i, a concrete sharing environment
3. </Ci,~,(CShr~c,.., CShr~c..)) E InstrEnvConc((Ti.', <AShr~-, AShr~,,.)))
4. (T.str', (AShr~-.t., AShr~..,.)) is an abstract sharing environment
s. (to..., (CSh4c. . . . , CSh,~ .... )) is a eo,~erete sha~i,~g environment
8. icatt: { 1 , . . . , m } ~ { 1 , . . . , n } is an injection
O. (ICo,t, (CShr~:o.,, CShr~o.,)) ----Extend(QCi,~,(CShr~;., CShr~,.)), (/C,.,tr ,
(CSh,~.... , CSh,~..,.)), i~o.), is ~ot Mt
10. (To,~te, (AShr~ro.,, AShr~-o,.)) = AbstrExtend((Ti. t, (AShr~-, AShr~.~)),
(7:,,,tr ' , (AShr~r,., AShr.~.,.)), ic~u)
Theorem 4.2.44 (Safety of AbstrExtend) Assuming Condition J.~.J3, it ]ol-
lows that
P a r t 1. /Co,,t E TGEnvConc(To,,t'),
P a r t 2. CShr~co~,,CShr~o~, are pre-sharing components for Ko,,t and
TransitiveCIosure(CShr~co. ' U CShr~c~ is a concrete sharing component for
~ O~g s
Part I. is a restatement of Theorem 2.3.14. Part 2. follows from the fact that
Extend is well-defined ( L e m m a 4.2.40). The proof of Part 3. will follow at the
end of the section, after we have related the auxiliary function AbstrRevProj used
in the definition of AbstrExtend to its counterpart RevProj in the definition of
Extend.
AbsPairT,/c(RevProj(Ppc.,., i<=,)) =
{(7-[sel(#c, i<=.(k).s)], 7-[Sei(SC, i<~ I
k, I 9 {1,..., m} & (k.s,l.r) e Px:..,.}.
For arbitrary k, s, 1, r such that k, l 9 {1,..., m} and (k.s, l.r) E P~:..,., we must
prove that
We observe that
AbsPairTo,`t,/Co,`t(AccNew ) C_ C. (4.57)
AbsPalrTo,`t,/C o,`t(New)
= AbsPair.-r ~- (RevProj(CShr~: i~Iz)),
by the definition of New,
C AbstrRevProj(T~,,,, AbsPairT,,,,jC,,t,(CShr~..,.), To,,,, icazz),
by Lemma 4.2.45 (the safety of AbstrRevProj),
C AbstrRevProj(T,,,,, TGShift(T,0t,, AShr~-.,.), To,`,, it,u),
by Condition 4.2.43.6,
and the monotonicity of AbstrRevProj,
-- B,
by definition of B.
Summarizing, we have
AbsPairTo,`t,iCo,`t(New ) C B. (4.58)
Finally, we prove (4.56).
AbsPair.]'o~,,.iCo,,(TransitiveCIosure(AccNew U New))
C AlternatingCl~ /C (AccNew), AbsPair~ /C (New)),
by Lemma 4.1.21 (the safety of AlternatingClosure),
C_AlternatingClosure(C, B),
by (4.57), (4.58),
and the monotonicity of AlternatingCIosure.
[]
4.3 Evaluation
In this section, we show some of the results obtained by a prototype implemen-
tation based on the specifications for primitive operations given in Section 4.2.
4.3. E V A L U A T I O N 111
Because most Prolog implementations do not create any sharing when a variable
gets bound to an atom or an integer, a small optimization with respect to the
formal specifications is incorporated, avoiding the introduction of such sharing
edges. Detailed tables representing the results produced by the prototype are
given in Appendix A. So here we discuss only one example in detail, after which
we comment on a possible refinement of the sharing analysis, based on a new
concept that characterizes meaningful sharing edges for a given type graph (cfr.
Section 4.1.2). Next, based on our experience with the prototype implementa-
tion, we discuss the strength of the sharing analysis (e.g. the gains that can be
expected from using the alternating closure) and we point to a few shortcomings
(e.g. the remaining causes of imprecision and the main sources of inefficiency).
For the analysis, we use depth bound two for the tree nodes labeled 1. The
success substitution ~o,,t = (To,,t', ASharingT-o~,), computed by the abstract in-
terpretation procedure, is given by
Int Or V lnt Or t
Figure 4.11: Abstract sharing environments ~in and flout for the predicate
insert/3.
In Section 4.1.2, we already discussed the binding states derived for the pro-
gram points ( ~ and (~) . Figure 4.5 (page 59) represents the restricted
sharing environments before and after the selection operation _0T = t (_L, _F,
_R). Here, we restrict ourselves to a discussion of the results obtained for the
program point ~ , just before the recursive call. If we order the variables of
the clause according to the tuple (_E, _0T, _Jr, i l L , _L, _F, _~), then the
abstract sharing environment ;3 = (T *, ASharingsr) computed by the abstract
interpretation procedure, is given by
Int t t V Or Int Or
//',,",, /;',,",,
Or Int Or V " Int Or empty I t ~ crop,y[ t
I @9
Figure 4.12: Abstract sharing environment/3 for point (~) in the predicate
insert/3.
I
a a
I
b !',.-_r
C
Figure 4.13: Irrelevant sharing arcs - - a concrete term exhibiting such sharing
cannot be constructed.
The semantics of the type graph, and hence of the sharing edges, is formally
defined by a concretization function that may be adjusted to a particular ap-
plication. Figure 4.13.d shows a self-edge that is irrelevartt if the concretization
4.3. EVALUATION 115
AbsUUnify(A,[B])
Tin <> ~ Tout <>
Or Or Or
nil
r V 8.,~ ml
l"" :.)
r ~ "" 6
function generates non-circular terms only (which is the case in our application).
This is appropriate if the abstract domain is intended to be used for the analy-
sis of systems that perform the occur check. If the intended application is e.g.
loop detection for systems not performing the occur check, the concretization
function has to consider circular terms too, and the self-edge in Figure 4.13.d
will be relevant, indicating possibly circular terms. We will come back to this in
Section 4.3.3 below.
The definition of an abstract sharing component and the specifications of the
abstract primitive operations ignore the possible presence or creation of irrele-
vant edges. An irrelevant edge does not affect the outcome of the concretization
function for some abstract sharing environment, because it does not correspond
to sharing that is possible in any of the concrete terms represented by the un-
derlying abstract term environment. Removing the irrelevant edges from an
abstract sharing environment 131, leads to an equivalent abstract sharing envi-
ronment/32, in the sense that InstrEnvConc(/31) = InstrEnvConc(/32). Note that
the order relation, defined in Section 4.1.4, does not recognize such equivalent
sharing environments either. We have
9 Expressing the sharing component for type graph Ti,~ (input sharing edge
(1)) in terms of type graph To~t by the operation Convert (which leads
116 CHAPTER 4. S H A R I N G A N A L Y S I S
Using this concept, we can now give a more restrictive definition for abstract
sharing components.
Definition 4.3.2 For ar~ abstract t e r m environment 7- t containing the type
graph T = (NodesT, ForwardArcs~r, BackArcs~r), we call a set of unordered pairs
(re, n) such that ra, n E Nodes~-, Label(m) = Label(n), Label(m) • 'Or', and
Relevant(T ~, (m, n)), an abstract sharing component for T .
as edges connecting two nodes representing type graphs with an empty intersec-
tion [38, 39]). The irrelevant edges shown in Figure 4.13.a, 4.13.c and 4.13.d,
in fact connect the roots of two subgraphs that represent sets of terms of which
no elements can occur as (distinct and independent) subterms within a term
represented by the enclosing type graph. These edges can be characterized as
the edges connecting two nodes nl and n~ for which the sets of ancestor nodes
w.r.t, the type graph do not have a functor node n! in common, such that nl
and n2 are descendants of (or equal to) two differer~t children of n I.
We expect that, based on properties of normalized type graphs (such as the
absence of empty nodes), and the precise definition of the concretization function
of type graphs, it can be proved that these are the only possible irrelevant edges,
and that the characterization given above is correct.
Further investigations should address the following issues:
1. Which operations possibly create irrelevant edges. (For example, the irrel-
evant edge in the success substitution ~2 for the example in the previous
section is created by the AlternatingClosure operation.)
2. What is the impact of detecting irrelevant edges at intermediary stages on
the precision and the efficiency of the analysis.
In the remainder of the section, we illustrate the first and the third cause of
imprecision.
The larger the type graphs, the more precise sharing information can be
represented. However as one can expect, the execution time for the abstract
interpretation of a program grows with the size of the type graphs used. Hence,
we must look for some compromise. The size (i.e.the number of nodes) of the
type graphs, as derived by the abstract interpretation of a program, depends on
the depth bound restriction for the function symbols that occur in the recursivc
type-graph branches. The type-graph operations for abstract interpretation fold
the type graphs at places where the depth bound is violated.
A general solution should choose an appropriate depth bound based on the
analysis of the source code of the program. W e expect the optimal depth bound
restriction for a functor occurring in a recursive branch of a type graph to bc
one larger then the maximal depth of the terms in selection operations occurring
in the source code and involving that functor.
In the prototype the depth bound is filledin by hand. For the test programs
(see Appendix A), which are all in normal form (all terms have maximal depth
one), depth bound two is sufficient to get good precision (depth bound one is
not). Consider Program 4.2, for splitting an input list into two lists, the first
consisting of the elements at even positions, the second of the elements at odd
positions. The program on the left is in normal form, the program on the right
is not.
For the recursive clause of the program on the left,the prototype using depth
bound two derives that, after the extension operation that follows the first t w o
calls (_X -- [_al-Xl], _XI = E_bJ_.r]) (see Figure 4.15.a), the top listcell of the
_X variable is not shared with any other node, and the top listcell of the variable
_Xl is only shared with the second list cell of _X. The sharing information is
precise enough to allow compile-time garbage collection:the top list cells of _X
and _X1 can be reused in the two subsequent construction operations, because
they do not share with any of the variables needed in the remainder of the
clause _Y, _a, _rl, .2, _b, _.r2, _r, nor with the output arguments X, _2
(remember that the abstract sharing relation is not transitive). However, for the
program on the right, which is not in normal form, depth bound three is needed
to obtain a similar precision. The domair~ of this program is smaller: we lose
detailed information about the program variable _Xl. Using depth bound two
(scc Figure 4.15.b), we derive that the second list cell of _.X possibly shares with
list cells of program variable _r. This means that the prototype detects only
that the top list cell of _X can be reused, not its second listcell.
4.3. EVALUATION 119
(a) (b)
split( I , _Y, _Z ) :- split( ..X, _Y, ..Z ) "-
I = [_alli], _Xi = [_bl_r], ..X = [ _ a , _ b l - . r ] ,
-Y = [_al _ . r l ] , -u = [_al-rl] ,
7. = [_b l-r2], 7. = [_bl _ r 2 3 ,
split( _ r , _rl, ._r2 ) . split( _r, _rl, _r2 ).
split( _X, _Y, _Z ) :- split( I , _Y, _Z ) "-
I = [_alll], _Xl =. [ ] , _X = [ _ a ] ,
_Y = [_al_Yi], _Yi = [], -Y = [ _ a ] ,
7. = []. 7. = [].
split( _X, _Y, _Z ) :- split( I , _Y, _Z ) .-
I= [], I = [],
_Y = [], _Y = [],
7. = []. = [2.
Program 4.2:split/3
<>
V V Int I n t / ~ ~ V _ ~ ~V
<>
~ V V Int I n t / ~ . ~ V
I Int
nil
(b)
F i g u r e 4.15: T h e r e l a t i o n b e t w e e n t h e d e p t h b o u n d r e s t r i c t i o n , i m p r e c i s i o n i n
t h e s h a r i n g a n a l y s i s a n d t h e n o r m a l f o r m of P r o l o g p r o g r a m s .
120 C H A P T E R 4. SHARING A N A L Y S I S
Program 4 . 3 : p r o c e s s . m a x ~ i n / 2
Program 4 . 4 : a l t e r n a t e / 2
Or Or Or 3 Or
..:.4
V Or V Or v nil ".A.. ~..-~ V nil" ~ .""
combining edge (3) with itself (it is both a converted input sharing edge (1) and
a binding edge created by the abstract unification operation). However, in the
concrete terms represented by the input type graph, either both input terms are
ground lists and unification will not create any new sharing, or at least one of
them is a free variable such that there is no input sharing of list constructor
cells. In other words, the corresponding concrete unification will not give rise
to any alternating paths: the self-edges derived by the abstract unification are
redundant. Note that in this case these edges happen to be irrelevant w.r.t, the
type graph (see Section 4.3.2). However, when trees of integers are used instead
of lists, removing irrelevant edges will not solve the problem of imprecision in
this example.
In Figure 4.17, a similar unification of subgraphs is considered (with depth
bound one for list constructor nodes). In the output type graph on the right, the
edge connecting the list constructor nodes is again both an input sharing edge
and an edge created by the abstract unification operation. In this case however,
the corresponding concrete unifications can give rise to alternating paths (e.g.
consider the input term (.(1, .(2, ~ ) ) , .(1, .(2, .(3,_Z))))): the self-edges derived
by the abstract unification correctly indicate the possible creation of circular
terms. In the case of a system performing the occur-check, such concrete unifi-
cations will result in failure and the corresponding abstract unification can safely
remove irrelevant edges.
Note that the underlying integrated type and mode analysis can also be used
to predict the possible creation of circular terms. The unification of A and B can
create a circular binding if the values of A and B just before unification share
a variable or if they both have internal sharing. Such information is available
in the constraint sets SVal, NUni and PShr too (see Section 2.3.2). However,
according to that criterion, a lot more abstract unification calls will possibly
create circular terms than will be derived by the structure sharing analysis.
The following example motivates the use of twofold sharing components.
Consider the recursive clause of the append/3 program, called with the first two
arguments linear lists of integers that possibly share at the list cell level and the
third argument a free variable (Figure 4.18.a).
~tppend(l, _Y, 7.) :- I = [gl_U], _Z = [_EI_W], append(_U, _Y, _W).
122 C H A P T E R 4. S H A R I N G A N A L Y S I S
Or Or Or Or
~ >..\. 9. . . .
tA3 'A3 t t Int Or \ # ',. Int Or N~
vvvv ~
....
W
Or ' Int Or V
Int
(a) (b)
Figure 4.18: Abstract sharing environments for the append/3 program, at the
entry point (a) and before the recursivc call (b).
At the program point preceding the recursive call, we have the abstract
sharing environment shown in Figure 4.18.b. Old edges are drawn as short
dashed lines, accumulated new edges as long dashed lines, and edges in the
alternating closure as dotted lines. If we do not make that distinction, then
the primitive operations will not derive that the sharing edge (2) between _U
and _Y, which is input sharing for the recursive call append(_U, _Y, _W), does
not represent possibly new sharing created by the recursive call. As mentioned
in Section 4.1.2, a safe extension operation exists for an abstract domain of
sharing environments that contain a single sharing component. This extension
operation considers all output sharing edges of a call as possibly created by the
call. However, for the example at hand, such an operation would consider the
sequence of edges (1-2-3) as an alternating sequence and hence would generate a
self-edge in the list node marked with *, indicating that the input list _X possibly
ends up with internal sharing of structure after the recursive call (at the element
level in the case that the concretization function only generates non-circular
terms, or at the list cell level in the case that the concretization function allows
circular terms). Of course, one can argue that, in this particular case where
the input terms are ground, no new sharing would be introduced in the input
list _X by the implementations that we consider. So, instead of treating sharing
4.3. EVALUATION 123
f g Or f g ,g
g~ g~ V . g
i(i. ,/. a....;,
L
I "''~a ..... "'''
a
l
a
redundam ~ " ~ a :"'a[ "" ,'
1. The size of the type graphs (for large type graphs, large sets of sharing
edges must be dealt with). The size of the type graphs depends on the
depth bound for function symbols occurring in recursive branches, but also
on program specific properties such as the number of different functors
occurring in the source code, and the size of the domain of the clauses (see
Program 4.2).
BindingEdges~(~n, To=t, i, j) =
((To,t[(i,f)],7"o,t[(j,f)])I 3 f : f isafunctor,a constantor V }
& (i, f), (j, f) e S(To,t)
D e f i n i t i o n 4.3.4 For an abstract germ environmeni Tin ~, 1 <_ i , Q , . . . , i j <_
arit~(%~[d) ,~ch *hat i, i x , . . . , ij are pairwi,e distinct, f is a f~netor of ari~ j
and To,t" = TGUnify(T/,,', i, f ( i l , . . . , ij)),
BindingEdge%(~,L, To.t, i, f ( Q, . . . , ij ) ) - -
f.. g
and
i, f( il, . . . , ij ) )
BindingEdges(Y/,,, To,,t,
C TGShift(To,,t, BindingEdge%(T/,,, Tout, i, f ( i l , . . . , i j ) ) ) (4.60)
Another issue to investigate is the representation of the abstract sharing com-
ponents. Formally, the abstract domain we are working with is the set of equiv-
alence classes of abstract sharing environments (see the definition of the con-
cretization function InstrEnvConc (Definition 4.1.18) and the specifications of
Section 4.2). The prototype implementation manipulates maximal representa-
tions of the abstract sharing components, i.e. sets of sharing edges that are closed
under TGShift. Also, the safety of the Convert operation (Lemma 4.2.13) cru-
cially depends on the application of the TGShift operation on its argument before
translating it to the output type graph. It is not obvious at what intermediary
stages of the primitive operations the TGShift operation can be dispensed with
in order to improve the efficiency. Based on the properties of normalized type
graphs, it may be interesting to search for a unique and well-defined canoni-
cal representative for a class of equivalent abstract sharing environments that
contain the same abstract term environment. (Note: Equivalent abstract term
environments do not have a unique representative.) Removing irrelevant edges
(defined in Section 4.3.2) and edges that result from some other edge using the
TGShift operation in general does not lead to a unique minimal representation
for an abstract sharing component, because edges can be mutually related by
TGShift, due to the existence of back-arcs. For example, removing either of the
two edges in Figure 4.20 will yield a minimal abstract sharing component.
Chapter 5
Liveness Analysis
This chapter has the same overall structure as the previous one. In the first
section, we motivate the design of the liveness environments. The second section
contains the formal definitions of the primitive operations and their soundness
proofs. The final section discusses the strength of the liveness analysis.
structures very naturally follows from the representation of terms and sharing
between term substructures used in the previous chapter. Further, we show that
the domain of liveness environments has the algebraic structure required by the
framework of abstract interpretation.
Q : - A1,...,An.
R :- D1,...,P . . . . ,Dq.
P :- B I , . . . , (~ Bj,...,Bm.
?-0.
( B j , B j + I , . . . , B m , D / t , . . . , D q , . . . , A/+l,...,g~n)tr.
The left-to-right computation rule selects the call Bja for execution. Bj+I . . . . .
A,~ are the pending calls - - in fact renamed copies of calls appearing in the bodies
of the program clauses - - and the substitution cr is the current variable-binding
environment. The path of active calls, as represented by the run-time control
structures of an interpreter, consists of the calls q,Ai . . . . ,P,Bj. The binding
environments associated with the active clauses would be marked as accessible
in the case of run-time garbage collection.
Deducing information about the future need to access term structures from
the source text of the program, is a complicated task. We reduce the prob-
lem by considering forward executions only. This restriction is justified if the
trailing mechanism is enhanced such that at run time, before overwriting any
structures, it preserves a copy of the old values that are needed on backtracking
(see Section 3.3). Further, in order to facilitate passing the liveness information
inductively down the proof trees, we introduce the distinction between globally
5.1. LIVENESS ENVIRONMENTS 129
/r
DI ... P Dk ... Dq
In the figure, variables are renamed to avoid name conflicts between different
clause instances. The labels ~ , . . . , ( ~ indicate points in the c o m p u t a t i o n
where we consider snapshots of the local clause environment. The concrete
sharing environments are shown in Figure 5.3. In the figures, program variable
names label the roots' outarcs. Shared substructures are shown as connected
by dashed arcs. Corresponding substructures of shared structures also share;
130 C H A P T E R 5. L I V E N E S S A N A L Y S I S
| 9
however, we show only the principal sharing arcs to avoid cluttering the figures.
Variables are represented by nodes labeled V, with aliases connected by sharing
arcs. Nodes decorated with * are locally live, nodes decorated with ** are
globally live. Substructures of live structures are also live; again, we indicate
only the principal live nodes.
The concrete sharing environment ( ~ of Figure 5.3 represents the initial
binding state for the call of append(_X0, frO, 7.0).
= ([1],[2],v)
CSharingjc,. = (0, @)
The arguments _X0 and _Y0 are ground single element listswithout any sharing.
Assuming that 7.0 is the qucry's intended solution, _Z0 is marked globally live at
point (~. The structures of-El, _UI, _YI and _Wl are locally live at point ~ )
because they occur in the second and third call of that clause instance. The fact
that _7,1 is globally live at point (~) is inherited from the (globally) liveness of
_7.0 at point ( ~ . This illustrates how (local and/or global) livencss information
is passed down on procedure entry to the environments at lower levels of the
proof tree. The unification _Xl = [_El I _UI] after program point (~) boils
down to a selection of subterms, because the program variable _Xl is a ground
list on entry of the call and _El, _UI are uninstantiated variables. In particular,
the variable -El gets bound to the value 1 and the variable _UI gets the value nil
(see the environment (~)). In the logic programming systems that we consider,
a copy is m a d e whenever a variable is bound to an atom or an integer; so no new
sharing is introduced by this unification. If the list elements were compound
term structures and/or the tail of the list was not empty, pointers would be
5.1. LIVENESS ENVIRONMENTS 131
@ @
o
V V V V V
A
I nil
A
2 nil
A
1 nil
A
2 nil
9 |
<~
1 nil V V 1 nil V
A
1 nil
A
2 nil
A
1 nil
A
2 nil
?~'.v "
(2) C)
nil V I Bil
A
2 nil
A
1 nil
/%:::<:z~,,
2 nil ~2 mill 1 p~'.
2 nil
9
1 nil 2 nil 1
A
2 nil
Figure 5.3: The concrete sharing environments at the points shown in Figure 5.2,
annotated with liveness information.
132 CHAPTER 5. L I V E N E S S A N A L Y S I S
created from the variables _El, _U1 to the respective subterms of ..Xl and the
sharing of structure thus introduced would be represented by new sharing edges
in the concrete environment ( ~ . For this point, we now discuss how the liveness
information can be used to determine which storage cells become garbage after
they are referenced. Figure 5.3. ( ~ shows the liveness information of interest.
The program variable -21 is still globally live and _U1, _Y1, _Wl are locally live.
The program variable _El is no longer locally live, because the only reference
is in the next call. Because .21 is a free variable at that point, we know that
the unification _Zl = [_El I _W1] is in fact a construction operation requiring
the allocation of a new list-cell on the heap. However, because the top list-cell
of _Xl is neither locally, nor globally live, it is a candidate to be used instead.
Note that the list-cell of variable _YI is not; it will be referenced by the recursive
call. At point ( ~ , the concrete sharing environment indicates that sharing
between -21 and _W1 is created by the construction operation. The set of locally
live structures is empty because the next call is the last one within the current
clause environment. The set of globally live structures still contains the output
argument .21. Since .21 got instantiated by the unification, its substructures
are live too. Moreover, also _W1 is globally live because it shares with the tail
of .21. Indeed, the liveness property propagates through shared structures. We
introduce a LiveCIosure operation to make explicit the interaction between the
sharing and the liveness relations when needed.
D e f i n i t i o n 5.1.1 For a concrete term ]C, a set [-Ic C_ O(]C) and PIe a set of
unordered pairs (r, s) such ~ha~ r, s 9 O ( ~ ) , we de~ne
are ground lists without any sharing and the third argument is a free variable
which is live. The sharing environments ( ~ and (~) illustrate the binding
states after the recursive call append(_gl,_Vl,_tr and after the initial query
append(-/O,_YO, 7.0) respectively.
If we assume that the intended solution of the initial query consists of the
values of 7.0 and -/0, then both must be marked globally live in point ( f ) . At
program point (~), this results in -/1 and _Zl being globally live; hence, the top
list-cell of _I1 is no longer a candidate for reuse by the construction operation
_.Z1 = [~..1 I _~13.
If both kinds of subgoals are likely to occur at run time, the compiler can
generate two specialized versions for the append procedure: one that leaves
the input list of the first argument intact and another less memory consuming
version that reuses the list-cells of the first argument to build the output list
returned in the third argument. The purpose of liveness analysis is to derive at
compile time, which calls of the append/3 predicate in the source programs can
be specialized to the more efficient version.
_x= I @
nrev (_U, _~U), 7. : I _w], |
Last = [_E], 3~ append ( _U, _Y, _W). 7(~
append(_RU, _Last, _Y) <~)
o o
a . ..... .- SJ b ni l b nil
b nil
clause variables named I 2 , -Y2, _.E~, _Us, .-RU2, -Last2. Apart from determining
the liveness component, procedure entry proceeds as discussed in Section 4.1.
The value of -Ul is passed to I 2 , that of --RU1 is passed to -Y2, and sharing
between subterms of -U1 and -RUI is passed on as sharing between subterms of
I 2 and -Y2 - - at least it would be if there were any sharing between -Ui and
-RU1 in the example. The program variables -E2, -U2, -RU2, _Last2, which occur
only in the body of the child invocation of the recursive clause, are initialized as
free variables that do not take part in the sharing relation.
Computing the liveness component is more involved. First, we must make
explicit the set of terms that are needed to complete the computation after
returning from the call nrev(_U1,_RU1). The set consists of the terms that are
(locally and/or globally) live at program point (~), or that are sharing with any
such live terms. The set of globally live terms is passed down from the calling
environment. For Figure 5.4 on the left, if we assume that the term -Y1 is the
intended solution of the n r e v / 2 invocation, then -Y1 (labeled **) will be the only
globally live part of the environment; it does not share with other subterms. We
will represent globally live terms as a set of occurrences, the concrete liveness
component, and add it to the concrete sharing environments. The set of locally
live terms can be derived from the source text of the clause and comprises the
terms _Last1, -El, -RUt (labeled 9 in Figure 5.4 on the left). Note that -Y1 is
5.1. LIVENESS ENVIRONMENTS 135
<>
X ~ I *
V atom OR V V OR V ** V V V V
atom
/
9~nil
/ ~ _
OR ." .
//~
~,sI atom
il
/@"
atom
atom
Or V V V V V V V V V V V
" ' - . . . . . . . . . .-~176
lnt V..."
in fact both locally and globally live at point O 9 Because the set of locally
live terms is only temporarily needed during the procedure-entry operation and
because locally live components of successive program points do not depend on
each other (they are determined by the source code only), we will not include
them as a separate component in the environments.
From the set of (localiy a n d / o r globally) live subterms, as determined above,
we must select those that compose the structures of -U1 and A~U1, the arguments
of the next call. And at last, we must establish the corresponding subterms in
the structures of I 2 and -Y2 as the (globally) liveness component of the child
clause invocation (labeled ** in Figure 5.4 on the right).
At the abstract level, although terms are replaced by type graphs, the pro-
cess is similar. Nodes of type graphs needed to complete the computation are
determined, the information is restricted to the type graphs for -Ux and --RU1, the
arguments of the call, and passed on to I 2 and -u the arguments of the head.
Figure 5.5 illustrates the abstract analogue of the operation shown in Figure 5.4.
Remember that, before restricting the information to the arguments of the
call, the liveness information has to be made explicit first. While in the concrete
case, liveness propagates through the TransitiveClosure of the sharing compo-
nents, obviously, for abstract environments it propagates through the
AJternatingClosure of the twofold sharing component. However, as Figure 5.6
illustrates, when propagating the abst.racL global liveness information, we can
136 CHAPTER 5. LIVENESS ANALYSIS
get an even more precise result if we take into account that the liveness is al-
ready closed with respect to the first sharing component. Suppose that the
predicate p/3, defined by the single clause p(_Y,7.,_U) : - _Y=_U, q( 7.,_U)., is
called in the environment of Figure 5.6.a. Here, sharing edges from the first (or
old) sharing component are represented as dashed arcs, those of the second (or
new) sharing component as dotted arcs. The _X0 subgraph nodes are marked
locally live, the first sharing component is empty, the second component repre-
sents potential sharing between _X0 and _u and between _Y0 and 7.0, but not
between _X0 and 7.0. On procedure entry to p/3, the sharing information of Fig-
ure 5.6.a results in a non-empty first sharing component for the environment of
Figure 5.6.b and an empty second sharing component (see Section 4.2.2). Prop-
agation of the liveness information through the AIternatingCIosure operation, will
mark the term _Y1 as globally live but not the term 7.1 (see Figure 5.6.b). Intu-
itively, it means that the global liveness of the term _Y1 and the sharing between
_Y1 and 7.1 cannot occur in the same execution state. Figure 5.6.c represents
the environment at the program point just before the call of q(7.1,_U1). The
unification _YI=_U1 has introduced new sharing between _Y1 and _U1. Now, if
procedure entry to the predicate q/2 propagates the global liveness information
through the alternating closure of the twofold sharing component, then both
7.2 and _U2 will be marked as globally live (Figure 5.6.d). It should be clear
that marking _Z2 as a live term introduces imprecision because, as mentioned
above, the global liveness of the term _Y1 and the sharing between _Y1 and _7,1
cannot occur in the same execution state. To overcome this problem, we define
a AItLiveCIosure operation that considers only paths starting with a new sharing
edge from a live node.
AltLiveClosure(LT-,PT1, P7"2) =
s v S.) a fi.ite seque.ce ove
PT"z u P7"2 such that S : S,, & R1 E L7 &
S (VlE~7:l<l<n:=>St=Rt+l) &
[VkeM: (l<2k+l<n ~(R=k+,,Szk+I)9
(1 _< 2k < n = (R=~, S=~) e P7"1)]
Note however that in the case of the naive reverse program, using the improved
closure operation has no effect, because all relevant sharing edges belong to the
second sharing component; the first sharing component is empty for all program
points.
The next operation to consider is unification. The two basic forms of uni-
fication that can occur in a normal-form logic program (i.e., X = Y and X --
f ( X l , . . . , Xn)) are discussed at length in Section 4.1 for sharing environments.
We now extend the unification to incorporate the liveness component. As is
mentioned in the previous section, for concrete terms the liveness property is
closed under substructures. Therefore, when unification further instantiates a
5.1. LIVENESS ENVIRONMENTS 137
<>
V b nil V
b nit a nil
OR * V atom OR V
atom OR il il
live structure, we add the new nodes to the concrete liveness component. Also,
unification can introduce new sharing, but as nodes that share with a live node
become live implicitly, no special action must be undertaken.
At the abstract level, the effect of unification is to modify the type graphs,
say T into T ~. When T has live nodes, updating the liveness component entails
determining the corresponding nodes in the new type graph T ~. As type graphs
are essentially deterministic, top-down tree automata, the problem is similar to
determining whether two automata recognize intersecting sets. Once the cor-
responding nodes are determined, it is straightforward to update the liveness
components. The propagation of liveness across new sharing edges occurs im-
plicitly. The propagation of liveness down new descendent arcs follows from
the interpretation of the liveness environments, as defined by the concretlzation
function (Section 5.1.3). Figures 5.7, 5.8 and 5.9, 5.10 illustrate the concrete and
abstract liveness environments before and after the unification _Z = [_E [ 3~]
in the a p p e n d / ] predicate.
Finally, we consider procedure exit. Figure 5.11 shows a concrete liveness
environment in an invocation of n r e v at the point the final append is called
(Program point ( ~ ) ) . Figure 5.12 shows the concrete liveness environment of
the append clause at the point execution of the body is completed (Program
138 C H A P T E R 5. L I V E N E S S A N A L Y S I S
<>
b nil V
AAA
b nil a nfl b V . . . . . . . . . -"
s S ,-'"
Figure 5.9: The concrete liveness environment at point @ .
<>
OR . atom OR 9 V
A
atom OR . ] nil atom V s ." I nil
--
&/L/ ....~
9 ~ d nil atom o" ~ ~ atom
atom
<>
V a s9 9 9
s S
A
b nil
~. b nil ,, '
A
b nil a
9
\',
nit tI t ........... "
I # # #S
/,,
##
,' a nil
~'~ a nil
<>
Lastl
a 9 sS
A
a 9 ,
A
b . . ,,' 9
I I
b nil b
/% nil !t a
9
nil
b nil a ifil
point ( ~ ) ) . The restriction of this state to the variables -X2, -Y2, and 7,2 has to
be passed back to -RU1, -Last1, and -Y1, respectively. No liveness information
need be returned. The result is shown in Figure 5.13. We see that -Y1, a live
variable, has been bound to the list [b,a], and that, due to the generated sharing
_Last1 becomes live as well, although implicitly.
When moving from the concrete to the abstract level, we have similar op-
erations. The abstract liveness environment must be restricted to the variables
occurring in the head of the clause and the type graphs and sharing information
has to be returned to the caller. The extension operation (Section 4.2.3) prop-
agates the updates of the variables participating in the call to the variables in
the caller's environment: Further type graphs may need to be modified owing to
shared variables; sharing has to be updated in terms of the new type graphs; the
new sharing interacts with the sharing present in the caller's environment before
the call, inducing further sharing. The updating of the liveness component in the
caller's environment implicitly follows from the modifications in the type graphs
140 C H A P T E R 5. LIVENESS ANALYSIS
<>
V** atom OR OR
atom OR
.~ni s ' I il atom nil
s s S ~
9 | tS atom atom
atom
Figure 5.14: The abstract liveness environment in the caller's environment
at point (~) - - First of two inputs to procedure exit.
OR atom OR OR
1 atom/Ot~ ";
,~ni2
" .atom ttttt "'-2r nil atom,), / atom
atom , .. jr:tom ., , sr
<>
9 atom OR OR #l .
A A"
atom OR atom OR , . ~ nil . ~ nil t atom nil
and the sharing relation. Figures 5.14-5.16 illustrate the abstract analogue of
the operation shown in Figure 5.11-5.13. Notice the possible sharing between
-Y1 and _Last1 in Figure 5.16, which is not present in Figure 5.13. This is due
to the possibility that ~Ul is the empty list in Figure 5.14.
The following lemmastates that the AItLiveCIosure operation for abstract liveness
components safely approximates the LiveClosure operation of concrete liveness
components.
If s E Ljc, then it follows from the definition of AbsNodeT,/C, that T[SeI(/C, s)] E
AbsNodeT,/c(LJc), and by the definition of AItLiveCIosure we have T[SeI(/C, s)] E
AttLiyeC$osure(AbsNodeT,/c(Ljc), AbsPairT,/c(P~cl) , AbsPairT,/c(Pjcu)), so we are
done. Now, suppose s g kx:, but (3r E Lpc : (r,s) E TransitiveCIosure(Px:l U
PJc2)). By the definition of TransitiveCIosure, there exists a finite sequence E
over PJcl U P~:2 such that
r E L~,
E = for s o m e E
r = rl~s = Sn~
Vt E ~ : 1 < l < n =~ sl - rl+l.
144 CHAPTER 5. L I V E N E S S A N A L Y S I S
Proof. This follows from the property of Upp and the definitions of < z , and
LiveUpp. []
5.2.1 Unification
As for the sharing analysis, we specify separately a concrete and an abstract live-
ness unification for the two basic forms of unification that can occur in normal-
form Prolog programs: Xi = Xj and X~ = f ( X i ~ , . . . , X i , ) . The unification
operation has to ensure that whenever the term environment becomes further
instantiated due to the unification, the associated liveness component of the
concrete environment is closed with respect to the TermShift operation (see Sec-
tion 5.1.2). Changes in the global liveness of terms that are due to an update of
the sharing component are not made explicit.
5.2.1.1 Xi : Xj
D e f i n i t i o n 5.2.1 For a concrete liveness environment (/C,,~, CSharingjc~,
CLivejc,..) and 1 < i < ff < arity(tCin[e]),
LiveUnify((/Ci,~, CSharing~c,,,, CLivejc,,,), i, j) =
if mgu(S:,./i,/c,,,/j) i, fail
then fail
else (ICout, CSharingx:.~,, CLivex:.~,),
where
CSharingjc.,.)
(ICon, t, = Unify((/Cin, CSharingjc,,,),i,j),
CLivejco,,, = TermShift(/Co,,t, CLivejc~,,).
The followinglemma states that the concrete unification operation LiveUnify(-, i,
j) is well-defined on the domain of concrete liveness environments.
148 C H A P T E R 5. LIVENESS ANALYSIS
where
We still have to generalize the Convert procedure for liveness components. The
function Convert takes an abstract liveness component for a type graph Ti,, and
reexpresses it for a type graph To,,t. In particular, the function will be used for a
type graph To,,t that describes a superset of instantiations of terms represented
by the type graph Ti,,.
The following theorem gives the correctness condition for the liveness unification.
AbsNodeT,/c(TermShift(/C,Lx:)) C TGShift(T,AbsNodeT,/c(L~)).
Proof. Expanding the definitions of AbsNodeT./C and TermShift we have
SeI(/C,r.t) -- Sel(/C,r).T.
It follows that
T[SeI(/C,r.t)] E TGShift(T,{'T[SeI(/C,r)]})
by the definition of TGShift,
= TGShift(T, AbsNodeT,/C({r}))
by the definition of AbsNodeT,/C.
In summary,
The lemma follows from (5.7), (5.8), and the monotonicity of TGShift and
AbsNodeT,/C. []
= r)],
by (5.11),
E AbsNode~,~,Ki,~(LK:~.),
from r E Ljc~ and the definition of AbsNode~.,K:i,,,
C_ TGShift(T~,,, AbsNodeT~.,K;in(L~c,.)),
by the definition of TGShift.
Summarizing, we have
The desired result is now obtained from (5.9), (5.10), (5.11), (5.12) and an
application of the first disjunct in the definition of Convert (Definition 5.2.4,
page 149).
case 2: (ICi,,/r E Vsrs). Here we have
E AbsNodeT~,,,/Q,,(L~:~.),
from r E L~c~.and the definition of AbsNode~jQ.,
C TGShift(T~n,AbsNode~jQ~(L~c,~)),
by the definition of TGShift.
Summarizing, we have
~,,[Sel(/ci,,, r)] e TGShift(~,~,AbsNode~.,/C~.(L~c,.)). (5.15)
The desired result is now obtained from (5.9), (5.10), (5.13), (5.14), (5.15)
and an application of the second disjunct in the definition of Convert (Def-
inition 5.2.4, page 149).
D
Proof. [ T h e o r e m 5.2.5 ( S a f e t y of AbstrkiveUnify(-, i, j))]
We must prove that
(/Co,,,,CSharingx:..,,CLive~co.,)E LiveEnvConc((To'`,',ASharing:ro.,,ALivesro.,)),
that is, the following conditions must be satisfied.
1. (/Co.,, CSharing~c~ 9 InstrEnvConc((To,,,', ASharing:ro.,)).
2. CLivex:o., is a concrete liveness component for (~Co'`,, CSharing~:o.,).
3. AbsNode./-o,,,,/Co,,~(CLivejc~ C TGShift(To'`t, ALivecro.,).
Condition 1. is a restatement of Theorem 4.2.11. Condition 2. follows from
Lemma 5.2.2. Using the assumptions of Theorem 5.2.5.5 and 5.2.5.6 and expand-
ing the definitions of ilveUnify and AbstrLiveUnify, we can rewrite Condition 3.
a8
AbsNodeTo'`t,/Co'`,(TermShift(/Co'`t,CLivejc,~))
C_ TGShift(To'`,, Convert(~,~,ALive~%,To,,,)). (5.16)
We observe that
AbsNodeTo'`t,/Co'`(TermShift(
, /Co'`,,CLivejc,~))
C_TGShift(To'`,,AbsNodeTo,,,,/Co'`,(CLivejc~))
by Lemma 5.2.6 (the safety of TGShift),
C__TGShift(To'`,,Convert(~,~,AbsNode~,~,/C~,~(CLivejc,~),7o,,,))
by Lemma 5.2.7 (the safety of Convert),
and the monotonicity of TGShift,
C_TGShift(To.,,Convert(7~,~,TGShift(~,,, ALiveT-,~),To'`,))
by the assumptionof Theorem 5.2.5.3c,
and the monotonicity of TGShlft and Convert,
= TGShift(To,,,, Convert(Tin, ALive~%, To'`,))
by the definition of Convert,
and the idempotence of TGShift.
Summarizing, we get (5.16), as desired. [-1
5.2. P R I M I T I V E O P E R A T I O N S 153
5.2.1.2 Xi = f ( X i , , . . . , X i ~ )
D e f i n i t l o n 5 . 2 . 8 For a corLerete liveness environment (ICin, CSharingx:,,,
Cl_ivex:,.~) and 1 < i, i l , . . . , ij < arity(ICi~[e]) such that i, i x , . . . , ij are pairwise
distinct, and f in a functor of arity j,
where
The following lemma states that the operation LiveRestrict is well-defined on the
set of concrete liveness environments.
Proof. By the definition of LiveRestrict and Lemma 4.2.28, we know that (K~,tr,
CSharingpc.,,./is a concrete sharing environment. From
LiveClosore(Pro](Ljc,~,
icGzz),Pro~(CSharimg~,it.ll))
C__ Pro](LiveClosore(bc,~, CSh~ring,c,.),i~.). (s.iw)
156 C H A P T E R 5. L I V E N E S S A N A L Y S I S
Summarizing, we have
where
(7-,st, ~, ASharing=r..,.) = AbstrRestrict((Ti. e, ASharingn~), lean),
TGShift(Tr, tr, ALive=r.,.) = AbstrProj(Ti,~, C U B,T,.,tr, icau),
and
C = AItLiveClosure(TGShift(T,,,,ALive:q~),TGShift(T~,,,AShr~- ),
TGShift(Ti,~, AShr~2..)) ,
B = LiveCIosure(Rstr(Ti,~, Nodes=r~,domt(~it), ASharingT,~).
The following theorem gives the correctness condition for the AbstrLiveRestrict
operation.
AbsNode'T,.,t,.,IC,.,t,.(Proj(L~,=, icau))
{~r,t,[Sel()C,,tr, k.S)] I k E {I,..., m} ~ ic~u(k).sE Lx:,~}.
=
For arbitrary k, s such that k E {I,..., m} and icau(k).s E Lic,~, we must prove
that
AbsNodeT,/c(LiveC1osure(Ljc, Px:))
C_ LiveClosure(AbsNodeT,/c(L~c),AbsPalrT,/c(Px: )).
For arbitrary k, s satisfying k E domt~iz & k.s E O(IC), we must prove that
3. AbsNodeTr,t,.K:r,tr(CLivex:..,.)_CTGShift(Tr,tr,ALiver..,.).
Condition 1. follows from Theorem 4.2.31, and Condition 2. follows from Lemma
5.2.14. Expanding the definitions of LiveRestrict and AbstrLiveRestrict, we can
rewrite Condition 3. as
Summarizing, we have
Summarizing, we have
The information that a compiler needs to decide upon the liveness of some
term at a particular program point, requires deriving the set of locally live terms
at that program point, in addition to the full set of globally live terms. Given a
set domtau, characterizing the variables occurring in the subgoals of the clause
162 C H A P T E R 5. L I V E N E S S A N A L Y S I S
that follow the program point considered, the formalization of the operation is
similar to the intermediate step of the Restrict operation.
The special versions of the liveness restriction operation needed by the proce-
dure-exit operation (cfr. Section 4.2.2) are given below. Note that the full live-
ness component need not be computed, because the set of locally live terms is
empty at the end of the clause, and the set of sharing edges relevant for propagat-
ing the globally liveness property is passed upwards on procedure exit. We omit
the proofs of the well-definedness of the concrete operation and the soundness
of the abstract operation, as they are straightforward.
where
In the first step of procedure exit, the abstract liveness environments at the
last program point of the applicable clauses are restricted to the arguments of
the head of each clause by means of the Abstrl_iveRestrict2 operation, and the
I_iveUpp operation is used to compute an upper approximation of these restricted
liveness environments. In this section, we present only the extension operation
that is needed in the second step of the procedure-exit operation to regain the
information about the calling environment that was lost on procedure entry.
where
Note that the value of CLivejc.., does not depend on CLivejc.... and that the
conditions on the relationship between /Ci, and Er,tr and their sharing compo-
nents are the same as in Section 4.2.3. Changes in the global liveness of terms
that are due to an update of the sharing component are not made explicit.
The following lemma states that the operation LiveExtend is well-defined on
the set of concrete liveness environments.
Lemma 5.2.24 Let (/Ci~, CSharingjq~, CLive~) and (/C~,tr, CSharingjc .....
CUve .... ) be concrete iveness environment,, , eh that =
arity( IC,,t,[e]) = r a & m <_ n, and lean: { 1 , . . . , m} --* { 1 , . . . , n} an injection. Let
(/Co,,:, CSharingjc .... CLivejc~ = LiveExtend((K:i,,, CSharing~c,,., CLivejc,.,), (/C.,t.,
CSharingjc .... , CLivejc.... ),ican). Then (ICo,,t, CSharingjc .... CLivejco~,) is a con-
crete liveness environment.
5.3 Evaluation
After discussing one example in detail, we relate the precision of the liveness
analysis to the strength of the sharing analysis. Next, we take the viewpoint of
a compiler writer and investigate the practical usefulness of the results obtained
by the abstract interpretation procedure for the liveness environments introduced
in Section 5.1.
Program 5 . 2 : q s o r t / 3 (Quicksort)
See Figure 5.17 (top left) for a graphical representation. The third argument is
specified to be globally live, because it is the intended solution of the initial goal.
Note that we specify the type of the second argument (which is the accumulating
parameter) to be DList. This is an overestimation because in an initial coztcrete
call, its value will be n i l usually. However for the recursive calls, the accumu-
lating parameter is a list in general. Our analysis uses depth bound two for the
list cell nodes. The success substitution ~o,,t = (To,,t ~, ASharingT"o.,, ALiveTo.,),
computed by the abstract interpretation procedure (Figure 5.17 top right), is
166 C H A P T E R 5. L I V E N E S S A N A L Y S I S
Or Or V Or Or Or
d d d,, d d
I I I "- I ,,'1
Int Int Int 99 Int .," lnt
Ts <>
M L H T Sm ** G r ** S m l 9
v
ht ~ t ,'" i|
such that
Only the principal sharing edges are represented. The full set of sharing edges
is obtained by applying the TGShift operation. We see that the output argu-
m e n t , ..ges, gets bound to a list that possibly shares with the input value of
the accumulating parameter. Intuitively, this is correct because in the first pro-
gram clause, the value of the accumulating parameter is passed on as the output
value. Note that there is potential sharing between the input list that is to be
sorted and the output list, but only on the level of the list elements (which are
structured terms of type d(Int)).
The analysis of the quicksort program q s o r t / 3 entails an analysis of the
p a r t i t i o n / 4 predicate, namely for the abstract liveness environment /31 =
(7-~, ASharingT-1, ALiveT-1), where
Note that there is potential sharing between the input list and both the lists of
the third and fourth argument, at the level of the list elements, but not between
the output lists among themselves. Now, let us consider the program point in the
second clause of the p a r t i t i o n / 4 predicate, right after the selection operation.
The point is of particular interest to the compiler when it checks for the possible
creation of garbage cells. If we order the variables of the clause according to
the tuple ( _~, _i., _.It, _T, _Sin, _Gr, _Sml ), then the environment derived
by the analysis is given by fl, = (T:, ASharing=ro, ALiveT,>,
From the representation in Figure 5.17, it is clear that the top list cell of the
program variable _L is not shared with any live term. We marked both the
globally and locally live subterms. Remember that ALiveT-, contains only the
globally live terms. Similar results are obtained for the selection operations in
the third clause of partition/4 and the second clause of qsort/3.
The result of the liveness analysis can be interpreted as follows. For an
initial goal described by (T~,~e, ASharing~q~, ALiveT~), such that the input list of
the first argument is not live when q s o r t / 3 is called, the top list cell of the
program variable _.L becomes garbage after each of the selections in the source
code, as it will not be shared with any live term at run time. Consequently the
compiler can generate code such that all the list cells of the input list are reused
to construct the output list. The list cell containing the pivot element can be
reused in the q s o r t predicate to construct Acc2 = [_3 I A c c l ] ; and in the
call p a r t i t i o n ( _ l t , _T, _U1, _U2), all the list cells that _T is made of, can be
reused to construct _U1 and _U2. The recursive calls of q s o r t / 3 will then once
again reuse the list cells of _U1 and _U2. We conclude that the sorting algorithm
can work in-place; the only heap-memory required is that occupied by the input
data structures.
We believe that our analysis performs quite well compared with other related
methods reported in the literature. In the work of Hudak [33], a version of the
quicksort program for sorting arrays of integers is discussed. Their results are
very similar to ours. The analysis infers that all the updates in the program
can be done destructively. The analysis of a Lisp-like version of the quicksort
program in the framework of Inoue, Seki and Yagi [35], also deduces that the
168 CHAPTER 5. L I V E N E S S A N A L Y S I S
Program 5 . 3 : s a m e l e a v e s / 2
garbage list ceils created by the auxiliary functions (namely for extracting the
elements that are greater, resp. smaller, than the pivot element, and for append-
ing two sublists), can be reclaimed (i.e. appended to a free-list) after the calling
function has finished its execution; they do not address the issue of local storage
overwriting, reusing the input list cells to construct the output list.
The intended solution of the initial goal basically is a yes/no answer. How-
ever, we assume that the input list of the second argument is also needed in
some further computations. Therefore, the second argument is specified to be
globally live. Our analysis uses depth bound two for the tree nodes labeled t.
The success substitution ~o~,t = ( 7:.o,,t ,ASharingTo.,,ALive:ro~,), computed by
the abstract interpretation procedure, is equivalent to 13in. The analysis of the
program s a m e l e a v e s / 2 entails an analysis of the predicate p r o f i l e / 2 ; once for
5.3. EVALUATION 169
tr pr x y z t2
Or Or t V** Or Or Or V
' "
,,LAJ LLAJ " "-
l
I I , I
A / ~ a I ~''1"
Int lnt
Figure 5.18: Abstract liveness environments 8~- for sameleaves/2 and 8s for
profile/2.
garbage and because the construction operation that follows requires two heap
cells for tree nodes labeled t, it would be most desirable too. As we discussed in
Section 4.3.3 for the s p l i t / 3 predicate where a similar problem occurred, the
precision of the sharing analysis can be improved, either by using depth bound
three for the tree nodes, or by breaking the unification up into simple normal-
form operations, thereby introducing a few more local program variables. The
result of the analysis for the latter case can be found in Appendix A. It confirms
our intuition that the rearrangement of the subtrees in the third clause of the
p r o f i l e / 2 program can be done in-place.
9Now, for the case that the analysis starts from the liveness environment ~fl~,
we obtain for program point ( ~ the abstract substitution/~,.= (T~, ASharingT,.,
Alive:rr),
7-, ::= (t(LvTreeOne,LvTree), List, LvTree, LvTree, LvTree, V),
ASharing=r,. = (0, AShr~- ),
AShr~,. = { (T,.[(I,/;).(I,~)], T~.[(3,~)]),
T,[(4,t)] ),
(T,.[(1,t).(2, g)], T,.[(5,$)]) },
ALive:r,. = { T,.[(1,t)] }.
Thus, we get the same imprecise sharing information as in/3s. But in this case
the program variable _tr is globally live instead of _pr, so there are no garbage
cells created and the imprecision is insignificant. Two new tree-cells have to be
allocated anyway.
In Section 4.3.3, we discussed a few other causes of imprecision in the sharing
analysis, not all of which can be resolved. More experiments with concrete pro-
grams will be needed to measure the impact of such imprecision on the amount
of garbage that our analysis cannot detect. For those cases, we will have to rely
on run-time garbage collection to compensate for the imprecision in the liveness
analysis. However for future work, we think it is more important to look for ef-
ficient ways to reuse the garbage cells that are detected by the analysis. Before
we consider that issue in the next section, we want to make some final remarks
concerning the analysis of the p r o f i l e / 2 program.
Consider program point @ and the variables of the clause ordered according
to the tuple ( _ t r , _pr, _u, _ p r t a i l , _y ). The environment derived by the
analysis in the case that the mode of use is described by/~2, is given by tip =
( T ; , ASharingT-p, ALiveT-p),
the cell. If the analysis starts from a mode of use corresponding to the first
call of p r o f i l e / 2 in the s a ~ e l e a v e s / 2 program, as described by ~1, we derive
/3q = (T~, ASharing=rq, ALive=rq) where
X
REF I I " / '~ ",,
f f Z
Y REF
\\
Z VAR STRUCT
REF "
f/1
- -~
,'
]
I I
w4 g.~ .,~
cONST
I I
3 ~" 3
Local R e u s e
query #S #C functor #D
nrev(List,V) 1 1 91 2 o
append(List,List,V) 1 1 .12 i
qs o r t ( List, List, V) 1 1 .12 o
p a r t i t ion(Int,List,V,V) 2 2 .12 2
profile(LvTree,V) 3 2 t/2 2
2 0 lv/1 0
0 2 ./2 o
buildtree(List,Wree,V) 1 0 ./2 0
ins e r r (Int,Tree,V) 2 3 t/3 2
sift(List,V) 1 1 .12 1
remove(Int,List,V) 2 1 ./2 1
permutation(List,V) 0 1 912 0
select(V,List,V) 2 1 .12 1
Program 5 . 4 : b u i l d t r e e / 3
but # D = 0. This means that the construction and selection do not occur in
the same chunk. In order to allow a destructive update, either a permanent
variable must be used to keep track of the garbage cell, or a more sophisticated
technique such as code migration [26] must be used. If that extension is added to
the compiler described in [52], than it will be possible to generate code such that
the program works in-place. The only heap-memory required is that occupied
by the input data structures.
For queries such as s e l e c t ( V , L i s t , V ) and remove(Int,List,V), we have # S >
# C = # D . The s e l e c t / 3 predicate is used by the p e r m u t a t i o n / 2 procedure (see
page 177). The remove/3 predicate (see Appendix A) is called as an auxiliary
procedure by the s i f t / 2 program to sift out the prime numbers according to
Eratosthenes' sieve algorithm. For these queries, more garbage cells are created
(and detected) than can actually be reused within the clauses themselves. Run-
time garbage collection is still needed to discard the garbage cells, unless some
techniques for non-local reuse are developed.
The same holds for the tree-manipulating program b u i l d t r e e / 3 , which trans-
forms a list into a sorted binary tree (see Program 5.4). The analysis detects
that the list cells become garbage in the second clause, however they cannot be
reused locally. Analyzing b u i l d t r e e / 3 entails analyzing i n s e r t / 3 , which in-
serts one element into a binary tree. We have # C > # S = # D , indicating that
the tree can be modified in place, but extra heap space is needed for the new
element that is inserted. Interesting enough, this results in the same memory
usage as the skillful programmer obtains by using open-ended trees.
If the query specifications for these programs allow sharing at the element
level of the input lists or trees, essentially the same results are obtained as
above. For example, when reversing a list of free variables that share (e.g.
n r e v ( [ X , Y , X , Y , Z ] ,Out)), the reverse procedure can still work in-place (see
Section A.3). However, if sharing is allowed between different input lists (trees)
at the list-cell (tree-cell) level, it is generally unsafe to reuse the cells (see Sec-
tion A.2).
In some cases a reordering of subgoals (or code migration [26]) may be de-
5.3. EVALUATION 175
sirable. For instance, when the append/3 program is used to split a list into two
sublists, the compiler has to reorder the unification operations in order to benefit
from the liveness information. The normal form of the append/3 predicate is as
follows.
The first and second arguments are specified to be globally live, because they
are the intended solution for the initial goal. The success substitution ;3o,~t =
(To,,t ~, AShadngTo.,, Al_ivezo~,>, computed by the abstract interpretation proce-
dure, is
From the representation in Figure 5.20, it is clear that the top list cell of the
program variable 7. is not shared with any live term, nor is its value needed for
the recursive call. So, if the construction operation _.X = E-El_U] were delayed
until the selection operation _Z = E_EI_W3 is done, it could reuse the garbage
176 C H A P T E R 5. LIVENESS A N A L Y S I S
Int
Figure 5.20: Abstract liveness environments/3i,~ and/3~ for append/3.
cell. Note the sharing of the integer term between the program variables _X and
_.E. This sharing is created by the construction operation, at a time the variable
_.E is still unbound. But the selection operation, giving the integer value, does
not introduce any sharing with the first integer element of the list _Z, because of
the optimization mentioned in the beginning of Section 4.3. After restricting the
abstract substitution to the arguments of the recursive call, we obtain a liveness
environment that is equivalent to the initial substitution ~in.
Non-local R e u s e
To allow non-local reuse, an integration of the liveness analysis and the storage
allocation algorithm of the compiler is needed in order to have the liveness en-
vironments reflect what garbage cells are back in use. Presently, the analyzer
assumes that no garbage cells are reused. Consequently, the dead cells detected
at program points following some procedure call that is not a selection oper-
ation itself are only true garbage cells if they were not already reused inside
the procedure (otherwise they m a y be dangling references). For example, con-
sider Program 5.5 to generate all the permutations of a given list and an initial
abstract liveness environment {~,e, ASharing~q~, ALive~ri~), such that
Ti,, ::= (List, V),
List ::= nil I '.'(Int, List),
ASharing~r,,~ = (0, 0),
aLive~-,.. = {T~.[(2,v)]}.
The analysis of the program permutat• entails an analysis of the se:].ect/3
predicate, namely for the abstract liveness environment ~I ----{TI, ASharingTl,
ALive~rl) such that
T1 ::= (V, List, V),
ASharlngTl = {0, r
ALive~rl = { 7"1[{1, V)],T1[{3, V)] }.
Program 5 . 5 : p e r m u t a t i o n / 2
Program 5 . 6 : s o r t / 2
clause, the cell can be reused in the construction that follows. For the first
clause, there is no local opportunity to reuse the cell. Now, consider program
point (~) of the p e r m u t a t i o n / 2 predicate and an ordering of the program
variables according to the tuple ( _Xl, _Z, _Ys, _Zl, _Zs ). For that point,
we derive the liveness environment/3, = (T~, ASharingT-,, ALive~-,), such that
This means that the top-list cell of the _X1 program variable becomes garbage.
However, the latter is only true if the code generated for the s e l e c t / 3 predicate
did not introduce destructive assignments. If the code for the s e l e c t / 3 predicate
is specialized such that the garbage cell it produces in the second clause is
locally reused, then the liveness analysis for the p e r m u t a t i o n / 2 program should
be redone. In order to reuse the garbage cell created in the first clause of
the s e l e c t / 3 predicate, for the construction operation in the second clause of
p e r m u t a t i o n / 2 , we need some technique to pass on its address. The garbage
cell may or may not be the first list cell of the variable _I1.
As another illustration of an opportunity for non-local reuse of garbage cells,
consider Program 5.6 for sorting a linear list via the construction of an ordered
binary tree. In the program point ( ~ ) , it is detected that the full list that the
178 C H A P T E R 5. L I V E N E S S A N A L Y S I S
program variable I s is bound to, is turned into garbage. Intuitively, the list
could be reused by the makelist/3 predicate that has to construct a new list
of the same length. However, additional run-time bookkeeping data areas will
be needed in an interpreter to bring about this kind of storage overwriting. W e
did not investigate the problem of keeping track of garbage cells, which m a y be
quite complicated.
Chapter 6
Conclusion
This book addresses t h e problem of memory reuse for logic programs through
program analysis rather than by run-time garbage collection. The aim is to
derive run-time properties that can be used at compile time to specialize the
target code for a given set of queries and to introduce destructive assignments in a
safe and transparent way. The derivation process is constructed as an application
of abstract interpretation for logic programs. The development of the application
is greatly facilitated by the structure of the underlying framework [11], which
allows to focus on local properties of the abstract domain and operations in order
to guarantee correctness and termination of the global analysis.
A modular design of the abstract domain in successive layers was essential to
handle the complexity of the problem at hand. The first layer consists of the type
analysis for logic programming languages that was previously developed in [40]
and that provides a characterization of the logical terms to which variables can
be bound during program execution. Our contribution consists of two additional
layers consisting of an abstract domain and primitive operations for sharing and
liveness analysis, respectively.
The central problem in program analysis for compile-time garbage collection
is detecting the sharing of term substructures that can occur during program
execution. In order to justify our analysis based on abstract interpretation, we
introduce variants of the concrete domain and operations that are augmented
with information about the term structures shared in actual implementations.
We show that these instrumented versions of the concrete domain and opera-
tion characterize the sharing that takes place in standard implementations and
that they are safely approximated by the abstract domain and operation. In a
subsequent step, we enhance the abstract domain and operations for type and
sharing analysis, into an abstract domain representing liveness information as
well. The abstract operations for this final domain compute for each program
point an upper approximation of the set of term structures that are needed to
finish the execution of the program. The compiler can exploit such information
to generate code avoiding the copying of data structures that have no further
references.
180 CHAPTER 6. CONCLUSION
may be needed, because the main limitation of the current approach is the com-
plexity of the analysis. However, we expect that this problem may be overcome
in the future and we believe that practical analyses based on our work can be
used in compilers and will lead to substantially more efficient implementations
of logic programming languages.
Other possible applications of the sharing information derived include occur-
check reduction and detection of goal independence for AND-parallel program
execution. In previous analysis systems aimed at the application of occur-check
reduction, the detection of circular structures is stated in terms of properties of
the abstract substitutions holding in the program point prior to the unification,
or in terms of conditions holding during the abstract unification algorithm. The
abstract substitution holding after the unification does not provide any informa-
tion about the loops that are possibly created. The idea is that the substitution
only has to provide information about unifications that succeed in a system per-
forming the occur-check. For instance, also the underlying integrated type and
mode analysis can be used to predict the possible creation of circular terms.
The structure-sharing analysis developed in this book yields a better criterion
for loop detection. In many cases, the precision is sufficient to detect the absence
of circular terms after unification in a system that does not perform the occur
check, or to provide detailed information about the nature of the loops that are
possibly created.
Many parallel Prolog implementations restrict to Independent/Restricted
And-Parallelism (IAP). In such systems, the usual semantics of a sequential
program execution is preserved. The subgoals in the body of a clause are ex-
ecuted in parallel provided that they are independent: that is, if the bindings
of one goal cannot interact with the bindings of another goal. Compile-time
knowledge about the variables that may share and about which goals instan-
tiate shared variables, is useful to reduce run-time dependency checking and
scheduling overhead. Although knowledge about the sharing of structured terms
is not required for the automatic parallelization of logic programs using IAP, the
greater precision of such sharing information may be beneficial.
Appendix A
Detailed Examples
We have used the prototype to analyze a set of small and well-known (pure)
Prolog programs manipulating data structures. We give a detailed description
of the abstract AND-OR-graphs computed for these programs. The abstract
liveness environments obtained for the different program points in a predicate
definition, are collected in a table.
A type graph is represented as a tuple of types. The ith type in the tuple is the
type of the i th variable in the tuple of variable names representing the domain
of the program point that the type graph is associated with. E.g., (_.X, _u
_Z) represents the domain at some program point, and ( V, L i s t , . ( I n t , V ) )
represents an abstract type graph T for this domain, defined by the following
two grammar rules.
T ::= ( V , L i s t , . ( I n t , V ) )
List ::= nil [ .( Int, List)
9 The heading gives the tuple of variable names appearing in the initial goal,
and the input and output abstract liveness environments.
9 For each clause, the first entry gives the clause number (as shown in the
Prolog code defining the predicate), and the tuple of variable names ap-
184 A P P E N D I X A. D E T A I L E D E X A M P L E S
pearing in the clause, i.e. the domain of the clause. For some of the in-
teresting program points of the clause, an entry in the table is given with
the name of the abstract substitution at the program point and its value,
the abstract liveness environment. Empty sharing or liveness components
in the abstract environments are not shown.
Recall that the full set of sharing edges, holding at some program point, is in fact
the AltematingCIosure of the ASbr~- and AShr~- components shown in the tables.
Also, in order to interpret the Akive7- component, one has to take into account
the sharing relation, first applying the AltkiveClosure(Akive~r,AShr~-,AShr~-) op-
eration to obtain the set of globally live terms. The set of locally live terms is
not represented in the liveness environments (see Section 5.1.2). To ease the
interpretation of the results, we added for the selection operations the output
abstract liveness environment, restricted to the domain of the selection opera-
tion. These liveness environments are particularly important for the application
of compile-time garbage collection. The liveness components shown in these re-
stricted output liveness environments (indicated by a superscript r), consist of
the full se~ of locally and globally live terms, as defined by (5.27) on page 162.
Because most Prolog implementations do not create any sharing when a
variable gets bound to an atom or an integer, a small optimization with respect
to the formal specifications is incorporated into the prototype, avoiding the
introduction of such sharing edges.
Note that for the sharing component of the query form, the user can choose
to specify the sharing edges as old or as new edges, or to use a combination of
the two.
A.1 List of T y p e s
We first give some shorthands for type (sub)graphs used frequently throughout
the examples. Unless explicitly stated otherwise, we assume that for the func-
tors that occur in some recursive branch of a type graph, the depth restriction
is two.
T r e e ::= empty ] t ( T r e e , I n t , T r e e )
TreeOne ::= t ( Tree, Int, Tree)
A.2 append/3
The following two tables give the abstract liveness environments that result from
the abstract interpretation of the append/3 program, when the mode of use is
to append the ground input lists of the first and second arguments. In the first
table, we assume that there is no input sharing between the arguments, in the
second table, there is potential sharing between the input arguments. Note that
for f~4 in the second table, [(_X, . / 2 ) ] 6 Akive~r, whereas in the first table
[ ( _ X , . / 2 ) , ( 2 , . / 2 ) ] 6 Aiive~-. Thus, it is correctly derived that in the case of
input sharing between the first two arguments, the selection and construction
operations in the second clause cannot be replaced by a destructive assignment.
Note also that the value of AShr~- is the same for the corresponding entries of
the two tables.
Prolog code:
The following table illustrates how the sharing and liveness analyses perform
when one uses the invertibility property of Prolog. The table shows the ab-
stract liveness environments that result from the abstract interpretation of the
append/3 program, when the mode of use is to split the ground input list of the
third argument into two lists, returned in the first and second arguments.
From/Ss, the compiler derives that _Z = [_E I _W], is a selection operation.
From/~s, it is derived that the top-list cell can be reused in a construction. How-
ever, the only construction in the clause, _X = [_E I _U], precedes the selection.
The compiler has to reorder the subgoals in order to benefit from the liveness
information provided.
Call: /9~ append( _a, _B, _C )
A.3 nrev/2
We now consider the program for naive list reversal, called with a list of free
variables that may share with one another. From the table below, it is derived
that the procedure can still work in-place.
Prolog code:
( _X, _u
;37 T ( VLisZ, V>
AShr,} { ([(D[,./2),(1,V)], [(_X,./2),(1,V)])}
ALive~r { [(_Y,V)] }
The query above causes a recursive call of the n r e v / 2 predicate for a more
general abstract substitution ;3~ (obtained as a restriction of the substitution ;3s
to the domain of the call n r e v ( _U, ~U)), for which the list elements of the first
argument are also live. Indeed, when n r e v ( _U, ~U) is called, the list elements
of _U possibly share with _E due to the input sharing in _X, and _E is still needed
for the construction operation _Last = [_E] that follows the recursive call of
n r e v / 2 . The table below only contains the program points of interest for this
more general query.
Call: ;3~ nrev( I, _Y )
( _X, _Y>
T < VList, V>
AShr,~ { ([(~,./2),(1,v)], [(~x,./2),(1,v)])}
ALive~r { [ ( _ X , . / 2 ) , ( 1 , V ) ] , [(_Y,V)]}
T ( V L i s t , VList>
AShr,~ { ([(_x,.12),(i,v)], [(_x, .12) , (1,v)] )}
AShr,~- { ([(I,./2),(1,V)], [(_Y,./2),(1,V)])}
ALiveT { [(_X,./2),(l,V)], [(_Y,.12)]}
1) < _X, _Y )
A T < vni t, v>
AShr,~ { ([(_~,./2),(1,v)], [(~,./2),(1,v)])}
ALive~r { [(_X,./2),(1,V)], [(_Y,V)]}
T ( n i l , nil)
ALive~r { [(_Y,nil)] }
190 A P P E N D I X A. DETAILED E X A M P L E S
During the iteration process of the fixpoint computation, the append/3 predicate
is called in the second clause of the n r e v / 2 program for a sequence of liveness
environments of increasing generality. The variable A{U is successively bound to
the empty list, a list of at most one element, at most two elements, and at last
to a list of any possible length. The introduction of recursive types during the
abstract interpretation process is controlled by the depth restriction, which is
two for the list-functor ./2. The following table shows the successive abstract
substitutions (restrictions of/~7) for which append/3 is called.
Call: /~ append( ..RU, l a s t , • )
The table below only contains the interesting program points for the most general
of these queries. The program points are as indicated in Section A.2. Note that
the input sharing edge ( [ ( l a s t , . / 2 ) , ( 1 , V ) ] , [(_Last, . / 2 ) , ( 1 , V ) ] ) , is in
fact an irrelevant edge. The program variable _Last is bound to a single element
list, consisting of one free variable: there is no internal sharing possible.
From/~4 we see that _X = [_E [ _U] is a selection, from /~4 that the top
list cell of _X is turned into garbage, and from /3s that _Z = [_E I _W] is a
construction that needs the allocation of a list cell.
192 A P P E N D I X A. D E T A I L E D E X A M P L E S
~s T ( V, V L i s t , V, VListOne, . ( V , n i l ) , V)
AShr,~ { ([(_X,./2),(I,V)], [(_Y,./2),(I,V)]),
([(_X.,./2), (2,./2), (1,V)], [(_Y, ./2), (1,V)]),
([(_Y, ./2), (I,V)], [(_Y, ./2), (1,V)]),
([(_x, ./2), (1,v)], [(_x, ./2), ( ! , v ) ] ) ,
([(_x, ./2), (2, ./2), ( l , v ) ] , [(_x, ./2), (1,v)]),
([(_x,./2),(2,./2),(1,v)],
[(I,./2),(2,./2),(1,v)])}
AShr~- { ( [ ( _ X , . 1 2 ) , C 1 , V ) ] , [(_E,V)]),
([(_U, . 1 2 ) ] , [(_X, . / 2 ) , (2, . / 2 ) ] ) }
ALive,]- { [(_Y,./2),(I,V)], [(_X.,./2),(2,./2),(1,V)],
[(_X, ./2), (1,V)], [(_Z,V)]}
Prolo# code:
From/34, the compiler derives that _0T = t(_L, _F, _~), is a selection opera-
tion. From/~4, it is derived that the top-tree cell can be reused in a construction.
The unification ~ T = t(_}lL, _F, ~ ) is such a construction operation, within
the same chunk as the selection operation.
Prolog code:
( _ A . _B, _C)
~ T (List, Tree, V>
ALive~r { [(_C,V)]}
#~ T ( List, T r e e , Tree)
AShr~ { ([(_C,t/3)], [(~,t/3)])}
ALiveT { [(_C,t/3)]}
196 A P P E N D I X A. DETAILED E X A M P L E S
Prolog code:
A.5. PERMUTATION~2 AND SELECT~3 197
From the abstract substitution ffl in the first clause, it is clear that a garbage
cell is created at run time. However, there is no opportunlty for the local reuse
of that cell in the same clause. If the procedure is called by the permutation/2
program shown below, then there will correspond one construction operation 7.1
-- [ 7. I 7.s ] in the permutation/2 program to each selection operation _YI
= [ _X I _Z1 ] in the select/3 program. Unfortunately, we do not know of an
easy strategy to pass on the address of the garbage cell for such non-local reuse.
A free-listor garbage-trail could be added to the run-time control structures of
an interpreter in order to record the address and the size of the garbage cells
detected. But the operations for handling the garbage cells in such a free-list
will be more complicated than stack operations, and the overhead introduced
m a y exceed the gain to be expected from reusing storage. Unless this problem
is solved, the permutation/2 procedure cannot really work in-place.
Prolog code:
A.6 split/3
The split/3 predicate illustrates that for programs in normal form, depth bound
two yields sufficient precision to detect the garbage cells. A drawback of normal-
form Prolog programs is the large set of program variables and consequently the
large type graphs, sharing and liveness sets.
Garbage list-cells are detected for the program points ~[, ~ and/7~.
Prolog code:
/72 1 1 = [ _ b l = ] ,
/73 -Y = [~I=1],
/74 7. : [_b{ = 2 ] ,
/75 split( _r, _rl, _r2 ). /76
(2) spilt( i, _Y, 7. ) "-
/77 _x = [_al_Xl].
/7, I I [], =
/79 -Y = [_a[_Y1],
/710 _Y1 = [], /711 _Z = []. /712
(3) split( _X, _Y, 7. ) :-
/713 _X = [], /714 -Y = [], /71s _Z = [].
In Section 5.3, we discussed the results of the liveness analysis for the quicksort
program using an accumulating parameter. Similar results are obtained for the
simple quicksort program using append/3. First we consider the analysis of
the partition/4 predicate, which is called by both versions of the quicksort
program. We specify input sharing and liveness components according to the
use of the predicate in the quicksort program given below.
Prolog code:
(1) partition(.M,_L,_Sm,_Gr) :-
~i -L = nil,
~2 _Sm = nil,
~s _Gr = nil. ~4
(2) partition(~,_L,~m,_Gr) :-
#s r = [~I-T],~
~e - ~ = < ~,
~7 _Sm = [_H I _Sml] ,
~e partition(~M, _T, _Sml, _Gr).
(3) partition(_M,_L,_Sm,_Gr) :-
# i o r . = [_~ I - T ] , ~o
,~11 'R' > _M,
#n _Gr = [_B I _Grl],
~lS partit ion (_M, _T, _Sm, _Gr I). ~14
Prolog code:
204 A P P E N D I X A. D E T A I L E D E X A M P L E S
~2 A s s = nil. ~3
(2) qsort(_X, _Res) "-
#4 _x = [_H I _T],
#5 p a r t i t i o n ( _H, _T, _U1, _U2 ),
~e qsort( _Ul, _Resl ),
/~7 qsort( _U2, _Res2 ),
/gs ~ = [.B I _-Kes2],
append( _Resl, _R, _Res ). ~10
( _A, _B)
~ 7- < List, v>
ALive~r { [(_B,V)] }
#~ 7- < List, List>
ALive~r { [(_B, ./2)3}
1) ( rues, ~)
fll 7- ( V, List)
ALivecr { [(-~es,V)] }
~2 7- < v, nil)
ALivecr { [(_Res,V)]}
fls 7- ( nil, nil)
ALivesr { [(_I{es,nil)]}
2) ( _H, _P~, ..Res, _B.esl, _Kes2, _T, _U1, _U2, _X)
~'4 7 ( V, V, V, V, V, V, V, V, List)
ALive~- { [(ross,v)]}
7- ( Int . . . . . . . . . List ..... ListOne)
AShr,~- { ([(_T, ./2)] , [ ( - X , . / 2 ) , ( 2 , . / 2 ) ] ) }
ALive:r { [(I,./2),(2,./2)], [(_T, ./2)] , [(_H,Int)]}
fl~ 7- ( Int, V, V, V, V, List, V, V, ListOne)
AShr~ { ([(_T, ./2)] , [(_X, . / 2 ) , (2, . / 2 ) ] ) }
ALive~r { [(_Res,V)] }
f16 7 ( Int, V, V, V, V, List, List, List, ListOne)
AShr.~- { ([(_T, ./2)], [(_X, ./2), (2, . / 2 ) ] ) }
ALive~r { [(_Res,V)]}
f17 7- ( Int, V, V, List, V, List, List, List, ListOne
AShr~- { ([(_T,./2)], [(_X, ./2), (2, . / 2 ) ] ) }
ALive~r { ['(./{es ,V)] }
f18 7 ( I n t , V, V, L i s t , L i s t , L i s t , L i s t , L i s t ,
ListOne)
AShr~ { ([(_T, ./2)], [(_X, . / 2 ) , ( 2 , . / 2 ) 3 ) }
ALive:r { [(_Res,V)] }
A.8. SAMELEAVES/2 AND PROFILE~2 205
Prolog code:
( _A, .3)
~1 7- ( LvTree, LvTree)
ALiveT { [(_B,t/2)], [(_B,iv/1)]}
~ 7- ( LvTree, LvTree)
ALive~ { [(_B,ivli)], [(_B,tl2)]}
1) ( _,, _X, _y)
~1 7- ( V , LvTree, LvTree)
ALive~ { [(_y,t/2)], [ ( _ y , l v / i ) ] }
~2 7- ( L i s t O n e , LvTree, LvTree>
ALiveT { [(_y,t/2)], [(_y,lv/1)]}
~3 7- ( ListOne, LvTree, LvTree)
ALive7 { [(_y,t/2)], [ ( _ y , l v / l ) ] }
The analysis of the predicate sameleaves/2 entails an analysis of the predicate
p r o f i l e / 2 for two different abstract liveness environments. In the first call, the
p r o f i l e / 2 predicate has to construct a linear list containing the leaf nodes of
the input tree.
Call: ~ p r o f i l e ( _A, _B )
(_A. _.)
~ Y ( LvTree, V)
ALiveT { [(_B,V)] }
In the second call it only has to test whether a given linear list contains the leaf
nodes of the input tree.
A.8. SAMELEAVES/2 AND PROFILE~2 207
( _.h, _.B)
#3 ,/- ( LvTree, ListOne>
ALive~ { [(_a,t/2)], [(A,Zvll)]}
We only consider the first query, for which the table below indicates that the
rearrangement of the input tree in the third clause, can be done in-place. For
the second query this will not be the case as the input tree of the first argument
is live. Note that for large type graphs (e.g. in f~14, the type graph has 39 nodes)
the sets of sharing edges may blowup exponentially. This is a major difficulty
for the approach taken.
<_A, _B>
~ 7- ( LvTree, V)
ALiveT { [(_B,V)]}
/302 7" < LvTree, ListOne)
ALivey- { [(_B, ./2)3}
I) ( -F, _tr, _u )
/74 iT ( V, LvTree, V)
A Live,r { [(_pr,V)]}
~4 7" ( _, iv(Int), Int>
ALive=r { [(_u, I n t ) ] }
#5 T ( V, l v ( I n t ) , Int)
A Live,r { [(_pr,V)] }
#6 ~T (.(Int,nil), lv(Int), Int)
ALive~ { [(_pr,./2)]}
2) ( _pr, _prtail, _ t l , _tr, _u, _y)
~7 T ( V, V, V, LvTree, V, V)
ALive~ { [(_pr,V)] }
T ( . . . . LvTree, LvTree0ne, _, LvTree)
AShr~ { ( [ ( _ t l , t / 2 ) ] , [(_tr,t/2), (I ,t/2)] ),
( [ ( _ t l , l v l l ) ] , [(_tr,tl2), ( l , l v l l ) ] ) ,
( [(_y,t/2)], [(_tr,t/2), (2,t/2)]),
( [ ( _ y , l v / l ) ] , [(_tr,t/2), ( 2 , 1 v / l ) ] ) }
ALive~ { [(_tr,t/2), ( l , l v / l ) ] , [(_tr,t/2), ( l , t / 2 ) ] ,
[(_tr,t/2), (2,Zvll)], [(_tr,tl2), (2,t12)],
[(_y,tl2)], [(_y,lvll)], [(_tl,tl2)], [(_t1,1vll)]}
208 A P P E N D I X A. D E T A I L E D E X A M P L E S
Prolog code:
(1) s i f t ( I, _Y ) "-
#4 _X = Ell_Is],
#5 -Y = [_z$_es],
•6 remove( _I, _Is, _New ),
#~ sift( _New, 2 s ). #s
210 A P P E N D I X A. DETAILED E X A M P L E S
(_A, _B)
#~ 7- < List, v>
ALive~r { [(_B,V)] }
#~ 7" ( List, List>
ALive~r { [(_B, . / 2 ) ] }
I) ( _x, _Y)
#1 T ( List, V)
Akive~- { [(_Y,V)]}
#3 ~F ( n i l , nil>
Ative~r { [(_Y,nil)] }
2) (_I, _Is, _New, _Ps, _X, _Y)
( V, V, V, V, List, V)
ALive~r { [(_Y,V)] }
T ( Int, List . . . . . ListOne, _)
AShr,} { ([(Ts,.12)], [(~,./2),(2,./2)])}
ALive,r { [(_X,./2),(2,./2)], [(_Is,./2)], [(_I,Int)]}
#s 7- ( Int, List, V, V, ListOne, V)
AShr,} { ([(_Zs,.12)], [(_x,.12),(2,.12)3)}
ALive~r { [(_Y,V)]}
#6 :T ( Int, List, V, V, List0ne, .(Int,V))
AShr,} { ([(_Is,.12)], [(_x,.12),(2,.12)]),
([(_Y, ./2),(2,v)], [(~s,V)])}
ALive,r { [(_Y, ./2)]}
Prolog code:
[4] B.I.M., B-3078, Everberg, Belgium. ProLog by BIM -3.0- Reference Man-
ual, Nov. 1990.
[20] A. Colmerauer. Prolog and infinite trees. In Clark and T~irnlund [16], pages
231-251.
[69] C. Pyo and U. S. Reddy. Inference of polymorphic types for logic programs.
In E. L. Lusk and R. A. Overbeek, editors, Proceedings of the 1989 North
American Conference on Logic Programming, pages 1115-1132, Cambridge,
1989. MIT Press.
[73] A. Taylor. LIPS on a MIPS, results from a Prolog compiler for a RISC.
In D. H. D. Warren and P. Szeredi, editors, Proceedings of the Seventh
International Conference on Logic Programming, pages 174-185, Jerusalem,
1990. MIT Press, Cambridge.
[77] P. Van Roy and A. M. Despain. The benefits of global dataflow analysis for
an optimizing Prolog compiler. In S. Debray and M. Hermenegildo, editors,
Proceedings of the 1990 North American Conference on Logic Programming,
pages 501-515, Austin, 1990. MIT Press, Cambridge.
Vol. 596: L.-H. Eriksson, L. Halln~is, P. Schroeder-Heister Vol. 613: J. P. Myers, Jr., M. J. O ' D o n n e l l (Eds.),
IEds.), Extensions of Logic Programming. Proceedings, Constructivity in Computer Science. Proceedings, 1991.
1991. VII, 369 pages. 1992. (Subseries LNAI). X, 247 pages. 1992.
Vol. 597: H. W. Guesgen, J. Hertzberg, A Perspective of Voh 614: R. G. Herrtwich (Ed.), Network and Operating
Constraint-Based Reasoning. VIII, 123 pages. 1992. System Support for Digital Audio and Video. Proceedings,
(Subseries LNAI). 1991. XII, 403 pages. 1992.
Vol. 598: S. Brookes, M. Main, A. Melton, M. Mislove, D. Vol. 615: O. Lchrmann Madsen (Ed.), ECOOP '92. Euro-
Schmidt (Eds.), Mathematical Foundations of Programming pean Conference on Object Oriented Programming. Pro-
Semantics. Proceedings, 1991. VIII, 506 pages. 1992. ceedings. X, 426 pages. 1992.
Voh 599: Th. Wetter, K.-D. Althoff, J. Boose, B. R. Gaines, Vol. 616: K. Jensen (Ed.), Application and Theory of Petri
M. Linster, F. Schmalhofer (Eds.), Current Developments Nets 1992. Proceedings, 1992. VIII, 398 pages. 1992.
in Knowledge Acquisition EKAW '92. Proceedings. XIII, Vol. 617: V. MaHk, O. St6pfinkovfi, R. Trappl (Eds.), Ad-
444 pages. 1992. (Subseries LNAI). vanced Topics in Artificial Intelligence. Proceedings, 1992.
Vol. 600: J. W. de Bakker, C. Huizing, W. P. de Roever, G. IX, 484 pages. 1992. (Subseries LNAI).
Rozenberg (Eds.), Real Time: Theory in Practice. Proceed- Vol. 618: P. M. D. Gray, R. J. Lucas (Eds.), Advanced
ings, 1991. VIII, 723 pages. 1992. Database Systems. Proceedings, 1992. X, 260 pages. 1992.
Vol. 601: D. Dolev, Z. Galil, M. Rodeh (Eds.L Theory of Vol. 619: D. Pearce, H. Wansing (Eds.), Nonclassical Log-
Computing and Systems. Proceedings, 1992. VIII, 220 ics and Information Proceedings. Proceedings, 1990. VII,
pages. 1992. 171 pages. 1992. (Subseries LNAI).
Vol. 602: 1. Tomek (Ed.), Computer Assisted Learning. Pro Vol. 620: A. Nerode, M. Taitslin (Eds.), Logical Founda-
ceedings, 1992. X, 615 pages. 1992. tions of Computer Science Tver '92. Proceedings. IX,
Vol. 603: J. van Katwijk (Ed.), Ada: Moving Towards 20(10. 514 pages. 1992.
Proceedings, 1992. Vlll, 324 pages. 1992. Voh 621: O. Nurmi, E. Ukkonen (Eds.), Algorithm Theory
Vol. 604: F. Belli, F.-J. Radermacher (Eds.), Industrial and SWAT '92. Proceedings. VIII, 434 pages. 1992.
Engineering Applications of Artificial Intelligence and Vol. 622: F. SchmalhotEr, G. Strube, Th. Wetter (Eds.),
Expert Systems. Proceedings, 1992. XV, 702 pages. 1992. Contemporary Knowledge Engineering and Cognition. Pro
(Subseries LNAI). ceedings, 1991. XII, 258 pages. 1992. (Subseries LNAI).
Vol. 605: D. Etiemble, J.-C. Syre (Eds.), PARLE '92. Par- Vol. 623: W. Kuich (Ed.), Automata, Language,; and Pro
allel Architectures and Languages Europe. Proceedings, gramming. Proceedings, 1992. XII, 721 pages. 1992.
1992. XVII, 984 pages. 1992.
Voh 624: A. Voronkov (Ed.), Logic Programming and Au
Vol. 606: D. E. Knuth, Axioms and Hulls. IX, 109 pages. tomated Reasoning. Proceedings, 1992. X[V, 509 pages.
1992. 1992. (Subseries LNAI).
Vol. 607: D. Kapur (Ed.), Automated Deduction CADE- Voh 625: W. Vogler, Modular Construction and Partial
11. Proceedings, 1992. XV, 793 pages. 1992. (Subseries Order Semantics of Petri Nets. IX, 252 pages. 1992.
LNAI).
Vol. 626: E. BOrger, G. JSger, H. Kleine Brining, M. M .
Voh 608: C. Frasson, G. Gauthier, G. 1. McCalla (Eds.), Richter (Eds.), Computer Science Logic. Proceedings, 1991.
Intelligent Tutoring Systems. Proceedings, 1992. XIV, 686 VIII, 428 pages. 1992.
pages. 1992.
Vol. 628: G. Vosselman, Relational Matching. IX, 190
Vol. 6(19: G. Rozenberg (Ed.), Advances in Petri Nets 1992. pages. 1992.
VIII, 472 pages. 1992.
Vol. 629: I. M. Havel, V. Koubek (Eds.), Mathematical
Vol. 610: F. von Martial, Coordinating Plans of Autono- Foundations of Computer Science 1992. Proceedings. IX,
mous Agents. XII, 246 pages. 1992. (Subseries LNAI). 521 pages. 1992.
Vol. 611 : M. P. Papazoglou, J. Zeleznikow (Eds.), The Next Voh 630: W. R. Cleaveland (Ed.), CONCUR '92. Proceed
Generation of Information Systems: From Data to Knowl- ings. X, 580 pages. 1992.
edge. VIII, 310 pages. 1992. (Subseries LNAI).
Vol. 631: M. Bruynooghe, M. Wirsing (Eds.), Program-
Vol. 612: M. Tokoro, O. Niers'trasz, P. Wegner (Eds.), ming Language Implementation and Logic Programming.
Object-Based Concurrent Computing. Proceedings, 1991. Proceedings, 1992. XI, 492 pages. 1992.
X, 265 pages. 1992.
Vol. 632: H. Kirchner, G. Levi (Eds.), Algebraic and Logic Vol. 656: M. Rusinowitch, J. L. R6my (Eds.), Conditional
Programming. Proceedings, 1992. IX, 457 pages. 1992. Term Rewriting Systems. Proceedings, 1992. XI, 501 pages.
Vol. 633: D. Pearce, G. Wagner (Eds.), Logics in A|. Pro- 1993.
ceedings. VIII, 410 pages. 1992. (Subseries LNAI). Vol. 657: E. W. Mayr (Ed.), Graph-Theoretic Concepts in
Vol. 634: L. Bough, M. Cosnard, Y. Robert, D. Trystram Computer Science. Proceedings, 199Z VIII, 350 pages.
(Eds.), Parallel Processing: CONPAR 92 - VAPP V. Pro- 1993.
ceedings. XVII, 853 pages. 1992. Vol. 658: R. A. Rueppel (Ed.), Advances in Cryptology -
Vol. 635: J. C. Derniame (Ed.), Software Process Technol- EUROCRYPT '92. Proceedings, 1992. X,493 pages. 1993.
ogy. Proceedings, 1992. VIII, 253 pages. 1992. Vol. 659: G. Brewka, K. P. Jantke, P. H. Schmitt (Eds.),
Vol. 636: G. Comyn, N. E. Fuchs, M. J. Ratcliffe (Eds.), Nonmonotonic and Inductive Logic. Proceedings, 1991.
Logic Programming in Action. Proceedings, 1992. X, 324 VIII, 332 pages. 1993. (Subseries LNAI).
pages. 1992. (Subseries LNAI). Vol. 660: E. Lamma, P. Mello (Eds.), Extensions of Logic
Vol. 637: Y. Bekkers, J. Cohen (Eds.). Memory Manage- Programming. Proceedings, 1992. VIII, 417 pages. 1993.
ment. Proceedings, 1992. Xl, 525 pages. 1992. (Subseries LNA1).
Vol. 639: A. U. Frank, 1. Campari, U. Formentini (Eds.), Voh 661: S. J. Hanson, W. Remmele, R. L. Rivest (Eds.),
Theories and Methods of Spatio-Temporal Reasoning in Machine Learning: From Theory to Applications. VIll, 271
Geographic Space. Proceedings, 1992. XI,431 pages. 1992. pages. 1993.
Voh 640: C. Sledge (Ed.), Software Engineering Educa- Vol. 662: M. Nitzberg, D. Mumford, T. Shiota, Filtering,
tion. Proceedings, 1992. X, 451 pages. 1992. Segmentation and Depth. VIII, 143 pages. 1993.
Vol. 641 : U. Kastens, P. Pfahler (Eds.), Compiler Construc- Vol. 663: G. v. Bochmann, D. K. Probst (Eds.), Computer
tion. Proceedings, 1992. VIII, 320 pages. 1992. Aided Verification. Proceedings, 1992. IX, 422 pages.
1993.
Vol. 642: K. P. Jantke (Ed.), Analogical and Inductive In-
ference. Proceedings, 1992. VIII, 319 pages. 1992. Vol. 664: M. Bezem, J. F. Groote (Eds.), Typed Lambda
(Subseries LNAI). Calculi and Applications. Proceedings, 1993. VII1, 433
pages. 1993.
Vol. 643: A. Habel, Hyperedge Replacement: Grammars
and Languages. X, 214 pages. 1992. Voh 665: P. Enjalbert, A. Finkel, K. W. Wagner (Eds.),
STACS 93. Proceedings, 1993. XIV, 724 pages. 1993.
Vol. 644: A. Apostolico, M. Crochemore, Z. Galil, U.
Manber (Eds.), Combinatorial Pattern Matching. Proceed- Vol. 666: J. W. de Bakker, W.-P. de Roever, G. Rozenberg
ings, 1992. X, 287 pages. 1992. (Eds.), Semantics: Foundations and Applications. Proceed-
ings, 1992. VII1, 659 pages. 1993.
Vol. 645: G. Pernul, A M. Tjoa (Eds.), Entity-Relationship
Approach - ER '92. Proceedings, 1992. Xl, 439 pages, Vol. 667: P. B. Brazdil (Ed.), Machine Learning: ECML -
1992. 93. Proceedings, 1993. XII, 471 pages. 1993. (Subseries
LNAI).
Vol. 646: J. Biskup, R. Hull (Eds.), Database Theory -
ICDT '92. Proceedings, 1992. IX, 449 pages. 1992. Vol. 668: M.-C. Gaudel, J.-P. Jouannaud (Eds.), TAPSOFT
'93: Theory and Practice of Software Development. Pro-
Vol. 647: A. Segall, S. Zaks (Eds.), Distributed Algorithms. ceedings, 1993. XII, 762 pages. 1993.
X, 380 pages. 1992.
VoL 669: R. S. Bird, C. C. Morgan, J. C. P. Woodcock
Vol. 648: Y. Deswarte, G. Eizenberg, J.-J. Quisquater (Eds.), Mathematics of Program Construction. Proceedings,
(Eds.), Computer Security - ESORICS 92. Proceedings. 1992. VIII, 378 pages. 1993.
XI, 451 pages. 1992.
Vol. 670: J. C. P. Woodcock, P. G. Larsen (Eds.), FME
Vol. 649: A. Pettorossi (Ed.), Meta-Programming in Logic.
"93: Industrial-Strength Formal Methods. Proceedings,
Proceedings, 1992. Xll, 535 pages. 1992. 1993. XI, 689 pages. 1993.
Vol. 650: T. lbaraki, Y. lnagaki, K. lwama, T. Nishizeki,
Vol. 671: H. J. Ohlbach (Ed.), GWAI-92: Advances in
M. Yamashita (Eds.), Algorithms and Computation. Pro- Artificial Intelligence. Proceedings, 1992. XI, 397 pages.
ceedings, 1992. XI, 510 pages. 1992. 1993. (Subseries LNAI).
Vol. 651: R. Koymans, Specifying Message Passing and
Vol. 672: A. Barak, S. Guday, R. G. Wheeler, The MOS1X
Time-Critical Systems with Temporal Logic. IX, 164 pages.
Distributed Operating System. X, 221 pages. 1993.
1992.
Vol. 673: G. Cohen, T. Mora, O. Moreno (Eds.), AAECC-
Vol. 652: R. Shyamasundar (Ed.), Foundations of Software
10: Applied, Algebra, Algebraic Algorithms and Error-
Technology and Theoretical Computer Science. Proceed-
Correcting Codes. Proceedings, 1993. X, 355 pages 1993.
ings, 1992. XIII, 405 pages. 1992.
Vol. 674: G. Rozenberg (Ed.), Advances in Petri Nets 1993.
Vol. 653: A. Bensoussan, J.-P. Verjus (Eds.), Future Ten-
VII, 457 pages. 1993.
dencies in Computer Science, Control and Applied Math-
ematics. Proceedings, 1992. XV, 371 pages. 1992. Vo[. 675: A. Mulkers, Live Data Structures in Logic Pro-
grams. VIII, 220 pages. 1993.
Vol. 654: A. Nakamura, M. Nivat, A. Saoudi, P. S. P. Wang,
K. Inoue (Eds.). Prallel Image Analysis. Proceedings, 1992.
VIII, 312 pages. 1992.
Vol. 655: M. Bidoit, C. Choppy (Eds.), Recent Trends in
Data Type Specification. X, 344 pages. 1993.