Lecture Notes in Computer Science Edited by G. Goos and J. Hartmanis Advisory Board: W. Brauer
D. Gries
J. Stoer
675 ...

Author:
Anne Mulkers

This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!

Lecture Notes in Computer Science Edited by G. Goos and J. Hartmanis Advisory Board: W. Brauer

D. Gries

J. Stoer

675

Anne Mulkers

Live Data Structures in Logic Programs Derivation by Means of Abstract Interpretation

Springer-Verlag Berlin Heidelberg NewYork London Paris Tokyo Hong Kong Barcelona Budapest

Series Editors Gerhard Goos Universit~it Karlsruhe Postfach 69 80 Vincenz-Priessnitz-Stra6e 1 W-7500 Karlsruhe, FRG

Juris Hartmanis Cornell University Department of Computer Science 4130 Upson Hall Ithaca, NY 14853, USA

Author Anne Mulkers Department of Computer Science, K.U. Leuven Celestijnenlaan 200 A, B-3001 Heverlee, Belgium

CR Subject Classification (1991): F.3.1, D.3.4, 1.2.2-3

ISBN 3-540-56694-5 Springer-Verlag Berlin Heidelberg New York ISBN 0-387-56694-5 Springer-Verlag New York Berlin Heidelberg

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 Abstract interpretation is a general approach for program analysis to discover at compile time properties of the run-time behavior of programs, as a basis to perform sophisticated compiler optimizations. Several frameworks of abstract interpretation for logic programs have been presented [11, 25, 27, 43, 48, 49, 51, 55, 57, 65, 81, 82]. A framework is a parameterized construction for the static analysis of programs, together with theorems that ensure the soundness and termination of the analysis. To complete the construction, an application specific domain and primitive operations satisfying certain safety conditions must be provided. This book elaborates on an application for such a generic framework. The framework used [11] belongs to the class of top-down abstract interpretation methods and collects the information derived in an abstract AND-OR-graph that represents the set of concrete proof trees that can possibly occur when executing the source program. The starting point of the present work is the previously developed application of integrated type and mode analysis [38]. The purpose of that application was to guide the compiler, based on a characterization of the entry uses of the program, to generate code that is more specific for the calls that can occur at run time. In an attempt to give further guidance to the compiler, we address the problem of compile-time garbage collection, the purpose of which is to (partially) shift run-time storage reclamation overhead to compile time. In applicative programming languages, the programmer has no direct control over storage utilization, and run-time garbage collection is necessary. Garbage collection involves a periodic disruption of the program execution, during which usually a marking and compaction algorithm is employed. Such schemes are expensive in time. Our research shows that at compile time useful and detailed information about the liveness of term substructures can be deduced which the compiler can use to improve the allocation of run-time structures. In fact, it provides a technique to automatically introduce destructive assignments into logic languages in a safe and transparent way, thereby reducing the rate at which garbage cells are created. The resulting system gets near to the methods of storage allocation used in imperative programming languages. The global flow analysis to be performed on Prolog source programs in order to derive the liveness of data structures is constructed in three layers. The

vI 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.

Leuven, March 1993

Anne Mulkers

Contents Introduction

1

Abstract Interpretation Basic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . Abstract Interpretation Framework . . . . . . . . . . . . . . . Overview of the F r a m e w o r k . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . Primitive Operations ....................... A b s t r a c t I n t e r p r e t a t i o n Procedure . . . . . . . . . . . . . . . . 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 . . . . . . . . . Rigid a n d I n t e g r a t e d T y p e G r a p h s . . . . . . . . . . . . . . . . Type-graph Environments ..................... 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 . . . . . .

5 5 7 8 10 11 14 16 16 23 25

Related Work 3.1 Aliasing a n d P o i n t e r A n a l y s i s . . . . . . . . . . . . . . . . . . . 3.2 Reference C o u n t i n g a n d Liveness Analysis . . . . . . . . . . . . 3.3 Code O p t i m i z a t i o n . . . . . . . . . . . . . . . . . . . . . . . . .

31 31 38 41

2.1 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.3 2.3.1 2.3.2 2.3.3

3

4

Sharing Analysis 4.1 4.1.1 4.1.2 4.1.3 4.1.4 4.2 4.2.1 4.2.1.1 4.2.1.2 4.2.2 4.2.3 4.3 4.3.1 4.3.2

Sharing Environments . . . . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . . . 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 . . . . . . . . . . T h e Concrete a n d A b s t r a c t D o m a i n s . . . . . . . . . . . . . . . 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 . . . . . . . . . . . Primitive Operations ....................... Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Xi :

Xj

Xi :

f(Xil,...,Xij)

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Procedure E n t r y . . Procedure Exit . . . Evaluation . . . . . Example: i n s e r t / 3 Relevance of S h a r i n g

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ........................ Edges . . . . . . . . . . . . . . . . . . . .

47 47 48 55 62 66 68 68 69 85 93 98 110 111 114

v lll

CONTENTS

4.3.3 4.3.4

6

Imprecision in the S h a r i n g Analysis . . . . . . . . . . . . . . . Efficiency of the S h a r i n g Analysis . . . . . . . . . . . . . . . .

117 123

LivenessAnalysis 5.1 Liveness E n v i r o n m e n t s . . . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . 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 . . . . . . . . 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 . . . . . . . . . . . . . . . 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 . . . . . . . . . . . 5.2 Primitive Operations . . . . . . . . . . . . . . . . . . . . . . . Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 5.2.1.1 Xi = X j . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1.2 X i : f ( X i , , . . . , X i j ) . . . . . . . . . . . . . . . . . . . . . . 5.2.2 Procedure E n t r y . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.3 Procedure Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Example: q s o r t / 3 . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 Precision of the Liveness Analysis . . . . . . . . . . . . . . . . 5.3.3 T h e Practical Usefulness of Liveness I n f o r m a t i o n . . . . . . . .

127 127 128 133 141 145 147 147 147 153 154 163 165 165 168 171

. . . . .

. .

Conclusion

179

Appendix; Detailed Examples A.1 List of T y p e s . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2 append/3 ......................... ..... A.3 nrev/2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.4 buildtree/2 and insert/3 . . . . . . . . . . . . . . . . . . . . . . A.5 p e r m u t a t i o n / 2 a n d select/3 . . . . . . . . . . . . . . . . . . . . A.6 split/3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.7 qsort/2 and partition/4 . . . . . . . . . . . . . . . . . . . . . . A.8 s a m e l e a v e s / 2 a n d profile/2 . . . . . . . . . . . . . . . . . . . . A.9 sift/2 a n d r e m o v e / 3 . . . . . . . . . . . . . . . . . . . . . . . .

183 184 185 188 193 196 199 202 205 209

Bibliography

213

Chapter 1

Introduction In conventional languages, such as C or Pascal, the p r o g r a m m e r explicitly controls the utilization of m e m o r y by means of declarations and destructive assignments. For example, when reversing a linear list L, the list cells of the original list can be reused to construct the reversed list in the case that the original list is no longer needed for further computations. It is up to the p r o g r a m m e r to decide whether he needs to preserve the old list intact and construct a reversed list which has only the list elernertLs in c o m m o n with the list L (e.g. Rev_L1 in Figure 1.1), rather than reuse the list-constructor cells of L as well (e.g. Rev_L2).

_1

-,

, I

I

,

-J

-,

,

I

I

,

_1

-~

,

I

i

[

Rev_L2

/

\"

/

\"

-,

RevL 1 /

J e

Figure 1.1: Reversing a linear list. Applicative languages, in their pure form, do not have destructive assignments. Also type declarations are often absent. The declarative nature of these languages is often cited as an i m p o r t a n t advantage, which allows p r o g r a m m e r s to focus on the logic of the problems they have to solve, rather than on more technical aspects such as search control and efficient m e m o r y usage. Unfortunately, the performance of current implementations of applicative languages does not compare well with procedural languages yet. To achieve better utilization of memory, global flow analysis techniques are being developed that are concerned with determining the type and liveness of d a t a structures that are dynamically

2

C H A P T E R 1. I N T R O D U C T I O N

append (nil, _Y, _Y). append([_E I _UJ,_Y,[_E I _W]) :- append(_U,_Y,_W). nrev(nil, nil). nrev([_E I _U], _Y) :-nrev(_U, _KU), append(_KU, [~], ~). P r o g r a m 1.h n r e v / 2 (Naive reverse)

created during program execution. Knowledge about the lifetime of d a t a structures guides the compiler in the generation of target code to reuse heap storage t h a t is no longer accessible from program variables, i.e. to introduce destructive operations and avoid the copying of data structures that have no subsequent references. In this book, we address the problem of liveness analysis for the class of pure Horn clause logic programs. The language considered has a countable set of variables (Vats), and countable sets of function and predicate symbols. A term is a variable, a constant, or a compound term f ( Q , . . . , t , ~ ) where f is a n-ary function symbol and the t~ are terms. An atom has the form p ( Q , . . . , tin) where p is a m - a r y predicate symbol and the ti are terms. A body is a (possibly empty) finite conjunction of atoms, written A1,..., A,~. A clause consists of an a t o m (its head) and a body and is written A : - B. A program consists of a finite number of clauses. A query or goalconsists of a body only, written 7- B. We assume that the reader is acquainted with the basic terminology of logic programming and the execution mechanism of Prolog which is based on unification and backtracking. Features such as assert and retract are not considered, i.e. we assume that any source code for the predicates that can be executed at run time is available to the compiler. The handling of d a t a structures is very flexible in Prolog. D a t a manipulation (record allocation as well as record access and parameter passing) is achieved entirely via unification. An optimizing compiler can translate general unification to more conventional m e m o r y manipulation operations if information is available about the mode of use of the predicates. When at run time a compound term becomes accessible for the first time, we can say the term is being constructed. When a pattern is matched against a compound t e r m that is already accessible, we can say the components of the term are being selected. Integrated type and mode analysis in m a n y cases allows to predict at compile time whether a unification is a selection rather than a construction operation. Selection statements in particular are good candidates to check for the possible creation of garbage cells, i.e. cells that have no further references. Consider the Prolog Program 1.1 for naive list reversal. We use the convention that variable names start with an underscore. If we assume that queries to n r e v / 2 are restricted to have as first argument a list that is no longer referenced after the call, and as second argument a free variable to return the output, then it is possible to generate target code for this program that allocates no new

list-constructor cells, but rather reuses the list cells of the first argument. Indeed, under the assumption, the integrated type and mode analysis will infer t h a t each call to the recursive clause of n r e v / 2 has as its first argument a list, and as second argument a free variable. The unification of the call with the clause head selects the head and tail of the first argument list. The principal list-constructor cell of this list on the contrary has no subsequent references in the clause following the unification of the call with the clause head. This means that the compiler can recognize the principal list cell as garbage and generate target code t h a t reuses it. For instance, consider the caI1 to a p p e n d / 3 m a d e by the same clause. A single element list [_E] needs to be constructed. Instead of allocating a new cell, the compiler can reuse the garbage cell that was detected. Note that the problem is more complex if there may be multiple references to the cells of the input list. Most implementations of unification unify a variable and a compound structure by making the variable a reference to the structure not a copy of the structure. The representations in m e m o r y of the logical terms to which variables can be bound typically share some of their structure: while the denoted terms make up a forest of trees, their representations form a more general directed acyclic graph. This is why in general the sharing analysis plays a crucial part in the liveness analysis. In the above example, we can also infer that, the first two arguments in each call to a p p e n d / 3 will be lists and that the third argument will be a free variable. Again, it is possible to detect that, after invocation of the recursive clause of append/3, the principal cell of the first argument is garbage and can be reused to construct the value of the third (output) argument. Thus, all list constructions in this example can reuse garbage list ceils, eliminating all allocation operations. Since the reused cells would otherwise be garbage, we have eliminated the garbage-collection overhead associated with the n r e v / 2 procedure. Moreover, a compiler can detect that the element field of each reused list cell already contains the value desired in the cells new use. The operations filling in these car fields can be eliminated from the generated target code. The resulting code closely resembles how a p r o g r a m m e r using an imperative language would solve the problem of reversing a linear list of linked records. In the present work, we propose an abstract domain and operations to analyze the liveness of d a t a structures within a framework of abstract interpretation. Chapter 2 presents the principles of abstract interpretation for logic programs, and the application of type and mode analysis on which the domain for liveness analysis is based. In Chapter 3, we discuss work related to the application of compile-time garbage collection in the context of both logic and functional prog r a m m i n g languages. In Chapter 4, we formalize an abstract interpretation for analyzing how the terms to which program variables are bound at run time, can share substructure in storage. We also augment the usual concrete semantics with information about sharing of t e r m structures and discuss whether any implementation c o m m i t m e n t s are implied. As argued above, the sharing analysis constitutes a prerequisite for the liveness analysis. The latter is presented in Chapter 5. In both Chapter 4 and 5, the emphasis is mainly on the precision

4

C H A P T E R 1. I N T R O D U C T I O N

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 analysis 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 verification 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 domains and operations supplied to complete the construction obey certain safety requirements. The standard theory of lattices provides the conceptual framework for program 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 existence of a solution for the fixpoint problem posed by the abstract semantics of the program. Let (C, E) be the concrete domain, and (A, _ _F, _NR).

Program 4.h i n s e r t / 3

(R1, Sn) I 3(R1, E1)...(P.r,, Sn) a finite sequence over B U C such that (Vg 6 IN : 1 < t < n =~ & = Rt+I) and [Vk6]PC: ( ( l < 2 k _ < n ~ ( R 2 k , Sak) E B ) & (1 < 2 k + l < n ==~(R2t+I, Saj,+I) 6 C ) ) v

((t < 2k

3 , _NT = t(_.L, _F, ~ R ) , insert(_E, _~, _NR).

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. Runtime 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 transforms 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 inserts 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 Section 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.

append( append(

_3(,

_u

:- _X = nil, _Y = _Z . :- _X = [_El_u], _Z = [_El_W], append(_U, _Y, _W)

_7, )

_X, _Y, _7, )

Consider the initial abstract liveness environment ~i,~ =

(T~,~", ASharingT,~.,

A Live~r~,,), where ~,~ List

::= ::= = ----

ASharing,z~ ALive~r,~

(V, V, List), nil l ' . ' ( I n t , List),

(@,@>, {T~,~[(1,V)], T~,,[(2, V>]}.

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 procedure, is

To,,t AShr~-o,,,

::= = =

(List, List, List), (@, AShr.-~o,,,>, ( ( To,,t[(2,'.')], To,,t[(3,'.'>]) },

ALive~-o.,

=

{ To,,t[(1, '.')], To~,t[(2, '.'>] }.

ASharing.2-o,,,

The sharing between the second and the third argument after the call, obviously results from the unification _u = _Z in the first program clause. In the recursive clause, the unification _X = [_El_U] is now a construction operation, while the unification _Z = [_El _W] is a selection operation. If we order the variables of the clause according to the tuple (_X, _Y, 7. _E, _U, _W>,then the environment derived by the analysis for the program point just before the recursive call, is given by ~s = (T:, ASharing:Ts, ALive:rs),

'7-,, ASharingT,

::= =

('.'(Int,V), V, ListOne, Int, V, List >, (0, AShr~- ),

AShr~,

=

{ ( T,[] ) },

{ T,[(2, V>], T,[] }.

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

Tin

Ts ~ nil

Int ~'~

v

.

Int V Or~

hit ~V-'--""--'~t~n~l~.~ii,,,sni~/~ 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 environments 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 operation 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, consider 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

::= ::=

(List, V), 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 { 7"1[{1, V)],T1[{3, V)] }.

ALive~rl

For program point (~) (resp. ( ~ ) ) in the definition of s e l e c t / 3 , it is detected that the top-list cell of the input argument _Yl becomes garbage. In the second

5.3.

177

EVALUATION

permutation( _XI, _YI ) :_XI = nil, _YI : nil. permutation( _XI, _ZI ) :select(

7., _X1, _Ys ),

select(

_YI = [ _X i -ZZ ] select( _X, _Y1, 7.1 ) :-

V, V, V, List, List, V) ([(_X,./2)], [(_Y,./2)])} [(I,V)]} Int, List, _, List0ne .... ) ([(I,./2),(2,./2)], [(_U,./2)])} [(I,./2)], [(_U,./2)], [(_E,Int)]} Int, List, V, List0ne, List, V) ([(_X, ./2)] , ['(_Y,./2)]), ([(_X,./2),(2,.12)],

[(_Y, . / 2 ) ] ) }

AShr,~-

{ ( [ ( I , . / 2 ) , ( 2 , . / 2 ) ] , [(_U,./2)])} { [(2Z,V)] } ( Int, List, V, List0ne, List, .(Int,V)> { ([(_X, ./2)] , [(_Y,./2)]), ( [ ( I , . / 2 ) , ( 2 , . / 2 ) ] , [(_Y, ./2)1)]. { ([(_X,./2),(2,./2)], [(_U,./2)]),

ALive:r

{ [(_Z, ./2)] }

AShr~ ALivey T AShr~

([(_Z, . / 2 ) , ( 2 , V ) ] ,

f17

[(_W,V)])}

T

( Int, List, List, List0ne, List, List0ne)

AShr~

{ ([(_X,./2)], [(_Y,./2)]), ( [ ( I , . / 2 ) , ( 2 , . / 2 ) ] , [(_Y, . / 2 ) ] ) } { ([(_Y,./2)], [ ( 1 , . / 2 ) , ( 2 , . / 2 ) ] ) , ([(_Y,./2)], [(_W,./2)]), ([(_X,./2),(2,./2)], [(_U,./2)]), ([(_Z, ./2), (2, . / 2 ) ] , [(_W, ./2)])} { [(_Z, . / 2 ) ] }

AShr~ ALiveT

The following table illustrates how the sharing and liveness analyses perform when one uses the invertibility property of Prolog. The table shows the abstract 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. However, 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, _.B, _C) ~

~o3

T

( V , V, List>

ALiveT 7AShr~ ALive,]-

{ [(..B,V)], [(_A,V)]} (List, List, List> { ([(_B,. 12)3, [(_C,. 12)3 )} { [(_B, ./2)] , [(_A,./2)]}

188

APPENDIX A. DETAILED EXAMPLES

1)

~2 ~3 2) f14

&

(_X, _Y, _Z) T

ALive,:r T ALiveT T AShr~ ALive~r

{ [(_Y,V)], [(1,v)]}

T ALive~r T AShr,~

ALive,r T AShr~ ALive~r T AShr~ALive1 T AShr~-

ALive~r

A.3

( nil, V, List> { [(_Y,V)], [(/,nil)]} (nil, List, List> { ([(_Y,.12)], [(_z, .12)] )} { [(_Y, .12)] , [(_X,nil)]} ( ~ , _u, _w, _x, _Y. _z> ( V, V, V, V, V, L i s t )

{ [(_Y,V)], [(_x,v)]} ( V, V, V, .(V,V), V, List) { ([([..X, . / 2 ) , (1,V)], ['(..E,V)]),

( [(_x,./2), (2 ,v)], [(_u, v) ] )} { [(_Y,V)], [ ( . / , . / 2 ) ] } ( Int, _, List, _, _, ListOne> { ([(_w,./2)], [ ( _ z , . 1 2 ) , ( 2 , . / 2 ) 3 ) } { [(_Z,.12),(2,.12)], [(_W, .12)] , [(_E,Int)]} ( Int, V, List, . ( I n t , V ) , V, ListOne) { ( [(_X,./2), ( I , I n t ) ] , [(_E, I n t ) ] ), ( [(_x, ./2), (2,v)], [(_u,v)] ), ( [(_w,. 12)3, [(_z,. 12), (2,./2)3 )} { [(_Y,V)], [ ( _ X , . / 2 ) ] }

( Int, List, List, ListOne, List, ListOne) { ( [ ( _ Z , . / 2 ) , ( 2 , . / 2 ) ] , [(_Y,./2)]), ([(_X,./2), ( l , I n t ) ] , [(_E,Int)]), ( [(_X,./2), ( 2 , . / 2 ) ] , [(._u,./2)] ), ( [(_w, ./2)] , [(_.Z, ./2) , ( 2 , . / 2 ) ] ), ([(_Y, . / 2 ) ] , [(_W, . / 2 ) ] ) } { [(_Y,./2)], [(_x,./2)]}

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:

(1) ~ , v (

_x, _Y) .~1 -X = n i l , ~2 -Y : nil.

#3

(2) nrev( _X, _Y) ",04 -X = [ -E [ -U ],

,~4

A.3. NREV/2

189

;35 nrev( _U, _RU), ;36 _Last = [_El, ;37 append( _.RU, _Last, _Y). ;3s

Note that the selection _X = [ _E I _U ], and the construction operation _Last = [_El of the second clause, do not occur in the same chunk (Section 3.3). Moreover, the variable _X in the recursive clause is a temporary variable because it only occurs in the head and the first call. If it is made a permanent variable by the compiler, its value will be saved on the local stack, and the garbage cell it is pointing to at program point ;36, will be accessible for reuse in the construction

_Last = [_El. Call:

;3~ nrev( _X, _Y ) ( _X, _u

;37

T AShr,} ALive~r

( VLisZ, V> { ([(D[,./2),(1,V)], { [(_Y,V)] }

[(_X,./2),(1,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

T AShr,~ ALive~r T AShr,~ AShr,~ALiveT

1) A

T AShr,~ ALive~r T ALive~r

)

( _X, _Y> < VList, V> { ([(~,./2),(1,v)], [(~x,./2),(1,v)])} { [ ( _ X , . / 2 ) , ( 1 , V ) ] , [(_Y,V)]} ( V L i s t , VList>

{ ([(_x,.12),(i,v)], [(_x, .12) , (1,v)] )} { ([(I,./2),(1,V)], [(_Y,./2),(1,V)])} { [(_X,./2),(l,V)], [(_Y,.12)]} < _X, _Y ) < vni t, v>

{ ([(_~,./2),(1,v)], { [(_X,./2),(1,V)], ( n i l , nil)

{ [(_Y,nil)] }

[(~,./2),(1,v)])} [(_Y,V)]}

190

A P P E N D I X A. DETAILED E X A M P L E S

2) T AShr~ALive~r T AShr~-

(_E, last, _KU, _U, I, _Y)

{ ([(_x,.12),Cl,V)], [(_x,.12),(1,v)])} { [(_x,.12),(1,v)], [(_Y,v)]} ( V ..... VList, VList0ne, _)

{ ([(_x,.12),(1,v)], [(_x,.12),(1,v)]), ([(_x, ./2), (2, ./2), (1,v)], [(_x, ./2), (1,v)]), ([(I,.12),(2,.12),(1,v)3, [(I,.12),(2,.12),(1,v)I)}

AShr~

{ ([(_x,.12),C1,v)], [(_E,V)]), ([(_U, ./2)], [(_X,./2),(2,./2)])} ALive:T { [(_x,.12),(2,.12)], [(_u, .12)] , [(_E,V)], [(_X, . 1 2 ) , ( I , V ) ] } T (V, V, V, VList, VList0ne, V) AShr~- { ([(_x,.12),(1,v)], [(_x,.12),(1,v)]),

([(_x, .12), (2, .12), (1,v)], [(_x, .12), (1,v)]), ( [(_x,./2), (2,./2), (1,v)], [(_X, ./2), (2, ./2), (1 ,V)] )}

AShr~ ALive~ T AShr~-

AShr~-

ALiveT

{ ([(_x,.12),(1,v)], [(_E,V)]), ([(_U, ./2)], [(_X,./2),(2,./2)])} { [(_X,./2),(2,./2),(1,V)], [(_X,./2),(1,V)], [(_Y,V)]} ( V, V, VList, VList, VList0ne, V)

{ ([(_x,.12),(1,v)], [(_x,.12),(1,v)]), ([(_X, ./2), (2, ./2), (1,V)], [(_X, ./2), (1,V)]), ( [(_x, ./2), (2, ./2), (1,v)], [(_X,./2),C2,./2),Cl,V)])} { ([(_X,./2),(2,./2),(I,V)], [(_~U,./2),(I,V)]), ( [(_x, ./2), (1,v)], [(_E,V)]), ([(_U, ./2)], [(_X, ./2), (2, ./2)]), ([(_U, ./2), (I,V)], [(_3.U,./2), (1,V)])} { [(_X,./2),C2,./2),(1,V)], [ ( I , . / 2 ) , ( 1 , V ) ] ,

[(_Y,v)]}

#7

T AShr~-

( V, .(V,nil), VList, VList, VList0ne, V)

{ ([(_x,.12),(1,v)],

[(_x,.12),(1,v)]), [ ( I , ./2), (1,V)]), ( [C-X, ./2), (2, ./2), (1,V)], [(_X,./2),(2,./2),(1,V)])} ([(I,./2),(2,./2),(1,V)],

AShr~

ALiveT

{ ([(_X,./2),(I,V)], [(last,./2),(l,V)]), ([(_X,./2),(2,./2),(1,V)], [(_~U,./2),(1,V)]), ([(_x, ./2), (1,v)], [(_E,V)]), ([(_U, ./2)], [(_X, ./2), (2, ./2)]), ([(_u, . / 2 ) , ( 1 , v ) ] , [(_Ru, . / 2 ) , ( 1 , v ) ] ) , ( [(_Last; . / 2 ) , (1,V)], [(_E,V)] )} { [(./,.12),(2,./2),(1,V)], [(_X,./2),(I,V)], [(_Y,V)] }

A.3. NREV/2

191

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:

~ /~4

/~ append( ..RU, l a s t ,

(

7AShr~ ALiveT T AShr~r

last, _RU,

â€¢

)

_Y)

( . ( v , ~ i l ) , ~ i l , v> { ([(_Last,./2),(l,V)], [(_Last,./2),(l,V)])} { [(_Last,./2),(l,V)], [(_Y,V)]} ( . ( V , n i l ) , n i l l . ( V , n i l ) , V) { ([(_RU,./2),(I,V)], [(last,./2),(l,V)]), ([(.Last,./2),(1,V)], [(Iast,./2),(1,V)]), ([(..I{U,./2),(1,V)], [(_RU,./2),(1,V)])}

ALiveT fl# T AShr~-

{ [(last,./2),(l,V)], [(_RU,./2),(I,V)], [(_Y,V)]} (.(V,nil), nill.(V,nill.(V,nil)), V) { ([(..RU,./2),(1,V)],

[(iast,./2),(i,V)]),

( [(_RU,./2), (2,./2), (I ,V)], [(_Last,./2), (I ,V)] ), ([(_Last,./2),(1,V)],

[(Iast,./2),(1,V)]),

([(_KU,./2),(2,./2),(1,V)], ( [(_RU, . / 2 ) , ( 1 , V ) ] ,

[(_R,U,./2),(1,V)]),

[(..RU, . / 2 ) , ( 1 , V ) ] ) ,

( [ ( ~ U , . / 2 ) , ( 2 , ./2), ( l , V ) ] , [(~U,./2),(2,./2),(1,V)])} ALive=r T AShr~ ALive:,-

{ [(_Last,./2),(1,V)], [(_RU,./2),(2,./2),(t,V)], [(_RU, . / 2 ) , ( 1 , V ) ] , [(_Y,V)] }

(.(v,niz),

Vnist, V)

{ ([(_.KU,./2),(1,V)], [(last,./2),(1,V)]), ([(last, ./2), (1,V)], [(iast, ./2), (1,V)]), ([(_.RU,./2), ( 1 , V ) ] , [(_KU, . / 2 ) , ( 1 , V ) ] ) }

{ [(_Last,./2),(l,V)], [(_RU,./2),(I,V)], [(_Y,V)]}

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 Call:

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

~/ append( _RU, l a s t ,

_Y )

( last, _RU, _Y)

( . ( V , n i l ) , VList, V) { ([(_Ru,./2),Cl,V)], [ ( i a s t , . / 2 ) , C l , v ) ] ) , ( [ ( l a s t , ./2), (1,V)], [ ( l a s t , ./2), (1,V)]), ([(_RU,./2),(1,V)], [(_RU,./2),C1,V)])} ALiveT { [ ( i a s t , . / 2 ) , ( 1 , V ) ] , [(_RU,./2),(1,V)], [(_Y,V)]} ( . ( V , n i l ) , VList, VListOne) T AShr~- { ( [ ( l a s t , . / 2 ) , ( 1 , V ) ] , [ ( l a s t , . / 2 ) , ( 1 , V ) ] ) , ([(_RU, ./2), (1,V)], [ ( l a s t , ./2), (1,V)]), ( [(_~u, ./2), (1,v)], [(_Ru, ./2), (1,v)])} AShr,~ { ([(_Y,./2),(2,./2)], [ ( l a s t , . / 2 ) ] ) , ( [(_Y, ./2)], [(last, ./2)]), ([(_~u, ./2), (1,v)], [(_Y, ./2),(1,v)]), ([(_RU, ./2), (1,V)], [(_Y, ./2), (2, ./2), (1,V)])} ALive,r { [ ( I a s t , . / 2 ) , ( 1 , v ) ] , [(_Y,./2)], [ (_RU, ./2), (1,V)] } < _x, _Y, 7> T ( VList, . (V,nil), V) AShr,-~ { ([(_x,./2),Cl,V)], [(_Y,./2),Cl,V)]), ( [(_Y, ./2), (1,v)], [(_Y, ./2), (1,v)]), ([(_x, ./2), Cl,V)], [(_x, ./2), (1,v)])} ALive~T { [(_Y,./2),(1,V)], [(_X,./2),(1,V)], [(_Z,V)]} T ( n i l , .(V,nil), .(V,nil)) AShr~ { ([(_Y,./2),CI,V)], [(_Y,./2),(1,V)])} AShr,~ { ([(_z,./2)], [(_Y,./2)])} ALive7 { [(_Y,./2),Cl,V)], [(_z,./2)]} (_E, _U, _8, _X, _Y, _Z) T ( V, V, V, VList, .(V,nil), V) AShr,~ { ([(_x,./2),Cl,v)], [(_Y,./2),(1,v)]), ( [(_Y, ./2),(1,v)], [(_Y, ./2), (1,v)]), ([(_x,./2),Cl,V)], [(_x, ./2), (1,v)])} ALive~- { [(_Y,./2),Cl,V)], [(_x,./2),Cl,V)], [(_z,v)]} T ( V, VList, _, VListOne .... ) AShr,-~ { ( [ ( I , . / 2 ) , ( 1 , v ) ] , [(_x,./2),Cl,V)]), ([(_x,./2), (2, ./2), (1,v)], [(_x, ./2), (1,v)]), ([C_x,./2),C2,./2),Cl,v)], [(_x, ./2), (2,./2), (1,v)])} AShr,~ { ([(_x,./2),(1,v)], [(_E,v)]), ([(_u,./2)], [(_x, ./2), (2, ./2)])} ALive,r { [(_x,./2),(2,./2)], [(_u,./2)], [(_E,V)], [(_X, ./2),(1,V)]}

AShr.~

1) 31

2)

A.4. BUILDTREE/2 AND INSERT/3

~s

T

( V, V L i s t , V, VListOne, . ( V , n i l ) ,

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)]),

AShr~ALive,]-

A.4

193 V)

([(_x,./2),(2,./2),(1,v)], [(I,./2),(2,./2),(1,v)])} { ( [ ( _ X , . 1 2 ) , C 1 , V ) ] , [(_E,V)]), ([(_U, . 1 2 ) ] , [(_X, . / 2 ) , (2, . / 2 ) ] ) }

{ [(_Y,./2),(I,V)], [(_X.,./2),(2,./2),(1,V)], [(_X, ./2), (1,V)], [(_Z,V)]}

buildtree/2 and insert/3

We discussed the results of the sharing analysis for the i n s e r t / 3 predicate in Section 4.3. The following table contains the results of both the sharing and liveness analysis for the first and the second clause. The analysis results for the third clause are similar to those for the second clause. (Note: /Ts is not included in the table as it is identical to/3s.)

Prolo# code:

(1) insert(E, _OT, _}IT) "/71 _OT = empty, /72 -}IT = t(empty, _E, empty). (2) insert(_E, _OT, X T ) "/74 _OT : t(_L, _F, _R),

/Ts -E :< -F, /76 _NT = t(_NL, _F, _R), /77 i n s e r t ( _ E , _L, _NL). /78 (3) i n s e r t ( _ E , _OT, i T ) "_OT = t(_L, _F, _R), /71o _E > ~,

/711 _NT = t(_L, _F, J R ) , 312 insert(_E,

_R, A~R). /713

#3

194 Call:

/~ ~o~

APPENDIX

~/x insert( _A, _B, _C )

( _A, _B, _C) ( I n t , Tree, V) { [(_c,v)]} ( I n t , Tree, TreeOne) z AShr,-} { ([(._B,tl3)], [(_C,t13),C3,t13)]), ([(_B,tl3)], [(_C,t/3), (I ,t/3)] )} ALiveT- { [(_C,tl3)] }

T ALiveT

1)

#1 #2 #3 2) #4

A. D E T A I L E D E X A M P L E S

( _E, ]IT, _OT)

T ( Int, V, T r e e ) ALive,r { [(]IT,V)]} T ( I n t , V, empty) ALive~- { [(]IT,V)]} T (Int, t(empty,lnt,empty), empty) ALiveT- { [(]IT,tl3)] } ( _E, _F, _L, ]IL, ]IT, _0T, _R) ( Int, V, V, V, V, Tree, V)

ALiveT T AShr,}

{ [(]IT,V)]} ( _, Int, Tree . . . . . TreeOne, Tree) { ([(_L,t/3)], [ ( _ 0 T , t / 3 ) , ( l , t / 3 ) ] ) , ([(_R,t/3)], [(_0T,t/3), (3,t13)])} ALiveT- { [(mT,t/3),(l,t/3)], [(mT,t/3),(3,t/3)], [(_R,t/3)], [(_L,t/3)], [ ( - F , I n t ) ] } T ( Int, Int, Tree, V, V, TreeOne, Tree) AShr,} { ([( L , t / 3 ) ] , [(_OT,t/3), ( 1 , t / 3 ) ] ) , ( [ ( ] t , t l 3 ) ] , [(_OT,tl3), (3,t13)])} ALive,r { [(]IT,V)]} T ( Int, Int, Tree, V, t(V,Int,Tree), TreeOne, Tree) AShr,} { ([(]]T,t/3), (3,t/3)], [(]IT,t/3), (3,t/3)]), ( [(â€¢ [(_0T,t/3), (I ,t/3)] ), ([(_~,t13)], [(_OT,t/3),(3,t/3)]), ( [ ( ] I T , t / 3 ) , (1,V)], [(]In,v)]), ( [(]iT,t/3), (3,t/3)], [(_R,t/3)])} ALive~r { [(]IT,t/3)] }

A.4. BUILDTREE/2 AND INSERT~3 T

AShr~

195

(Int, Int, Tree, TreeOne, t(TreeOne,lnt,Tree), TreeOne,Tree) { ([(mZ,t/S),(1,t/3)], [(~T,t/S),(1,t/3)]),

[(tiT,t/3) ( 1 , t / 3 ) ] , [(_NL,Z/3),(3,t/3)]), [(_OT,t/3) ( 1 , t / 3 ) ] , [ ( J L , t / 3 ) , ( 1 , t / 3 ) ] ) , [(JT,t/3) (1,t/3)], [(_L,t/3)]), [(_DT,tI3) ( 3 , t / 3 ) ] , [(_NT,t/3),(3,t/3)]), [(JOT,t/3),(1,t/3)]), [(â€¢ [(_R,t/3)] [ ( _ 0 T , t / 3 ) , ( 3 , t / 3 ) ] ) , [(_~lT,t/3) ( 1 , t / S ) ] , [ ( _ N L , t / 3 ) , ( 1 , t / 3 ) ] ) , [(~T,t/3) ( l , t / 3 ) ] , [(_NL,t/3),(3,t/3)]), [(_NT,t/3) ( 1 , t / 3 ) ] , [(_NL,t/3)]), , [(JIT,t/3) ( 3 , t / 3 ) ] , [(_R,t/3)]), [(_L,t/S)] [ ( _ _ % l L , t / S ) , ( 3 , t / 3 ) ] ) , [(_L,t/3)] [ ( _ I q L , t / 3 ) , ( 1 , t / 3 ) ] ) } [(_NT~t/3)] }

ALiveT-

From/34, the compiler derives that _0T = t(_L, _F, _~), is a selection operation. 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:

(i) buildtree(l, #I

L =

(2) buildtree(i

_OT, ~T) nil,

:-

, ~T, JT) "-

#4 -L = [-E; ] ~ ] , /~4 #s insert(~, _OT, _T), #6 buildtree(~, _T, _NT). #7

#~ buildtree( _A, 8, _C )

Call :

( _ A . _B, _C)

~ #~

T

(List, Tree, V>

ALive~r

{ [(_C,V)]}

T

( List,

AShr~ ALiveT

{ ([(_C,t/3)], { [(_C,t/3)]}

T r e e , Tree)

[(~,t/3)])}

196

A P P E N D I X A. DETAILED E X A M P L E S

1)

#1 &

< _L,

-)IT, _OT)

( L i s t , V, Tree) T ALive,r { [(_NT,V)] } ( nil, V, Tree)

T

ALiveT- { [(iT,V)]}

#s 2)

( Itil, Tree, Tree)

T

AShr~- { ([(JT,t/3)], [(nT,t/3)])} ALiveT- { [(JT,tl3)] }

< _E, _L, _NT, _OT, _R, _T) ( V, L i s t , V, Tree, V, V) ALive~ { [(JT,V)]} ( Int, ListOne, _, _, List, _) T AShr,~ { ([(_~, . / 2 ) ] , [(_L, . / 2 ) , (2, . / 2 ) ] ) } ALiveT" { [(_L,.12),(2,.12)], [(-~,.12)3, [(_E,Int)]} ( Int, ListOne, V, Tree, List, V) T T

AShr~- { ([(_E,./2)], [(_L,.12),(2,./2)])} ALiveT" { [(_NT,V)] } 7( Int, ListOne, V, Tree, List, TreeOne) AShr~ { ([(/~,./2)], [(i,./2),(2,./2)]), ([(â€¢T,t/3)], [(_T,t/S),(3,t/3)]), [(_T,t/3), (1,t/3)])}

([(.DT,t/3)],

ALive~r { [(JT,V)]} T

AShr~-

ALiveT-

( I n t , ListOne, Tree, Tree, L i s t , TreeOne) { ( [ ( _ D T , t / 3 ) ] , [(-NT,Z/3)]), ( [ ( - P . , . / 2 ) ] , [(.L, . / 2 ) , (2, . / 2 ) ] ) , ( [ ( _ D T , t / 3 ) ] , [(_T,t/3),(3,t/3)]), ([(_OT,t/3)], [ ( _ T , t / 3 ) , ( 1 , t / 3 ) ] ) , ([(_/~T,t/3)], [ ( _ T , t / 3 ) ] ) , ([(JT,t/3)], [(_T,t/3),(3,t/3)]), ([(JT,t/3)], [(_T,t/3), (1,t/3)])} { [(JT,t/3)]}

From ~4, the compiler derives that _L = [ _~ I _R], is a selection operation. From /~4, it is derived that the top-list cell can be reused in a construction. However, there is no local opportunity to reuse the list cell.

A.5

permutation/2 and select/3

We use the s e l e c t / 3 program to point out that the garbage cells detected at a program point following a procedure call different from the unification builtin =/2, are only true garbage cells if they are not already reused by the called procedure. The reason is that the analysis presently assumes that no garbage cells are reused. Prolog code:

A.5. PERMUTATION~2 AND SELECT~3

197

(i) select( _X, _YI, _Zl) "-

#i

_Yz = [ _ x

I 71].

(2) select( _X, _YI, _Zl) "#2 _Y1 : [ _Y I _Ys ]~ #311= [_Y 1 7s], #4 select( I , _Ys, _Zs). #s #~ select( _A, _B, _(~ )

Call :

#~

T

( _A, _B, _C) ( V, List, V)

ALiveT- { [(J,v)], [(_c,v)]} ( Int, ListOne, List)

AShr,~ ALive,r l) #1

[(_C,./2)3)}

T

{ [(_A,Int)], [(_C,./2)]} ( I, _YI, _Zl> ( V, List, V)

ALive,r

{ [(~,v)], [(7.1,v)3}

T

( Int, List0ne, List) { ([(_YI,./2),(2,./2)3, [(~Zl,./2)])} { [(_YI,./2),(2,./2)], [(_X,Int)], [(_ZI,./2)]} (_X, _YI, _ZI, _Y, _Ys, ~Zs> ( V, List, V, V, V, V> { [(_X,V)], [(_Zl,V)]} ( _, ListOne, _, Int, List, _) { ([(_Y1,.12),(2,.12)], [(_Ys,.12)])} { [(_YI,./2),(2,./2)], [(_Y,Int)], [(_Ys,./2)]} ( V, List0ne, V, Int, List, V) { ([(_YI,.12),(2,.12)3, [(_Ys, .12)] )}

AShr,~ ALive,r 2)

#2

{ ([(_B,./2),(2,./2)],

T ALive~r T

AShr~ ALiveT T

AShr,~ ALive,r

{ [(_x,v)], [(~Zl,V)]}

(V, ListOne, .(Int,V), Int, List, V) { ([(~Zl,./2),(2,V)], [(~Zs,V)]), ([(_Y1, .12), (2,. 12)], [(_Ys, .12)])} ALive~r { [(I,V)], [(i1,.12)]} ( Int, .(Int,List0ne), List0ne, Int, List0ne, T List) AShr~- { ([(_ZI,./2),(2,./2)], [(_YI,./2),(2,./2)]), ([(_Zs,./2)], [(_YI,./2),(2, /2)]), ( [(_Ys, .12), (2, .12)], [( 7.1, 12), (2,. 12)3 ), ( [(_Ys, .12), (2, .12)], [(_Zs, 12)]), ([(31, .12), (2, .12)], [(_Ys, 12),(2,.12)]), ( [(_Y1, .12), (2,. 12)], [(_Ys, 12)]), ( [(_Zl, ./2), (2, ./2)], [(_Zs, 12)])} ALive~r { [(1,Int)], [(i1,.12)]}

T

AShr,~

198

A P P E N D I X A. DETAILED E X A M P L E S

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:

(i) permutation( _Xl, _YI ) "#I _Xl = nil, #2 _YI = nil. #3 (2) permutation( _XI, 7.1 ) "#4 select( 7., II, _Ys ), ~4 #5 z l = [7. I 7s], #6 permutation( _Ys, 7.s ). #7

#~ permutation( _A, _B )

Call:

#~

T

( _A, _B> < List, V>

ALive~-

{ [(_B,V)] }

#~

7"

( List, List>

AhiveT'7ALive~iT ALive~iT

{ [(_B, ./2)]} ( _Xl, _n) ( List, V) { [(_YI,V)] } < nil, v> { [(_YI,V)] } ( nil, nil)

ALiveT

{ [(_YI, nil)]}

I) #i #2

#3 2) #4 ~4

( 1 1 , _Ys, 7., 7.1, _Zs> iT ALive~ iT

AShr~ALive~-

< { < {

List, V, V, V, V> [(7.1,V)]} nist0ne, List, Int . . . . )

([(_Ys, ./2)], [(_XI,./2), (2, . / 2 ) ] ) } { [(_Ys,./2)], [ ( 7 . , I n t ) ] , [ ( _ X I , . / 2 ) , ( 2 , . / 2 ) ] }

A.6. SPLIT/3 /75

/76

T AShr~ ALiveT T AShr~

199

( { { ( {

ListOne, List, Int, V, V)

([(_Ys,.12)],[(Ii,.12),(2,.12)])} [(li,v)]} List0ne, List, Int, .(Int,V), V)

([(_Ys,./2)],[(_XI,./2),(2,./2)]),

([(Ti,./2),(2,v)],[(is,V)])}

/77

ALiveTT AShr~ALiveT-

{ [(7.i,.12)]} ( L i s t O n e , L i s t , Int, ListOne, L i s t ) { ([(_Ys,./2}],E(_Xl,./2),(2,./2)]), ([(7.1,./2),(2, . / 2 ) ] , [(_Zs, . / 2 ) ] ) } { [(_Zl,./2)]}

Note that the top-list cell of the variable _Xl is derived to become a garbage cell in fl'44. However, this is only true if the code generated for the s e l e c t / 3 predicate did not introduce destructive assignments. The garbage cell created in the first clause of the s e l e c t / 3 predicate, may or may not be the first list cell of the variable _Xl.

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 normalform 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:

(i) split( _X, _Y, 7. ) "/72 1 1 = [ _ b l = ] , /73 -Y = [~I=1], /74 7. : [_b{ = 2 ] , /75 split( _r, _rl, _r2 ). (2) spilt( i, _Y, 7. ) "/77 _x = [_al_Xl]. /7, I I [], /79 -Y = [_a[_Y1],

/76

=

/710 _Y1 = [], /711 _Z = [].

/712

(3) split( _X, _Y, 7. ) :/713 _X = [], /714 -Y = [], /71s _Z = []. Call:

~

split( _.A, _13, _C )

200

APPENDIX A. DETAILED EXAMPLES

T ALive,r T ALive,r 1)

T ALive~r T AShr~ ALive,r

#2

T

AShr,~ ALive,r T AShr~ALive~ T AShr,}

ALive~ T AShr,}

ALive~r T

(_A, _B, _C) ( L i s t , v, v>

{ [(~,v)],

[(_c,v)]}

(List, List, List) { [(-B, .12)] , [(_C,.12)]} ( I, _Y, _Z, _a, II, _b, _r, _rl, _r2 ) < L i s t , v, v, v, v, v, v, v, v)

{ [(_Y,V)], [(_z,v)]} (List0ne ..... Int, List ....... _> { ([(-Xl,./2)], [(_X,./2),(2,./2)])} { [(_X,./2),(2,./2)], [(_Xl,./2)], [(_a,Int)]} ( ListOne, V, V, Int, List, V, V, V, V) { ([(_XI,.12)], [(_X,./2),(2,.12)])}

{ [(_Y,V)], [(_z,v)]} ( ........

ListOne, Int, List, _, _)

{ ([(_r,./2)], [(_xi,./2),(2,./2)])} { [(_X1,.12),(2,.12)], [(_b,Int)], [(_r,.12)]} (.(Int,ListOne), V, V, Int, ListOne, Int, List,

V, V) { ([C-r,./2)], [(-X,./2),(2,./2)]), ([(_r, . / 2 ) ] , [(_X1,./2),(2,./2)]), ( [(_Xl, ./2), ( 2 , . / 2 ) ] , [ ( _ X , . / 2 ) , ( 2 , . / 2 ) ] ) , ([(_XI, ./2)], [(_X,./2),(2,./2)])} { [(_Y,V)], [(_Z,V)]} (.(Int,ListOne),.(Int,V), V, Int, ListOne, Int, List,V,V) { ([(_Y,./2),(2,V)], [(_rl,V)]), C[(_xl, . / 2 ) ] , [(_x, ./2), (2, . / 2 ) ] ) , ([(_XI,./2),(2,./2)], [(_X, ./2), (2, ./2)]), ( [(_r, . / 2 ) ] , [(_X1, ./2), (2, . / 2 ) ] ) , ([(_r, . / 2 ) ] , [(_X, ./2), (2, ./2)])}

{ [(_Y,./2)], [(_z,v)]}

(.(Int,ListOne), .(Int,V), .(Int,V), Int, ListOne, Int, List, V, V) AShr,} { ( [ ( 1 , . / 2 ) , ( 2 , V ) ] , [(=2,V)]), ([(_r, . / 2 ) ] , [ ( _ X , . / 2 ) , ( 2 , . / 2 ) ] ) , ([(_r, ./2)], [(_XI,./2),(2,./2)]), ( [ ( I 1 , ./2), (2, . / 2 ) ] , [(_X, ./2), (2, . / 2 ) ] ) , ([(_Xl, ./2)], [(_X,./2),(2,./2)]), ( [(_Y, .12), (2,V)], [(_r1,V)])} [C 7.,./2)]} ALive~T { [(_u

201

A.6. SPLIT~3

&

:T

AShr~

(.(Int,ListOne), ListOne, ListOne, Int, ListOne, Int, List, List, List) { ([(_Z,.12),(2,.12)], [(__r2,.12)]), ([(_r, .12)], [(_X, .12), (2, .12)]), ( [(_r, .12)], [(-/I, .12), (2, .12)]), ([(_X1,.12),(2,.12)], [(-/,.12),(2,.12)]), ( [ ( - / I , .12)], [(_X, .12), (2, .12)]),

([(_Y, .12), (2, .12)], [(_rl, .12)])} ALivesr { [(_Y,.12)], [(_z,.12)]} 2)

< -/, _y, _z, _~, - / I , _y1> < L i s t , v, v, v, v, v>

ALive,r

{ [(_Y,v)], [(_z,v)]}

AShr~ALivecr

{ ([(_x1,.12)], [(-/,.12),(2,.12)])} { [ ( - / , . / 2 ) , ( 2 , . / 2 ) ] , [ ( - / 1 , . / 2 ) ] , [(._a,Int)]}

:7-

( ListOne, V, V, Int, List, V> { ([(-/1,./2)], [(-/,./2),(2,./2)])} { [(_Y,V)], [(_z,v)]} ( . ( i n t , = i l ) , v, v, int, n i l , v) { ([(11, n i l ) ] , [ ( - / , . / 2 ) , ( 2 , n i l ) ] ) } { [(_Y,V)], [(_z,v)]} (.(Int,nil), .(Int,V), V, Int, nil, V) { ([(_Y,./2),(2,V)], [(_YI,V)]), ([(_XI, nil)I, [(-/,.12),(2, nil)I)} { [(_Y,./2)], [(_Z,V)]} (.(Int,nil), .(Int,nil), V, Int, nil, nil> { ([(_Y,./2),(2, n i l ) I , [(_Y1, n i l ) I ) , ([(-/I, nil)I, [(-/,./2),(2, nil)])}

( ListOne ..... Int, List _> ~'s

AShr~ALive~ AShr~ ALive~r ~lO T AShr~ALive~r ~ii ~AShr~ALiveT Bi2 T

AShr~ALive~ 3) ~ls

T

{ [(_Y,.12)], [(z,v)]} (.(Int,nil), .(Int,nil), nil, Int, nil, nil> { ([(_Y,./2),(2, n i l ) I , [(_YI, n i l ) I ) , ([(./1, n i l ) I , [ ( - / , . / 2 ) , ( 2 , n i l ) I ) } { [(_Y, ./2)] , [(_Z, n i l ) ] } ( -/, _y, _z> (List,

v, v>

Ative~T { [(_Y,V)], [(_Z,V)]} ( n i l , V, V> ~14 T ALive~ { [(_Y,v)], [( v,v)]} ( ~iI, ~ii, v> ~i5 T ALive,r { [(_Y, n i l ) I , [(_Z,V)]} ( n i l , n i l , nil) ALive~ { [(_Y, n i l ) I , [ ( 7 , n i l ) I }

202

APPENDIX A. DETAILED EXAMPLES

A.7

qsort/2

and partition/4

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).

Call

~14

~i1 partition( _A, _]3, _C, _D )

:

(_A, ..B, _C, _I))

#~ 7-

< I.t, List, v, v)

ALive~ { [(_D,V)], [(_C,V)], [(_A,Int)]} ~oI

7-

( Int, List, List, List) { [(_A,Int)], [(_C,./2)], [(_D,./2)]} (-/4, _L, _Sm, _Gr) ( I n t , L i s t , V, V)

ALiveT X~2 7ALive~ ~3 7ALiveT

{ [(_Gr,V)], [(_Sm,V)], [(_N,Int)]} ( I n t , n i l , V, V) { [(_Gr,V)], [(~Sm,V)], [(_H,Int)]} ( I n t , n i l , n i l , V) { [(_Gr,V)], [(_Sin, nil)l, [(_~,Int)]}

~4

( Int, nil, nil, nil) { [(_Gr, nil)l, [(_Sm, nil)l, [(_M,Int)]}

1) ~,

7ALive~

7ALive~,

A. 7. QSORT/2 AND PARTITION/4 2) #5

(_M, _i, _H, _T, _Sin, _Gr, _Sin1) 7-

ALive:r 7-

AShr~ ALive3/3e

ALiveT 7AShr~

( Int, ListOne, Int, List, ListOne, List, List) { ([(_T, ./2)] , [(_L,./2),(2,./2)3),

ALive~r

([(~Sm,./2),(2,./2)], [(_Sml,./2)])} { [(_Gr,./2)], [(_Sin,./2)], [(_M,Int)]}

7-

7-

AShr~ ~9

3) /3to 7~10

( I n t , List, V, V, V, V, V) { [(_Gr,V)], [(_SIn,V)], [(J4,Int)]} ( _, ListOne, Int, List, _, _, _)

{ ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } { [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] , [(_T, ./2)] , [(_II,Int)]} ( I n t , ListOne, I n t , L i s t , V, V, V) { ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } { [(_Gr,V)], [(_Sin,V)], [(34,Znt)]} ( Int, ListOne, Int, List, .(Int,V), V, V) { ([(_T, ./2)] , [ ( _ ~ , . / 2 ) , ( 2 , . / 2 ) ] ) , ( [(_Sm, ./2), (2,V)], [(_Sml,V)])} { [(_Gr,V)], [(~Sm,./2)], [(A~,Znt)]}

AShr~ ALiveT #a

( _M, _L, _It, _T, _Sin, _Gr, _Grl) ( I n t , L i s t , V, V, V, V, V)

ALive:r

{ [(_Gr,V)], [(_Sm,V)], [(_M,Int)]}

7-

( _, ListOne, I n t , L i s t . . . . .

AShr~ ALiveT

{ { /311 7" ( AShr~ { ALiveT { /313 7-

/314

203

_)

([(_7,./2)], [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] , [(_T, ./2)] , [ ( ] ~ , I n t ) ] } Int, List0ne, Int, List, V, V, V)

([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } [(_Gr,V)], [(~Sm,V)], [(_M,Znt)]}

( Int, ListOne, Int, List, V, . ( I n t , V ) ,

AShr~

{ ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) , (F(_Gr,./2),(2,V)], [(_Grl,V)] )}

ALive~r

{ [(_Gr,./2)], [(_SIn,V)], [(_M,Int)]}

V)

T

( Int, ListOne, Int, List, List, ListOne, List)

AShr,}

{ ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) , (r(_Gr,./2), ( 2 , . / 2 ) ] , [(_Grl, . / 2 ) ] ) }

ALive~r

{ [(_Gr,./2)],

[(.5m,./2)],

[(.~,Int)]}

We omitted/3~, and/312 from the table, as they are identical to/3e, respectively /311 9

From/3s (resp./31o), the compiler derives that _.L = [~t I _T], is a selection operation. From fi~5 (resp. ~ o ) , it is derived that the top-list cell can be reused in the construction _Sin = [_It [ _Sml], (resp. _Gr = [_B I _Grl]). For both clauses, the selection and construction operations occur within a single chunk. Note that there is no sharing between the output lists of the third and the fourth argument.

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

(i) qsort(-~, _Res) "~1 -X nil, =

~2 A s s = nil. (2) qsort(_X, _Res) "-

~3

#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

~/2 qsort( _A, _B )

Call:

( _A, _B) < List, v> ALive~r { [(_B,V)] }

~ 7#~ 1)

< List, List> ALive~r { [(_B, ./2)3} ( rues, ~)

fll

7-

( V, List)

~2

7-

< v, nil)

7-

ALivecr { [(-~es,V)] } ALivecr { [(_Res,V)]}

fls

7-

( nil, nil)

ALivesr

{ [(_I{es,nil)]}

2) ~'4

( _H, _P~, ..Res, _B.esl, _Kes2, _T, _U1, _U2, _X) 7

( V, V, V, V, V, V, V, V, List)

ALive~-

{ [(ross,v)]}

7-

( { { ( { { (

AShr,~-

ALive:r fl~

7-

AShr~ ALive~r f16

7

Int . . . . . . . . . List ..... ListOne) ([(_T, ./2)] , [ ( - X , . / 2 ) , ( 2 , . / 2 ) ] ) } [(I,./2),(2,./2)], [(_T, ./2)] , [(_H,Int)]} Int, V, V, V, V, List, V, V, ListOne) ([(_T, ./2)] , [(_X, . / 2 ) , (2, . / 2 ) ] ) } [(_Res,V)] } Int, V, V, V, V, List, List, List, ListOne)

AShr.~- { ([(_T, ./2)], [(_X, ./2), (2, . / 2 ) ] ) } ALive~r { [(_Res,V)]} f17 f18

7-

( Int, V, V, List, V, List, List, List, ListOne

AShr~ALive~r

{ ([(_T,./2)], [(_X, ./2), (2, . / 2 ) ] ) } { ['(./{es ,V)] }

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~ ALive:r

{ ([(_T, ./2)], [(_X, . / 2 ) , ( 2 , . / 2 ) 3 ) } { [(_Res,V)] }

A.8.

#9

SAMELEAVES/2 AND PROFILE~2 T

AShr,} ALive~r /31o T

205

< Int, List0ne, V, List, List, List, List, List, ListOne) { ([(_T,.12)], [(I, .12) , (2, .12)3 ) , ( [(A~,./2), (2,./2)], [(]~es2, ./2)] )}

{ [(~es,V)]} ( Int, List0ne, List, List, List, List, List, List, List0ne)

AShr,~

{ ([(-~es2,.12)], [(~es,.12)]), ( [ ( _ T , 12)],[(_x,.12),(2, 12)]), ( [(_~,. 12), (2,. 12)], [(_.R,es2,. 12)] ), ( [(_.~es,. 12)], [(R, 12)] ), ( [(~es,. 12)], [(~,. 12), (2,. 12)] )}

ALive~r

{ [(~es,.12)]}

From/34, the compiler derives that _X = [_B I _T], is a selection operation. From/3~4 , it is derived that the top-list cell can be reused in the construction = [_H I _Res2]. However, the selection and construction operations do not occur within a single chunk. If the temporary variable I is made a permanent variable by the compiler, its value will be saved on the local stack, and the garbage cell it is pointing to, will be accessible for reuse in the construction ~ = [At I _~es2] at program point/3s. Code migration would provide an alternative solution (see Section 3.3). Note that the restriction of/39 to the variables of the call a p p e n d ( A e s l , _K, A~es), yields the abstract substitution/3~, for which the analysis results are shown in Section A.2. Also, it can be seen from/36 that the terms bound to _Ul and _U2 are ground and that the terms bound to _Kesl and ~ e s 2 are independent free variables when the subgoal q s o r t ( _ U 1 , _ R e s l ) , in the body of the recursive clause for q s o r t / 2 , is called. Therefore, if the results of the analysis were used for an IAPimplementation of Prolog (Section 3.1), the subgoals qsort(_U1, _Kesl) and qsort(_U2, _Ires2) could be run in parallel without any run-time groundness or independence checks.

A.8

sameleaves/2 and profile/2

In Section 5.3, the s a r a e l e a v e s / 2 program served to clarify the relationship between the depth restriction for type graphs, the normal form of the program and the precision of the liveness analysis. The results of this section are for the program in normal form and depth bound two for the t / 2 functor.

Prolog code: (i) sameleaves( _x, _y ) "/31 profile( _x, _w ), /32 profile( _y, _w ).

206

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

(1) p r o f i l e (

_tr, _pr ) :-

~4

_tr = l v ( _ u ) , ~5 m r = [_u]. ~6

(2) profile( _tr, _pr ) "~7 _tr = t ( _tl, _y), ff~7 ~8 _ t l = l v ( _ u ) .

~9 _pr = [_u[ _prtail] , ~1o profile( _y, _prtail ). (3) profile( _tr, _pr ) :~lZ _tr = t ( _ t l , _ z ) , fills ~13 _ t l = t(_x,_y), ~ s ~4

_t2 = t ( _ x , _ t 3 ) ,

~5

_t3 = t ( _ y , ~.),

~16 profile( _t2, _pr ).

~11

~17

The intended solution of an initial call to the sa.meleaves/2 predicate usually is a yes/no answer. In the query as specified below, the input list of the second argument is assumed to be needed in some further computations. Note that for program point ~2, it is derived that the program variable _w is bound to a ground list containing at least one element. Call: /9~ sameleaves( -A, _B ) ~1

7-

( _A, .3) ( LvTree, LvTree)

ALiveT

{ [(_B,t/2)], [(_B,iv/1)]}

~

7-

( LvTree, LvTree)

ALive~

{ [(_B,ivli)], ( _,, _X, _y)

1) ~1 ~2

~3

[(_B,tl2)]}

7-

( V , LvTree, LvTree)

ALive~

{ [(_y,t/2)], [ ( _ y , l v / i ) ] }

7-

( L i s t O n e , LvTree, LvTree>

ALiveT

{ [(_y,t/2)], [(_y,lv/1)]}

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 Call:

l~3 p r o f i l e (

207

_A, -13 )

( _.h, _.B)

#3

,/ALive~

( LvTree, ListOne> { [(_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.

~

7ALiveT

/302 7" I) /74

~4 #5 #6 2) ~7

( LvTree, V) { [(_B,V)]} < LvTree, ListOne)

ALivey-

{ [(_B, ./2)3}

iT

( -F, _tr, _u ) ( V, LvTree, V)

A Live,r 7" ALive=r T A Live,r

{ [(_pr,V)]} ( _, iv(Int), Int>

~T ALive~

(.(Int,nil), lv(Int), Int) { [(_pr,./2)]} ( _pr, _prtail, _ t l , _tr, _u, _y)

{ [(_u, I n t ) ] } ( V, l v ( I n t ) , Int) { [(_pr,V)] }

T

( V, V, V, LvTree, V, V)

ALive~

{ [(_pr,V)] }

T

( . . . . LvTree, LvTree0ne, _, LvTree) { ( [ ( _ t l , t / 2 ) ] , [(_tr,t/2), (I ,t/2)] ),

AShr~

( [ ( _ t l , l v l l ) ] , [(_tr,tl2), ( l , l v l l ) ] ) , ALive~

( [(_y,t/2)], [(_tr,t/2), (2,t/2)]), ( [ ( _ y , l v / l ) ] , [(_tr,t/2), ( 2 , 1 v / l ) ] ) } { [(_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 #8

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

:T

< V, V, LvTree, LvTreeOne, V, LvTree)

AShr~-

{ ([(_ti,tl2)], [(_tr,t/2),(1,t/2)]), ([(_tl,Zvll)], [(_tr,tl2),(1,Zv/1)]), ([(_y,tl2)], [(_tr,tl2), (2,t12)]), ( [ ( _ y , l v l l ) ] , [(_tr,t/2), (2,1v/I)])}

ALiveT- { [(_pr,V)] }

T

( . . . . iv(Tnt), _, T~t, _) { [(_u,Xnt)] } ( V, V, iv(Int), t(Iv(Int),LvTree), Int, LvTree) { ([(_t1,1vll)], [ ( _ t r , t l 2 ) , ( l , l v / 1 ) ] ) , ( [(_y,t/2)], [(_tr,t/2), (2,t/2)]), ([(_y,lv/1)], [(_tr,t/2), (2,iv/I)])} { [(_pr,V)] } ( _pr, _tl, _t2, _t3, _tr, _~, 4 , ~) ( V , V, V, V, LvTree, V, V, V)

ALiw~r

{ [(mr,V)]}

ALive,r AShr~ ALive:r 3)

#i2 ~12

T

( _, LvTree . . . . .

AShy-

{ ([(_tl,tl2)], [(_tr,tl2),(l,t/2)]), ( [ ( _ t l , l v l l ) ] , [(_tr,t/2),(1,1v/1)]), ([(~z,t/2)], [ ( _ t r , t / 2 ) , ( 2 , t / 2 ) ] ) , ( [ ( _ z , l v / l ) ] , [(_tr,tl2), (2, i v / l ) ] )}

LvTreeOne . . . . .

ALiveT- { [ ( _ t r , t / 2 ) , ( 1 , i v / I ) ] , [(_tr,t/2),(2,1v/1)],

LvTree)

[(_tr,t/2), (1,t/2)], [(_tr,t/2),(2,t/2)],

[( 7.,t12)], [ ( _ z , l v l l ) ] , [(_tl,zl2)], [ ( _ t l , l v l l ) ] } ( V, LvTree, V, V, LvTreeOne, V, V, LvTree)

/$13 AShr,~

{ ([(_ti,t/2)], [(_tr,t/2),Cl,t/2)]), ( [ ( _ t l , l v l l ) ] , [(_tr,t/2), (I, i v / l ) ] ), ([(_z,t/2)], [ ( _ t r , t / 2 ) , ( 2 , t / 2 ) ] ) , ( [ ( _ z , l v / l ) ] , [(_tr,t/2),(2,1v/1)])} ALive~T { [(mr,V)]}

~s

~r

( _, LvTreeOne . . . . . . .

AShr.~

{ ([(_x,t/2)], [ ( _ t l , t / 2 ) , ( 1 , t / 2 ) ] ) , (F(_x,lv/1)], [(_tl,t/2), (I, I v / l ) ] ), ([(_y,t/2)], [ ( _ t l , t / 2 ) , ( 2 , t / 2 ) ] ) , ( [ ( _ y , l v l l ) ] , [(_t i , t / 2 ) , (2,1v/I)])} { [(_tl,t/2),Cl,Zvll)], [(_tl,t/2),(l,tl2)], [(_tl,t/2),(2,Zv/1)], [(_t1,Z/2),(2,t/2)], [(_y,t/2)], [(_y,lv/l)], [(_x,t/2)], [(_x,lv/l)]}

ALive~

LvTree, LvTree, _)

A.9. SIFT/2 AND REMOVE/3 #14 T

209

( V, LvTree0ne, V, V, t(LvTree0ne,LvTree), LvTree, LvTree, LvTree) { ([(~r,t/2),(1,t/2)], [(_x,t/2)]),

AShr,~

[(_tr ,t/2), (1,t/2), (2,1v/1)], [(_x,Zv/1)]), [(_tr t/2), (1,t/2), (1,Zv/1)], [(_x,Zv/1)]), [(_tr t/2), (1,t/2)], [(_y,t/2)]), [(_tr t/2), (1,t/2), (2,1v/1)], [(_y,lv/1)] ), [(_tr t / 2 ) , ( 1 , t / 2 ) , ( 1 , 1 v / 1 ) ] , [(_y,lv/1)]), [(_tl t / 2 ) ] , [ ( _ t r , t / 2 ) , ( 1 , t / 2 ) ] ) , [(_tl t/2),(2,t/2)], [(_tr,t/2),(l,t/2)]), [(_tl t/2),(l,t/2)], [(_tr,t/2),(l,t/2)]), [(_tl t/2), (2,1v/l)], [(_tr,t/2), (l,t/2), (l,lv/l)]) [(_tl t12), ( 1 , 1 v / l ) ] , [(_tr,t/2), (1 ,t12), (2,1v/1)]) [(_z,t/2)],

[(_tr,t/2),(2,t/2)]),

[(_z,lv/l)], [(_tr,t/2),(2,1v/l)]), [(_x,t/2)], [ ( _ t l , t / 2 ) , ( 1 , t / 2 ) ] ) , [(_x,Zv/1)], [ ( _ t l , t / 2 ) , ( 1 , i v / 1 ) ] ) , [(_y,t/2)], [ ( _ t l , t / 2 ) , ( 2 , t / 2 ) ] ) , [(_y,lv/1)], [(_tl,t/2),(2,1v/1)])}

ALive~

A.9

[(_pr,V)]}

sift/2

and remove/3

The sift/2 program can be used to sift out the prime numbers according to Eratosthenes' sieve algorithm. In the tables below, we only show the abstract substitutions of interest. Garbage cells are detected in the program point ;~4 for the s i f t / 2 program, and in the program points ~ and ~ for the remove/3 program. We analyze the remove/3 predicate for an initial query corresponding to the restriction of the abstract substitution ~76 in the second clause of the s i f t / 2 program. Note that there is no local opportunity to reuse the garbage cell in the third clause of the remove/3 predicate.

Prolog code: (1) s i f t (

I,

_Y ) "-

(2) si~t( _x, _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

#~ sift( _A, _B )

Call:

#~ #~ I) #1 #3

(_A, _B) 7ALive~r 7" ALive~r

< List, v>

T

{ [(_B,V)] } ( List, List> { [(_B, . / 2 ) ] } ( _x, _Y) ( List, V)

Akive~-

{ [(_Y,V)]}

~F

( n i l , nil> { [(_Y,nil)] }

Ative~r

2)

(_I, _Is, _New, _Ps, _X, _Y)

( V, V, V, V, List, V)

#s #6

ALive~r T AShr,} ALive,r 7AShr,} ALive~r :T AShr,} ALive,r

{ [(_Y,V)] } ( Int, List . . . . . ListOne, _) { ([(Ts,.12)], [(~,./2),(2,./2)])} { [(_X,./2),(2,./2)], [(_Is,./2)], [(_I,Int)]} ( Int, List, V, V, ListOne, V)

{ ([(_Zs,.12)], [(_x,.12),(2,.12)3)} { [(_Y,V)]} ( Int, List, V, V, List0ne, .(Int,V))

{ ([(_Is,.12)], [(_x,.12),(2,.12)]), ([(_Y, ./2),(2,v)], [(~s,V)])} { [(_Y, ./2)]}

Prolog code:

(1) remove( -P, _X, _Y ) .fl~ _.x =

[3, fl~ _Y = [3. "-

(2) remove( _P, _x, _Y ) #4 _X = [ _ I I _ I s ] ,

#s

/~4

#s -Y = [__llJis], #6 n o t ( 0 i s _I rood _P), #z remove( _P, _Is, J i s ). (3) remove( _P, I , -Nis ) :-

,88

#g _X : [_II_Is], #~9 #10 0 is _I rood _P,

#11 remove( _P, _Is, Jis ).

#12

A.9. SIFT~2 AND REMOVE~3 ~i~ remove( _I, _Is, _New )

Call:

~

211

iv

( _I, _Is, -New) ( Int, List, V>

A Live,r

{ [(mew,V)]} ( Int, List, List> ALive~r { [(mew,./2)]}

/5'0 2 iV

~) ~i

flS

< 2 , ~, _Y>

( Int, List, V> ALiveT- { [(_Y,V)]} 7( Int, nil, nil> ALive~- { [(_Y,nil)]} iv

2)

ALive7 AShr,~ ALive~#s

T

(_I, _Is, _Nis, _P, _X, _Y) (V, V, V, Int, List, V> { [(_Y,V)] } ( Int, List ..... ListOne, _) { ([(_Is, ./2)] , [(_X,./2),(2,./2)])} { [(_X,./2),(2,./2)], [(_Is, ./2)] , [(_I,Int)]} ( Int, List, V, Int, ListOne, V)

AShr~- { ([(_Is,./2)], ALive~- { [(_Y,V)] } #7

iv

AShr~

[(_X,./2),(2,./2)])}

< Int, List, V, Int, ListOne, .(Int,V)) { ([(_Is,./2)], [(_X,./2),(2,./2)]), ([(_Y, ./2), (2,V)], [(_Nis,V)] )}

ALive~r { [(_Y, ./2)3 }

3)

( _I, _Is, mis, _P, _x> ( v, v, v, Int, List)

ALiveT

{ [(mis,V)]} ( Int, List ..... ListOne)

AShr~ { ([(_Is,./2)], [(_X,./2),(2,./2)])} ALive~r { [(_X,./2),(2,./2)3, [(_Is, ./2)] , [(_I,Int)]} ( Int, List, V, Int, ListOns> #11 iv AShr~- { ( [ ( 1 s , . / 2 ) ] , [ ( I , . / 2 ) , ( 2 , . / 2 ) ] ) } ALive:,- { [(mis,V)3}

Bibliography [1] S. Abramsky and C. Hankin, editors. Abstract Interpretation of Declarative Languages. Ellis Horwood Series in Computers and their Applications. Ellis Horwood, Chichester, 1987. [2] K. Appleby, M. Carlsson, S. Haridi, and D. Sahlin. Garbage collection for Prolog based on WAM. Commun. ACM, 31(6):719-741, 1988. [3] K. R. Apt. Logic programming. In J. Van Leeuwen, editor, Handbook of Theoretical Computer Science, Volume B: Formal Models and Semantics, pages 493-574. Elsevier, 1990. [4] B.I.M., B-3078, Everberg, Belgium. ProLog by BIM -3.0- Reference Manual, Nov. 1990. [5] G. Birkhoff. Lattice Theory, volume 25 of American Mathematical Society Colloquium Publications. American Mathematical Society Providence, 1979. [6] P. Boizumault. PROLOG l'implantation. Masson, Paris, 1988. [7] M. Bruynooghe. The memory management of Prolog implementations. In Clark and Ts [16], pages 83-98. [8] M. Bruynooghe. Garbage collection in Prolog interpreters. In J. A. Campbell, editor, Implementations of Prolog, Ellis Horwood Series in Artificial Intelligence, pages 259-267. Ellis Horwood Limited, 1984. [9] M. Bruynooghe. Compile-time garbage collection. Report CW43, Department of Computer Science, Katholieke Universiteit Leuven, Apr. 1986. [10] M. Bruynooghe. Compile-time garbage collection or how to transform programs in an assignment-free language into code with assignments. In L. G. L. T. Meertens, editor, Program Specification and Transformation, pages 113-129. North-Holland, 1987. [11] M. Bruynooghe. A practical framework for the abstract interpretation of logic programs. Journal of Logic Programming, 10(2):91-124, Feb. 1991.

214

BIBLIOGRAPHY

[12] M. Bruynooghe and G. Janssens. An instance of abstract interpretation integrating type and mode inferencing. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 669-683, Seattle, 1988. MIT Press, Cambridge.

[13]

M. Bruynooghe, G. Jansscns, A. Callebaut, and B. Demoen. Abstract interpretation: Towards the global optimization of Prolog programs. In Proceedings of the Fourth Symposium on Logic Programming, pages 192204, San Francisco, 1987. IEEE Computer Society Press.

[14]

J. A. Campbell, editor. Implementations of Prolog. Ellis Horwood Series in Artificial Intelligence. Ellis Horwood Limited, 1984.

[15]

D. R. Chase, M. Wegman, and F. K. Zadeck. Analysis of pointers and structures. Proceedings of the ACM SIGPLAN'90 Conference on Programming Language Design and Implementation, SIGPLAN Notices, 25(6):296-310, 1990.

[16]

K. L. Clark and S.-A. Tarnlund, editors. Logic Programming. Academic Press, N.Y., 1982.

[17]

M. Codish, D. Dams, and E. Yardeni. Abstract unification and a bottomup analysis to detect aliasing in logic programs. Technical Report csg0-10, Department of Computer Science, Weizmann Institute of Science, Israel, May 1990.

[18]

M. Codish, D. Dams, and E. Yardeni. Bottom-up abstract interpretation of logic programs. Technical Report CS90-24, Department of Computer Science, Weizmann Institute of Science, Israel, Oct. 1990.

[19]

M. Codish, D. Dams, and E. Yardeni. Derivation and safety of an abstract unification algorithm for groundness and aliasing analysis. In K. Furukawa, editor, Proceedings of the Eighth International Conference on Logic Programming, pages 79-93, Paris, 1991. MIT Press, Cambridge.

[20] A. Colmerauer. Prolog and infinite trees. In Clark and T~irnlund [16], pages 231-251.

[21]

A. Cortesi and G. Fild. Abstract interpretation of Prolog: The treatment of the built-ins. Rapporto Interno 11, Department of Mathematics, University of Padova, 1991.

[22]

A. Cortesi, G. Fild, and W. Winsborough. Prop revisited: Propositional formula as abstract domain for groundness analysis. In Proceedings of the Sizth Annual IEEE Symposium on Logic in Computer Science, pages 322327. IEEE Computer Society Press, 1991.

BIBLIOGRAPHY

215

[23] P. Cousot. Semantic foundations of program analysis. In S. S. Muchnick and N. D. Jones, editors, Program Flow Analysis: Theory and Applications, pages 303-342. Prentice-Hall, 1981. [24] P. Cousot and R. Cousot. Abstract interpretation: A unified lattice model for static analysis of programs by construction or approximation of fixpoints. In Proceedings of the Fourth A CM Symposium on Principles of Programming Languages, pages 238-252, Los Angeles, 1977. [25] S. K. Debray. Efficient dataflow analysis of logic programs. In Proceedings of the Fifteenth A CM Symposium on Principles of Programming Languages, pages 260-273, San Diego, California, 1988. [26] S. K. Debray. A simple code improvement scheme for Prolog. In G. Levi and M. Martelli, editors, Proceedings of the Sizth International Conference on Logic Programming, pages 17-32, Lisbon, 1989. MIT Press, Cambridge. Also in Journal of Logic Programming, 13(1):57-88, 1992. [271 S. K. Debray. Static inference of modes and data dependencies in logic programs. ACM Trans. Prog. Lang. Syst., 11(3):418-450, 1989. [28] S. K. Debray and D. S. Warren. Automatic mode inference for Prolog programs. In Proceedings of the Third Symposium on Logic Programming, pages 78-88, Salt Lake City, Utah, 1986. IEEE Computer Society Press. [29] I. Foster. Copy avoidance through local reuse. Technical Report Preprint MCS-P99-0989, Mathematics and Computer Science Division, Argonne National Laboratory, Sept. 1989. [30] I. Foster and W. Winsborough. Copy avoidance through compile-time analysis and local reuse. In Proceedings of the International Logic Programming Symposium, Cambridge, 1991. MIT Press. [31] J. Gallagher and M. Bruynooghe. The derivation of an algorithm for program specialisation. In D. H. D. Warren and P. Szeredi, editors, Proceedings of the Seventh International Conference on Logic Programming, pages 732746, Jerusalem, 1990. MIT Press, Cambridge. Also in New Generation Computing, Vol. 9, Nos. 3,4, 1991. [32] S. I-Iorwitz, P. Pfeiffer, and T. Reps. Dependence analysis for pointer variables. Proceedings of the ACM SIGPLAN'S9 Conference on Programming Language Design and Implementation, SIGPLAN Notices, 24(7):2840, 1989. [33] P. Hudak. A semantic model for reference counting and its abstraction. In Abramsky and Hankin [1], pages 45-62. [34] G. Huet. Confluent reductions: Abstract properties and applications to term rewriting systems. Journal of the Association for Computing Machinery, 27(4):797-821, 1980.

216

BIBLIOGRAPHY

[35] K. Inoue, H. Seki, and H. Yagi. Analysis of functional programs to detect run-time garbage cells. A CM Transactions on Programming Languages and Systems, 10(4):555-578, 1988. [36] K. Inoue and K. Torii. Implementation and analysis of compile-time garbage collection. New Generation Computing, 10(1):101-119, 1991. [37] D. Jacobs and A. Langen. Accurate and efficient approximation of variable aliasing in logic programs. In E. Lusk and R. Overbeek, editors, Proceedings of the North American Conference on Logic Programming, pages 154-165, Cambridge, 1989. MIT Press. [38] G. Janssens. Deriving Run Time Properties of Logic Programs by Means of Abstract Interpretation. P h . D . thesis, Department of Computer Science, Katholieke Universiteit Leuven, Mar. 1990. [39] G. 3anssens and M. Bruynooghe. Deriving descriptions of possible values of program variables by means of abstract interpretation: Definitions and proofs. Report CW108, Department of Computer Science, Katholieke Universiteit Leuven, Mar. 1990. [40] G. Janssens and M. Bruynooghe. Deriving descriptions of possible values of program variables by means of abstract interpretation. Journal of Logic Programming, 13(2&3):205-258, July 1992. [41] G. Janssens, B. Demoen, and Y. Willems. Execution mechanism for Prolog. Report CW53, Department of Computer Science, Katholieke Universiteit Leuven, 1987. [42] T. P. Jensen and T. $ . Mogensen. A backwards analysis for compiletime garbage collection. In N. Jones, editor, ESOP'90 Proceedings Third European Symposium on Programming, Lecture Notes in Computer Science 432, pages 227-239. Springer-Verlag, N.Y., 1990. [43] N. D. Jones and H. Scndergaard. A semantic-based framework for the abstract interpretation of Prolog. In Abramsky and Hankin [1], pages 123142. [44] F. Klu~niak. Type synthesis for ground Prolog. In J.-L. Lassez, editor, Proceedings of the Fourth International Conference on Logic Programming, pages 788-816, Melbourne, 1987. MIT Press. [45] F. Klu~niak. Compile-time garbage collection for ground Prolog. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 1490-1505, Seattle, 1988. MIT Press, Cambridge. [46] J. R. Larus and P. N. Hilfinger. Detecting conflicts between structure accesses. Proceedings of the A CM SIGPLAN'88 Conference on Programming Language Design and Implementation, SIGPLAN Notices, 23(7):2134, 1988.

BIBLIOGRAPHY

217

[47] J.-L. Lassez, M. J. Maher, and K. Marriott. Unification revisited. In J. Minker, editor, Foundations of Deductive Databases and Logic Programming, pages 587-625. Morgan Kaufmann Publishers Inc., Los Altos, 1988. [48] B. Le Charlier, K. Musumbu, and P. Van Hentenryck. A generic abstract interpretation algorithm and its complexity analysis. In K. Furukawa, editor, Proceedings of the Eighth International Conference on Logic Programming, pages 64-78, Paris, 1991. MIT Press, Cambridge. [49] B. Le Charlier and P. Van Hentenryck. Experimental evaluation of a generic abstract interpretation algorithm for Prolog. Technical Report CS-91-55, Institute of Computer Science, University of Namur, and Dept. of Computer Science, Brown University, Aug. 1991. [50] J. W. Lloyd. Foundations of Logic Programming. Springer Series : Symbolic Computation - Artificial Intelligence. Springer-Verlag, second, extended edition, 1987. [51] O. Mallet. Interprdtation Abstraite Appliqude ~la Compilation et la Paralldlisation en Programmation Logique. Ph.D. thesis, L'l~cole Polytechnique de Paris, June 1992. [52] A. Mari~n, G. Janssens, A. Mulkers, and M. Bruynooghe. abstract interpretation: An experiment in code generation. M. Martelli, editors, Proceedings of the Sizth International Logic Programming, pages 33-47, Lisbon, 1989. MIT Press,

The impact of In G. Levi and Conference on Cambridge.

[53] K. Marriott and H. SOndergaard. Bottom-up abstract interpretation of logic programs. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 733-748, Seattle, 1988. MIT Press, Cambridge. [54] K. Marriott and H. Sendergaard. On Prolog and the occur-check problem. SIGPLAN Notices, 24(5):76-82, 1989. [55] K. Marriott and H. Sondergaard. Semantics-based dataflow analysis of logic programs. In G. Ritter, editor, Information Processing 89, pages 601-606. North-Holland, N.Y., 1989. [56] C. S. Mellish. Some global optimizations for a Prolog compiler. Journal of Logic Programming, 2:43-66, 1985. [57] C. S. Mellish. Abstract interpretation of Prolog programs. In Abramsky and Hankin [1], pages 181-198. [58] P. Mishra. Towards a theory of types in Prolog. In Proceedings of the 1084 International Symposium on Logic Programming, pages 289-298, Atlantic City, 1984. IEEE Computer Society Press.

218

BIBLIOGRAPHY

[59] A. Mulkers. Deriving Live Data Structures in Logic Programs by Means of Abstract Interpretation. Ph.D. thesis, Department of Computer Science, Katholieke Universiteit Leuven, Dec. 1991. [60] A. Mulkers, W. Winsborough, and M. Bruynooghe. Analysis of shared data structures for compile-time garbage collection in logic programs. In D. H. D. Warren and P. Szeredi, editors, Proceedings of the Seventh International Conference on Logic Programming, pages 747-762, :Jerusalem, 1990. MIT Press, Cambridge. [61] A. Mulkers, W. Winsborough, and M. Bruynooghe. Analysis of shared data structures for compile-time garbage collection in logic programs (extended version). Report CW117, Department of Computer Science, Katholieke Universiteit Leuven, Oct. 1990. [62] A. Mulkers, W. Winsborough, and M. Bruynooghe. Static analysis of logic programs to detect run-time garbage cells. In P. Dewilde and :J. Vandewalle, editors, Proceedings of the International Conference on Computer Systems and Software Engineering, pages 526-531. IEEE Computer Society Press, May 1992. [63] K. Muthukumar and M. Hermenegildo. Determination of variable dependence information through abstract interpretation. In E. Lusk and R. Overbeek, editors, Proceedings of the North Americas Conference on Logic Programming, pages 166-185, Cambridge, 1989. MIT Press. [64] K. Muthukumar and M. Hermenegildo. Combined determination of sharing and freeness of program variables through abstract interpretation. In K. Furukawa, editor, Proceedings of the Eighth International Conference on Logic Programming, pages 49-63, Paris, 1991. MIT Press, Cambridge. [65] U. Nilsson. Systematic semantic approximations of logic programs. In P. Deransart and J. Matuszyfiski, editors, Proceedings of the International Workshop on Programming Language Implementation and Logic Programming, Lecture Notes in Computer Science 456, pages 293-306. Springer-Verlag, 1990. [66] U. Nilsson. Abstract interpretation: A kind of magic. In P. Deransart and J. Matuszyfiski, editors, Proceedings of the International Workshop on Programming Language Implementation and Logic Programming, Lecture Notes in Computer Science. Springer-Verlag, 1991. [67] E. Pittomvils, M. Bruynooghe, and Y. Willems. Towards a real time garbage collector for Prolog. In Proceedings of the Symposium on Logic Programming, pages 185-198, Boston, 1985. IEEE Computer Society Press. [68] D. A. Plaisted. The occur-check problem in Prolog. J. New Generation Computing, 2(4):309-322, 1984. Also in: Proceedings of the International Symposium on Logic Programming, pages 272-280, Atlantic City, 1984. IEEE Computer Society Press.

BIBLIOGRAPHY

219

[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. [70] H. Sendergaard. An application of abstract interpretation of logic programs: Occur check reduction. In B. Robinet and R. Wilhelm, editors, ESOP'86 Proceedings European Symposium on Programming, Lecture Notes in Computer Science 213, pages 327-338. Springer-Verlag, N.Y., 1986. [71] A. Tarski. A lattice-theoretical fixpoint theorem and its applications. Pacific J. Math., 5:285-309, 1955. [72] A. Taylor. Removal of dereferencing and trailing in Prolog compilation. In G. Levi and M. Martelli, editors, Proceedings of the Sizth International Conference on Logic Programming, pages 48-60, Lisbon, 1989. MIT Press, Cambridge. [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. [74] A. Taylor. High Performance Prolog Implementation. P h . D . thesis, University of Sydney, June 1991. [75] W. Thomas. Automata on infinite objects. In J. Van Leeuwen, editor, Handbook of Theoretical Computer Science, Volume B: Formal Models and Semantics, pages 133-191. Elsevier, 1990. [76] M. Van Caneghem. L'anatomie de Prolog. InterEditions, 1986. [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. [78] P. L. Van Roy. Can Logic Programming Ezecute as Fast as Imperative Programming. Ph.D. thesis, University of California, Berkeley, Dec. 1990. [79] P. Vataja and E. Ukkonen. Finding temporary terms in Prolog programs. In ICOT, editor, Proceedings of the International Conference on Fifth Generation Computer Systems, pages 275-282, Tokyo, 1984. Ohmsha, LTD. and North-Holland. [80] D. H. D. Warren. An abstract Prolog instruction set. Technical report, SRI International, Artificial Intelligence Center, 1983.

220

BIBLIOGRAPHY

[81] W. Winsborough. Path-dependent reachability analysis for multiple specialization. In E. Lusk and R. Overbeek, editors, Proceedings of the North American Conference on Logic Programming, pages 133-153, Cambridge, 1989. MIT Press. [82] W. Winsborough. Multiple specialization using minimal-function graph semantics. Journal of Logic Programming, 13(2&3):259-290, July 1992. [83] W. Winsborough and M. Bruynooghe. Approximating unification over representations of sets of nonground terms. Draft, 1990. [84] W. Winsborough and A. Wvsrn. Transparent and-parallelism in the presence of shared free variables. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 749-764, Seattle, 1988. MIT Press, Cambridge. [85] E. Yardeni and E. Shapiro. A type system for logicprograms. In E. Shapiro, editor, Concurrent Prolog: Collected Papers (Volume ~), chapter 28, pages 211-244. M I T Press, Cambridge, 1987. Also in Journal of Logic Programming, Vol. 10(2):125-154 (1991). [86] 3. Zobel. Derivation of polymorpl~ic types for Prolog programs. In 3.-L. Lassez, editor, Proceedings of the Fourth International Conference on Logic Programming, pages 817-838, Melbourne, 1987. MIT Press.

Lecture Notes in Computer Science For information about Vols. 1-595 please contact your bookseller or Springer-Verlag

Vol. 596: L.-H. Eriksson, L. Halln~is, P. Schroeder-Heister IEds.), Extensions of Logic Programming. Proceedings, 1991. VII, 369 pages. 1992. (Subseries LNAI).

Vol. 613: J. P. Myers, Jr., M. J. O ' D o n n e l l (Eds.), Constructivity in Computer Science. Proceedings, 1991. X, 247 pages. 1992.

Vol. 597: H. W. Guesgen, J. Hertzberg, A Perspective of Constraint-Based Reasoning. VIII, 123 pages. 1992. (Subseries LNAI).

Voh 614: R. G. Herrtwich (Ed.), Network and Operating System Support for Digital Audio and Video. Proceedings, 1991. XII, 403 pages. 1992.

Vol. 598: S. Brookes, M. Main, A. Melton, M. Mislove, D. Schmidt (Eds.), Mathematical Foundations of Programming Semantics. Proceedings, 1991. VIII, 506 pages. 1992.

Vol. 615: O. Lchrmann Madsen (Ed.), ECOOP '92. European Conference on Object Oriented Programming. Proceedings. X, 426 pages. 1992.

Voh 599: Th. Wetter, K.-D. Althoff, J. Boose, B. R. Gaines, M. Linster, F. Schmalhofer (Eds.), Current Developments in Knowledge Acquisition EKAW '92. Proceedings. XIII, 444 pages. 1992. (Subseries LNAI).

Vol. 616: K. Jensen (Ed.), Application and Theory of Petri Nets 1992. Proceedings, 1992. VIII, 398 pages. 1992.

Vol. 600: J. W. de Bakker, C. Huizing, W. P. de Roever, G. Rozenberg (Eds.), Real Time: Theory in Practice. Proceedings, 1991. VIII, 723 pages. 1992.

Vol. 617: V. MaHk, O. St6pfinkovfi, R. Trappl (Eds.), Advanced Topics in Artificial Intelligence. Proceedings, 1992. IX, 484 pages. 1992. (Subseries LNAI). Vol. 618: P. M. D. Gray, R. J. Lucas (Eds.), Advanced Database Systems. Proceedings, 1992. X, 260 pages. 1992.

Vol. 601: D. Dolev, Z. Galil, M. Rodeh (Eds.L Theory of Computing and Systems. Proceedings, 1992. VIII, 220 pages. 1992.

Vol. 619: D. Pearce, H. Wansing (Eds.), Nonclassical Logics and Information Proceedings. Proceedings, 1990. VII, 171 pages. 1992. (Subseries LNAI).

Vol. 602: 1. Tomek (Ed.), Computer Assisted Learning. Pro ceedings, 1992. X, 615 pages. 1992.

Vol. 620: A. Nerode, M. Taitslin (Eds.), Logical Foundations of Computer Science Tver '92. Proceedings. IX, 514 pages. 1992.

Vol. 603: J. van Katwijk (Ed.), Ada: Moving Towards 20(10. Proceedings, 1992. Vlll, 324 pages. 1992. Vol. 604: F. Belli, F.-J. Radermacher (Eds.), Industrial and Engineering Applications of Artificial Intelligence and Expert Systems. Proceedings, 1992. XV, 702 pages. 1992. (Subseries LNAI). Vol. 605: D. Etiemble, J.-C. Syre (Eds.), PARLE '92. Parallel Architectures and Languages Europe. Proceedings, 1992. XVII, 984 pages. 1992.

Voh 621: O. Nurmi, E. Ukkonen (Eds.), Algorithm Theory SWAT '92. Proceedings. VIII, 434 pages. 1992. Vol. 622: F. SchmalhotEr, G. Strube, Th. Wetter (Eds.), Contemporary Knowledge Engineering and Cognition. Pro ceedings, 1991. XII, 258 pages. 1992. (Subseries LNAI). Vol. 623: W. Kuich (Ed.), Automata, Language,; and Pro gramming. Proceedings, 1992. XII, 721 pages. 1992.

Vol. 606: D. E. Knuth, Axioms and Hulls. IX, 109 pages. 1992.

Voh 624: A. Voronkov (Ed.), Logic Programming and Au tomated Reasoning. Proceedings, 1992. X[V, 509 pages. 1992. (Subseries LNAI).

Vol. 607: D. Kapur (Ed.), Automated Deduction CADE11. Proceedings, 1992. XV, 793 pages. 1992. (Subseries LNAI).

Voh 625: W. Vogler, Modular Construction and Partial Order Semantics of Petri Nets. IX, 252 pages. 1992.

Voh 608: C. Frasson, G. Gauthier, G. 1. McCalla (Eds.), Intelligent Tutoring Systems. Proceedings, 1992. XIV, 686 pages. 1992. Vol. 6(19: G. Rozenberg (Ed.), Advances in Petri Nets 1992. VIII, 472 pages. 1992.

Vol. 626: E. BOrger, G. JSger, H. Kleine Brining, M. M . Richter (Eds.), Computer Science Logic. Proceedings, 1991. VIII, 428 pages. 1992. Vol. 628: G. Vosselman, Relational Matching. IX, 190 pages. 1992.

Vol. 610: F. von Martial, Coordinating Plans of Autonomous Agents. XII, 246 pages. 1992. (Subseries LNAI).

Vol. 629: I. M. Havel, V. Koubek (Eds.), Mathematical Foundations of Computer Science 1992. Proceedings. IX, 521 pages. 1992.

Vol. 611 : M. P. Papazoglou, J. Zeleznikow (Eds.), The Next Generation of Information Systems: From Data to Knowledge. VIII, 310 pages. 1992. (Subseries LNAI).

Voh 630: W. R. Cleaveland (Ed.), CONCUR '92. Proceed ings. X, 580 pages. 1992.

Vol. 612: M. Tokoro, O. Niers'trasz, P. Wegner (Eds.), Object-Based Concurrent Computing. Proceedings, 1991. X, 265 pages. 1992.

Vol. 631: M. Bruynooghe, M. Wirsing (Eds.), Programming Language Implementation and Logic Programming. Proceedings, 1992. XI, 492 pages. 1992.

Vol. 632: H. Kirchner, G. Levi (Eds.), Algebraic and Logic Programming. Proceedings, 1992. IX, 457 pages. 1992. Vol. 633: D. Pearce, G. Wagner (Eds.), Logics in A|. Proceedings. VIII, 410 pages. 1992. (Subseries LNAI). Vol. 634: L. Bough, M. Cosnard, Y. Robert, D. Trystram (Eds.), Parallel Processing: CONPAR 92 - VAPP V. Proceedings. XVII, 853 pages. 1992. Vol. 635: J. C. Derniame (Ed.), Software Process Technology. Proceedings, 1992. VIII, 253 pages. 1992. Vol. 636: G. Comyn, N. E. Fuchs, M. J. Ratcliffe (Eds.), Logic Programming in Action. Proceedings, 1992. X, 324 pages. 1992. (Subseries LNAI).

Vol. 656: M. Rusinowitch, J. L. R6my (Eds.), Conditional Term Rewriting Systems. Proceedings, 1992. XI, 501 pages. 1993. Vol. 657: E. W. Mayr (Ed.), Graph-Theoretic Concepts in Computer Science. Proceedings, 199Z VIII, 350 pages. 1993. Vol. 658: R. A. Rueppel (Ed.), Advances in Cryptology EUROCRYPT '92. Proceedings, 1992. X,493 pages. 1993. Vol. 659: G. Brewka, K. P. Jantke, P. H. Schmitt (Eds.), Nonmonotonic and Inductive Logic. Proceedings, 1991. VIII, 332 pages. 1993. (Subseries LNAI).

Vol. 637: Y. Bekkers, J. Cohen (Eds.). Memory Management. Proceedings, 1992. Xl, 525 pages. 1992.

Vol. 660: E. Lamma, P. Mello (Eds.), Extensions of Logic Programming. Proceedings, 1992. VIII, 417 pages. 1993. (Subseries LNA1).

Vol. 639: A. U. Frank, 1. Campari, U. Formentini (Eds.), Theories and Methods of Spatio-Temporal Reasoning in Geographic Space. Proceedings, 1992. XI,431 pages. 1992.

Voh 661: S. J. Hanson, W. Remmele, R. L. Rivest (Eds.), Machine Learning: From Theory to Applications. VIll, 271 pages. 1993.

Voh 640: C. Sledge (Ed.), Software Engineering Education. Proceedings, 1992. X, 451 pages. 1992.

Vol. 662: M. Nitzberg, D. Mumford, T. Shiota, Filtering, Segmentation and Depth. VIII, 143 pages. 1993.

Vol. 641 : U. Kastens, P. Pfahler (Eds.), Compiler Construction. Proceedings, 1992. VIII, 320 pages. 1992.

Vol. 663: G. v. Bochmann, D. K. Probst (Eds.), Computer Aided Verification. Proceedings, 1992. IX, 422 pages. 1993.

Vol. 642: K. P. Jantke (Ed.), Analogical and Inductive Inference. Proceedings, 1992. VIII, 319 pages. 1992. (Subseries LNAI). Vol. 643: A. Habel, Hyperedge Replacement: Grammars and Languages. X, 214 pages. 1992. Vol. 644: A. Apostolico, M. Crochemore, Z. Galil, U. Manber (Eds.), Combinatorial Pattern Matching. Proceedings, 1992. X, 287 pages. 1992. Vol. 645: G. Pernul, A M. Tjoa (Eds.), Entity-Relationship Approach - ER '92. Proceedings, 1992. Xl, 439 pages, 1992. Vol. 646: J. Biskup, R. Hull (Eds.), Database Theory ICDT '92. Proceedings, 1992. IX, 449 pages. 1992. Vol. 647: A. Segall, S. Zaks (Eds.), Distributed Algorithms. X, 380 pages. 1992. Vol. 648: Y. Deswarte, G. Eizenberg, J.-J. Quisquater (Eds.), Computer Security - ESORICS 92. Proceedings. XI, 451 pages. 1992.

Vol. 664: M. Bezem, J. F. Groote (Eds.), Typed Lambda Calculi and Applications. Proceedings, 1993. VII1, 433 pages. 1993. Voh 665: P. Enjalbert, A. Finkel, K. W. Wagner (Eds.), STACS 93. Proceedings, 1993. XIV, 724 pages. 1993. Vol. 666: J. W. de Bakker, W.-P. de Roever, G. Rozenberg (Eds.), Semantics: Foundations and Applications. Proceedings, 1992. VII1, 659 pages. 1993. Vol. 667: P. B. Brazdil (Ed.), Machine Learning: ECML 93. Proceedings, 1993. XII, 471 pages. 1993. (Subseries LNAI). Vol. 668: M.-C. Gaudel, J.-P. Jouannaud (Eds.), TAPSOFT '93: Theory and Practice of Software Development. Proceedings, 1993. XII, 762 pages. 1993. VoL 669: R. S. Bird, C. C. Morgan, J. C. P. Woodcock (Eds.), Mathematics of Program Construction. Proceedings, 1992. VIII, 378 pages. 1993.

Vol. 649: A. Pettorossi (Ed.), Meta-Programming in Logic. Proceedings, 1992. Xll, 535 pages. 1992.

Vol. 670: J. C. P. Woodcock, P. G. Larsen (Eds.), FME "93: Industrial-Strength Formal Methods. Proceedings, 1993. XI, 689 pages. 1993.

Vol. 650: T. lbaraki, Y. lnagaki, K. lwama, T. Nishizeki, M. Yamashita (Eds.), Algorithms and Computation. Proceedings, 1992. XI, 510 pages. 1992.

Vol. 671: H. J. Ohlbach (Ed.), GWAI-92: Advances in Artificial Intelligence. Proceedings, 1992. XI, 397 pages. 1993. (Subseries LNAI).

Vol. 651: R. Koymans, Specifying Message Passing and Time-Critical Systems with Temporal Logic. IX, 164 pages. 1992.

Vol. 672: A. Barak, S. Guday, R. G. Wheeler, The MOS1X Distributed Operating System. X, 221 pages. 1993.

Vol. 652: R. Shyamasundar (Ed.), Foundations of Software Technology and Theoretical Computer Science. Proceedings, 1992. XIII, 405 pages. 1992. Vol. 653: A. Bensoussan, J.-P. Verjus (Eds.), Future Tendencies in Computer Science, Control and Applied Mathematics. Proceedings, 1992. XV, 371 pages. 1992. 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.

Vol. 673: G. Cohen, T. Mora, O. Moreno (Eds.), AAECC10: Applied, Algebra, Algebraic Algorithms and ErrorCorrecting Codes. Proceedings, 1993. X, 355 pages 1993. Vol. 674: G. Rozenberg (Ed.), Advances in Petri Nets 1993. VII, 457 pages. 1993. Vo[. 675: A. Mulkers, Live Data Structures in Logic Programs. VIII, 220 pages. 1993.

D. Gries

J. Stoer

675

Anne Mulkers

Live Data Structures in Logic Programs Derivation by Means of Abstract Interpretation

Springer-Verlag Berlin Heidelberg NewYork London Paris Tokyo Hong Kong Barcelona Budapest

Series Editors Gerhard Goos Universit~it Karlsruhe Postfach 69 80 Vincenz-Priessnitz-Stra6e 1 W-7500 Karlsruhe, FRG

Juris Hartmanis Cornell University Department of Computer Science 4130 Upson Hall Ithaca, NY 14853, USA

Author Anne Mulkers Department of Computer Science, K.U. Leuven Celestijnenlaan 200 A, B-3001 Heverlee, Belgium

CR Subject Classification (1991): F.3.1, D.3.4, 1.2.2-3

ISBN 3-540-56694-5 Springer-Verlag Berlin Heidelberg New York ISBN 0-387-56694-5 Springer-Verlag New York Berlin Heidelberg

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 Abstract interpretation is a general approach for program analysis to discover at compile time properties of the run-time behavior of programs, as a basis to perform sophisticated compiler optimizations. Several frameworks of abstract interpretation for logic programs have been presented [11, 25, 27, 43, 48, 49, 51, 55, 57, 65, 81, 82]. A framework is a parameterized construction for the static analysis of programs, together with theorems that ensure the soundness and termination of the analysis. To complete the construction, an application specific domain and primitive operations satisfying certain safety conditions must be provided. This book elaborates on an application for such a generic framework. The framework used [11] belongs to the class of top-down abstract interpretation methods and collects the information derived in an abstract AND-OR-graph that represents the set of concrete proof trees that can possibly occur when executing the source program. The starting point of the present work is the previously developed application of integrated type and mode analysis [38]. The purpose of that application was to guide the compiler, based on a characterization of the entry uses of the program, to generate code that is more specific for the calls that can occur at run time. In an attempt to give further guidance to the compiler, we address the problem of compile-time garbage collection, the purpose of which is to (partially) shift run-time storage reclamation overhead to compile time. In applicative programming languages, the programmer has no direct control over storage utilization, and run-time garbage collection is necessary. Garbage collection involves a periodic disruption of the program execution, during which usually a marking and compaction algorithm is employed. Such schemes are expensive in time. Our research shows that at compile time useful and detailed information about the liveness of term substructures can be deduced which the compiler can use to improve the allocation of run-time structures. In fact, it provides a technique to automatically introduce destructive assignments into logic languages in a safe and transparent way, thereby reducing the rate at which garbage cells are created. The resulting system gets near to the methods of storage allocation used in imperative programming languages. The global flow analysis to be performed on Prolog source programs in order to derive the liveness of data structures is constructed in three layers. The

vI 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.

Leuven, March 1993

Anne Mulkers

Contents Introduction

1

Abstract Interpretation Basic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . Abstract Interpretation Framework . . . . . . . . . . . . . . . Overview of the F r a m e w o r k . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . Primitive Operations ....................... A b s t r a c t I n t e r p r e t a t i o n Procedure . . . . . . . . . . . . . . . . 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 . . . . . . . . . Rigid a n d I n t e g r a t e d T y p e G r a p h s . . . . . . . . . . . . . . . . Type-graph Environments ..................... 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 . . . . . .

5 5 7 8 10 11 14 16 16 23 25

Related Work 3.1 Aliasing a n d P o i n t e r A n a l y s i s . . . . . . . . . . . . . . . . . . . 3.2 Reference C o u n t i n g a n d Liveness Analysis . . . . . . . . . . . . 3.3 Code O p t i m i z a t i o n . . . . . . . . . . . . . . . . . . . . . . . . .

31 31 38 41

2.1 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.3 2.3.1 2.3.2 2.3.3

3

4

Sharing Analysis 4.1 4.1.1 4.1.2 4.1.3 4.1.4 4.2 4.2.1 4.2.1.1 4.2.1.2 4.2.2 4.2.3 4.3 4.3.1 4.3.2

Sharing Environments . . . . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . . . 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 . . . . . . . . . . T h e Concrete a n d A b s t r a c t D o m a i n s . . . . . . . . . . . . . . . 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 . . . . . . . . . . . Primitive Operations ....................... Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Xi :

Xj

Xi :

f(Xil,...,Xij)

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Procedure E n t r y . . Procedure Exit . . . Evaluation . . . . . Example: i n s e r t / 3 Relevance of S h a r i n g

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ........................ Edges . . . . . . . . . . . . . . . . . . . .

47 47 48 55 62 66 68 68 69 85 93 98 110 111 114

v lll

CONTENTS

4.3.3 4.3.4

6

Imprecision in the S h a r i n g Analysis . . . . . . . . . . . . . . . Efficiency of the S h a r i n g Analysis . . . . . . . . . . . . . . . .

117 123

LivenessAnalysis 5.1 Liveness E n v i r o n m e n t s . . . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . 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 . . . . . . . . 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 . . . . . . . . . . . . . . . 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 . . . . . . . . . . . 5.2 Primitive Operations . . . . . . . . . . . . . . . . . . . . . . . Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 5.2.1.1 Xi = X j . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1.2 X i : f ( X i , , . . . , X i j ) . . . . . . . . . . . . . . . . . . . . . . 5.2.2 Procedure E n t r y . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.3 Procedure Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Example: q s o r t / 3 . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 Precision of the Liveness Analysis . . . . . . . . . . . . . . . . 5.3.3 T h e Practical Usefulness of Liveness I n f o r m a t i o n . . . . . . . .

127 127 128 133 141 145 147 147 147 153 154 163 165 165 168 171

. . . . .

. .

Conclusion

179

Appendix; Detailed Examples A.1 List of T y p e s . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2 append/3 ......................... ..... A.3 nrev/2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.4 buildtree/2 and insert/3 . . . . . . . . . . . . . . . . . . . . . . A.5 p e r m u t a t i o n / 2 a n d select/3 . . . . . . . . . . . . . . . . . . . . A.6 split/3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.7 qsort/2 and partition/4 . . . . . . . . . . . . . . . . . . . . . . A.8 s a m e l e a v e s / 2 a n d profile/2 . . . . . . . . . . . . . . . . . . . . A.9 sift/2 a n d r e m o v e / 3 . . . . . . . . . . . . . . . . . . . . . . . .

183 184 185 188 193 196 199 202 205 209

Bibliography

213

Chapter 1

Introduction In conventional languages, such as C or Pascal, the p r o g r a m m e r explicitly controls the utilization of m e m o r y by means of declarations and destructive assignments. For example, when reversing a linear list L, the list cells of the original list can be reused to construct the reversed list in the case that the original list is no longer needed for further computations. It is up to the p r o g r a m m e r to decide whether he needs to preserve the old list intact and construct a reversed list which has only the list elernertLs in c o m m o n with the list L (e.g. Rev_L1 in Figure 1.1), rather than reuse the list-constructor cells of L as well (e.g. Rev_L2).

_1

-,

, I

I

,

-J

-,

,

I

I

,

_1

-~

,

I

i

[

Rev_L2

/

\"

/

\"

-,

RevL 1 /

J e

Figure 1.1: Reversing a linear list. Applicative languages, in their pure form, do not have destructive assignments. Also type declarations are often absent. The declarative nature of these languages is often cited as an i m p o r t a n t advantage, which allows p r o g r a m m e r s to focus on the logic of the problems they have to solve, rather than on more technical aspects such as search control and efficient m e m o r y usage. Unfortunately, the performance of current implementations of applicative languages does not compare well with procedural languages yet. To achieve better utilization of memory, global flow analysis techniques are being developed that are concerned with determining the type and liveness of d a t a structures that are dynamically

2

C H A P T E R 1. I N T R O D U C T I O N

append (nil, _Y, _Y). append([_E I _UJ,_Y,[_E I _W]) :- append(_U,_Y,_W). nrev(nil, nil). nrev([_E I _U], _Y) :-nrev(_U, _KU), append(_KU, [~], ~). P r o g r a m 1.h n r e v / 2 (Naive reverse)

created during program execution. Knowledge about the lifetime of d a t a structures guides the compiler in the generation of target code to reuse heap storage t h a t is no longer accessible from program variables, i.e. to introduce destructive operations and avoid the copying of data structures that have no subsequent references. In this book, we address the problem of liveness analysis for the class of pure Horn clause logic programs. The language considered has a countable set of variables (Vats), and countable sets of function and predicate symbols. A term is a variable, a constant, or a compound term f ( Q , . . . , t , ~ ) where f is a n-ary function symbol and the t~ are terms. An atom has the form p ( Q , . . . , tin) where p is a m - a r y predicate symbol and the ti are terms. A body is a (possibly empty) finite conjunction of atoms, written A1,..., A,~. A clause consists of an a t o m (its head) and a body and is written A : - B. A program consists of a finite number of clauses. A query or goalconsists of a body only, written 7- B. We assume that the reader is acquainted with the basic terminology of logic programming and the execution mechanism of Prolog which is based on unification and backtracking. Features such as assert and retract are not considered, i.e. we assume that any source code for the predicates that can be executed at run time is available to the compiler. The handling of d a t a structures is very flexible in Prolog. D a t a manipulation (record allocation as well as record access and parameter passing) is achieved entirely via unification. An optimizing compiler can translate general unification to more conventional m e m o r y manipulation operations if information is available about the mode of use of the predicates. When at run time a compound term becomes accessible for the first time, we can say the term is being constructed. When a pattern is matched against a compound t e r m that is already accessible, we can say the components of the term are being selected. Integrated type and mode analysis in m a n y cases allows to predict at compile time whether a unification is a selection rather than a construction operation. Selection statements in particular are good candidates to check for the possible creation of garbage cells, i.e. cells that have no further references. Consider the Prolog Program 1.1 for naive list reversal. We use the convention that variable names start with an underscore. If we assume that queries to n r e v / 2 are restricted to have as first argument a list that is no longer referenced after the call, and as second argument a free variable to return the output, then it is possible to generate target code for this program that allocates no new

list-constructor cells, but rather reuses the list cells of the first argument. Indeed, under the assumption, the integrated type and mode analysis will infer t h a t each call to the recursive clause of n r e v / 2 has as its first argument a list, and as second argument a free variable. The unification of the call with the clause head selects the head and tail of the first argument list. The principal list-constructor cell of this list on the contrary has no subsequent references in the clause following the unification of the call with the clause head. This means that the compiler can recognize the principal list cell as garbage and generate target code t h a t reuses it. For instance, consider the caI1 to a p p e n d / 3 m a d e by the same clause. A single element list [_E] needs to be constructed. Instead of allocating a new cell, the compiler can reuse the garbage cell that was detected. Note that the problem is more complex if there may be multiple references to the cells of the input list. Most implementations of unification unify a variable and a compound structure by making the variable a reference to the structure not a copy of the structure. The representations in m e m o r y of the logical terms to which variables can be bound typically share some of their structure: while the denoted terms make up a forest of trees, their representations form a more general directed acyclic graph. This is why in general the sharing analysis plays a crucial part in the liveness analysis. In the above example, we can also infer that, the first two arguments in each call to a p p e n d / 3 will be lists and that the third argument will be a free variable. Again, it is possible to detect that, after invocation of the recursive clause of append/3, the principal cell of the first argument is garbage and can be reused to construct the value of the third (output) argument. Thus, all list constructions in this example can reuse garbage list ceils, eliminating all allocation operations. Since the reused cells would otherwise be garbage, we have eliminated the garbage-collection overhead associated with the n r e v / 2 procedure. Moreover, a compiler can detect that the element field of each reused list cell already contains the value desired in the cells new use. The operations filling in these car fields can be eliminated from the generated target code. The resulting code closely resembles how a p r o g r a m m e r using an imperative language would solve the problem of reversing a linear list of linked records. In the present work, we propose an abstract domain and operations to analyze the liveness of d a t a structures within a framework of abstract interpretation. Chapter 2 presents the principles of abstract interpretation for logic programs, and the application of type and mode analysis on which the domain for liveness analysis is based. In Chapter 3, we discuss work related to the application of compile-time garbage collection in the context of both logic and functional prog r a m m i n g languages. In Chapter 4, we formalize an abstract interpretation for analyzing how the terms to which program variables are bound at run time, can share substructure in storage. We also augment the usual concrete semantics with information about sharing of t e r m structures and discuss whether any implementation c o m m i t m e n t s are implied. As argued above, the sharing analysis constitutes a prerequisite for the liveness analysis. The latter is presented in Chapter 5. In both Chapter 4 and 5, the emphasis is mainly on the precision

4

C H A P T E R 1. I N T R O D U C T I O N

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 analysis 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 verification 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 domains and operations supplied to complete the construction obey certain safety requirements. The standard theory of lattices provides the conceptual framework for program 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 existence of a solution for the fixpoint problem posed by the abstract semantics of the program. Let (C, E) be the concrete domain, and (A, _ _F, _NR).

Program 4.h i n s e r t / 3

(R1, Sn) I 3(R1, E1)...(P.r,, Sn) a finite sequence over B U C such that (Vg 6 IN : 1 < t < n =~ & = Rt+I) and [Vk6]PC: ( ( l < 2 k _ < n ~ ( R 2 k , Sak) E B ) & (1 < 2 k + l < n ==~(R2t+I, Saj,+I) 6 C ) ) v

((t < 2k

3 , _NT = t(_.L, _F, ~ R ) , insert(_E, _~, _NR).

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. Runtime 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 transforms 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 inserts 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 Section 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.

append( append(

_3(,

_u

:- _X = nil, _Y = _Z . :- _X = [_El_u], _Z = [_El_W], append(_U, _Y, _W)

_7, )

_X, _Y, _7, )

Consider the initial abstract liveness environment ~i,~ =

(T~,~", ASharingT,~.,

A Live~r~,,), where ~,~ List

::= ::= = ----

ASharing,z~ ALive~r,~

(V, V, List), nil l ' . ' ( I n t , List),

(@,@>, {T~,~[(1,V)], T~,,[(2, V>]}.

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 procedure, is

To,,t AShr~-o,,,

::= = =

(List, List, List), (@, AShr.-~o,,,>, ( ( To,,t[(2,'.')], To,,t[(3,'.'>]) },

ALive~-o.,

=

{ To,,t[(1, '.')], To~,t[(2, '.'>] }.

ASharing.2-o,,,

The sharing between the second and the third argument after the call, obviously results from the unification _u = _Z in the first program clause. In the recursive clause, the unification _X = [_El_U] is now a construction operation, while the unification _Z = [_El _W] is a selection operation. If we order the variables of the clause according to the tuple (_X, _Y, 7. _E, _U, _W>,then the environment derived by the analysis for the program point just before the recursive call, is given by ~s = (T:, ASharing:Ts, ALive:rs),

'7-,, ASharingT,

::= =

('.'(Int,V), V, ListOne, Int, V, List >, (0, AShr~- ),

AShr~,

=

{ ( T,[] ) },

{ T,[(2, V>], T,[] }.

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

Tin

Ts ~ nil

Int ~'~

v

.

Int V Or~

hit ~V-'--""--'~t~n~l~.~ii,,,sni~/~ 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 environments 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 operation 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, consider 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

::= ::=

(List, V), 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 { 7"1[{1, V)],T1[{3, V)] }.

ALive~rl

For program point (~) (resp. ( ~ ) ) in the definition of s e l e c t / 3 , it is detected that the top-list cell of the input argument _Yl becomes garbage. In the second

5.3.

177

EVALUATION

permutation( _XI, _YI ) :_XI = nil, _YI : nil. permutation( _XI, _ZI ) :select(

7., _X1, _Ys ),

select(

_YI = [ _X i -ZZ ] select( _X, _Y1, 7.1 ) :-

V, V, V, List, List, V) ([(_X,./2)], [(_Y,./2)])} [(I,V)]} Int, List, _, List0ne .... ) ([(I,./2),(2,./2)], [(_U,./2)])} [(I,./2)], [(_U,./2)], [(_E,Int)]} Int, List, V, List0ne, List, V) ([(_X, ./2)] , ['(_Y,./2)]), ([(_X,./2),(2,.12)],

[(_Y, . / 2 ) ] ) }

AShr,~-

{ ( [ ( I , . / 2 ) , ( 2 , . / 2 ) ] , [(_U,./2)])} { [(2Z,V)] } ( Int, List, V, List0ne, List, .(Int,V)> { ([(_X, ./2)] , [(_Y,./2)]), ( [ ( I , . / 2 ) , ( 2 , . / 2 ) ] , [(_Y, ./2)1)]. { ([(_X,./2),(2,./2)], [(_U,./2)]),

ALive:r

{ [(_Z, ./2)] }

AShr~ ALivey T AShr~

([(_Z, . / 2 ) , ( 2 , V ) ] ,

f17

[(_W,V)])}

T

( Int, List, List, List0ne, List, List0ne)

AShr~

{ ([(_X,./2)], [(_Y,./2)]), ( [ ( I , . / 2 ) , ( 2 , . / 2 ) ] , [(_Y, . / 2 ) ] ) } { ([(_Y,./2)], [ ( 1 , . / 2 ) , ( 2 , . / 2 ) ] ) , ([(_Y,./2)], [(_W,./2)]), ([(_X,./2),(2,./2)], [(_U,./2)]), ([(_Z, ./2), (2, . / 2 ) ] , [(_W, ./2)])} { [(_Z, . / 2 ) ] }

AShr~ ALiveT

The following table illustrates how the sharing and liveness analyses perform when one uses the invertibility property of Prolog. The table shows the abstract 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. However, 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, _.B, _C) ~

~o3

T

( V , V, List>

ALiveT 7AShr~ ALive,]-

{ [(..B,V)], [(_A,V)]} (List, List, List> { ([(_B,. 12)3, [(_C,. 12)3 )} { [(_B, ./2)] , [(_A,./2)]}

188

APPENDIX A. DETAILED EXAMPLES

1)

~2 ~3 2) f14

&

(_X, _Y, _Z) T

ALive,:r T ALiveT T AShr~ ALive~r

{ [(_Y,V)], [(1,v)]}

T ALive~r T AShr,~

ALive,r T AShr~ ALive~r T AShr~ALive1 T AShr~-

ALive~r

A.3

( nil, V, List> { [(_Y,V)], [(/,nil)]} (nil, List, List> { ([(_Y,.12)], [(_z, .12)] )} { [(_Y, .12)] , [(_X,nil)]} ( ~ , _u, _w, _x, _Y. _z> ( V, V, V, V, V, L i s t )

{ [(_Y,V)], [(_x,v)]} ( V, V, V, .(V,V), V, List) { ([([..X, . / 2 ) , (1,V)], ['(..E,V)]),

( [(_x,./2), (2 ,v)], [(_u, v) ] )} { [(_Y,V)], [ ( . / , . / 2 ) ] } ( Int, _, List, _, _, ListOne> { ([(_w,./2)], [ ( _ z , . 1 2 ) , ( 2 , . / 2 ) 3 ) } { [(_Z,.12),(2,.12)], [(_W, .12)] , [(_E,Int)]} ( Int, V, List, . ( I n t , V ) , V, ListOne) { ( [(_X,./2), ( I , I n t ) ] , [(_E, I n t ) ] ), ( [(_x, ./2), (2,v)], [(_u,v)] ), ( [(_w,. 12)3, [(_z,. 12), (2,./2)3 )} { [(_Y,V)], [ ( _ X , . / 2 ) ] }

( Int, List, List, ListOne, List, ListOne) { ( [ ( _ Z , . / 2 ) , ( 2 , . / 2 ) ] , [(_Y,./2)]), ([(_X,./2), ( l , I n t ) ] , [(_E,Int)]), ( [(_X,./2), ( 2 , . / 2 ) ] , [(._u,./2)] ), ( [(_w, ./2)] , [(_.Z, ./2) , ( 2 , . / 2 ) ] ), ([(_Y, . / 2 ) ] , [(_W, . / 2 ) ] ) } { [(_Y,./2)], [(_x,./2)]}

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:

(1) ~ , v (

_x, _Y) .~1 -X = n i l , ~2 -Y : nil.

#3

(2) nrev( _X, _Y) ",04 -X = [ -E [ -U ],

,~4

A.3. NREV/2

189

;35 nrev( _U, _RU), ;36 _Last = [_El, ;37 append( _.RU, _Last, _Y). ;3s

Note that the selection _X = [ _E I _U ], and the construction operation _Last = [_El of the second clause, do not occur in the same chunk (Section 3.3). Moreover, the variable _X in the recursive clause is a temporary variable because it only occurs in the head and the first call. If it is made a permanent variable by the compiler, its value will be saved on the local stack, and the garbage cell it is pointing to at program point ;36, will be accessible for reuse in the construction

_Last = [_El. Call:

;3~ nrev( _X, _Y ) ( _X, _u

;37

T AShr,} ALive~r

( VLisZ, V> { ([(D[,./2),(1,V)], { [(_Y,V)] }

[(_X,./2),(1,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

T AShr,~ ALive~r T AShr,~ AShr,~ALiveT

1) A

T AShr,~ ALive~r T ALive~r

)

( _X, _Y> < VList, V> { ([(~,./2),(1,v)], [(~x,./2),(1,v)])} { [ ( _ X , . / 2 ) , ( 1 , V ) ] , [(_Y,V)]} ( V L i s t , VList>

{ ([(_x,.12),(i,v)], [(_x, .12) , (1,v)] )} { ([(I,./2),(1,V)], [(_Y,./2),(1,V)])} { [(_X,./2),(l,V)], [(_Y,.12)]} < _X, _Y ) < vni t, v>

{ ([(_~,./2),(1,v)], { [(_X,./2),(1,V)], ( n i l , nil)

{ [(_Y,nil)] }

[(~,./2),(1,v)])} [(_Y,V)]}

190

A P P E N D I X A. DETAILED E X A M P L E S

2) T AShr~ALive~r T AShr~-

(_E, last, _KU, _U, I, _Y)

{ ([(_x,.12),Cl,V)], [(_x,.12),(1,v)])} { [(_x,.12),(1,v)], [(_Y,v)]} ( V ..... VList, VList0ne, _)

{ ([(_x,.12),(1,v)], [(_x,.12),(1,v)]), ([(_x, ./2), (2, ./2), (1,v)], [(_x, ./2), (1,v)]), ([(I,.12),(2,.12),(1,v)3, [(I,.12),(2,.12),(1,v)I)}

AShr~

{ ([(_x,.12),C1,v)], [(_E,V)]), ([(_U, ./2)], [(_X,./2),(2,./2)])} ALive:T { [(_x,.12),(2,.12)], [(_u, .12)] , [(_E,V)], [(_X, . 1 2 ) , ( I , V ) ] } T (V, V, V, VList, VList0ne, V) AShr~- { ([(_x,.12),(1,v)], [(_x,.12),(1,v)]),

([(_x, .12), (2, .12), (1,v)], [(_x, .12), (1,v)]), ( [(_x,./2), (2,./2), (1,v)], [(_X, ./2), (2, ./2), (1 ,V)] )}

AShr~ ALive~ T AShr~-

AShr~-

ALiveT

{ ([(_x,.12),(1,v)], [(_E,V)]), ([(_U, ./2)], [(_X,./2),(2,./2)])} { [(_X,./2),(2,./2),(1,V)], [(_X,./2),(1,V)], [(_Y,V)]} ( V, V, VList, VList, VList0ne, V)

{ ([(_x,.12),(1,v)], [(_x,.12),(1,v)]), ([(_X, ./2), (2, ./2), (1,V)], [(_X, ./2), (1,V)]), ( [(_x, ./2), (2, ./2), (1,v)], [(_X,./2),C2,./2),Cl,V)])} { ([(_X,./2),(2,./2),(I,V)], [(_~U,./2),(I,V)]), ( [(_x, ./2), (1,v)], [(_E,V)]), ([(_U, ./2)], [(_X, ./2), (2, ./2)]), ([(_U, ./2), (I,V)], [(_3.U,./2), (1,V)])} { [(_X,./2),C2,./2),(1,V)], [ ( I , . / 2 ) , ( 1 , V ) ] ,

[(_Y,v)]}

#7

T AShr~-

( V, .(V,nil), VList, VList, VList0ne, V)

{ ([(_x,.12),(1,v)],

[(_x,.12),(1,v)]), [ ( I , ./2), (1,V)]), ( [C-X, ./2), (2, ./2), (1,V)], [(_X,./2),(2,./2),(1,V)])} ([(I,./2),(2,./2),(1,V)],

AShr~

ALiveT

{ ([(_X,./2),(I,V)], [(last,./2),(l,V)]), ([(_X,./2),(2,./2),(1,V)], [(_~U,./2),(1,V)]), ([(_x, ./2), (1,v)], [(_E,V)]), ([(_U, ./2)], [(_X, ./2), (2, ./2)]), ([(_u, . / 2 ) , ( 1 , v ) ] , [(_Ru, . / 2 ) , ( 1 , v ) ] ) , ( [(_Last; . / 2 ) , (1,V)], [(_E,V)] )} { [(./,.12),(2,./2),(1,V)], [(_X,./2),(I,V)], [(_Y,V)] }

A.3. NREV/2

191

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:

~ /~4

/~ append( ..RU, l a s t ,

(

7AShr~ ALiveT T AShr~r

last, _RU,

â€¢

)

_Y)

( . ( v , ~ i l ) , ~ i l , v> { ([(_Last,./2),(l,V)], [(_Last,./2),(l,V)])} { [(_Last,./2),(l,V)], [(_Y,V)]} ( . ( V , n i l ) , n i l l . ( V , n i l ) , V) { ([(_RU,./2),(I,V)], [(last,./2),(l,V)]), ([(.Last,./2),(1,V)], [(Iast,./2),(1,V)]), ([(..I{U,./2),(1,V)], [(_RU,./2),(1,V)])}

ALiveT fl# T AShr~-

{ [(last,./2),(l,V)], [(_RU,./2),(I,V)], [(_Y,V)]} (.(V,nil), nill.(V,nill.(V,nil)), V) { ([(..RU,./2),(1,V)],

[(iast,./2),(i,V)]),

( [(_RU,./2), (2,./2), (I ,V)], [(_Last,./2), (I ,V)] ), ([(_Last,./2),(1,V)],

[(Iast,./2),(1,V)]),

([(_KU,./2),(2,./2),(1,V)], ( [(_RU, . / 2 ) , ( 1 , V ) ] ,

[(_R,U,./2),(1,V)]),

[(..RU, . / 2 ) , ( 1 , V ) ] ) ,

( [ ( ~ U , . / 2 ) , ( 2 , ./2), ( l , V ) ] , [(~U,./2),(2,./2),(1,V)])} ALive=r T AShr~ ALive:,-

{ [(_Last,./2),(1,V)], [(_RU,./2),(2,./2),(t,V)], [(_RU, . / 2 ) , ( 1 , V ) ] , [(_Y,V)] }

(.(v,niz),

Vnist, V)

{ ([(_.KU,./2),(1,V)], [(last,./2),(1,V)]), ([(last, ./2), (1,V)], [(iast, ./2), (1,V)]), ([(_.RU,./2), ( 1 , V ) ] , [(_KU, . / 2 ) , ( 1 , V ) ] ) }

{ [(_Last,./2),(l,V)], [(_RU,./2),(I,V)], [(_Y,V)]}

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 Call:

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

~/ append( _RU, l a s t ,

_Y )

( last, _RU, _Y)

( . ( V , n i l ) , VList, V) { ([(_Ru,./2),Cl,V)], [ ( i a s t , . / 2 ) , C l , v ) ] ) , ( [ ( l a s t , ./2), (1,V)], [ ( l a s t , ./2), (1,V)]), ([(_RU,./2),(1,V)], [(_RU,./2),C1,V)])} ALiveT { [ ( i a s t , . / 2 ) , ( 1 , V ) ] , [(_RU,./2),(1,V)], [(_Y,V)]} ( . ( V , n i l ) , VList, VListOne) T AShr~- { ( [ ( l a s t , . / 2 ) , ( 1 , V ) ] , [ ( l a s t , . / 2 ) , ( 1 , V ) ] ) , ([(_RU, ./2), (1,V)], [ ( l a s t , ./2), (1,V)]), ( [(_~u, ./2), (1,v)], [(_Ru, ./2), (1,v)])} AShr,~ { ([(_Y,./2),(2,./2)], [ ( l a s t , . / 2 ) ] ) , ( [(_Y, ./2)], [(last, ./2)]), ([(_~u, ./2), (1,v)], [(_Y, ./2),(1,v)]), ([(_RU, ./2), (1,V)], [(_Y, ./2), (2, ./2), (1,V)])} ALive,r { [ ( I a s t , . / 2 ) , ( 1 , v ) ] , [(_Y,./2)], [ (_RU, ./2), (1,V)] } < _x, _Y, 7> T ( VList, . (V,nil), V) AShr,-~ { ([(_x,./2),Cl,V)], [(_Y,./2),Cl,V)]), ( [(_Y, ./2), (1,v)], [(_Y, ./2), (1,v)]), ([(_x, ./2), Cl,V)], [(_x, ./2), (1,v)])} ALive~T { [(_Y,./2),(1,V)], [(_X,./2),(1,V)], [(_Z,V)]} T ( n i l , .(V,nil), .(V,nil)) AShr~ { ([(_Y,./2),CI,V)], [(_Y,./2),(1,V)])} AShr,~ { ([(_z,./2)], [(_Y,./2)])} ALive7 { [(_Y,./2),Cl,V)], [(_z,./2)]} (_E, _U, _8, _X, _Y, _Z) T ( V, V, V, VList, .(V,nil), V) AShr,~ { ([(_x,./2),Cl,v)], [(_Y,./2),(1,v)]), ( [(_Y, ./2),(1,v)], [(_Y, ./2), (1,v)]), ([(_x,./2),Cl,V)], [(_x, ./2), (1,v)])} ALive~- { [(_Y,./2),Cl,V)], [(_x,./2),Cl,V)], [(_z,v)]} T ( V, VList, _, VListOne .... ) AShr,-~ { ( [ ( I , . / 2 ) , ( 1 , v ) ] , [(_x,./2),Cl,V)]), ([(_x,./2), (2, ./2), (1,v)], [(_x, ./2), (1,v)]), ([C_x,./2),C2,./2),Cl,v)], [(_x, ./2), (2,./2), (1,v)])} AShr,~ { ([(_x,./2),(1,v)], [(_E,v)]), ([(_u,./2)], [(_x, ./2), (2, ./2)])} ALive,r { [(_x,./2),(2,./2)], [(_u,./2)], [(_E,V)], [(_X, ./2),(1,V)]}

AShr.~

1) 31

2)

A.4. BUILDTREE/2 AND INSERT/3

~s

T

( V, V L i s t , V, VListOne, . ( V , n i l ) ,

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)]),

AShr~ALive,]-

A.4

193 V)

([(_x,./2),(2,./2),(1,v)], [(I,./2),(2,./2),(1,v)])} { ( [ ( _ X , . 1 2 ) , C 1 , V ) ] , [(_E,V)]), ([(_U, . 1 2 ) ] , [(_X, . / 2 ) , (2, . / 2 ) ] ) }

{ [(_Y,./2),(I,V)], [(_X.,./2),(2,./2),(1,V)], [(_X, ./2), (1,V)], [(_Z,V)]}

buildtree/2 and insert/3

We discussed the results of the sharing analysis for the i n s e r t / 3 predicate in Section 4.3. The following table contains the results of both the sharing and liveness analysis for the first and the second clause. The analysis results for the third clause are similar to those for the second clause. (Note: /Ts is not included in the table as it is identical to/3s.)

Prolo# code:

(1) insert(E, _OT, _}IT) "/71 _OT = empty, /72 -}IT = t(empty, _E, empty). (2) insert(_E, _OT, X T ) "/74 _OT : t(_L, _F, _R),

/Ts -E :< -F, /76 _NT = t(_NL, _F, _R), /77 i n s e r t ( _ E , _L, _NL). /78 (3) i n s e r t ( _ E , _OT, i T ) "_OT = t(_L, _F, _R), /71o _E > ~,

/711 _NT = t(_L, _F, J R ) , 312 insert(_E,

_R, A~R). /713

#3

194 Call:

/~ ~o~

APPENDIX

~/x insert( _A, _B, _C )

( _A, _B, _C) ( I n t , Tree, V) { [(_c,v)]} ( I n t , Tree, TreeOne) z AShr,-} { ([(._B,tl3)], [(_C,t13),C3,t13)]), ([(_B,tl3)], [(_C,t/3), (I ,t/3)] )} ALiveT- { [(_C,tl3)] }

T ALiveT

1)

#1 #2 #3 2) #4

A. D E T A I L E D E X A M P L E S

( _E, ]IT, _OT)

T ( Int, V, T r e e ) ALive,r { [(]IT,V)]} T ( I n t , V, empty) ALive~- { [(]IT,V)]} T (Int, t(empty,lnt,empty), empty) ALiveT- { [(]IT,tl3)] } ( _E, _F, _L, ]IL, ]IT, _0T, _R) ( Int, V, V, V, V, Tree, V)

ALiveT T AShr,}

{ [(]IT,V)]} ( _, Int, Tree . . . . . TreeOne, Tree) { ([(_L,t/3)], [ ( _ 0 T , t / 3 ) , ( l , t / 3 ) ] ) , ([(_R,t/3)], [(_0T,t/3), (3,t13)])} ALiveT- { [(mT,t/3),(l,t/3)], [(mT,t/3),(3,t/3)], [(_R,t/3)], [(_L,t/3)], [ ( - F , I n t ) ] } T ( Int, Int, Tree, V, V, TreeOne, Tree) AShr,} { ([( L , t / 3 ) ] , [(_OT,t/3), ( 1 , t / 3 ) ] ) , ( [ ( ] t , t l 3 ) ] , [(_OT,tl3), (3,t13)])} ALive,r { [(]IT,V)]} T ( Int, Int, Tree, V, t(V,Int,Tree), TreeOne, Tree) AShr,} { ([(]]T,t/3), (3,t/3)], [(]IT,t/3), (3,t/3)]), ( [(â€¢ [(_0T,t/3), (I ,t/3)] ), ([(_~,t13)], [(_OT,t/3),(3,t/3)]), ( [ ( ] I T , t / 3 ) , (1,V)], [(]In,v)]), ( [(]iT,t/3), (3,t/3)], [(_R,t/3)])} ALive~r { [(]IT,t/3)] }

A.4. BUILDTREE/2 AND INSERT~3 T

AShr~

195

(Int, Int, Tree, TreeOne, t(TreeOne,lnt,Tree), TreeOne,Tree) { ([(mZ,t/S),(1,t/3)], [(~T,t/S),(1,t/3)]),

[(tiT,t/3) ( 1 , t / 3 ) ] , [(_NL,Z/3),(3,t/3)]), [(_OT,t/3) ( 1 , t / 3 ) ] , [ ( J L , t / 3 ) , ( 1 , t / 3 ) ] ) , [(JT,t/3) (1,t/3)], [(_L,t/3)]), [(_DT,tI3) ( 3 , t / 3 ) ] , [(_NT,t/3),(3,t/3)]), [(JOT,t/3),(1,t/3)]), [(â€¢ [(_R,t/3)] [ ( _ 0 T , t / 3 ) , ( 3 , t / 3 ) ] ) , [(_~lT,t/3) ( 1 , t / S ) ] , [ ( _ N L , t / 3 ) , ( 1 , t / 3 ) ] ) , [(~T,t/3) ( l , t / 3 ) ] , [(_NL,t/3),(3,t/3)]), [(_NT,t/3) ( 1 , t / 3 ) ] , [(_NL,t/3)]), , [(JIT,t/3) ( 3 , t / 3 ) ] , [(_R,t/3)]), [(_L,t/S)] [ ( _ _ % l L , t / S ) , ( 3 , t / 3 ) ] ) , [(_L,t/3)] [ ( _ I q L , t / 3 ) , ( 1 , t / 3 ) ] ) } [(_NT~t/3)] }

ALiveT-

From/34, the compiler derives that _0T = t(_L, _F, _~), is a selection operation. 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:

(i) buildtree(l, #I

L =

(2) buildtree(i

_OT, ~T) nil,

:-

, ~T, JT) "-

#4 -L = [-E; ] ~ ] , /~4 #s insert(~, _OT, _T), #6 buildtree(~, _T, _NT). #7

#~ buildtree( _A, 8, _C )

Call :

( _ A . _B, _C)

~ #~

T

(List, Tree, V>

ALive~r

{ [(_C,V)]}

T

( List,

AShr~ ALiveT

{ ([(_C,t/3)], { [(_C,t/3)]}

T r e e , Tree)

[(~,t/3)])}

196

A P P E N D I X A. DETAILED E X A M P L E S

1)

#1 &

< _L,

-)IT, _OT)

( L i s t , V, Tree) T ALive,r { [(_NT,V)] } ( nil, V, Tree)

T

ALiveT- { [(iT,V)]}

#s 2)

( Itil, Tree, Tree)

T

AShr~- { ([(JT,t/3)], [(nT,t/3)])} ALiveT- { [(JT,tl3)] }

< _E, _L, _NT, _OT, _R, _T) ( V, L i s t , V, Tree, V, V) ALive~ { [(JT,V)]} ( Int, ListOne, _, _, List, _) T AShr,~ { ([(_~, . / 2 ) ] , [(_L, . / 2 ) , (2, . / 2 ) ] ) } ALiveT" { [(_L,.12),(2,.12)], [(-~,.12)3, [(_E,Int)]} ( Int, ListOne, V, Tree, List, V) T T

AShr~- { ([(_E,./2)], [(_L,.12),(2,./2)])} ALiveT" { [(_NT,V)] } 7( Int, ListOne, V, Tree, List, TreeOne) AShr~ { ([(/~,./2)], [(i,./2),(2,./2)]), ([(â€¢T,t/3)], [(_T,t/S),(3,t/3)]), [(_T,t/3), (1,t/3)])}

([(.DT,t/3)],

ALive~r { [(JT,V)]} T

AShr~-

ALiveT-

( I n t , ListOne, Tree, Tree, L i s t , TreeOne) { ( [ ( _ D T , t / 3 ) ] , [(-NT,Z/3)]), ( [ ( - P . , . / 2 ) ] , [(.L, . / 2 ) , (2, . / 2 ) ] ) , ( [ ( _ D T , t / 3 ) ] , [(_T,t/3),(3,t/3)]), ([(_OT,t/3)], [ ( _ T , t / 3 ) , ( 1 , t / 3 ) ] ) , ([(_/~T,t/3)], [ ( _ T , t / 3 ) ] ) , ([(JT,t/3)], [(_T,t/3),(3,t/3)]), ([(JT,t/3)], [(_T,t/3), (1,t/3)])} { [(JT,t/3)]}

From ~4, the compiler derives that _L = [ _~ I _R], is a selection operation. From /~4, it is derived that the top-list cell can be reused in a construction. However, there is no local opportunity to reuse the list cell.

A.5

permutation/2 and select/3

We use the s e l e c t / 3 program to point out that the garbage cells detected at a program point following a procedure call different from the unification builtin =/2, are only true garbage cells if they are not already reused by the called procedure. The reason is that the analysis presently assumes that no garbage cells are reused. Prolog code:

A.5. PERMUTATION~2 AND SELECT~3

197

(i) select( _X, _YI, _Zl) "-

#i

_Yz = [ _ x

I 71].

(2) select( _X, _YI, _Zl) "#2 _Y1 : [ _Y I _Ys ]~ #311= [_Y 1 7s], #4 select( I , _Ys, _Zs). #s #~ select( _A, _B, _(~ )

Call :

#~

T

( _A, _B, _C) ( V, List, V)

ALiveT- { [(J,v)], [(_c,v)]} ( Int, ListOne, List)

AShr,~ ALive,r l) #1

[(_C,./2)3)}

T

{ [(_A,Int)], [(_C,./2)]} ( I, _YI, _Zl> ( V, List, V)

ALive,r

{ [(~,v)], [(7.1,v)3}

T

( Int, List0ne, List) { ([(_YI,./2),(2,./2)3, [(~Zl,./2)])} { [(_YI,./2),(2,./2)], [(_X,Int)], [(_ZI,./2)]} (_X, _YI, _ZI, _Y, _Ys, ~Zs> ( V, List, V, V, V, V> { [(_X,V)], [(_Zl,V)]} ( _, ListOne, _, Int, List, _) { ([(_Y1,.12),(2,.12)], [(_Ys,.12)])} { [(_YI,./2),(2,./2)], [(_Y,Int)], [(_Ys,./2)]} ( V, List0ne, V, Int, List, V) { ([(_YI,.12),(2,.12)3, [(_Ys, .12)] )}

AShr,~ ALive,r 2)

#2

{ ([(_B,./2),(2,./2)],

T ALive~r T

AShr~ ALiveT T

AShr,~ ALive,r

{ [(_x,v)], [(~Zl,V)]}

(V, ListOne, .(Int,V), Int, List, V) { ([(~Zl,./2),(2,V)], [(~Zs,V)]), ([(_Y1, .12), (2,. 12)], [(_Ys, .12)])} ALive~r { [(I,V)], [(i1,.12)]} ( Int, .(Int,List0ne), List0ne, Int, List0ne, T List) AShr~- { ([(_ZI,./2),(2,./2)], [(_YI,./2),(2,./2)]), ([(_Zs,./2)], [(_YI,./2),(2, /2)]), ( [(_Ys, .12), (2, .12)], [( 7.1, 12), (2,. 12)3 ), ( [(_Ys, .12), (2, .12)], [(_Zs, 12)]), ([(31, .12), (2, .12)], [(_Ys, 12),(2,.12)]), ( [(_Y1, .12), (2,. 12)], [(_Ys, 12)]), ( [(_Zl, ./2), (2, ./2)], [(_Zs, 12)])} ALive~r { [(1,Int)], [(i1,.12)]}

T

AShr,~

198

A P P E N D I X A. DETAILED E X A M P L E S

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:

(i) permutation( _Xl, _YI ) "#I _Xl = nil, #2 _YI = nil. #3 (2) permutation( _XI, 7.1 ) "#4 select( 7., II, _Ys ), ~4 #5 z l = [7. I 7s], #6 permutation( _Ys, 7.s ). #7

#~ permutation( _A, _B )

Call:

#~

T

( _A, _B> < List, V>

ALive~-

{ [(_B,V)] }

#~

7"

( List, List>

AhiveT'7ALive~iT ALive~iT

{ [(_B, ./2)]} ( _Xl, _n) ( List, V) { [(_YI,V)] } < nil, v> { [(_YI,V)] } ( nil, nil)

ALiveT

{ [(_YI, nil)]}

I) #i #2

#3 2) #4 ~4

( 1 1 , _Ys, 7., 7.1, _Zs> iT ALive~ iT

AShr~ALive~-

< { < {

List, V, V, V, V> [(7.1,V)]} nist0ne, List, Int . . . . )

([(_Ys, ./2)], [(_XI,./2), (2, . / 2 ) ] ) } { [(_Ys,./2)], [ ( 7 . , I n t ) ] , [ ( _ X I , . / 2 ) , ( 2 , . / 2 ) ] }

A.6. SPLIT/3 /75

/76

T AShr~ ALiveT T AShr~

199

( { { ( {

ListOne, List, Int, V, V)

([(_Ys,.12)],[(Ii,.12),(2,.12)])} [(li,v)]} List0ne, List, Int, .(Int,V), V)

([(_Ys,./2)],[(_XI,./2),(2,./2)]),

([(Ti,./2),(2,v)],[(is,V)])}

/77

ALiveTT AShr~ALiveT-

{ [(7.i,.12)]} ( L i s t O n e , L i s t , Int, ListOne, L i s t ) { ([(_Ys,./2}],E(_Xl,./2),(2,./2)]), ([(7.1,./2),(2, . / 2 ) ] , [(_Zs, . / 2 ) ] ) } { [(_Zl,./2)]}

Note that the top-list cell of the variable _Xl is derived to become a garbage cell in fl'44. However, this is only true if the code generated for the s e l e c t / 3 predicate did not introduce destructive assignments. The garbage cell created in the first clause of the s e l e c t / 3 predicate, may or may not be the first list cell of the variable _Xl.

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 normalform 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:

(i) split( _X, _Y, 7. ) "/72 1 1 = [ _ b l = ] , /73 -Y = [~I=1], /74 7. : [_b{ = 2 ] , /75 split( _r, _rl, _r2 ). (2) spilt( i, _Y, 7. ) "/77 _x = [_al_Xl]. /7, I I [], /79 -Y = [_a[_Y1],

/76

=

/710 _Y1 = [], /711 _Z = [].

/712

(3) split( _X, _Y, 7. ) :/713 _X = [], /714 -Y = [], /71s _Z = []. Call:

~

split( _.A, _13, _C )

200

APPENDIX A. DETAILED EXAMPLES

T ALive,r T ALive,r 1)

T ALive~r T AShr~ ALive,r

#2

T

AShr,~ ALive,r T AShr~ALive~ T AShr,}

ALive~ T AShr,}

ALive~r T

(_A, _B, _C) ( L i s t , v, v>

{ [(~,v)],

[(_c,v)]}

(List, List, List) { [(-B, .12)] , [(_C,.12)]} ( I, _Y, _Z, _a, II, _b, _r, _rl, _r2 ) < L i s t , v, v, v, v, v, v, v, v)

{ [(_Y,V)], [(_z,v)]} (List0ne ..... Int, List ....... _> { ([(-Xl,./2)], [(_X,./2),(2,./2)])} { [(_X,./2),(2,./2)], [(_Xl,./2)], [(_a,Int)]} ( ListOne, V, V, Int, List, V, V, V, V) { ([(_XI,.12)], [(_X,./2),(2,.12)])}

{ [(_Y,V)], [(_z,v)]} ( ........

ListOne, Int, List, _, _)

{ ([(_r,./2)], [(_xi,./2),(2,./2)])} { [(_X1,.12),(2,.12)], [(_b,Int)], [(_r,.12)]} (.(Int,ListOne), V, V, Int, ListOne, Int, List,

V, V) { ([C-r,./2)], [(-X,./2),(2,./2)]), ([(_r, . / 2 ) ] , [(_X1,./2),(2,./2)]), ( [(_Xl, ./2), ( 2 , . / 2 ) ] , [ ( _ X , . / 2 ) , ( 2 , . / 2 ) ] ) , ([(_XI, ./2)], [(_X,./2),(2,./2)])} { [(_Y,V)], [(_Z,V)]} (.(Int,ListOne),.(Int,V), V, Int, ListOne, Int, List,V,V) { ([(_Y,./2),(2,V)], [(_rl,V)]), C[(_xl, . / 2 ) ] , [(_x, ./2), (2, . / 2 ) ] ) , ([(_XI,./2),(2,./2)], [(_X, ./2), (2, ./2)]), ( [(_r, . / 2 ) ] , [(_X1, ./2), (2, . / 2 ) ] ) , ([(_r, . / 2 ) ] , [(_X, ./2), (2, ./2)])}

{ [(_Y,./2)], [(_z,v)]}

(.(Int,ListOne), .(Int,V), .(Int,V), Int, ListOne, Int, List, V, V) AShr,} { ( [ ( 1 , . / 2 ) , ( 2 , V ) ] , [(=2,V)]), ([(_r, . / 2 ) ] , [ ( _ X , . / 2 ) , ( 2 , . / 2 ) ] ) , ([(_r, ./2)], [(_XI,./2),(2,./2)]), ( [ ( I 1 , ./2), (2, . / 2 ) ] , [(_X, ./2), (2, . / 2 ) ] ) , ([(_Xl, ./2)], [(_X,./2),(2,./2)]), ( [(_Y, .12), (2,V)], [(_r1,V)])} [C 7.,./2)]} ALive~T { [(_u

201

A.6. SPLIT~3

&

:T

AShr~

(.(Int,ListOne), ListOne, ListOne, Int, ListOne, Int, List, List, List) { ([(_Z,.12),(2,.12)], [(__r2,.12)]), ([(_r, .12)], [(_X, .12), (2, .12)]), ( [(_r, .12)], [(-/I, .12), (2, .12)]), ([(_X1,.12),(2,.12)], [(-/,.12),(2,.12)]), ( [ ( - / I , .12)], [(_X, .12), (2, .12)]),

([(_Y, .12), (2, .12)], [(_rl, .12)])} ALivesr { [(_Y,.12)], [(_z,.12)]} 2)

< -/, _y, _z, _~, - / I , _y1> < L i s t , v, v, v, v, v>

ALive,r

{ [(_Y,v)], [(_z,v)]}

AShr~ALivecr

{ ([(_x1,.12)], [(-/,.12),(2,.12)])} { [ ( - / , . / 2 ) , ( 2 , . / 2 ) ] , [ ( - / 1 , . / 2 ) ] , [(._a,Int)]}

:7-

( ListOne, V, V, Int, List, V> { ([(-/1,./2)], [(-/,./2),(2,./2)])} { [(_Y,V)], [(_z,v)]} ( . ( i n t , = i l ) , v, v, int, n i l , v) { ([(11, n i l ) ] , [ ( - / , . / 2 ) , ( 2 , n i l ) ] ) } { [(_Y,V)], [(_z,v)]} (.(Int,nil), .(Int,V), V, Int, nil, V) { ([(_Y,./2),(2,V)], [(_YI,V)]), ([(_XI, nil)I, [(-/,.12),(2, nil)I)} { [(_Y,./2)], [(_Z,V)]} (.(Int,nil), .(Int,nil), V, Int, nil, nil> { ([(_Y,./2),(2, n i l ) I , [(_Y1, n i l ) I ) , ([(-/I, nil)I, [(-/,./2),(2, nil)])}

( ListOne ..... Int, List _> ~'s

AShr~ALive~ AShr~ ALive~r ~lO T AShr~ALive~r ~ii ~AShr~ALiveT Bi2 T

AShr~ALive~ 3) ~ls

T

{ [(_Y,.12)], [(z,v)]} (.(Int,nil), .(Int,nil), nil, Int, nil, nil> { ([(_Y,./2),(2, n i l ) I , [(_YI, n i l ) I ) , ([(./1, n i l ) I , [ ( - / , . / 2 ) , ( 2 , n i l ) I ) } { [(_Y, ./2)] , [(_Z, n i l ) ] } ( -/, _y, _z> (List,

v, v>

Ative~T { [(_Y,V)], [(_Z,V)]} ( n i l , V, V> ~14 T ALive~ { [(_Y,v)], [( v,v)]} ( ~iI, ~ii, v> ~i5 T ALive,r { [(_Y, n i l ) I , [(_Z,V)]} ( n i l , n i l , nil) ALive~ { [(_Y, n i l ) I , [ ( 7 , n i l ) I }

202

APPENDIX A. DETAILED EXAMPLES

A.7

qsort/2

and partition/4

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).

Call

~14

~i1 partition( _A, _]3, _C, _D )

:

(_A, ..B, _C, _I))

#~ 7-

< I.t, List, v, v)

ALive~ { [(_D,V)], [(_C,V)], [(_A,Int)]} ~oI

7-

( Int, List, List, List) { [(_A,Int)], [(_C,./2)], [(_D,./2)]} (-/4, _L, _Sm, _Gr) ( I n t , L i s t , V, V)

ALiveT X~2 7ALive~ ~3 7ALiveT

{ [(_Gr,V)], [(_Sm,V)], [(_N,Int)]} ( I n t , n i l , V, V) { [(_Gr,V)], [(~Sm,V)], [(_H,Int)]} ( I n t , n i l , n i l , V) { [(_Gr,V)], [(_Sin, nil)l, [(_~,Int)]}

~4

( Int, nil, nil, nil) { [(_Gr, nil)l, [(_Sm, nil)l, [(_M,Int)]}

1) ~,

7ALive~

7ALive~,

A. 7. QSORT/2 AND PARTITION/4 2) #5

(_M, _i, _H, _T, _Sin, _Gr, _Sin1) 7-

ALive:r 7-

AShr~ ALive3/3e

ALiveT 7AShr~

( Int, ListOne, Int, List, ListOne, List, List) { ([(_T, ./2)] , [(_L,./2),(2,./2)3),

ALive~r

([(~Sm,./2),(2,./2)], [(_Sml,./2)])} { [(_Gr,./2)], [(_Sin,./2)], [(_M,Int)]}

7-

7-

AShr~ ~9

3) /3to 7~10

( I n t , List, V, V, V, V, V) { [(_Gr,V)], [(_SIn,V)], [(J4,Int)]} ( _, ListOne, Int, List, _, _, _)

{ ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } { [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] , [(_T, ./2)] , [(_II,Int)]} ( I n t , ListOne, I n t , L i s t , V, V, V) { ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } { [(_Gr,V)], [(_Sin,V)], [(34,Znt)]} ( Int, ListOne, Int, List, .(Int,V), V, V) { ([(_T, ./2)] , [ ( _ ~ , . / 2 ) , ( 2 , . / 2 ) ] ) , ( [(_Sm, ./2), (2,V)], [(_Sml,V)])} { [(_Gr,V)], [(~Sm,./2)], [(A~,Znt)]}

AShr~ ALiveT #a

( _M, _L, _It, _T, _Sin, _Gr, _Grl) ( I n t , L i s t , V, V, V, V, V)

ALive:r

{ [(_Gr,V)], [(_Sm,V)], [(_M,Int)]}

7-

( _, ListOne, I n t , L i s t . . . . .

AShr~ ALiveT

{ { /311 7" ( AShr~ { ALiveT { /313 7-

/314

203

_)

([(_7,./2)], [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] , [(_T, ./2)] , [ ( ] ~ , I n t ) ] } Int, List0ne, Int, List, V, V, V)

([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) } [(_Gr,V)], [(~Sm,V)], [(_M,Znt)]}

( Int, ListOne, Int, List, V, . ( I n t , V ) ,

AShr~

{ ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) , (F(_Gr,./2),(2,V)], [(_Grl,V)] )}

ALive~r

{ [(_Gr,./2)], [(_SIn,V)], [(_M,Int)]}

V)

T

( Int, ListOne, Int, List, List, ListOne, List)

AShr,}

{ ([(_T, ./2)] , [ ( _ L , . / 2 ) , ( 2 , . / 2 ) ] ) , (r(_Gr,./2), ( 2 , . / 2 ) ] , [(_Grl, . / 2 ) ] ) }

ALive~r

{ [(_Gr,./2)],

[(.5m,./2)],

[(.~,Int)]}

We omitted/3~, and/312 from the table, as they are identical to/3e, respectively /311 9

From/3s (resp./31o), the compiler derives that _.L = [~t I _T], is a selection operation. From fi~5 (resp. ~ o ) , it is derived that the top-list cell can be reused in the construction _Sin = [_It [ _Sml], (resp. _Gr = [_B I _Grl]). For both clauses, the selection and construction operations occur within a single chunk. Note that there is no sharing between the output lists of the third and the fourth argument.

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

(i) qsort(-~, _Res) "~1 -X nil, =

~2 A s s = nil. (2) qsort(_X, _Res) "-

~3

#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

~/2 qsort( _A, _B )

Call:

( _A, _B) < List, v> ALive~r { [(_B,V)] }

~ 7#~ 1)

< List, List> ALive~r { [(_B, ./2)3} ( rues, ~)

fll

7-

( V, List)

~2

7-

< v, nil)

7-

ALivecr { [(-~es,V)] } ALivecr { [(_Res,V)]}

fls

7-

( nil, nil)

ALivesr

{ [(_I{es,nil)]}

2) ~'4

( _H, _P~, ..Res, _B.esl, _Kes2, _T, _U1, _U2, _X) 7

( V, V, V, V, V, V, V, V, List)

ALive~-

{ [(ross,v)]}

7-

( { { ( { { (

AShr,~-

ALive:r fl~

7-

AShr~ ALive~r f16

7

Int . . . . . . . . . List ..... ListOne) ([(_T, ./2)] , [ ( - X , . / 2 ) , ( 2 , . / 2 ) ] ) } [(I,./2),(2,./2)], [(_T, ./2)] , [(_H,Int)]} Int, V, V, V, V, List, V, V, ListOne) ([(_T, ./2)] , [(_X, . / 2 ) , (2, . / 2 ) ] ) } [(_Res,V)] } Int, V, V, V, V, List, List, List, ListOne)

AShr.~- { ([(_T, ./2)], [(_X, ./2), (2, . / 2 ) ] ) } ALive~r { [(_Res,V)]} f17 f18

7-

( Int, V, V, List, V, List, List, List, ListOne

AShr~ALive~r

{ ([(_T,./2)], [(_X, ./2), (2, . / 2 ) ] ) } { ['(./{es ,V)] }

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~ ALive:r

{ ([(_T, ./2)], [(_X, . / 2 ) , ( 2 , . / 2 ) 3 ) } { [(_Res,V)] }

A.8.

#9

SAMELEAVES/2 AND PROFILE~2 T

AShr,} ALive~r /31o T

205

< Int, List0ne, V, List, List, List, List, List, ListOne) { ([(_T,.12)], [(I, .12) , (2, .12)3 ) , ( [(A~,./2), (2,./2)], [(]~es2, ./2)] )}

{ [(~es,V)]} ( Int, List0ne, List, List, List, List, List, List, List0ne)

AShr,~

{ ([(-~es2,.12)], [(~es,.12)]), ( [ ( _ T , 12)],[(_x,.12),(2, 12)]), ( [(_~,. 12), (2,. 12)], [(_.R,es2,. 12)] ), ( [(_.~es,. 12)], [(R, 12)] ), ( [(~es,. 12)], [(~,. 12), (2,. 12)] )}

ALive~r

{ [(~es,.12)]}

From/34, the compiler derives that _X = [_B I _T], is a selection operation. From/3~4 , it is derived that the top-list cell can be reused in the construction = [_H I _Res2]. However, the selection and construction operations do not occur within a single chunk. If the temporary variable I is made a permanent variable by the compiler, its value will be saved on the local stack, and the garbage cell it is pointing to, will be accessible for reuse in the construction ~ = [At I _~es2] at program point/3s. Code migration would provide an alternative solution (see Section 3.3). Note that the restriction of/39 to the variables of the call a p p e n d ( A e s l , _K, A~es), yields the abstract substitution/3~, for which the analysis results are shown in Section A.2. Also, it can be seen from/36 that the terms bound to _Ul and _U2 are ground and that the terms bound to _Kesl and ~ e s 2 are independent free variables when the subgoal q s o r t ( _ U 1 , _ R e s l ) , in the body of the recursive clause for q s o r t / 2 , is called. Therefore, if the results of the analysis were used for an IAPimplementation of Prolog (Section 3.1), the subgoals qsort(_U1, _Kesl) and qsort(_U2, _Ires2) could be run in parallel without any run-time groundness or independence checks.

A.8

sameleaves/2 and profile/2

In Section 5.3, the s a r a e l e a v e s / 2 program served to clarify the relationship between the depth restriction for type graphs, the normal form of the program and the precision of the liveness analysis. The results of this section are for the program in normal form and depth bound two for the t / 2 functor.

Prolog code: (i) sameleaves( _x, _y ) "/31 profile( _x, _w ), /32 profile( _y, _w ).

206

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

(1) p r o f i l e (

_tr, _pr ) :-

~4

_tr = l v ( _ u ) , ~5 m r = [_u]. ~6

(2) profile( _tr, _pr ) "~7 _tr = t ( _tl, _y), ff~7 ~8 _ t l = l v ( _ u ) .

~9 _pr = [_u[ _prtail] , ~1o profile( _y, _prtail ). (3) profile( _tr, _pr ) :~lZ _tr = t ( _ t l , _ z ) , fills ~13 _ t l = t(_x,_y), ~ s ~4

_t2 = t ( _ x , _ t 3 ) ,

~5

_t3 = t ( _ y , ~.),

~16 profile( _t2, _pr ).

~11

~17

The intended solution of an initial call to the sa.meleaves/2 predicate usually is a yes/no answer. In the query as specified below, the input list of the second argument is assumed to be needed in some further computations. Note that for program point ~2, it is derived that the program variable _w is bound to a ground list containing at least one element. Call: /9~ sameleaves( -A, _B ) ~1

7-

( _A, .3) ( LvTree, LvTree)

ALiveT

{ [(_B,t/2)], [(_B,iv/1)]}

~

7-

( LvTree, LvTree)

ALive~

{ [(_B,ivli)], ( _,, _X, _y)

1) ~1 ~2

~3

[(_B,tl2)]}

7-

( V , LvTree, LvTree)

ALive~

{ [(_y,t/2)], [ ( _ y , l v / i ) ] }

7-

( L i s t O n e , LvTree, LvTree>

ALiveT

{ [(_y,t/2)], [(_y,lv/1)]}

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 Call:

l~3 p r o f i l e (

207

_A, -13 )

( _.h, _.B)

#3

,/ALive~

( LvTree, ListOne> { [(_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.

~

7ALiveT

/302 7" I) /74

~4 #5 #6 2) ~7

( LvTree, V) { [(_B,V)]} < LvTree, ListOne)

ALivey-

{ [(_B, ./2)3}

iT

( -F, _tr, _u ) ( V, LvTree, V)

A Live,r 7" ALive=r T A Live,r

{ [(_pr,V)]} ( _, iv(Int), Int>

~T ALive~

(.(Int,nil), lv(Int), Int) { [(_pr,./2)]} ( _pr, _prtail, _ t l , _tr, _u, _y)

{ [(_u, I n t ) ] } ( V, l v ( I n t ) , Int) { [(_pr,V)] }

T

( V, V, V, LvTree, V, V)

ALive~

{ [(_pr,V)] }

T

( . . . . LvTree, LvTree0ne, _, LvTree) { ( [ ( _ t l , t / 2 ) ] , [(_tr,t/2), (I ,t/2)] ),

AShr~

( [ ( _ t l , l v l l ) ] , [(_tr,tl2), ( l , l v l l ) ] ) , ALive~

( [(_y,t/2)], [(_tr,t/2), (2,t/2)]), ( [ ( _ y , l v / l ) ] , [(_tr,t/2), ( 2 , 1 v / l ) ] ) } { [(_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 #8

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

:T

< V, V, LvTree, LvTreeOne, V, LvTree)

AShr~-

{ ([(_ti,tl2)], [(_tr,t/2),(1,t/2)]), ([(_tl,Zvll)], [(_tr,tl2),(1,Zv/1)]), ([(_y,tl2)], [(_tr,tl2), (2,t12)]), ( [ ( _ y , l v l l ) ] , [(_tr,t/2), (2,1v/I)])}

ALiveT- { [(_pr,V)] }

T

( . . . . iv(Tnt), _, T~t, _) { [(_u,Xnt)] } ( V, V, iv(Int), t(Iv(Int),LvTree), Int, LvTree) { ([(_t1,1vll)], [ ( _ t r , t l 2 ) , ( l , l v / 1 ) ] ) , ( [(_y,t/2)], [(_tr,t/2), (2,t/2)]), ([(_y,lv/1)], [(_tr,t/2), (2,iv/I)])} { [(_pr,V)] } ( _pr, _tl, _t2, _t3, _tr, _~, 4 , ~) ( V , V, V, V, LvTree, V, V, V)

ALiw~r

{ [(mr,V)]}

ALive,r AShr~ ALive:r 3)

#i2 ~12

T

( _, LvTree . . . . .

AShy-

{ ([(_tl,tl2)], [(_tr,tl2),(l,t/2)]), ( [ ( _ t l , l v l l ) ] , [(_tr,t/2),(1,1v/1)]), ([(~z,t/2)], [ ( _ t r , t / 2 ) , ( 2 , t / 2 ) ] ) , ( [ ( _ z , l v / l ) ] , [(_tr,tl2), (2, i v / l ) ] )}

LvTreeOne . . . . .

ALiveT- { [ ( _ t r , t / 2 ) , ( 1 , i v / I ) ] , [(_tr,t/2),(2,1v/1)],

LvTree)

[(_tr,t/2), (1,t/2)], [(_tr,t/2),(2,t/2)],

[( 7.,t12)], [ ( _ z , l v l l ) ] , [(_tl,zl2)], [ ( _ t l , l v l l ) ] } ( V, LvTree, V, V, LvTreeOne, V, V, LvTree)

/$13 AShr,~

{ ([(_ti,t/2)], [(_tr,t/2),Cl,t/2)]), ( [ ( _ t l , l v l l ) ] , [(_tr,t/2), (I, i v / l ) ] ), ([(_z,t/2)], [ ( _ t r , t / 2 ) , ( 2 , t / 2 ) ] ) , ( [ ( _ z , l v / l ) ] , [(_tr,t/2),(2,1v/1)])} ALive~T { [(mr,V)]}

~s

~r

( _, LvTreeOne . . . . . . .

AShr.~

{ ([(_x,t/2)], [ ( _ t l , t / 2 ) , ( 1 , t / 2 ) ] ) , (F(_x,lv/1)], [(_tl,t/2), (I, I v / l ) ] ), ([(_y,t/2)], [ ( _ t l , t / 2 ) , ( 2 , t / 2 ) ] ) , ( [ ( _ y , l v l l ) ] , [(_t i , t / 2 ) , (2,1v/I)])} { [(_tl,t/2),Cl,Zvll)], [(_tl,t/2),(l,tl2)], [(_tl,t/2),(2,Zv/1)], [(_t1,Z/2),(2,t/2)], [(_y,t/2)], [(_y,lv/l)], [(_x,t/2)], [(_x,lv/l)]}

ALive~

LvTree, LvTree, _)

A.9. SIFT/2 AND REMOVE/3 #14 T

209

( V, LvTree0ne, V, V, t(LvTree0ne,LvTree), LvTree, LvTree, LvTree) { ([(~r,t/2),(1,t/2)], [(_x,t/2)]),

AShr,~

[(_tr ,t/2), (1,t/2), (2,1v/1)], [(_x,Zv/1)]), [(_tr t/2), (1,t/2), (1,Zv/1)], [(_x,Zv/1)]), [(_tr t/2), (1,t/2)], [(_y,t/2)]), [(_tr t/2), (1,t/2), (2,1v/1)], [(_y,lv/1)] ), [(_tr t / 2 ) , ( 1 , t / 2 ) , ( 1 , 1 v / 1 ) ] , [(_y,lv/1)]), [(_tl t / 2 ) ] , [ ( _ t r , t / 2 ) , ( 1 , t / 2 ) ] ) , [(_tl t/2),(2,t/2)], [(_tr,t/2),(l,t/2)]), [(_tl t/2),(l,t/2)], [(_tr,t/2),(l,t/2)]), [(_tl t/2), (2,1v/l)], [(_tr,t/2), (l,t/2), (l,lv/l)]) [(_tl t12), ( 1 , 1 v / l ) ] , [(_tr,t/2), (1 ,t12), (2,1v/1)]) [(_z,t/2)],

[(_tr,t/2),(2,t/2)]),

[(_z,lv/l)], [(_tr,t/2),(2,1v/l)]), [(_x,t/2)], [ ( _ t l , t / 2 ) , ( 1 , t / 2 ) ] ) , [(_x,Zv/1)], [ ( _ t l , t / 2 ) , ( 1 , i v / 1 ) ] ) , [(_y,t/2)], [ ( _ t l , t / 2 ) , ( 2 , t / 2 ) ] ) , [(_y,lv/1)], [(_tl,t/2),(2,1v/1)])}

ALive~

A.9

[(_pr,V)]}

sift/2

and remove/3

The sift/2 program can be used to sift out the prime numbers according to Eratosthenes' sieve algorithm. In the tables below, we only show the abstract substitutions of interest. Garbage cells are detected in the program point ;~4 for the s i f t / 2 program, and in the program points ~ and ~ for the remove/3 program. We analyze the remove/3 predicate for an initial query corresponding to the restriction of the abstract substitution ~76 in the second clause of the s i f t / 2 program. Note that there is no local opportunity to reuse the garbage cell in the third clause of the remove/3 predicate.

Prolog code: (1) s i f t (

I,

_Y ) "-

(2) si~t( _x, _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

#~ sift( _A, _B )

Call:

#~ #~ I) #1 #3

(_A, _B) 7ALive~r 7" ALive~r

< List, v>

T

{ [(_B,V)] } ( List, List> { [(_B, . / 2 ) ] } ( _x, _Y) ( List, V)

Akive~-

{ [(_Y,V)]}

~F

( n i l , nil> { [(_Y,nil)] }

Ative~r

2)

(_I, _Is, _New, _Ps, _X, _Y)

( V, V, V, V, List, V)

#s #6

ALive~r T AShr,} ALive,r 7AShr,} ALive~r :T AShr,} ALive,r

{ [(_Y,V)] } ( Int, List . . . . . ListOne, _) { ([(Ts,.12)], [(~,./2),(2,./2)])} { [(_X,./2),(2,./2)], [(_Is,./2)], [(_I,Int)]} ( Int, List, V, V, ListOne, V)

{ ([(_Zs,.12)], [(_x,.12),(2,.12)3)} { [(_Y,V)]} ( Int, List, V, V, List0ne, .(Int,V))

{ ([(_Is,.12)], [(_x,.12),(2,.12)]), ([(_Y, ./2),(2,v)], [(~s,V)])} { [(_Y, ./2)]}

Prolog code:

(1) remove( -P, _X, _Y ) .fl~ _.x =

[3, fl~ _Y = [3. "-

(2) remove( _P, _x, _Y ) #4 _X = [ _ I I _ I s ] ,

#s

/~4

#s -Y = [__llJis], #6 n o t ( 0 i s _I rood _P), #z remove( _P, _Is, J i s ). (3) remove( _P, I , -Nis ) :-

,88

#g _X : [_II_Is], #~9 #10 0 is _I rood _P,

#11 remove( _P, _Is, Jis ).

#12

A.9. SIFT~2 AND REMOVE~3 ~i~ remove( _I, _Is, _New )

Call:

~

211

iv

( _I, _Is, -New) ( Int, List, V>

A Live,r

{ [(mew,V)]} ( Int, List, List> ALive~r { [(mew,./2)]}

/5'0 2 iV

~) ~i

flS

< 2 , ~, _Y>

( Int, List, V> ALiveT- { [(_Y,V)]} 7( Int, nil, nil> ALive~- { [(_Y,nil)]} iv

2)

ALive7 AShr,~ ALive~#s

T

(_I, _Is, _Nis, _P, _X, _Y) (V, V, V, Int, List, V> { [(_Y,V)] } ( Int, List ..... ListOne, _) { ([(_Is, ./2)] , [(_X,./2),(2,./2)])} { [(_X,./2),(2,./2)], [(_Is, ./2)] , [(_I,Int)]} ( Int, List, V, Int, ListOne, V)

AShr~- { ([(_Is,./2)], ALive~- { [(_Y,V)] } #7

iv

AShr~

[(_X,./2),(2,./2)])}

< Int, List, V, Int, ListOne, .(Int,V)) { ([(_Is,./2)], [(_X,./2),(2,./2)]), ([(_Y, ./2), (2,V)], [(_Nis,V)] )}

ALive~r { [(_Y, ./2)3 }

3)

( _I, _Is, mis, _P, _x> ( v, v, v, Int, List)

ALiveT

{ [(mis,V)]} ( Int, List ..... ListOne)

AShr~ { ([(_Is,./2)], [(_X,./2),(2,./2)])} ALive~r { [(_X,./2),(2,./2)3, [(_Is, ./2)] , [(_I,Int)]} ( Int, List, V, Int, ListOns> #11 iv AShr~- { ( [ ( 1 s , . / 2 ) ] , [ ( I , . / 2 ) , ( 2 , . / 2 ) ] ) } ALive:,- { [(mis,V)3}

Bibliography [1] S. Abramsky and C. Hankin, editors. Abstract Interpretation of Declarative Languages. Ellis Horwood Series in Computers and their Applications. Ellis Horwood, Chichester, 1987. [2] K. Appleby, M. Carlsson, S. Haridi, and D. Sahlin. Garbage collection for Prolog based on WAM. Commun. ACM, 31(6):719-741, 1988. [3] K. R. Apt. Logic programming. In J. Van Leeuwen, editor, Handbook of Theoretical Computer Science, Volume B: Formal Models and Semantics, pages 493-574. Elsevier, 1990. [4] B.I.M., B-3078, Everberg, Belgium. ProLog by BIM -3.0- Reference Manual, Nov. 1990. [5] G. Birkhoff. Lattice Theory, volume 25 of American Mathematical Society Colloquium Publications. American Mathematical Society Providence, 1979. [6] P. Boizumault. PROLOG l'implantation. Masson, Paris, 1988. [7] M. Bruynooghe. The memory management of Prolog implementations. In Clark and Ts [16], pages 83-98. [8] M. Bruynooghe. Garbage collection in Prolog interpreters. In J. A. Campbell, editor, Implementations of Prolog, Ellis Horwood Series in Artificial Intelligence, pages 259-267. Ellis Horwood Limited, 1984. [9] M. Bruynooghe. Compile-time garbage collection. Report CW43, Department of Computer Science, Katholieke Universiteit Leuven, Apr. 1986. [10] M. Bruynooghe. Compile-time garbage collection or how to transform programs in an assignment-free language into code with assignments. In L. G. L. T. Meertens, editor, Program Specification and Transformation, pages 113-129. North-Holland, 1987. [11] M. Bruynooghe. A practical framework for the abstract interpretation of logic programs. Journal of Logic Programming, 10(2):91-124, Feb. 1991.

214

BIBLIOGRAPHY

[12] M. Bruynooghe and G. Janssens. An instance of abstract interpretation integrating type and mode inferencing. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 669-683, Seattle, 1988. MIT Press, Cambridge.

[13]

M. Bruynooghe, G. Jansscns, A. Callebaut, and B. Demoen. Abstract interpretation: Towards the global optimization of Prolog programs. In Proceedings of the Fourth Symposium on Logic Programming, pages 192204, San Francisco, 1987. IEEE Computer Society Press.

[14]

J. A. Campbell, editor. Implementations of Prolog. Ellis Horwood Series in Artificial Intelligence. Ellis Horwood Limited, 1984.

[15]

D. R. Chase, M. Wegman, and F. K. Zadeck. Analysis of pointers and structures. Proceedings of the ACM SIGPLAN'90 Conference on Programming Language Design and Implementation, SIGPLAN Notices, 25(6):296-310, 1990.

[16]

K. L. Clark and S.-A. Tarnlund, editors. Logic Programming. Academic Press, N.Y., 1982.

[17]

M. Codish, D. Dams, and E. Yardeni. Abstract unification and a bottomup analysis to detect aliasing in logic programs. Technical Report csg0-10, Department of Computer Science, Weizmann Institute of Science, Israel, May 1990.

[18]

M. Codish, D. Dams, and E. Yardeni. Bottom-up abstract interpretation of logic programs. Technical Report CS90-24, Department of Computer Science, Weizmann Institute of Science, Israel, Oct. 1990.

[19]

M. Codish, D. Dams, and E. Yardeni. Derivation and safety of an abstract unification algorithm for groundness and aliasing analysis. In K. Furukawa, editor, Proceedings of the Eighth International Conference on Logic Programming, pages 79-93, Paris, 1991. MIT Press, Cambridge.

[20] A. Colmerauer. Prolog and infinite trees. In Clark and T~irnlund [16], pages 231-251.

[21]

A. Cortesi and G. Fild. Abstract interpretation of Prolog: The treatment of the built-ins. Rapporto Interno 11, Department of Mathematics, University of Padova, 1991.

[22]

A. Cortesi, G. Fild, and W. Winsborough. Prop revisited: Propositional formula as abstract domain for groundness analysis. In Proceedings of the Sizth Annual IEEE Symposium on Logic in Computer Science, pages 322327. IEEE Computer Society Press, 1991.

BIBLIOGRAPHY

215

[23] P. Cousot. Semantic foundations of program analysis. In S. S. Muchnick and N. D. Jones, editors, Program Flow Analysis: Theory and Applications, pages 303-342. Prentice-Hall, 1981. [24] P. Cousot and R. Cousot. Abstract interpretation: A unified lattice model for static analysis of programs by construction or approximation of fixpoints. In Proceedings of the Fourth A CM Symposium on Principles of Programming Languages, pages 238-252, Los Angeles, 1977. [25] S. K. Debray. Efficient dataflow analysis of logic programs. In Proceedings of the Fifteenth A CM Symposium on Principles of Programming Languages, pages 260-273, San Diego, California, 1988. [26] S. K. Debray. A simple code improvement scheme for Prolog. In G. Levi and M. Martelli, editors, Proceedings of the Sizth International Conference on Logic Programming, pages 17-32, Lisbon, 1989. MIT Press, Cambridge. Also in Journal of Logic Programming, 13(1):57-88, 1992. [271 S. K. Debray. Static inference of modes and data dependencies in logic programs. ACM Trans. Prog. Lang. Syst., 11(3):418-450, 1989. [28] S. K. Debray and D. S. Warren. Automatic mode inference for Prolog programs. In Proceedings of the Third Symposium on Logic Programming, pages 78-88, Salt Lake City, Utah, 1986. IEEE Computer Society Press. [29] I. Foster. Copy avoidance through local reuse. Technical Report Preprint MCS-P99-0989, Mathematics and Computer Science Division, Argonne National Laboratory, Sept. 1989. [30] I. Foster and W. Winsborough. Copy avoidance through compile-time analysis and local reuse. In Proceedings of the International Logic Programming Symposium, Cambridge, 1991. MIT Press. [31] J. Gallagher and M. Bruynooghe. The derivation of an algorithm for program specialisation. In D. H. D. Warren and P. Szeredi, editors, Proceedings of the Seventh International Conference on Logic Programming, pages 732746, Jerusalem, 1990. MIT Press, Cambridge. Also in New Generation Computing, Vol. 9, Nos. 3,4, 1991. [32] S. I-Iorwitz, P. Pfeiffer, and T. Reps. Dependence analysis for pointer variables. Proceedings of the ACM SIGPLAN'S9 Conference on Programming Language Design and Implementation, SIGPLAN Notices, 24(7):2840, 1989. [33] P. Hudak. A semantic model for reference counting and its abstraction. In Abramsky and Hankin [1], pages 45-62. [34] G. Huet. Confluent reductions: Abstract properties and applications to term rewriting systems. Journal of the Association for Computing Machinery, 27(4):797-821, 1980.

216

BIBLIOGRAPHY

[35] K. Inoue, H. Seki, and H. Yagi. Analysis of functional programs to detect run-time garbage cells. A CM Transactions on Programming Languages and Systems, 10(4):555-578, 1988. [36] K. Inoue and K. Torii. Implementation and analysis of compile-time garbage collection. New Generation Computing, 10(1):101-119, 1991. [37] D. Jacobs and A. Langen. Accurate and efficient approximation of variable aliasing in logic programs. In E. Lusk and R. Overbeek, editors, Proceedings of the North American Conference on Logic Programming, pages 154-165, Cambridge, 1989. MIT Press. [38] G. Janssens. Deriving Run Time Properties of Logic Programs by Means of Abstract Interpretation. P h . D . thesis, Department of Computer Science, Katholieke Universiteit Leuven, Mar. 1990. [39] G. 3anssens and M. Bruynooghe. Deriving descriptions of possible values of program variables by means of abstract interpretation: Definitions and proofs. Report CW108, Department of Computer Science, Katholieke Universiteit Leuven, Mar. 1990. [40] G. Janssens and M. Bruynooghe. Deriving descriptions of possible values of program variables by means of abstract interpretation. Journal of Logic Programming, 13(2&3):205-258, July 1992. [41] G. Janssens, B. Demoen, and Y. Willems. Execution mechanism for Prolog. Report CW53, Department of Computer Science, Katholieke Universiteit Leuven, 1987. [42] T. P. Jensen and T. $ . Mogensen. A backwards analysis for compiletime garbage collection. In N. Jones, editor, ESOP'90 Proceedings Third European Symposium on Programming, Lecture Notes in Computer Science 432, pages 227-239. Springer-Verlag, N.Y., 1990. [43] N. D. Jones and H. Scndergaard. A semantic-based framework for the abstract interpretation of Prolog. In Abramsky and Hankin [1], pages 123142. [44] F. Klu~niak. Type synthesis for ground Prolog. In J.-L. Lassez, editor, Proceedings of the Fourth International Conference on Logic Programming, pages 788-816, Melbourne, 1987. MIT Press. [45] F. Klu~niak. Compile-time garbage collection for ground Prolog. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 1490-1505, Seattle, 1988. MIT Press, Cambridge. [46] J. R. Larus and P. N. Hilfinger. Detecting conflicts between structure accesses. Proceedings of the A CM SIGPLAN'88 Conference on Programming Language Design and Implementation, SIGPLAN Notices, 23(7):2134, 1988.

BIBLIOGRAPHY

217

[47] J.-L. Lassez, M. J. Maher, and K. Marriott. Unification revisited. In J. Minker, editor, Foundations of Deductive Databases and Logic Programming, pages 587-625. Morgan Kaufmann Publishers Inc., Los Altos, 1988. [48] B. Le Charlier, K. Musumbu, and P. Van Hentenryck. A generic abstract interpretation algorithm and its complexity analysis. In K. Furukawa, editor, Proceedings of the Eighth International Conference on Logic Programming, pages 64-78, Paris, 1991. MIT Press, Cambridge. [49] B. Le Charlier and P. Van Hentenryck. Experimental evaluation of a generic abstract interpretation algorithm for Prolog. Technical Report CS-91-55, Institute of Computer Science, University of Namur, and Dept. of Computer Science, Brown University, Aug. 1991. [50] J. W. Lloyd. Foundations of Logic Programming. Springer Series : Symbolic Computation - Artificial Intelligence. Springer-Verlag, second, extended edition, 1987. [51] O. Mallet. Interprdtation Abstraite Appliqude ~la Compilation et la Paralldlisation en Programmation Logique. Ph.D. thesis, L'l~cole Polytechnique de Paris, June 1992. [52] A. Mari~n, G. Janssens, A. Mulkers, and M. Bruynooghe. abstract interpretation: An experiment in code generation. M. Martelli, editors, Proceedings of the Sizth International Logic Programming, pages 33-47, Lisbon, 1989. MIT Press,

The impact of In G. Levi and Conference on Cambridge.

[53] K. Marriott and H. SOndergaard. Bottom-up abstract interpretation of logic programs. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 733-748, Seattle, 1988. MIT Press, Cambridge. [54] K. Marriott and H. Sendergaard. On Prolog and the occur-check problem. SIGPLAN Notices, 24(5):76-82, 1989. [55] K. Marriott and H. Sondergaard. Semantics-based dataflow analysis of logic programs. In G. Ritter, editor, Information Processing 89, pages 601-606. North-Holland, N.Y., 1989. [56] C. S. Mellish. Some global optimizations for a Prolog compiler. Journal of Logic Programming, 2:43-66, 1985. [57] C. S. Mellish. Abstract interpretation of Prolog programs. In Abramsky and Hankin [1], pages 181-198. [58] P. Mishra. Towards a theory of types in Prolog. In Proceedings of the 1084 International Symposium on Logic Programming, pages 289-298, Atlantic City, 1984. IEEE Computer Society Press.

218

BIBLIOGRAPHY

[59] A. Mulkers. Deriving Live Data Structures in Logic Programs by Means of Abstract Interpretation. Ph.D. thesis, Department of Computer Science, Katholieke Universiteit Leuven, Dec. 1991. [60] A. Mulkers, W. Winsborough, and M. Bruynooghe. Analysis of shared data structures for compile-time garbage collection in logic programs. In D. H. D. Warren and P. Szeredi, editors, Proceedings of the Seventh International Conference on Logic Programming, pages 747-762, :Jerusalem, 1990. MIT Press, Cambridge. [61] A. Mulkers, W. Winsborough, and M. Bruynooghe. Analysis of shared data structures for compile-time garbage collection in logic programs (extended version). Report CW117, Department of Computer Science, Katholieke Universiteit Leuven, Oct. 1990. [62] A. Mulkers, W. Winsborough, and M. Bruynooghe. Static analysis of logic programs to detect run-time garbage cells. In P. Dewilde and :J. Vandewalle, editors, Proceedings of the International Conference on Computer Systems and Software Engineering, pages 526-531. IEEE Computer Society Press, May 1992. [63] K. Muthukumar and M. Hermenegildo. Determination of variable dependence information through abstract interpretation. In E. Lusk and R. Overbeek, editors, Proceedings of the North Americas Conference on Logic Programming, pages 166-185, Cambridge, 1989. MIT Press. [64] K. Muthukumar and M. Hermenegildo. Combined determination of sharing and freeness of program variables through abstract interpretation. In K. Furukawa, editor, Proceedings of the Eighth International Conference on Logic Programming, pages 49-63, Paris, 1991. MIT Press, Cambridge. [65] U. Nilsson. Systematic semantic approximations of logic programs. In P. Deransart and J. Matuszyfiski, editors, Proceedings of the International Workshop on Programming Language Implementation and Logic Programming, Lecture Notes in Computer Science 456, pages 293-306. Springer-Verlag, 1990. [66] U. Nilsson. Abstract interpretation: A kind of magic. In P. Deransart and J. Matuszyfiski, editors, Proceedings of the International Workshop on Programming Language Implementation and Logic Programming, Lecture Notes in Computer Science. Springer-Verlag, 1991. [67] E. Pittomvils, M. Bruynooghe, and Y. Willems. Towards a real time garbage collector for Prolog. In Proceedings of the Symposium on Logic Programming, pages 185-198, Boston, 1985. IEEE Computer Society Press. [68] D. A. Plaisted. The occur-check problem in Prolog. J. New Generation Computing, 2(4):309-322, 1984. Also in: Proceedings of the International Symposium on Logic Programming, pages 272-280, Atlantic City, 1984. IEEE Computer Society Press.

BIBLIOGRAPHY

219

[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. [70] H. Sendergaard. An application of abstract interpretation of logic programs: Occur check reduction. In B. Robinet and R. Wilhelm, editors, ESOP'86 Proceedings European Symposium on Programming, Lecture Notes in Computer Science 213, pages 327-338. Springer-Verlag, N.Y., 1986. [71] A. Tarski. A lattice-theoretical fixpoint theorem and its applications. Pacific J. Math., 5:285-309, 1955. [72] A. Taylor. Removal of dereferencing and trailing in Prolog compilation. In G. Levi and M. Martelli, editors, Proceedings of the Sizth International Conference on Logic Programming, pages 48-60, Lisbon, 1989. MIT Press, Cambridge. [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. [74] A. Taylor. High Performance Prolog Implementation. P h . D . thesis, University of Sydney, June 1991. [75] W. Thomas. Automata on infinite objects. In J. Van Leeuwen, editor, Handbook of Theoretical Computer Science, Volume B: Formal Models and Semantics, pages 133-191. Elsevier, 1990. [76] M. Van Caneghem. L'anatomie de Prolog. InterEditions, 1986. [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. [78] P. L. Van Roy. Can Logic Programming Ezecute as Fast as Imperative Programming. Ph.D. thesis, University of California, Berkeley, Dec. 1990. [79] P. Vataja and E. Ukkonen. Finding temporary terms in Prolog programs. In ICOT, editor, Proceedings of the International Conference on Fifth Generation Computer Systems, pages 275-282, Tokyo, 1984. Ohmsha, LTD. and North-Holland. [80] D. H. D. Warren. An abstract Prolog instruction set. Technical report, SRI International, Artificial Intelligence Center, 1983.

220

BIBLIOGRAPHY

[81] W. Winsborough. Path-dependent reachability analysis for multiple specialization. In E. Lusk and R. Overbeek, editors, Proceedings of the North American Conference on Logic Programming, pages 133-153, Cambridge, 1989. MIT Press. [82] W. Winsborough. Multiple specialization using minimal-function graph semantics. Journal of Logic Programming, 13(2&3):259-290, July 1992. [83] W. Winsborough and M. Bruynooghe. Approximating unification over representations of sets of nonground terms. Draft, 1990. [84] W. Winsborough and A. Wvsrn. Transparent and-parallelism in the presence of shared free variables. In R. A. Kowalski and K. A. Bowen, editors, Proceedings of the Fifth International Conference and Symposium on Logic Programming, pages 749-764, Seattle, 1988. MIT Press, Cambridge. [85] E. Yardeni and E. Shapiro. A type system for logicprograms. In E. Shapiro, editor, Concurrent Prolog: Collected Papers (Volume ~), chapter 28, pages 211-244. M I T Press, Cambridge, 1987. Also in Journal of Logic Programming, Vol. 10(2):125-154 (1991). [86] 3. Zobel. Derivation of polymorpl~ic types for Prolog programs. In 3.-L. Lassez, editor, Proceedings of the Fourth International Conference on Logic Programming, pages 817-838, Melbourne, 1987. MIT Press.

Lecture Notes in Computer Science For information about Vols. 1-595 please contact your bookseller or Springer-Verlag

Vol. 596: L.-H. Eriksson, L. Halln~is, P. Schroeder-Heister IEds.), Extensions of Logic Programming. Proceedings, 1991. VII, 369 pages. 1992. (Subseries LNAI).

Vol. 613: J. P. Myers, Jr., M. J. O ' D o n n e l l (Eds.), Constructivity in Computer Science. Proceedings, 1991. X, 247 pages. 1992.

Vol. 597: H. W. Guesgen, J. Hertzberg, A Perspective of Constraint-Based Reasoning. VIII, 123 pages. 1992. (Subseries LNAI).

Voh 614: R. G. Herrtwich (Ed.), Network and Operating System Support for Digital Audio and Video. Proceedings, 1991. XII, 403 pages. 1992.

Vol. 598: S. Brookes, M. Main, A. Melton, M. Mislove, D. Schmidt (Eds.), Mathematical Foundations of Programming Semantics. Proceedings, 1991. VIII, 506 pages. 1992.

Vol. 615: O. Lchrmann Madsen (Ed.), ECOOP '92. European Conference on Object Oriented Programming. Proceedings. X, 426 pages. 1992.

Voh 599: Th. Wetter, K.-D. Althoff, J. Boose, B. R. Gaines, M. Linster, F. Schmalhofer (Eds.), Current Developments in Knowledge Acquisition EKAW '92. Proceedings. XIII, 444 pages. 1992. (Subseries LNAI).

Vol. 616: K. Jensen (Ed.), Application and Theory of Petri Nets 1992. Proceedings, 1992. VIII, 398 pages. 1992.

Vol. 600: J. W. de Bakker, C. Huizing, W. P. de Roever, G. Rozenberg (Eds.), Real Time: Theory in Practice. Proceedings, 1991. VIII, 723 pages. 1992.

Vol. 617: V. MaHk, O. St6pfinkovfi, R. Trappl (Eds.), Advanced Topics in Artificial Intelligence. Proceedings, 1992. IX, 484 pages. 1992. (Subseries LNAI). Vol. 618: P. M. D. Gray, R. J. Lucas (Eds.), Advanced Database Systems. Proceedings, 1992. X, 260 pages. 1992.

Vol. 601: D. Dolev, Z. Galil, M. Rodeh (Eds.L Theory of Computing and Systems. Proceedings, 1992. VIII, 220 pages. 1992.

Vol. 619: D. Pearce, H. Wansing (Eds.), Nonclassical Logics and Information Proceedings. Proceedings, 1990. VII, 171 pages. 1992. (Subseries LNAI).

Vol. 602: 1. Tomek (Ed.), Computer Assisted Learning. Pro ceedings, 1992. X, 615 pages. 1992.

Vol. 620: A. Nerode, M. Taitslin (Eds.), Logical Foundations of Computer Science Tver '92. Proceedings. IX, 514 pages. 1992.

Vol. 603: J. van Katwijk (Ed.), Ada: Moving Towards 20(10. Proceedings, 1992. Vlll, 324 pages. 1992. Vol. 604: F. Belli, F.-J. Radermacher (Eds.), Industrial and Engineering Applications of Artificial Intelligence and Expert Systems. Proceedings, 1992. XV, 702 pages. 1992. (Subseries LNAI). Vol. 605: D. Etiemble, J.-C. Syre (Eds.), PARLE '92. Parallel Architectures and Languages Europe. Proceedings, 1992. XVII, 984 pages. 1992.

Voh 621: O. Nurmi, E. Ukkonen (Eds.), Algorithm Theory SWAT '92. Proceedings. VIII, 434 pages. 1992. Vol. 622: F. SchmalhotEr, G. Strube, Th. Wetter (Eds.), Contemporary Knowledge Engineering and Cognition. Pro ceedings, 1991. XII, 258 pages. 1992. (Subseries LNAI). Vol. 623: W. Kuich (Ed.), Automata, Language,; and Pro gramming. Proceedings, 1992. XII, 721 pages. 1992.

Vol. 606: D. E. Knuth, Axioms and Hulls. IX, 109 pages. 1992.

Voh 624: A. Voronkov (Ed.), Logic Programming and Au tomated Reasoning. Proceedings, 1992. X[V, 509 pages. 1992. (Subseries LNAI).

Vol. 607: D. Kapur (Ed.), Automated Deduction CADE11. Proceedings, 1992. XV, 793 pages. 1992. (Subseries LNAI).

Voh 625: W. Vogler, Modular Construction and Partial Order Semantics of Petri Nets. IX, 252 pages. 1992.

Voh 608: C. Frasson, G. Gauthier, G. 1. McCalla (Eds.), Intelligent Tutoring Systems. Proceedings, 1992. XIV, 686 pages. 1992. Vol. 6(19: G. Rozenberg (Ed.), Advances in Petri Nets 1992. VIII, 472 pages. 1992.

Vol. 626: E. BOrger, G. JSger, H. Kleine Brining, M. M . Richter (Eds.), Computer Science Logic. Proceedings, 1991. VIII, 428 pages. 1992. Vol. 628: G. Vosselman, Relational Matching. IX, 190 pages. 1992.

Vol. 610: F. von Martial, Coordinating Plans of Autonomous Agents. XII, 246 pages. 1992. (Subseries LNAI).

Vol. 629: I. M. Havel, V. Koubek (Eds.), Mathematical Foundations of Computer Science 1992. Proceedings. IX, 521 pages. 1992.

Vol. 611 : M. P. Papazoglou, J. Zeleznikow (Eds.), The Next Generation of Information Systems: From Data to Knowledge. VIII, 310 pages. 1992. (Subseries LNAI).

Voh 630: W. R. Cleaveland (Ed.), CONCUR '92. Proceed ings. X, 580 pages. 1992.

Vol. 612: M. Tokoro, O. Niers'trasz, P. Wegner (Eds.), Object-Based Concurrent Computing. Proceedings, 1991. X, 265 pages. 1992.

Vol. 631: M. Bruynooghe, M. Wirsing (Eds.), Programming Language Implementation and Logic Programming. Proceedings, 1992. XI, 492 pages. 1992.

Vol. 632: H. Kirchner, G. Levi (Eds.), Algebraic and Logic Programming. Proceedings, 1992. IX, 457 pages. 1992. Vol. 633: D. Pearce, G. Wagner (Eds.), Logics in A|. Proceedings. VIII, 410 pages. 1992. (Subseries LNAI). Vol. 634: L. Bough, M. Cosnard, Y. Robert, D. Trystram (Eds.), Parallel Processing: CONPAR 92 - VAPP V. Proceedings. XVII, 853 pages. 1992. Vol. 635: J. C. Derniame (Ed.), Software Process Technology. Proceedings, 1992. VIII, 253 pages. 1992. Vol. 636: G. Comyn, N. E. Fuchs, M. J. Ratcliffe (Eds.), Logic Programming in Action. Proceedings, 1992. X, 324 pages. 1992. (Subseries LNAI).

Vol. 656: M. Rusinowitch, J. L. R6my (Eds.), Conditional Term Rewriting Systems. Proceedings, 1992. XI, 501 pages. 1993. Vol. 657: E. W. Mayr (Ed.), Graph-Theoretic Concepts in Computer Science. Proceedings, 199Z VIII, 350 pages. 1993. Vol. 658: R. A. Rueppel (Ed.), Advances in Cryptology EUROCRYPT '92. Proceedings, 1992. X,493 pages. 1993. Vol. 659: G. Brewka, K. P. Jantke, P. H. Schmitt (Eds.), Nonmonotonic and Inductive Logic. Proceedings, 1991. VIII, 332 pages. 1993. (Subseries LNAI).

Vol. 637: Y. Bekkers, J. Cohen (Eds.). Memory Management. Proceedings, 1992. Xl, 525 pages. 1992.

Vol. 660: E. Lamma, P. Mello (Eds.), Extensions of Logic Programming. Proceedings, 1992. VIII, 417 pages. 1993. (Subseries LNA1).

Vol. 639: A. U. Frank, 1. Campari, U. Formentini (Eds.), Theories and Methods of Spatio-Temporal Reasoning in Geographic Space. Proceedings, 1992. XI,431 pages. 1992.

Voh 661: S. J. Hanson, W. Remmele, R. L. Rivest (Eds.), Machine Learning: From Theory to Applications. VIll, 271 pages. 1993.

Voh 640: C. Sledge (Ed.), Software Engineering Education. Proceedings, 1992. X, 451 pages. 1992.

Vol. 662: M. Nitzberg, D. Mumford, T. Shiota, Filtering, Segmentation and Depth. VIII, 143 pages. 1993.

Vol. 641 : U. Kastens, P. Pfahler (Eds.), Compiler Construction. Proceedings, 1992. VIII, 320 pages. 1992.

Vol. 663: G. v. Bochmann, D. K. Probst (Eds.), Computer Aided Verification. Proceedings, 1992. IX, 422 pages. 1993.

Vol. 642: K. P. Jantke (Ed.), Analogical and Inductive Inference. Proceedings, 1992. VIII, 319 pages. 1992. (Subseries LNAI). Vol. 643: A. Habel, Hyperedge Replacement: Grammars and Languages. X, 214 pages. 1992. Vol. 644: A. Apostolico, M. Crochemore, Z. Galil, U. Manber (Eds.), Combinatorial Pattern Matching. Proceedings, 1992. X, 287 pages. 1992. Vol. 645: G. Pernul, A M. Tjoa (Eds.), Entity-Relationship Approach - ER '92. Proceedings, 1992. Xl, 439 pages, 1992. Vol. 646: J. Biskup, R. Hull (Eds.), Database Theory ICDT '92. Proceedings, 1992. IX, 449 pages. 1992. Vol. 647: A. Segall, S. Zaks (Eds.), Distributed Algorithms. X, 380 pages. 1992. Vol. 648: Y. Deswarte, G. Eizenberg, J.-J. Quisquater (Eds.), Computer Security - ESORICS 92. Proceedings. XI, 451 pages. 1992.

Vol. 664: M. Bezem, J. F. Groote (Eds.), Typed Lambda Calculi and Applications. Proceedings, 1993. VII1, 433 pages. 1993. Voh 665: P. Enjalbert, A. Finkel, K. W. Wagner (Eds.), STACS 93. Proceedings, 1993. XIV, 724 pages. 1993. Vol. 666: J. W. de Bakker, W.-P. de Roever, G. Rozenberg (Eds.), Semantics: Foundations and Applications. Proceedings, 1992. VII1, 659 pages. 1993. Vol. 667: P. B. Brazdil (Ed.), Machine Learning: ECML 93. Proceedings, 1993. XII, 471 pages. 1993. (Subseries LNAI). Vol. 668: M.-C. Gaudel, J.-P. Jouannaud (Eds.), TAPSOFT '93: Theory and Practice of Software Development. Proceedings, 1993. XII, 762 pages. 1993. VoL 669: R. S. Bird, C. C. Morgan, J. C. P. Woodcock (Eds.), Mathematics of Program Construction. Proceedings, 1992. VIII, 378 pages. 1993.

Vol. 649: A. Pettorossi (Ed.), Meta-Programming in Logic. Proceedings, 1992. Xll, 535 pages. 1992.

Vol. 670: J. C. P. Woodcock, P. G. Larsen (Eds.), FME "93: Industrial-Strength Formal Methods. Proceedings, 1993. XI, 689 pages. 1993.

Vol. 650: T. lbaraki, Y. lnagaki, K. lwama, T. Nishizeki, M. Yamashita (Eds.), Algorithms and Computation. Proceedings, 1992. XI, 510 pages. 1992.

Vol. 671: H. J. Ohlbach (Ed.), GWAI-92: Advances in Artificial Intelligence. Proceedings, 1992. XI, 397 pages. 1993. (Subseries LNAI).

Vol. 651: R. Koymans, Specifying Message Passing and Time-Critical Systems with Temporal Logic. IX, 164 pages. 1992.

Vol. 672: A. Barak, S. Guday, R. G. Wheeler, The MOS1X Distributed Operating System. X, 221 pages. 1993.

Vol. 652: R. Shyamasundar (Ed.), Foundations of Software Technology and Theoretical Computer Science. Proceedings, 1992. XIII, 405 pages. 1992. Vol. 653: A. Bensoussan, J.-P. Verjus (Eds.), Future Tendencies in Computer Science, Control and Applied Mathematics. Proceedings, 1992. XV, 371 pages. 1992. 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.

Vol. 673: G. Cohen, T. Mora, O. Moreno (Eds.), AAECC10: Applied, Algebra, Algebraic Algorithms and ErrorCorrecting Codes. Proceedings, 1993. X, 355 pages 1993. Vol. 674: G. Rozenberg (Ed.), Advances in Petri Nets 1993. VII, 457 pages. 1993. Vo[. 675: A. Mulkers, Live Data Structures in Logic Programs. VIII, 220 pages. 1993.

Our partners will collect data and use cookies for ad personalization and measurement. Learn how we and our ad partner Google, collect and use data. Agree & close