Principles of Program Analysis - Flemming Nielson, Hanne Riis Nielson, Chris Hankin
Principles of Program Analysis - Flemming Nielson, Hanne Riis Nielson, Chris Hankin
Principles of Program Analysis - Flemming Nielson, Hanne Riis Nielson, Chris Hankin
Principles
of Program Analysis
With 56 Figures and 51 Tables
~Springer
Flemming Nielson
Hanne Riis Nielson
The Technical University of Denmark
Informatics and Mathematical Modelling
Richard Petersens Plads, Bldg. 321
2800 Kongens Lyngby, Denmark
E-mail: {nielson, riis}@imm.dtu.dk
WWW: http://www.imm.dtu.dk/ -riis/PPA/ppa.html
Chris Hankin
Department of Computing
The Imperial College of Science, Technology, and Medicine
180 Queen's Gate, London SW7 2BZ, UK
E-mail: [email protected]
The use of general descriptive names, registered names, trademarks, etc. in this publication does not imply, even in
the absence of a specific statement, that such names are exempt from the relevant protective laws and regulations and
therefore free for general use.
Aims of the book. Our motivation for writing this book is twofold.
One is that for quite some time we have lacked a textbook for our own courses
on program analysis; instead we have been forced to base the courses on
conference papers supplemented with the occasional journal paper or chapter
from a text book out of print. The other is the growing feeling that the
various subcommunities in the field often study similar problems without
being sufficiently aware of insights or developments generally known in other
subcommunities. The idea then emerged that perhaps a text book could be
written that both would be useful for advanced courses on program analysis
and that would help increase the awareness in the field about the many
similarities between the various approaches.
There is an important analogy to complexity theory here. Consider re-
searchers or students looking through the literature for a clever algorithm
or heuristics to solve a problem on graphs. They might come across papers
on clever algorithms or heuristics for solving problems on boolean formulae
and might dismiss them on the grounds that graphs and boolean formulae
surely are quite different things. Yet complexity theory tells us that this is
quite a mistaken point of view. The notions of log-space reduction between
problems and of NP-complete problems lead to realising that problems may
appear unrelated at first sight and nonetheless be so closely related that a
good algorithm or heuristics for one will give rise to a good algorithm or
heuristics for the other. We believe it to be a sad fact that students of
programming languages in general, and program analysis in particular, are
much too often allowed to get away with making similarly naive decisions
and arguments. Program analysis is stiH far from being able to precisely re-
late ingredients of different approaches to one another but we hope that this
book will help to bring this about. In fact, we hope to shock quite a number
of readers by convincing them that there are important similarities between
the approaches of their own work and other approaches deemed to be of no
relevance.
This book concentrates on what we believe to be the four main approaches to
program analysis: Data Flow Analysis; Constraint Based Analysis; Abstract
VI Preface
Interpretation; and Type and Effect Systems. For each approach we aim
at identifying and describing the important general principles, rather than
presenting a cook-book of techniques and language constructs, with a view
to how the principles scale up for more complex programming languages and
analyses.
As a consequence we have deliberately decided that this book should not
treat a number of interesting approaches, some of which are dear to the
heart of the authors, in order to be able to cover the four main approaches
to some depth; the approaches not covered include denotationally based pro-
gram analysis, projection analysis, and logical formulations based on Stone
dualities. For reasons of space, we have also had to omit material that would
have had a natural place in the book; this includes a deeper treatment of
set constraints, fast techniques for implementing type based analyses, single
static assignment form, a broader treatment of pointer analyses, and the in-
terplay between analysis and transformation and how to efficiently recompute
analysis information invalidated by the transformation.
Appendices A and C review the concepts from partially ordered sets, graphs
and regular expressions that are used throughout the book. Appendix B is
more tutorial in nature since coinduction is likely to be a new concept for
most readers.
To help the reader when navigating through the book we provide a table of
contents, a list of tables, and a list of figures at the front of the book and an
index of the main concepts and an index of notation at the end of the book.
The index of notation is divided into three parts: first the mathematical
symbols (with an indication of where they take their parameters), then the
notation beginning with a greek letter, and finally the notation beginning
with a letter in the latin alphabet (regardless of the font used). The book
concludes with a bibliogmphy.
Our notation is mostly standard and is explained when introduced. However,
it may be helpful to point out that we will be using "iff" as an abbreviation
for "if and only if", that we will be using · · · [· · · ~ · · ·] to mean both syntactic
substitution as well as update of an environment or stare, and that we will
be writing · · · -tfin · · · for the set of finitary functions: these are the partial
functions with a finite domain. We also use Ă-notation for functions when it
improves the clarity of presentation: Ax. · · · x · · · stands for the unary function
f defined by f(x) = · · · x · · ·.
Most proofs and some technical lemmas and facts are in small print in order
to aid the reader in navigating through the book.
How to teach from the book. The book contains more material
than can be covered in a one semester course. The pace naturally depends
on the background of the students; we have taught the course at different
paces to students in their fourth year as well as to Ph.D.-students with a
variety of backgrounds. Below we summarise aur experiences on how many
lectures are needed for covering the various parts of the book; it supplements
the guide-lines presented above for how to read the book.
Two or three lectures should suffi.ce for covering all of Chapter 1 and possibly
some of the simpler concepts from Appendix A.1 and A.2.
Sections 2.1, 2.3 and 2.4 are likely to be central to any course dealing with
data flow analysis. Three to faur lectures should suffice for covering Sections
2.1 to 2.4 but five lectures may be needed if the students lack the necessary
background in operational semantics or lattice theory (Appendix A.1, A.2
and parts of A.3). - Sections 2.5 and 2.6 are more advanced. One or two
lectures suffi.ce for covering Section 2.5 but it is hard to pay justice to Section
2.6 in less than two lectures.
Faur or five lectures should suffi.ce for a complete treatment of both Chapter
3 and Appendix B; however, the concept of coinduction takes some time to
get used to and should be explained more than once.
Preface IX
1 Introduction 1
1.1 The Nature of Program Analysis 1
1.2 Setting the Scene o o 3
1.3 Data Flow Analysis o 5
1.301 The Equational Approach 5
1.302 The Constraint Based Approach 8
1.4 Constraint Based Analysis o 10
1.5 Abstract Interpretation 13
1.6 Type and Effect Systems o 17
1.601 Annotated Type Systems 18
1.602 Effect Systems 22
1.7 Algorithms o o o 25
1.8 Transformations 27
Concluding Remarks 29
Mini Projects 29
Exercises o o 31
6 Algorithms 365
6.1 Worklist Algorithms . . . . . . . . . . . . . . 365
6.1.1 The Structure of Worklist Algorithms 368
6.1.2 Iterating in LIFO and FIFO. 372
6.2 Iterating in Reverse Postorder . . . . 374
Contents XV
Index 433
Bibliography 439
List of Tables
3.1 Abstract Control Flow Analysis (Subsections 3.1.1 and 3.1.2). 146
3.2 The Structural Operational Semantics of FuN (part 1). 155
3.3 The Structural Operational Semantics of FUN (part 2). 156
3.4 Abstract Control Flow Analysis for intermediate expressions. 158
3.5 Syntax directed Control Flow Analysis. . 170
3.6 Constraint based Control Flow Analysis. . 174
3. 7 Algorithm for solving constraints. . . . . . 178
XVIII List of Tables
2.21 The effect of [x.sel: =niljf when #into(nu, H') :::;1. 126
2.22 The effect of [x.sel: =y]f when #into( ny, H') < 1. 127
2.23 du-and ud-chains. . . . . . . . . . . . . . . . . . . 132
Introduction
true answer
1
1
1
1
1
{d1, · · ·, dn, · · ·, dn+m}
safe answer
produce the safe "{ d1, · · · , dN }" too often as the analysis will then be utterly
useless. Note, that although the analysis does not give precise information it
may still give useful information: knowing that the value of y is one of 1, 2
and 27 just before the assignment z: =y still tells us that z will be positive,
and that z will fit within 1 byte of storage etc. To avoid confusion it may
help to be precise in the use of terminology: it is better to say "the values
of y possible at z : =y are among 1 and 2" than the slightly shorter and more
frequently used "the values of y possible at z: =y are 1 and 2".
Another common theme, to be stressed throughout this book, is that all
program analyses should be semantics based: this means that the information
obtained from the analysis can be proved to be safe (or correct) with respect
to a semantics of the programming language. It is a sad fact that new program
analyses often contain subtle bugs, and a formal justification of the program
analysis will help finding these bugs sooner rather than later. However, we
should stress that we do not suggest that program analyses be semantics
directed: this would mean that the structure of the program analysis should
reflect the structure of the semantics and this will be the case only for a few
approaches which are not covered in this book.
f RDentry(f) RDexit(f)
1 (x, ?),(y, ?),(z, ?) (x,?),(y,l),(z,?)
2 (x, ?),(y,l),(z, ?) (x,?),(y,l),(z,2)
3 (x, ?),(y,l),(y,5),(z,2),(z,4) (x,?),(y,l),(y,5),(z,2),(z,4)
4 (x, ?),(y,l),(y,5),(z,2),(z,4) (x,?),(y,l),(y,5),(z,4)
5 (x, ?),(y,l),(y,5),(z,4) (x, ?),(y,5), (z,4)
6 (x, ?),(y,l),(y,5),(z,2), (z,4) (x, ?),(y,6), (z,2), (z,4)
Consider the factorial program of Example 1.1. Here [y:=x] 1 reaches the
entry to [z: =1]2; to allow a more succinct presentation we shall say that
(y,1) reaches the entry to 2. Also we shall say that (x,?) reaches the entry to
2; here "?" is a special labei not appearing in the program and it is used to
record the possibility of an uninitialised variable reaching a certain program
point.
Full information about reaching definitions for the factorial program is then
given by the pair RD = (RDentry, RDexit) of functions in Table 1.1. Careful
inspection of this table reveals that the entry and exit information agree for
elementary blocks of the form [b]" whereas for elementary blocks of the form
[x := a]" they may differ on pairs (x, f'). We shall come back to this when
formulating the analysis in subsequent sections.
Returning to the discussion of safe approximation note that if we modify
Table 1.1 to include the pair (z,2) in RDentry(5) and RDexit(5) we still have
safe information about reaching definitions but the information is more ap-
proximate. However, if we remove (z,2) from RDentry(6) and RDexit(6) then
the information will no longer be safe - there exists a run of the factorial pro-
gram where the set { (x,?),(y,6),(z,4)} does not correctly describe the reaching
definitions at the exit of labei 6.
1 [y:=x]l 1
1
l[z:=1FI
1
1[y>1j3 1-=no"------1 [y:=OJ61
1yes 1
1[z: =z*y]41
1
1[y:=y-1]51
RDentry(2) RDexit(l)
1.3 Data Flow Analysis 7
that makes it clear that the labei "?" is to be used for uninitialised variables;
so in our case
RDentry(l) = {(x, ?), (y, ?), (z, ?)}
The least solution. The above system of equations defines the twelve
sets
RDentry(l), · · ·, RDexit(6)
in terms of each other. Writing RO for this twelve-tuple of sets we can regard
the equation system as defining a function F and demanding that:
where e.g.:
where it might be natural to take Var*= Var and Lab* = Lab. However,
it will simplify the presentation in this chapter to let Var* bea finite subset
of Var that contains the variables occurring in the program S* of interest
and similarly for Lab*. So for the example program we might have Var*=
{x, y, z} and Lab* = {1, · · ·, 6, ?}.
It is immediate that (P(Var* x Lab*)) 12 can be partially ordered by setting
RO ~ R0 1
iff Vi : RDi <:;; RD~
8 1 Introduction
imply
pn+l(0) = Fn(0)
But since pn+ 1 (0) = F(Fn(0)) this just says that Fn(0) is a fixed point of F
and hence that Fn(0) is a solution to the above equation system.
In fact we have obtained the least solution to the equation system. To see
this suppose that RO is some other solution, i.e. RD = F(RO). Then a
straightforward mathematical induction shows that Fn(0) !;;;; RO. Hence
the solution Fn(0) contains the fewest pairs of reaching definitions that is
consistent with the program, and intuitively, this is also the solution we want:
while we can add additional pairs of reaching definitions without making
the analysis semantically unsound, this will make the analysis less usable as
discussed in Section 1.1. In Exercise 1.7 we shall see that the least solution
is in fact the one displayed in Table 1.1.
shall present the constraint system for Reaching Definitions in such a way
that the relationship to the equational approach becomes apparent; however,
it is not a general phenomenon that the constraints are naturally divided into
two classes as was the case for the equations.
For the factorial program
[y:=x]\ [z:=1] 2 ; while [y>1] 3 do ([z:=z*y] 4 ; [y:=y-1] 5 ); [y:=0] 6
we obtain the following constraints for expressing the effect of elementary
blocks:
RDexit(l) ::J RDentry(l)\{(y,l) Il E Lab}
RDexit(l) ::J {(y, 1)}
RDexit(2) ::J RDentry(2)\{(z,l) Il E Lab}
RDexit(2) ::J {(z, 2)}
RDexit(3) ::J RDentry(3)
RDexit(4) ::J RDentry(4)\{(z,l) Il E Lab}
RDexit(4) ::J { (z, 4)}
RDexit(5) ::J RDentry(5)\{(y,l) Il E Lab}
RDexit(5) ::J {(y, 5)}
RDexit(6) ::J RDentry(6)\{(y,l) Il E Lab}
RDexit(6) ::J {(y, 6)}
let f = fn x => x 1;
g = fn y => y+2;
h = fn z => z+3
in (f g) + (f h)
The constraint system. One way to specify the Control Flow Anal-
ysis then is by means of a collection of constraints and we shall illustrate this
for the program of Example 1.2. There are three classes of constraints. One
class of constraints relate the values of function abstractions to their labels:
{fn x => [xjl} ~ C(2)
{fn y => [yj3} ~ C(4)
12 1 Introduction
Among other things this tells us that the function abstraction fn y => y is
never applied (since p(y) = 0) and that the program may only evaluate to
the function abstraction fn y => y (since C(5) = {fn y => [yj3}).
Note the similarities between the constraint based approaches to Data Flow
Analysis and Constraint Based Analysis: in both cases the syntactic structure
of the program gives rise to a set of constraints whose least solution is desired.
The main difference is that the constraints for the Constraint Based Analysis
have a more complex structure than those for the Data Flow Analysis.
Intuitively, a trace will record where the variables have obtained their values
in the course of the computation. So for the factorial program
We shall write DOM(tr) for the set ofvariables for which SRD(tr) is defined,
i.e. x E DOM(tr) iffsome pair (x,l) occurs intr.
In order for the Reaching Definitions Analysis to be correct (or safe) we shall
require that it captures the semantic reaching definitions, that is, if tr is a
14 1 Introduction
possible trace just before entering the elementary block labelled P then we
shall demand that
showing how the assignment statements give rise to extensions of the traces.
Here we write tr : (x, P) for appending an element (x, P) to a list tr, that
is ((xl,PI),···, (xn,Pn)): (x,P) equals ((xl,PI),···,(xn,Pn),(x,P)). Further-
more, we have
......
'Y(Y)
.•
y
P(Trace)
1'
-- Q
P(Var x Lab)
The idea is that the abstraction function a extracts the reachability informa-
tion present in a set of traces; it is natural to define
a(X) = {(x, SRD(tr)(x)) 1 x E DOM(tr) 1\ tr E X}
where we exploit the notion of semantically reaching definitions.
The concretisation function 1' then produces all traces tr that are consistent
with the given reachability information:
'Y(Y) = {tr 1 'Vx E DOM(tr): (x,SRD(tr)(x)) E Y}
and we shall say that (a, "f) is an adjunction, or a Galois connection, whenever
this condition is satisfied; this is illustrated in Figure 1.3. We shall leave it
to the reader to verify that (a, 'Y) as defined above does in fact fulfil this
condition.
and this just says that the least solution to the equation system defined by
ii o G o 1 is correct with respect to the collecting semantics, and similarly that
the least solution to the equation system of Section 1.3 is also correct with
respect to the collecting semantics. Thus it follows that we will only need to
show that the collecting semantics is correct- the correctness of the induced
analysis will follow for free.
For some analyses one is able to prove the stronger result ii o G o 1 = F.
Then the analysis is optimal (given the choice of approximate properties it
operates on) and clearly lfp(ii o G o 1) = lfp(F). In Exercise 1.4 we shall
study whether or not this is the case here.
One way to formalise this is by the following utterly trivial system of axioms
and inference rules:
18 1 Introduction
[x := a]R : E -+ E
[skip]e : E -+ E
SI : E -+ E s2 : E -+ E
SI; s2 : E-+ E
S:E-+E
while [b]f do S : E -+ E
Often a Type and Effect System can be viewed as the amalgamation of two
ingredients: an Effect System and an Annotated Type System. In an Effect
System we typically have judgements of the form S : E ~ E where the effect
<p tells something about what happens when S is executed: for example this
may be which errors might occur, which exceptions might be raised, or which
files might be modified. In an Annotated Type System we typically have
judgements of the form S : E1 -+ E2 where the Ei describe certain properties
of states: for example this may be that a variable is positive or that a certain
invariant is maintained. We shall first illustrate the latter approach for the
WHILE language and then illustrate the Effect Systems using the functional
language.
S: RO__, RO
[wh]
while [b]R do S : RO __, RO
of Example 1.1 we will proceed as follows. We shall write RDf for the set
{(x, ?), (y, 1), (y, 5), (z, 2), (z, 4)} and consider the body of the while-loop.
The axiom [ass] gives
[z: =z*y] 4 : RDf ---t { (x, ?), (y, 1), (y, 5), (z, 4)}
[y: =y-1] 5 : {(x, ?), (y, 1), (y, 5), (z, 4)} ---t {(x, ?), (y, 5), (z, 4)}
([z: =z*y] 4 ; [y: =y-1] 5 ): RDf ---t {(x, ?), (y, 5), (z, 4)}
Now {(x, ?), (y, 5), (z, 4)} ~ RDf so the subsumption rule gives:
[y:=xjl: {(x, ?), (y, ?), (z, ?)} ---t {(x, ?), (y, 1), (z, ?)}
[z:=1f: {(x, ?), (y, 1), (z, ?)} ---t {(x, ?), (y, 1), (z, 2)}
[y: =0] 6 : RDf ---t { (x, ?), (y, 6), (z, 2), (z, 4)}
Since {(x, ?), (y, 1), (z, 2)} ~ RDf we can apply the rules [seq] and [sub] to get
[ass] [X ·-
.-
a]i .• L...
~ {x} l ~
{(x,i)} L...
[skip] [skip]i : E f E
S1 : E ~ E S2 : E ~ E
[seq]
S 1; S 2 :
~
L...
X1UX2
(RD 1\X2)URD2
1 ~
L...
S1 : E {J 1 ~ E S2 : E ~2 ~ E
[i~
[wh]
while [b]i do S : E :o~ E
S:E~ E
[sub] X'
if X' <;; X and RD <;; RD'
S:E~ E
where X denotes the set of variables that definitely will be assigned in S and
RD denotes the set of reaching definitions that S might produce. The axioms
and rules are shown in Table 1.3 and are explained below.
The axiom for assignment simply expresses that the variable x definitely will
be assigned and that the reaching definition (x, f) is produced. In the rule
for sequencing the notation RD \X means {(x,f) E RD 1 x ţf X}. The
rule expresses that we take the union of the reaching definitions after having
removed entries from S 1 that are definitely redefined in S2 . Also we take the
union of the two sets of assigned variables. In the rule for conditiona! we
take the union of information about reaching definitions whereas we take the
intersection (rather than the union) of the assigned variables because we are
not completely sure what path was taken through the conditiona!. A similar
comment holds for the rule for the while-loop; here we can think of 0 as the
intersection between 0 (when the body is not executed) and X.
We have included a subsumption rule because this is normally the case for
such systems as we shall see in Chapter 5. However, in the system above there
is little need for it, and if one excludes it then implementation becomes very
straightforward: simply perform a syntax directed traversa! of the program
where the sets X and RD are computed for each subprogram.
Example 1.4 Let us once again consider the analysis of the factorial pro-
gram
22 1 Introduction
[z:=z*y] 4 : E {z} 1
{(z,4)}
E
[y:=y-1] 5 : E -uWw E
[y :=0] 6 : E {y} 1
{(y,6)}
E
showing that the program definitely will assign to y and z and that the final
value of y will be assigned at 6 and the final value of z at 2 or 4. •
rl-e:r
where r is a type environment that gives types to all free variables of e and
T is the type of e. For simplicity we shall assume that types are either base
1.6 Type and Effect Systems 23
types such as int and bool or they are function types written r 1 ----> r2. The
type system is given by the following axioms and rules:
r f-- X: Tx if r(x) = Tx
where we now have given fn x => x the name X and fn y => y the name
Y. To see that this program has type int ----> int we first observe that
[y f-t int]l-- y: int so:
Similarly, we have [x f-+ int ----> int]l-- x : int ----> int so:
[ ]1-- fnx x => x : ( int ----> int) ----> ( int ----> int)
[var]
[fn]
Î'f-fiL,rx=>e:7x cpU{1r}1 7&0
where <p is the effect, i.e. the names of the function abstractions that we
might apply when applying a function of this type.
We specify the analysis using judgements of the form
Î'f-e:7&r.p
r
where is the type environment that now gives the annotated type of ali
free variables, 7 is the annotated type of e, and r.p is the effect of evaluating
e. The analysis is specified by the axioms and rules in Table 1.4 which will
be explained below.
In the axiom [var] for variables we produce an empty effect because we assume
that the parameter mechanism is call-by-value and therefore no evaluation
takes place when mentioning a variable. Similarly, in the rule [fn] for function
abstractions we produce an empty effect: no evaluation takes place because
we only construct a closure. The body of the abstraction is analysed in
order to determine its annotated type and effect. This information is needed
to annotate the function arrow: ali the names of functions in the effect of
the body and the name of the abstraction itself may be involved when this
particular abstraction is applied.
Next, consider the rule [app] for function application e 1 e2 . Here we obtain
annotated types and effects from the operator e1 as well as the operand e2.
The effect of the application will contain the effect <p1 of the operator (because
we have to evaluate it before the application can take place), the effect <p2 of
1. 7 Algorithms 25
[] f-- (fnx x => x) (fny y => y): int ~ int & {X}
This shows that our example program may (in fact it will) apply the function
fn x => x but that it will not apply the function fn y => y. •
For a more general language we will also need to introduce some form of
subsumption rule in the manner of Tables 1.2 and 1.3; there are different
approaches to this and we shall return to this later. Effect Systems are
often implemented as extensions of type inference algorithms and, depending
on the form of the effects, it may be possible to calculate them on the fly;
alternatively, sets of constraints can be generated and solved subsequently.
We refer to Chapter 5 for more details.
1. 7 Algorithms
Let us now reconsider the problem of computing the least solution to the
program analysis problems considered in Data Flow Analysis and Constraint
Based Analysis.
Recall from Section 1.3 that we consider twelve-tuples RO E (P(Var* x
Lab*)) 12 of pairs of variables and labels where each label indicates an ele-
mentary block in which the corresponding variable was last assigned. The
equation or constraint system gives rise to demanding the least solution to an
equation RO= F(RO) or inclusion RO;;;) F(RO) where Fis a monotone func-
tion over (P(Var* x Lab*)) 12 . Due to the finiteness of (P(Var* x Lab*)) 12
the desired solution is in both cases obtainable as Fn(iJ) for any n such that
pn+ 1 (i$) = pn(iJ) and we know that such an n does in fact exist.
(RD1. · · ·, RD12)
(Fl(RO), ... 'F12(RO))
then in fact RDj C Fj(RD1, · · ·, RD12) and hence the size of RO increases by
at least one as we perform each iteration. This ensures termination since we
assumed that (P(Var* x Lab*)) 12 is finite.
The above algorithm is suitable for manually solving data flow equations and
constraint systems. To obtain an algorithm that is suitable for implemen-
tation we need to give more details about the choice of j so as to avoid an
extensive search for the value; we shall return to this in Chapters 2 and 6.
1.8 Transformations 27
RO f- 81 l> 8~
RO f- 81; 82 l> 8~;82
RO f- 82 l> 8~
RO f- 81; 82 l> 81;8~
RO f- 81 l> 8~
[i/1]
RO f- i f [b]R then 81 else 82 l> i f [b]R then 8~ else 82
RO f- 82 l> 8~
[ih]
RO f- if [b]R then 81 else 82 l> if [b]R then 81 else 8~
RO f- 8 1> 8'
[wh]
RO f- while [b]R do 8l> while [b]R do 8'
1.8 Transformations
RO f- 8 1> 8'
28 1 Introduction
expressing one step of the transformation process. We may define the trans-
formation using the axioms and rules in Table 1.6; they are explained below.
The first axiom [ass 1 ] expresses the first ingredient in Constant Folding as
explained above ~ the use of a variable can be replaced with a constant if it is
known that the variable always will be that constant; here we write a[y f--7 n]
for the expression that is as a except that all occurrences of y have been
replaced by n; also we write FV( a) for the set of variables occurring in a.
The second axiom [ass2 ] expresses the second ingredient of the transformation
~ expressions can be partially evaluated; it uses the fact that if an expression
contains no variables then it will always evaluate to the same value.
The last five rules in Table 1.6 simply say that if we can transform a sub-
statement then we can transform the statement itself. Note that the rules
(e.g. [seq1 ] and [seq2 ]) do not prescribe a specific transformation order and
hence many different transformation sequences may exist. Also note that the
relation RO f-- · [> · is neither reflexive nor transitive because there are no
rules that forces it to be so. Hence we shall often want to perform an entire
sequence of transformations.
Let us now see how to transform the program. From the axiom [ass 1 ] we
have
RO f-- [y: =x+10] 2 [> [y: =10+10] 2
and therefore the rules for sequencing gives:
RO f-- [x: =10] \ [y: =x+10] 2 ; [z: =y+10] 3 [> [x: =10F; [y: =10+10] 2 ; [z: =y+10] 3
This could be costly because once S 1 has been transformed into S2 we might
have to recompute Reaching Definitions Analysis for S2 before the trans-
formation can be used to transform it into s3 etc. It turns out that it is
sometimes possible to use the analysis for S 1 to obtain a reasonable analysis
for S 2 without performing the analysis from scratch. In the case of Reaching
Definitions and Constant Folding this is very easy: if RD is a solution to
Reaching Definitions for Si and RD f- Si [> Si+ 1 then RD is also a solution to
Reaching Definitions for Si+l - intuitively, the reason is that the transforma-
tion only changed things that were not observed by the Reaching Definitions
Analysis.
Concluding Remarks
In this chapter we have briefly illustrated a few approaches (but by no means
all) to program analysis. Clearly there are many differences between the four
approaches. However, the main aim of the chapter has been to suggest that
there are also more similarities than one would perhaps have expected at
first sight: in particular, the interplay between the use of equations versus
constraints. It is also interesting to note that some of the techniques touched
upon in this chapter have close connections to other approaches to reasoning
about programs; especially, some versions of Annotated Type Systems are
closely related to Hoare's logic for partial correctness assertions.
As mentioned earlier, the approaches to program analysis covered in this book
are semantics based rather than semantics directed. The semantics directed
approaches include the denotational based approaches [27, 86, 115, 117] and
logic based approaches [19, 20, 81, 82].
Mini Projects
In this mini project we shall increase our faith in the Type and Effect System
of Table 1.3 by proving that it is correct. This requires knowledge of regular
expressions and homomorphisms to the extent covered in Appendix C.
30 1 Introduction
First we shall show how to associate a regular expression with each state-
ment. We define a function S such that S(S) is a regular expression for each
statement S E Stmt. It is defined by structural induction (see Appendix B)
as follows:
S([x :=a]e)
S([skip]e) A
S(S1;S2) S(Sl) · S(S2)
S(if [b]e then S 1 else S2) S(Sl) + S(S2)
S(while b do S) (S(S) )*
The alphabet is {!; 1 x E Var*,C E Lab*} where Var* and Lab* are finite
and non-empty sets that contain all the variables and labels, respectively, of
the statement S* of interest. As an example, for S* being
as follows:
h ('e) ={ ! if y = x
y ·x A if y =J X
hy(S(S)) ~ ! · !*
as follows:
' ify=x 1\ i=f'
ht (!;) = { ? if y = X 1\ i -# f'
A ify#x
As an example h~(S(S*)) =! + (? · ?) and h~(S(S*)) =A. Next
is equivalent to
-.3w E C[S(S)] : ht (w) ends in!
and intuitively means than the last assignment to y could not have been
performed at the statement labelled R.'. Prove that
Exercises
Exercise 1.1 A variant of Reaching Definitions replaces RD E P(Var x
Lab) by RL E P(Lab ); the idea is that given the program, a labei should
suffice for finding the variables that may be assigned in some elementary
block bearing that labei. Use this as the hasis for modifying the equation
system given in Section 1.3 for RD to an equation system for RL. (Hint:
It may be appropriate to think of RD = {(x1, ?), · · ·, (xn, ?)} as meaning
RD = {(x1, ?xJ, · · ·, (xn, ?xJ} and then use RL = {?xp' · ·, ?xn}.) •
Exercise 1.2 Show that the solution displayed for the Control Flow Anal-
ysis in Section 1.4 is a solution. Also show that it is in fact the least solution.
(Hint: Consider the demands on C(2), C(4), p(x), C(l) and C(5).) •
for all j and (R01, · · ·, R012). Determine whether or not F = aoGo1. Prove
by numerica! induction on n that (a o G o 1)n(0) !;;:; Fn(0). Also prove that
a(Gn(0))!;;:; (a o G o 1)n(0) using that a(0) = 0 and G!;;:; G o 1 o & •
Exercise 1.5 Consider the Annotated Type System for Reaching Defini-
tions defined in Table 1.2 in Section 1.6 and suppose that we want to stick
to the first (and unsuccessful) explanation of what S: R0 1 ----+ R0 2 means in
terms of Data Flow Analysis. Can you change Table 1.2 (by modifying or
removing axioms and rules) such that this becomes possible? •
Exercise 1.6 Consider the Chaotic Iteration algorithm of Section 1.7 and
suppose that
holds immediately before the assignment to ROj; show that is also holds
afterwards. (Hint: Write RO' for (R01, .. ·, Fj (RO), .. ·, R0 12) and use the
monotonicity of F and RO!;;:; F(RO) to establish that RO!;;:; R0 1 !;;:; F(RO) !;;:;
F(R0 1 ).) •
Exercise 1. 7 Use the Chaotic Iteration scheme of Section 1.7 to show
that the information displayed in Table 1.1 is in fact the least fixed point of
the function F defined in Section 1.3. •
In this chapter we introduce techniques for Data Flow Analysis. Data Flow
Analysis is the traditional form of program analysis which is described in
many textbooks on compiler writing. We will present analyses for the simple
imperative language WHILE that was introduced in Chapter 1. This includes
a number of classical Data Flow Analyses: Available Expressions, Reaching
Definitions, Very Busy Expressions and Live Variables. We introduce an
operational semantics for WHILE and demonstrate the correctness of the Live
Variables Analysis. We then present the notion of Monotone Frameworks
and show how the examples may be recast as such frameworks. We continue
by presenting a worklist algorithm for solving fl.ow equations and we study
its termination and correctness properties. The chapter concludes with a
presentation of some advanced topics, including Interprocedural Data Flow
Analysis and Shape Analysis.
Throughout the chapter we will clarify the distinctions between intraproce-
dural and interprocedural analyses, between forward and backward analyses,
between may and must analyses (or union and intersection analyses), between
flow sensitive and fl.ow insensitive analyses, and between context sensitive and
context insensitive analyses.
init([x := a]e) e
init([skip]e) e
init(S1; S2) init(SI)
init(if [W then 81 else S2) e
init(while [W do S) e
We will also need a function
Note that the while-loop terminates immediately after the test has evaluated
to false.
where
labels(8) = {f 1 [B]t E blocks(8)}
flow([x := a]l) 0
flow([skip]l) 0
f1ow(81; 82) flow(81) U f1ow(82)
U{(f,init(82)) 1 f E fina1(81)}
flow(if [b]l then 81 else 82) flow(81) U flow(82)
u{(f, init(81)), (f, init(82))}
flow(while [b]l do 8) flow(8) U {(f, init(8))}
U{ (e', f) 1 e' E fina1(8)}
Example 2.1 Consider the following program, power, computing the x-th
power of the number stored in y:
{(1,2),(2,3),(3,4),(4,2)}
and for composite statements (meaning those not simply of the form [B]l)
the equation remains true when removing the {init(8)} component.
38 2 Data Flow Analysis
1 [z:=1jl 1
l
1 [x>O Fl--'n=o:..__--+
lyes
1 [z:=z*yp 1
Example 2.2 For the power program of Example 2.1, flo~ produces
{(2,1),(2,4), (3,2),(4,3)}
which corresponds to a modified version of the flow graph in Figure 2.1 where
the direction of the arcs has been reversed. •
In case final( S) contains just one element that will be the unique entry node
for the flow graph with nodes labels(S) and edges flo~(S). Also
and for composite statements the equation remains true when removing the
final(S) component.
This is the case whenever S* does not start with a while-loop. Similarly, we
shall frequently assume that the program s* has isolated exits; this means
that:
Example 2.3 The power program of Example 2.1 has isolated entries but
not isolated exits. It is clearly labei consistent as well as uniquely labelled. •
to produce the set of non-trivial expressions killed in the block. Test and
ski p blocks do not kill any expressions and assignments kill any expression
that uses the variable that appears in the left hand side of the assignment.
Note that in the clause for [x: =a]l we have used the notation a' E AExp*
to denote the fact that a' is a non-trivial arithmetic expression appearing in
the program.
A generated expression is an expression that is evaluated in the block and
where none of the variables used in the expression are later modified in the
block. The set of non-trivial generated expressions is produced by the func-
tion:
genAE : Blocks* ----+ P(AExp*)
The analysis itself is now defined by the functions AEentry and AEexit that
each map labels to sets of expressions:
For a label consistent program S* (with isolated entries) the functions can
be defined as in Table 2.1.
2.1 Intraprocedural Analysis 41
The set of expressions generated by the first assignment is {x+y}; the other
blocks do not generate expressions and no block kills any expressions. The
equations for AEentry and AEexit are as follows:
AEentry(l) = 0
AEentry(i') = AEexit(i) n AEexit (i")
AEentry(l") AEexit(i')
AEexit(i) AEentry(l) U {x+y}
AEexit(f') AEentry(i')
AEexit (f") AEentry(f")
42 2 Data Flow Analysis
c killAE(C) genAE(C)
1 0 {a+b}
2 0 {a*b}
3 0 {a+b}
4 {a+b, a*b, a+1} 0
5 0 {a+b}
AEentry(1) 0
AEentry(2) AEexit(1)
AEentry(3) AEexit (2) n AEexit (5)
AEentry(4) AEexit (3)
AEentry (5) AEexit(4)
AEexit (1) AEentry(1) U {a+b}
AEexit (2) AEentry (2) U { a*b}
AEexit (3) AEentry(3) U {a+b}
AEexit(4) AEentry(4)\ {a+b, a*b, a+1}
AEexit(5) AEentry (5) U { a+b}
c AEentry(C) AEexit(C)
1 0 {a+b}
2 {a+b} {a+b, a*b}
3 {a+b} {a+b}
4 {a+b} 0
5 0 {a+b}
2.1 Intraprocedural Analysis 43
Note that, even though a is redefined in the loop, the expression a+b is re-
evaluated in the loop and so it is always available on entry to the loop. On
the other hand, a*b is available on the first entry to the loop but is killed
before the next iteration. •
For each program point, which assignments may have been made
and not overwritten, when program execution reaches this point
along some path.
All of the assignments reach the entry of 4 (the assignments labelled 1 and
2 reach there on the first iteration); only the assignments labelled 1, 4 and 5
reach the entry of 5. •
produces the set of pairs of variables and labels of assignments that are
destroyed by the block. An assignment is destroyed if the block assigns a
new value to the variable, i.e. the left hand side of the assignment. To deal
with uninitialised variables we shall, as in Chapter 1, use the special label
?
"?" and we set Lab~ = Lab* U {?}.
The function
genRo : Blocks* ----. P(Var* x Lab:)
produces the set of pairs of variables and labels of assignments generated by
the block; only assignments generate definitions.
The analysis itself is now defined by the pair of functions RDentry and RDexit
mapping labels to sets of pairs of variables and labels (of assignment blocks):
?
RDentry, RDexit: Lab* ____.. P(Var* X LabJ
44 2 Data Flow Analysis
For a label consistent program S* (with isolated entries) the functions are
defined as in Table 2.2.
Similar to the previous example, this is a forward analysis but, as we shall
see, we are interested in the smallest sets satisfying the equation for RDentry·
An assignment reaches the entry of a block if it reaches the exit of any of
the blocks which precede it; if there are none the formula evaluates to 0.
The computation of the set of assignments reaching the exit of a block is
analogous to the Available Expressions Analysis.
We motivate the requirement for the smallest solution by consideration of
the program [z:=x+y]l;while [true]l' do [skip]l" corresponding to Figure
2.2 again. The equations for RDentry and RDexit are as follows:
Once again, we concentrate on the entry of the block labelled f', RDentry(l');
after some simplification we get
but this equation has many solutions: we can take RDentry(f') to be any
superset of {(x, ?), (y, ?), (z,f)}. However, since f' does not generate any new
definitions, the most precise solution is {(x, ?), (y, ?), (z, f)} - we require the
smallest solution to the equations.
Sometimes, when the Reaching Definitions Analysis is presented in the liter-
ature, one has RDentry(init(S*)) = 0 rather than RDentry(init(S*)) = {(x, ?) 1
x E FV(S*)}. This is correct only for programs that always assign variables
before their first use; incorrect optimisations may result if this is not the case.
The advantage of our formulation, as will emerge from Mini Project 2.2, is
that it is always semantically sound.
of Example 2.6:
c RDentry(C) RDexit(C)
1 { (x, ?), (y, ?) } {(y, ?), (x, 1)}
2 {(y, ?), (x, 1)} {(x, 1), (y, 2)}
3 {(x,1),(y,2),(y,4),(x,5)} {(x, 1),(y,2), (y,4),(x,5)}
4 {(x,1),(y,2),(y,4),(x,5)} {(x,1),(y,4), (x,5)}
5 {(x,1),(y,4),(x,5)} {(y,4),(x,5)}
•
2.1.3 Very Busy Expressions Analysis
An expression is very busy at the exit from a label if, no matter what path
is taken from the label, the expression must always be used before any of the
variables occurring in it are redefined. The aim of the Very Busy Expressions
Analysis is to determine:
The expressions a-b and b-a are both very busy at the start of the condi-
tiona!; they can be hoisted to the start of the conditiona} resulting in a space
saving in the size of the code generated for this program. •
The analysis is specified in Table 2.3. We have already defined the notion
of an expression being killed when we presented the Available Expressions
Analysis; we use an equivalent function here:
By analogy with the previous analyses, we also need to define how a block
generates additional very busy expressions. For this we use:
All of the expressions that appear in a block are very busy at the entry to
the block (unlike what was the case for Available Expressions).
2.1 Intraprocedural Analysis 47
The analysis itself is defined by the pair of functions VBentry and VBexit
mapping labels to sets of expressions:
For a label consistent program S* ( with isolated exits) they are defined as in
Table 2.3.
The analysis is a backward analysis and, as we shall see, we are interested in
the largest sets satisfying the equation for VBexit· The functions propagate
information against the flow of the program: an expression is very busy at
the exit from a block if it is very busy at the entry to every block that follows;
if there are none the formula evaluates to AExp*. However, no expressions
are very busy at the exit from any final block.
To motivate the fact that we require the largest set, we consider the situation
where we have a flow graph as shown in Figure 2.3; this flow graph might
correspond to the program:
VBentry(f) VBexit(f)
VBentry(f') VBexit(f')
VBentry(f") {x+1}
48 2 Data Flow Analysis
l
[8]-=no:..___[]j
fyes l
[]]
VBentry(l) VBexit(l)
VBentry(2) VBexit(2) U {b-a}
VBentry(3) = {a-b}
VBentry(4) VBexit(4) U {b-a}
VBentry(5) = {a-b}
2.1 Intraprocedural Analysis 49
l VBentry(l) VBexit(f.)
1 {a-b, b-a} {a-b, b-a}
2 {a-b, b-a} {a-b}
3 {a-b} 0
4 {a-b, b-a} {a-b}
5 {a-b} 0
•
2.1.4 Live Variables Analysis
A variable is live at the exit from a labei if there exists a path from the
labei to a use of the variable that does not re-define the variable. The Live
Variables Analysis will determine:
For each program point, which variables may be live at the exit
from the point.
We shall take the view that no variables are live at the end of the program;
for some applications it might be better to assume that all variables are live
at the end of the program
Live Variables analysis may be used as the hasis for Dead Code Elimination.
If the variable is not live at the exit from a labei then, if the elementary block
is an assignment to the variable, the elementary block can be eliminated.
The variable x is not live at the exit from labei 1; the first assignment of the
program is redundant. Both x and y are live at the exit from labei 3. •
The analysis is defined in Table 2.4. The variable that appears on the left
hand side of an assignment is killed by the assignment; tests and skip state-
ments do not kill variables. This is expressed by the function:
The function
corresponding to the fiow graph in Figure 2.3. The equations for the program
are:
LV entry(i") = {x}
LV exit(i) = LVentry(i') U LVentry(i")
LV exit (i') LVentry(i)
LV exit (i") 0
Suppose that we are interested in LVexit(i); after some calculation we get:
[x:=2jl; [y:=4] 2 ; [x:=1] 3 ; (if [y>x] 4 then [z:=y] 5 else [z:=y*y] 6 ); [x:=zf
i killLv(i) genLv(i)
1 {x} 0
2 {y} 0
3 {x} 0
4 0 {x,y}
5 {z} {y}
6 {z} {y}
7 {x} {z}
We get the following equations:
LVexit(1) = LVentry(2)
LVexit(2) = LVentry(3)
LVexit(3) = LVentry(4)
LV exit( 4) LVentry(5) U LVentry(6)
52 2 Data Flow Analysis
f LVentry(f) LVexit(f)
1 0 0
2 0 {y}
3 {y} {x,y}
4 {x,y} {y}
5 {y} {z}
6 {y} {z}
7 {z} 0
Note that we have assumed that all variables are dead at the end of the
program. Some authors assume that the variables of interest are output at
the end of the program; in that case LV exit (7) should be {x, y, z} which means
that LVentry(7), LVexit(5) and LVexit(6) should all be {y,z}. •
clear(x,f,f') = :lf1,···,fn:
(fi = f) 1\ (fn = f') 1\ (n >O) 1\
(ViE {1, · · · ,n -1}: (fi,fi+I) E flow(S*)) 1\
(Vi E {1, .. ·, n- 1} : -,def(x, fi)) 1\ use(x, fn)
Here the predicate use checks whether the variable is used in a block
and the predicate def checks whether the variable is assigned in a block:
2.1 Intraprocedural Analysis 53
as follows:
So ud(x, i') will return the labels where an occurrence of x at i' might have
obtained its value; this may be at a labei i in S* or x may be uninitialised as
indicated by the occurrence of "?". And du(x, i) will return the labels where
the value assigned to x at i might be used; again we distinguish between
the case where x gets its value within the program and the case where it is
uninitialised. It turns out that:
Before showing how ud- and du-chains can be used, we illustrate the functions
by a simple example.
ud(x, i) X y z du(x, i) X y z
1 0 0 0 1 0 0 0
2 0 0 0 2 {3,5,6} 0 0
3 {2} 0 {?} 3 0 0 0
4 0 0 0 4 0 0 {7}
5 {2} 0 0 5 0 0 {7}
6 {2} 0 0 6 0 {7} 0
7 0 {6} {4,5} 7 0 0 0
? 0 0 {3}
The table for ud shows that the occurrence of x in block 3 will get its value
in block 2 and the table for du shows that the value assigned to x in block 2
may be used in block 3, 5 and 6. •
One application of ud- and du-chains is for Dead Code Elimination; for the
program of Example 2.12 we may remove the block labelled 1 for example
54 2 Data Flow Analysis
by:
UD(x i) = { {i' 1 (x,l') E RDentry(l)} if x E ~enLv(Bl)
' 0 otherw1se
Similarly, we can define a function DU : Var* x Lab* ---+ P(Lab*) for du-
chains based on the functions we have seen previously. We shallleave this to
Mini Project 2.1 where we also consider the formal relationship between the
UD and DU functions and the functions ud and du.
a E State Var---+ Z
2.2 Theoretical Properties 55
A : AExp - t (State - t Z)
A[x]a = a(x)
A[n]a N[n]
B : BExp - t (State - t T)
B[not b]a = -B[b]a
B[bt opb b2]a B[b1]a opb B[b2]a
B[a1 opr a2]a A[a1]a opr A[a2]a
• the execution terminates after one step and we record that by giving
the resulting state a', or
• the execution does not terminate after one step and we record that by
a new configuration (S', a') where S' is the rest of the program and a'
is the updated state.
[i/1] (if [bjl then 81 else 82,a) --t (81,a) if B[b]a = true
[wh1] (while [bjl do 8, a) --t ((8; while [bjl do 8), a) if B[b]a = true
(ax
[ A[ ] ]) { A[a]a if x = y
1---t a a y = a(y) otherwise
The semantics of sequencing is given by the two rules [seq1] and [seq2 ] and
the idea is as follows. The first step of executing 8 1 ; 8 2 is the first step of
executing 8 1 . It may be that only one step is needed for 8 1 to terminate and
then the rule [seq2 ] applies and says that the new configuration is (82, a'}
refiecting that we are ready to start executing 82 in the next step. Alter-
natively, 8 1 may not terminate in just one step but gives rise to some other
configuration (8~, a'); then the rule [seq1 ] applies and it expresses that the
rest of 8 1 and all of 8 2 still have to be executed: the next configuration is
(8~;82,a').
The semantics of the conditiona! is given by the two axioms [ifd and [i/2 ] ex-
pressing that the first step of computation will select the appropriate branch
2.2 Theoretical Properties 57
Note that labels have no impact on the semantics: they are merely carried
along and never inspected. •
58 2 Data Flow Analysis
Lemma 2.14
(iv) If (S,a)-+ (S',a') then blocks(S) ;:2 blocks(S') and if Sis labei con-
sistent then so is S'. •
Proof The proof of (i) is by induction on the shape of the inference tree used to
establish (8, a) -+ a'; we refer to Appendix B for a brief introduction to the proof
principle. Consulting Table 2.6 we see that there are three non-vacuous cases:
The case [ass]. Then ([x := a]e, a)-+ a[x f-> A[a]a] and we get:
The case [wh1]. Then (while [W do S, u) --+ ((S; while [W do S), u) because B[b]u
= true and we get:
final(S; while [b]l do S) = final(while [b]t do S)
The case [i/1]. Then (if [W then S1 else S2, u) --+ (S~, u) because B[b]u = true
and we get:
fiow(S1) U fiow(S2)
U {(l, init(S1)), (f, init(S2))}
:J fiow(S1)
We make this definition because in the correctness proof we will want to use
the same solution for all statements derived from S*; this will be possible for
LV~(S*) but not for Lv=(s*).
Now consider a collection live of functions:
live 1= LV~(S)
if li ve solves LV~ ( S*). The following result shows that any solution of the
equation system is also a solution of the constraint system and that the least
solutions of the two systems coincide.
li ve li ve li ve
Using Tarski's Fixed Point Theorem (Proposition A.10) we now have that FL~ has
a least fixed point lip( FL~) such that
lip( F~) = n{li ve llive ;:::) FL~ (li ve)} = n{li ve llive = FL~ (li ve)}
and since lip( FL~) = FL~ (Jfp( FL~)) as well as lfp( F~) ;:::) FL~ (Jfp( F~)) this proves
the result. •
The next result shows that if we have a solution to the constraint system
corresponding to some statement sl then it will also be a solution to the
constraint system obtained from a sub-statement S 2; this result will be es-
sential in the proof of the correctness result.
We now have the following corollary expressing that the solution to the con-
straints of LV<;;; is preserved during computation; this is illustrated in Figure
2.4 for finite computations.
Lemma 2.18 If live f= LV<;;;(S) (with S being labei consistent) then for
all (i,i') E flow(S) we have liveexit(i) 2 liveentry(i'). •
Proof The result follows immediately from the construction of LV<;;(8). •
62 2 Data Flow Analysis
(S, a1) ---+ (S',aD ---+ ... ---+ (S", an ---+ a~'
Example 2.19 Consider the statement [x:=y+z]R and let Vi= {y, z} and
V2 = {x}. Then a1 "'V1 a2 means a1(y) = a2(y) A a1(z) = a2(z) and
a1 "'V2 a2 means a1(x) = a2(x).
Next suppose that ([x:=y+z]R,a1)---+ a~ and ([x:=y+z]~',a2 )---+ a~. Clearly
a1 "'V1 a2 ensures that a~ "'v2 a~. So if V2 is the set of variables live after
[x: =y+z]l then vl is the set of variables live before [x: =y+z]l. •
The correctness result will express that the relation "rv" is an invariant under
the computation. This is illustrated in Figure 2.5 for finite computations and
it is formally expressed by Corollary 2.22 below; to improve the legibility of
formulae we write:
N (i) liveentry (i)
Lemma 2.20 Assume live f= LV<;;: (S) with S being labei consistent. Then
o-1 "'X(€) o-2 implies o-1 "'N(l') o-2 for all (C,C') E flow(S). •
Proof Follows directly from Lemma 2.18 and the definition of "'V. •
Correctness result. We are now ready for the main result. It states
how semantically correct liveness information is preserved under each step of
the execution: (i) in the case where we do not immediately terminate and
(ii) in the case where we do immediately terminate.
Theorem 2.21
If live f= LV<;;:(S) (with S being labei consistent) then:
(i) if (S, o-1) ---. (S', a-~) and o-1 "'N(init(S)) o-2 then there exists
o-~ such that (S,a-2)---. (S',o-~) and o-~ rvN(init(S')) o-~, and
(ii) if (S, a-1) ___. o-~ and o-1 rvN(init(S)) o-2 then there exists o-~
such that (S,o-2)---. o-~ and o-~ rvx(init(S)) o-~
Proof The proof is by induction on the shape of the inference tree used to establish
(8,o-1)--+ (8',a~) and (8,a1)--+ a~, respectively.
The case [ass]. Then ([x := a]f,a1)--+ a![x f-+ A[a]a1] and from the specification
of the constraint system we have
and thus
a1 "'N(l) a2 implies A[a]a1 = A[a]a2
because the value of a is only affected by the variables occurring in it. Therefore,
taking
a;= a2[x f-+ A[a]a2]
we have that a~(x) = a~(x) and thus a~ "'X(i) a~ as required.
The case [skip]. Then ([skip]f, a1) --+ a1 and from the specification of the constraint
system
The case [seq2]. Then (S1;S2,cr1)-+ (S2,crD because (S1,cr1)-+ cr~. Once again
by Lemma 2.16, live is a solution to LV<;;;(S!) and thus by the induction hypothesis
there exists cr; such that:
Now
{(i', init(S2)) PE final(S1)} <;;; flow(S1; S2)
1
From the specification of LV<;;;(S), we have N(P) = liveentry(P) ::2 liveexit(P) = X(P)
and thus CT1 rv X (C) CT2.
This completes the proof. •
Finally, we have an important corollary which lifts the previous result to
program executions: (i) in the case where the derivation sequence has not
yet terminated and (ii) in the case it has terminated:
(i) if (S,cr1) -+* (S',crD and cr1 "'N(init(S)) cr2 then there exists cr2 such
that (S,cr2) -+* (S',crD and cr~ "'N(init(S')) cr2, and
2.3 Monotone Frameworks 65
(ii) if (S,a1) --+*a~ and a1 "'N(init(S)) a2 then there exists a~ such that
(S, a2) --+* a~ and a~ "'X(f) a~ for some f E final(S). •
Proof The proof is by induction on the length of the derivation sequence and uses
Theorem 2.21. •
where
• The forward analyses have F to be flow( S*) and then Analysis0 con-
cerns entry conditions and Analysis. concerns exit conditions; also the
equation system presupposes that s* has isolated entries.
• When U is n
we require the greatest sets that solve the equations
and we are able to detect properties satisfied by all paths of execution
reaching (or leaving) the entry (or exit) of a label; these analyses are
often called must analyses.
• When U is U we require the least sets that solve the equations and we
are able to detect properties satisfied by at least one execution path to
(or from) the entry (or exit) of a labei; these analyses are often called
may analyses.
Remark. Some authors propose a typology for Data Flow Analysis, char-
acterising each analysis by a triple from
where--+ means forwards, +-- means backwards, 1 means smallest and l means
largest. This leads to eight possible types of analysis- a cube. In fact, given
our association of n
with l and U with L the cube collapses to a square.
We have presented analyses of the following four types: (n, --+, l), (U, --+, 1),
(n, ,_, n and (U, ,_, 1). •
It is occasionally awkward to have to assume that forward analyses have iso-
lated entries and that backward analyses have isolated exits. This motivates
reformulating the above equations to be of the form
not hard to show that the unit is in fact the least element (with respect to
~). It has been customary to demand that the property space, L, is a join
semi-lattice with a unit and that it satisfies the Ascending Chain Condition.
As proved in Lemma A.8 of Appendix A this is equivalent to our assumption
that the property space, L, is a complete lattice satisfying the Ascending
Chain Condition. •
Some formulations of Monotone Ftameworks are expressed in terms of prop-
erty spaces satisfying a Descending Chain Condition and using a combination
operator n. It follows from the principle of lattice duality (see the Cond ud-
ing Remarks of Chapter 4) that this does not change the notion of Monotone
Ftamework.
The condition on the identity function is natural because of the skip state-
ment and the condition on composition of functions is natural because of
the sequencing of statements. Clearly one can take F to be the space of
monotone functions over L but it is occasionally advantageous to consider a
smaller set because it makes it easier to find compact representations of the
functions.
Some formulations of Monotone Ftameworks associate transfer functions with
edges (or flows) rather than nodes (or labels). A similar effect can be obtained
using the approach of Exercise 2.11.
Note that we do not demand that Fis a complete lattice or even a partially
ordered set although this is the case for the set of all monotone functions
from L to L (see Appendix A).
A somewhat stronger concept is that of a Distributive Framework. This is a
Monotone Ftamework where additionally all functions f in F are required to
be distributive:
f(h u h) = f(h) u f(h)
Since f(h U l 2 ) ;;:1 f(h) U f(l 2) follows from monotonicity, the only additional
demand is that f(h U l 2 ) ~ f(h) U f(l2)· When this condition is fulfilled it
is sometimes possible to get more efficient algorithms.
lnstances. The data flow equations make it clear that more than just
a Monotone (or Distributive) Ftamework is needed in order to specify an
analysis. To this end we define an instance, Analysis, of a Monotone (or
Distributive) Ftamework to consist of:
The instance then gives rise to a set of equations, Analysis=, of the form
considered earlier:
e { L_l iUE E
where Le = iUtf_E
Analysis. (f) = fe (Analysis f))
0 (
L 0 {(x, ?)lxEFV(S*)} 0 0
E { init(S*)} {init(S*)} final(S*) final(S*)
F flow(S*) flow(S*) flo~(S*) flo~(S*)
Lemma 2.25 Each ofthe four data fiow analyses in Figure 2.6 is a Mono-
tone Framework as well as a Distributive Framework. •
Proof To prove that the analyses are Monotone Frameworks we just have to
confirm that :F has the necessary properties:
The functions of :F are monotone: Assume that l ~ l'. Then (l \ lk) ~ (l' \ lk)
and, therefore ((l \ h) U l9 ) ~ ((l' \ lk) U l9 ) and thus f(l) ~ f(l') as required.
Note that this calculation is valid regardless of whether ~ is ~ or ;:2.
The identity function is in :F: It is obtained by taking both lk and l9 to be 0.
The functions of :F are closed under composition: Suppose f(l) = (l \ lk) U l9 and
f'(l) = (l \ lU U l~. Then we calculate:
of Examples 2.4 and 2.5 and let us specify it as an instance of the associated
Monotone Framework. The complete lattice of interest is
with least element {a+b, a*b, a+1 }. The set of transfer functions has the form
shown in Figure 2.6.
The instance of the framework additionally has the fiow { (1, 2), (2, 3), (3, 4),
(4, 5), (5, 3)} and the set of extremallabels is {1 }. The extrema} value is 0
72 2 Data Flow Analysis
JtE(Y) YU {a+b}
f~E(Y) Y U {a*b}
J:E(Y) Y U {a+b}
JtE(Y) Y \ {a+b, a*b, a+1}
JtE(Y) Y U {a+b}
Acp[n]â {~ ifâ=..l
otherwise
[b]": Jj-P(â) a
It is easy to verify that SÎaiecp and Fcp satisfy the requirements of being a
Monotone Framework (see Exercise 2.8).
Constant Propagation is a forward analysis, so for the program S* we take
the fl.ow, F, to be Bow(S*), the extremal labels, E, to be {init(S*)}, the
extrema! value, iCP, to be Ax. T, and the mapping, f.CP, of labels to transfer
functions is given in Table 2.7. The specification of the transfer functions
uses the function
-
AcP : AExp ~ (Statecp ~ ZT _d
for analysing expressions. Here the operations on Z are lifted to = ZI
Z U {.1, T} by taking Zl opa Z2 = Zl OPa Z2 if ZI, Z2 E Z (and where OPa is
the corresponding arithmetic operation on Z), z1 <>Pa z2 = .1 if z1 = .1 or
z2 = .1 and Z1 <>Pa z2 = T otherwise.
function f~P for [y:=x•x] 1 and let 0'1 and 0'2 be such that â'1(x) = 1 and â'2(x) = -1.
Then 0'1 U 0'2 maps x to T and thus f~P (0'1 U 0'2) maps y to T and hence fails to
record that y has the constant value 1. However, both ffP(â'I) and f~P(â'2) map y
to 1 and so does !? (0'1) U ffP (0'2). •
Writing W for the list ((2,3),(3,4),(4,5),(5,3)) and U for the set {a+b, a*b,
a+1 }, step 1 of the algorithm will initialise the data structures as in the
first row in Table 2.9. Step 2 will inspect the first element of the worklist
2.4 Equation Solving 75
and rows 2-7 represent cases where there is a change in the array Analysis
and hence a new pair is placed on top of the worklist; it is inspected in the
next iteration. Rows 8-12 represent cases where no modification is made in
the array and hence the worklist is getting smaller - the elements of W are
merely inspected. Step 3 will then produce the solution we already saw in
Example 2.5. •
Lemma 2.29 The worklist algorithm in Table 2.8 always terminates and
it computes the least (or MFP) solution to the instance of the framework
given as input. •
Proof First we prove the termination result. Step 1 and 3 are bounded loops over
finite sets and thus trivially terminate. Next consider step 2. Assume that there
76 2 Data Flow Analysis
are b labels in the program. Then the worklist initially has at most b2 elements;
the worst case is that F associates every label to every labei. Each iteration either
deletes an element from the worklist or adds up to b new elements. New elements
are added if for the pair selected in this iteration, (.e, f'), we have fe(Analysis[f]) !l
Analysis[f']; that is, fe(Analysis[.e]) :::J Analysis[f'] or they are incomparable. In either
case, the new value of Analysis[.e'] is strictly greater than the previous one. Since
the set of values satisfies the Ascending Chain Condition, this can only happen a
finite number of times. Thus the worklist will eventually be exhausted.
Next we prove the correctness result. Let Analysis0 and Analysis. be the least
solution to the instance given as input to the algorithm. The proof is now in three
parts: (i) first we show that on each iteration the values in Analysis are approxi-
mations to the corresponding values of Analysis0 , (ii) then we show that Analysis0
is an approximation to Analysis at the termination of step 2 of the algorithm, and
(iii) finally we combine these results.
Part (i). We show that
is an invariant of the loop of step 2. After step 1 we have Analysis[f] [;;; Analysis0 (f)
for allf because Analysis0 (f);;;) L whenever .eE E. After each iteration through the
loop either there is no change because the iteration just deletes an element from
the worklist or else Analysis[.e"] is unchanged for all .e" except for some .e'. In that
case there is some .e such that (.e, f') E F and
The inequality follows since fe is monotone and the last equation follows from
(Analysis 0 , Analysis.) being a solution to the instance.
Part (ii). On termination of the loop, the worklist is empty. We show that
and this invariant has been maintained ever since; hence this case cannot apply.
It follows that Analysis[f] was last updated in step 2. But at that time (f, .e') was
placed in the worklist once again. When considering (f, .e') in step 2 we then ensured
that
Analysis[f'] ;;;;) fe(Analysis[f])
and this invariant has been maintained ever since; hence this case cannot apply
either. This completes the proof by contradiction.
On termination of the loop we have:
W EE: Analysis[f];;;;) ~
This follows because it was established in step 1 and it is maintained ever since.
Thus it follows that at termination of step 2:
since Analysis0 (f) is the least solution to the above constraint system and MFPa
equals the final value of Analysis. Together with part (i) this proves that
process it- not counting the time needed to add new pairs to W; this yields
at most O(e · h) basic operations for step 2. Since h 2:: 1 and e 2:: b this
gives at most O(e · h) basic operations for the algorithm. (Since e :::; b2 a
potentially coarser bound is O(b2 • h).)
Paths. For the moment, we adopt the informal notion of a path to the
entry of a block as the list of blocks traversed from the start of the program
up to that block (but not including it); analogously, we can define a path
from an exit of the block. Data Flow Analyses determine properties of such
paths. Forward analyses concern paths from the initial block to the entry
of a block; backward analyses concern paths from the exit of a block to a
final block. The effect of a path on the state can be computed by composing
the transfer functions associated with the individual blocks in the path. In
the forward case we collect information about the state of affairs before the
block is executed and in the backward case we collect information about the
state of affairs immediately after the block has been executed. This informal
description contrasts with the approach taken in Section 2.1 and earlier in
this section; there we presented equations which were defined in terms of the
immediate predecessors (successors) of a block (as defined by the flow and
flo~ functions). We will see later that, for a large class of analyses, these
two approaches coincide.
For the formal development let us consider an instance (L, F, F, E, t, f.) of
a Monotone Framework. We shall use the notation l = [l1, · · ·, ln] for a
2.4 Equation Solving 79
Jc = Jcn O • •• O fRt O id
so that for the empty path we have fr J = id where id is the identity function.
By analogy with the definition of solutions to the equation system, in par-
ticular MFPa(C) and MFP.(C), we now define two components of the MOP
solution. The solution up to but not including C is
x:=[u!]; y:=[v!];
while [···]do
(if [· · ·] then x:=x * 10lu 1 1 + [u!]; y:=y * tolv,l + [v!] else
where sign gives the sign (which is 1 for a positive argument and Oor -1 otherwise)
and where the details of[···] are of no concern to us.
Then MOP.(P) will map z to 1 if and only if the Modified Post Correspondence
Problem has no solution. Since the Modified Post Correspondence problem is un-
decidable [76] so is the MOP solution for Constant Propagation (assuming that our
selection of arithmetic operations does indeed allow those used tobe defined). •
MOP versus MFP solutions. We shall shortly prove that the MFP
solution safely approximates the MOP solution (informally, MFP ;::;;) MOP).
In the case of a (n, --t, i) or (n, +--, i) analysis, the MFP solution is a subset of
the MOP solution (;::;;J is s;;); in the case of a (U, --t, !) or (U, +--, !) analysis,
the MFP solution is a superset of the MOP solution. We can also show
that, in the case of Distributive Frameworks, the MOP and MFP solutions
coincide.
Lemma 2.32 Consider the MFP and MOP solutions to an instance (L, :F,
F, B, t, f.) of a Monotone Framework; then:
lf the framework is distributive and if path0 ( l) =/:- 0 for all l in E and F then:
MFPo = MOPo and MFP. = MOP.
•
Proof It is straightforward to show that:
Note that MFPa is the least fixed point of the functional F defined by:
Next let us restrict the length of the paths used to compute MOPa; for n ~ O
define:
MOPo(P) = U{fj{t) 1f E patho(P), lfl < n}
Clearly, MOPa(P) =Un MOPo(f) and to prove MFPa;;;) MOPa is therefore suffices
to prove
Vn: MFPa;;;) MOPo
and we do so by numerica! induction. The hasis, MFPa ;;;) MO~, is trivial. The
inductive step proceeds as follows:
MFPa(P) = F(MFPa)(f)
2.4 Equation Solving 81
where we have used the induction hypothesis to get the first inequality. This com-
pletes the proof of MFPo ;;;) MOPo and MFP. ;;;) MOP•.
To prove the second part of the lemma we now assume that the framework is
distributive. Consider 1! in E or F. By assumption h is distributive, that is
!f(l 1 U l2) = !f(l 1 ) U h(l2), and from Lemma A.9 of Appendix A it follows that
whenever Y is non-empty. By assumption we also have path 0 (1!) -j. 0 and it follows
that
We shallleave it to Exercise 2.13 to show that the condition that path 0 (R) =/= 0
(for Rin E and F) does hold when the Monotone Framework is constructed
from a program S* in the manner of the earlier sections.
It is sometimes stated that the MOP solution is the desired solution and
that one only uses the MFP solution because the MOP solution might not be
82 2 Data Flow Analysis
computable. In order to validate this belief we would need to prove that the
MOP solution is semantically correct as was proved for the MFP solution in
Section 2.2 in the case of Live Variables Analysis- in the case of Live Variables
this is of course immediate since it is a Distributive Framework. We shall not
do so because it is always possible to formulate the MOP solution as an MFP
solution over a different property space (like P(L)) and therefore little is lost
by focusing on the fixed point approach to Monotone Frameworks. (Also
note that P(L) satisfies the Ascending Chain Condition when Lis finite.)
It uses the procedure fib that returns in v the Fibonacci number of z plus
the value of u. Both x and y are global variables whereas z, u and v are
formal parameters and hence local variables. •
Flow graphs for statements. The next step is to extend the defi-
nitions of the functions init, final, blocks, labels, and flow to specify the flow
graphs also for the procedure language. For the new statement we take:
Here (le; in) and (ix; lr) are new kinds of flows:
The definition of flow([call p(a, z)]~~) exploits the fact that the syntax of
procedure calls only allows us to use the (constant) name of a procedure
84 2 Data Flow Analysis
defined in the program; had we been allowed to use a variable that denotes
a procedure (e.g. because it was a formal parameter to some procedure or
because it was a variable being assigned some procedure) then it would be
much harder to define f:low([call p(a, z)]~~). This is often called the dynamic
dispatch problem and we shall deal with it in Chapter 3.
init(p) fn
final(p) {fx}
blocks(p) { isln, endfx} U blocks(S)
labels(p) {in, fx} U labels(S)
flow(p) = {(in, init(S))} U flow(S) U { (f, fx) 1 f E final(S)}
that clearly indicates the relationship between the labels of a procedure call
and the corresponding procedure body. This information will be used later to
analyse procedure calls and returns more precisely than is otherwise possible.
Indeed, suppose that inter-flow* contains (f~, ln, fx, f~) for i = 1, 2 in which
case f:low* contains (f~; ln) and (fx; f~) for i = 1, 2. But this "gives rise to"
the four tuples (f~, fn, fx, it) for i = 1, 2 and j = 1, 2 and only the tuples
with i = j match the return with the call: these tuples are exactly the ones
in inter-flow*.
2.5 lnterprocedural Analysis 85
is 1
1 no
l
1 [z<3j2 1
!yes
!
-
1[call fib(x,O,y)]Îo 1 1[v:=u+1]31 1[call fib(z-1,u,v)Jgl: -
! !
1[call fib(z-2,v,v)J?I:
1
J,
end8
inter-Bow* = {(9,1,8,10),(4,1,8,5),(6,1,8,7)}
and init* = 9 and final* = {10}. The corresponding flow graph is illustrated
in Figure 2.7. •
~ E Loc locations
An environment, p, will map the variables in the current scope to their loca-
tions, and a stare, .,-, will then specify the values of these locations:
Here Var* is the (finite) set of variables occurring in the program and
Loc --+fin Z denotes the set of partial functions from Loc to Z that have
a finite domain. Thus the previously used states a E State = Var* ---+ Z
have been replaced by the two mappings p and c; and can be reconstructed
as a = c; o p: to determine the value of a variable x we first determine its
location ~ = p(x) and next the value c;(~) stored in that location. For this
to work it is essential that c; o p : Var* ---+ Z is a total function rather than
a partial function; in other words, we demand that ran(p) <;;; dom( c;) where
ran(p) = {p(x) 1 x E Var*} and dom(c;) = {~ l<> is defined onO.
The locations of the global variables of the program P* are given by a top-level
environment denoted p*; we shall assume that it maps all variables to unique
locations. The semantics of statements is now given relative to modifications
of this environment. The transitions have the general form
in case that the computation does not terminate in one step, and the form
Note that there is no need to modify the semantics of arithmetic and boolean
expressions.
For procedure calls we make use of the top-level environment, p*, and we
take:
2.5 Interprocedural Analysis 87
The idea is that we allocate new locations 6 and 6 for the formal parameters
x and y, and we then make use of a bind-construct to combine the procedure
body S with the environment p*[x f-+ 6, y f-+ 6] in which it must be executed
and we also record that the final value of y must be returned in the actual
parameter z. At the same time the store is updated such that the new location
for x is mapped to the value of the actual parameter a whereas we do not
control the initial value, v, of the new location for y. The bind-construct is
only needed to ensure that we have static scope rules and its semantics is as
follows:
p f- * (bind p' in S then z: =y, ~) ~ (bind p' in S' then z: =y, ~')
The first rule expresses that executing one step of the body of the construct
amounts to executing one step of the construct itself; note that we use the
local environment when executing the body. The second rule expresses that
when the execution of the body finishes then so does execution of the con-
struct itself and we update the value of the global variable z to be that of the
local variable y; furthermore, there is no need for the local environment p' to
be retained as subsequent computations will use the previous environment p.
Remark. Although the semantics works with two mappings, an environ-
ment and a store, it is often the case that the analysis abstracts the state,
i.e. the composition of the environment and the store. The correctness of the
analysis will then have to relate the abstract state both to the environment
and the store.
The correctness result will often be expressed in the style of Section 2.2:
information obtained by analysing the original program will remain correct
under execution of the program. The semantics presented above deviates
from that of the WHILE-language in that it introduces the bind-construct
which is only used in the intermediate configurations. So in order to prove
the correctness result we will also need to specify how to analyse the bind-
construct. We refer to Chapter 3 for an illustration of how to do this. •
88 2 Data Flow Analysis
• for each procedure call (cal! p(a, z)]~~ we have two transfer functions
hc and hr corresponding to calling the procedure and returning from
the call, and
A.(f) fp_(Ao(f))
iUEE
iU fţ E
When inspecting this equation system is should be apparent that both proce-
dure calls (le; in) and procedure returns (ix; lr) are treated like goto's: there
is no mechanism for ensuring that information flowing along (le; in) from a
call to a procedure only flows back along (fx; lr) from the procedure to the
same call. (Indeed, nowhere does the formulation consult the interprocedu-
ral flow, IF.) Expressed in terms of the flow graph in Figure 2.7, there is
nothing preventing us from considering a path like (9, 1, 2, 4, 1, 2, 3, 8, 10) that
does not correspond to a run of the program. Intuitively, the equation system
considers a much too large set of "paths" through the program and hence
will be grossly imprecise (although formally on the safe side).
2.5 Interprocedural Analysis 89
The matching of calls and returns is ensured by the last kind of productions:
the flows (Re;Rn) and (Rx;Rr) are forced to obey a parenthesis structure in
that Re, Rn will be in the generated path only if there is a matching occur-
rence of Rx, Rr - and vice versa. Hen ce for a forward analysis, a terminating
computation will give rise to a complete path from init* to one of the labels
of final*. Note that the grammar constructed above will only have a finite
set of nonterminals because there is only a finite set of labels in P*.
Example 2.35 For the Fibonacci program of Example 2.33 we obtain the
following grammar (using forward flow and ignoring the parts not reachable
from CP9,10):
some procedures are entered but not yet exited. This will obviously include
all prefixes of the complete paths starting in E but we also have to take into
account prefixes of computations that might not terminate. To specify the
valid paths we therefore construct another grammar with productions:
The valid paths will then be generated by the nonterminal VP*. For a for-
ward analysis, to go from the labei ic of a procedure call to the program point
i there are two possibilities. One is that the call initiated at ic terminates
before reaching i and this corresponds to the second last kind of production
where we use the nonterminal CPtn,lx to generate the complete path corre-
sponding to executing the procedure body. The other possibility is that i
is reached before the call terminates and this corresponds to the last kind
of production where we simply use VPtn,l to generate a valid path in the
procedure body.
We can now modify the two sets of paths defined in Section 2.3 as follows
(keeping in mind that the definitions are implicitly parameterised on F, E
and IF):
vpath 0 (i) {[i~, · · ·, ln-l]i n 2: 1/\ ln = i 1\ [i1, ···,in] is a valid path}
vpath.(i) {[i1, · · ·, in]i n 2: 1/\ in = i 1\ [i1, ···,in] is a valid path}
Clearly the sets of paths are smaller than what would have resulted if we
had merely regarded (i 1; i2) as standing for (i 1, i2) and had used the notions
path0 (i) and path.(i) of Subsection 2.4.2.
Using valid paths we now define the MVP solution as follows:
and how to avoid taking too many invalid paths. An obvious approach is
to encode information about the paths taken into the data flow properties
themselves; to this end we introduce context information:
context information
The context may simply be an encoding of the path taken but we shall see
in Subsection 2.5.5 that there are other possibilities. We shall now show how
an instance of a Monotone Framework (as introduced in Section 2.3) can be
extended to take context into account.
(L, J, F, E, î, f)
• L = ~ - t L;
• the transfer functions in J are monotone; and
In other words, the new instance applies the transfer functions ofthe original
instance in a pointwise fashion.
Ignoring procedures, the data flow equations will take the form displayed
earlier:
A.(f) h(Ao(f))
for alllabels that do not labei a procedure call
(i.e. that do not occur as first or fourth components
of a tuple in IF)
where Sign = {-,O,+}. Thus Lsign describes sets of abstract states a•ign map-
ping variables to their possible signs. The transfer function J;ign associated
with the assignment [x :=a]" will now be written as
L-;;;;, = ~ ~ Lsign
but we shall prefer the following isomorphic definition
Thus f;;, describes sets of pairs of context and abstract states. The transfer
function associated with the assignment [x :=a]" will now be:
-
J;ign (Z) = UHo} x q;~ign(asign) 1 (o, asign) E Z}
J;:, k: (~ ~ L) ~ (~ ~ L)
In the case of our simple language we shall prefer to take both of these transfer
functions to be the identity function; i.e.
-r;: (Î) ~
k(Î)
for all Î E i. Hence the effect of procedure entry is handled by the trans-
fer function for procedure call (considered below) and similarly the effect of
procedure exit is handled by the transfer function for procedure return (also
2.5 Interprocedural Analysis 93
considered below). For more advanced languages where many semantic ac-
tions take place at procedure entry or exit it may be preferable to reconsider
this decision.
For a procedure call (fc, fn, fx, fr) E IF we shall define two transfer functions.
In our explanation we shall concentrate on the case of forward analyses where
P* contains [call p(a, z)]~~ as well as proc p(val x, res y) isln S endix.
Corresponding to the actual call we have the transfer function
- : (~ ~ L) ~ (~ ~ L)
!L
and it is used in the equation:
In other words, the transfer function modifies the data flow properties (and
the context) as required for passing to the procedure entry.
Corresponding to the return we have the transfer function
The first parameter of flc,tr describes the data flow properties at the call
point for the procedure and the second parameter describes the properties at
the exit from the procedure body. Ignoring the first parameter, the transfer
function modifies the data flow properties (and the context) as required for
passing back from the procedure exit. The purpose of the first parameter is
94 2 Data Flow Analysis
Variations. The functionality and use of J[;r (as well as Figure 2.8) is
sufficiently general that it allows us to deal with most of the scenarios found
in the literature. A simple example being the possibility to define
thereby completely ignoring the information before the call; this is illustrated
in Figure 2.9.
A somewhat more interesting example is the ability to define
that is global. (It may be worth noticing that the function JLI!r is com-
pletely additive if and only if it can be written in this form with
-
/[A;
c,r
and
fl!:tr being completely additive.)
Context-sensitive versus context-insensitive. So far we have
criticised the naive approach because it was unable to maintain the proper
2.5 Interprocedural Analysis 95
cedure calls we shall only record flows of the form (fc; fn) corresponding to
a procedure call. Formally we take
~ = Lab*
where the most recent labei fc of a procedure call is at the right end (just as
was the case for valid paths and paths); elements of ~ are called call strings.
We then define the extremal value î by the formula
î( 8) = { L if 8 = A
..1. otherwise
where A is the empty sequence corresponding to the fact that there are no
pending procedure calls when the program starts execution; t is the extremal
value available from the underlying Monotone Framework.
Example 2.37 For the Fibonacci program of Example 2.33 the following
call strings will be of interest:
A, [9], [9, 4], [9, 6], [9, 4, 4], [9, 4, 6], [9, 6, 4], [9, 6, 6], · · ·
2
f'Ltr
-- --
(l, l' )(8) = h,ir (l- (8), l'- ([8, fc]))
Here the information Î from the original call is combined with information fl
from the procedure exit using the function fl i : L x L __. L. However, only
information corresponding to the same conte:it; for call point fc is combined:
this is ensured by the two occurrences of 8 in the right hand side of the above
formula.
2.5 Interprocedural Analysis 97
Thus we extract all the information from the procedure body except for the
information about the formal parameters x and y and the actual parameter
z. For the formal parameters we rely on the information available before the
current call which is still correct and for the actual parameter we perform the
required update of the information. ~e that to facilitate this definition it
is crucial that the transfer function Jl c takes two arguments: information
from the call point as well as from thec'p~ocedure exit. •
Call strings of bounded length. Clearly the call strings can be-
come arbitrarily long because the procedures may be recursive. It is therefore
customary to restrict their length to be at most k for some number k ~ O;
the idea being that only the last k calls are recorded. We write this as
~ = Lab:'=k
î( o) = { L if o = A
j_ otherwise
Note that in the case k = O we have ~ = {A} which is equivalent to having
no context information.
Example 2.39 Consider the Fibonacci program of Example 2.33 and as-
sume that we are only interested in recording the last call, i.e. k = 1. Then
the call strings of interest are:
We shall now present the transfer functions for the g~eral case where call
strings have length at most k. The transfer function for procedure call JL
is redefined by
- -- 2 -- -
fLer (l, l' )(8) = h,eJl (8), l' ( 18, Re h))
as should be expected.
-
pign2
fc,P-r
(Y Y')
'
where Y, Y' ~ Var* -+ Sign. It is now easy to see that the analysis is context-
insensitive: at procedure return it is not possible to distinguish between the
different call points.
Let us next consider the case where k = 1. Here ~ = Lab U {A} and the
transfer functions for procedure call are:
tgn2
fc,l!-r
(Z, Z') u{{ 8} X <j>~i~,~~ ( O"~ign' O"~ign) 1 ( 8, O"~ign) E z
1\ (Re, O"~ign) E Z'}
-
Now the transfer function J;~gnl will mark all data from th~ll point Re with
that labei. Thus it does not harm that the information J;~gnl (Z) is merged
2.5 lnterprocedural Analysis 99
-
with similar information Jt~" 1 (Z) fro~nother procedure call. At the return
from the call the transfer function J;~~~~ selects those pairs (le, a~ign) E Z'
that are relevant for the current call and combines them with those pairs
(a, a~ign) E Z that describe the situation before the call; in particular, this
allows us to reset the context tobe that of the call point. •
meaning that the initial context is described by the initial abstract state. This
kind of context information is often called an assumption set and expresses
a dependency on data (as opposed to a dependency on control as in the case
of call strings).
.
-],
where </{ : D ---t P(D). The idea is as follows: a pair (8, d) E Z describes a
context and an abstract state for the current call. We now have to modify
the context to take the call into account, i.e. we have to determine the set of
possible abstract states in which the call could happen in the current context
and this is 8' = { d" 1 (8, d") E Z}. Given this context we proceed as in
the call string formulations presented above and mark the data flow property
fi- i
with this context.
Next we shall consider the transfer function c, r
for procedure return
and then use î = {(t, t)} as the extrema! value. So rather than basing the
embellished Monotone Framework on P(D) x D as above we now base it on
D x D. Of course, this is much less precise but, on the positive side, the size
of the data flow properties has been reduced dramatically.
2.5 Interprocedural Analysis 101
For a procedure call (fc, fn,!.:_, fr) E IF, i.e. [call p(a, z)J~: for forward anal-
yses, the transfer function JL is now defined by
---
the approach with large assumption sets.
The corresponding definition of the transfer function fi l for procedure
return then is c, r
inD*, the aim is to determine the set IAV(p) of global variables that might
be assigned directly or indirectly when p is called.
To compute these sets we need two auxiliary notions. The set AV(8) of
directly assigned variables gives for each statement 8 the set of variables
102 2 Data Flow Analysis
AV([skip] 1) 0
AV([x := a]l) {x}
AV(81;S2) AV(81) U AV(82)
AV(if [b]~' then 81 else 82) AV(81) u AV(82)
AV(while [W do 8) AV(S)
AV([call p(a, z)]~~) {z}
Similarly we shall need the set CP( 8) of immediately called procedures that
gives for each statement 8 the set of procedure names that could be directly
called in 8- but ignoring the effect of procedure callso It is defined inductively
upon the structure of 8:
CP([skip]~') 0
CP([x := a]~') 0
CP(81;82) CP(81) u CP(82)
CP(if [b]~' then 81 else 82) CP(81) u CP(82)
CP(while [b]' do 8) CP(8)
CP([call p(a, z)]~~) {p}
Both the sets AV( 8) and CP( 8) are well-defined by induction on the structure
of S; also it should be clear that they are context-insensitive in the sense that
any rearrangement of the statements inside 8 would have given the same
resulto The information in CP(o oo) can be presented graphically: let the
graph have a node for each procedure name as well as a node called main*
for the program itself, and let the graph have an edge from p (respectively
main*) to p' whenever the procedure body 8 of p has p' E CP(8) (respectively
p' E CP( S*)) o This graph is usually called the procedure call grapho
We can now formulate a system of data fl.ow equations that specifies how to
obtain the desired sets IAV(p):
By analogy with the considerations in Section 201 we want the least solution
of this system of equationso
2.5 Interprocedural Analysis 103
Example 2.42 Let us now consider the following version of the Fibonacci
program (omitting labels):
The associated procedure call graph is shown in Figure 2.11. The least solu-
tion to the equation system is
Note that the formulation of the example analysis did not associate infor-
mation with entries and exits of blocks but rather with the blocks (or more
generally the statements) themselves. This is a rather natural space saving
approach for a context-insensitive analysis. It also relates to the discussion
of Type and Effect Systems in Section 1.6: the "annotated base types" m
Table 1.2 versus the "annotated type constructors" in Table 1.3.
104 2 Data Flow Analysis
[malloc p]l
Arithmetic expressions are extended to use pointer expressions rather than
just variables, and an arithmetic expression can also be the constant nil.
2.6 Shape Analysis 105
The binary operations opa are as before, that is, they are the standard arith-
metic operations and in particular they do not allow pointer arithmetic. The
boolean expressions are extended such that the relational operators opr now
allow testing for the equality of pointers and also we shall allow unary opera-
tions opP on pointers (as for example is-nil and has-sel for each sel E Sel).
Note that arithmetic as well as boolean expressions can only access cells in
the heap, they cannot create new cells nor update existing cells.
The assignment statement takes the general form p: =a where p is a pointer
expression. In the case where p is just a variable we have an extension of the
ordinary assignment of the WHILE language and in the case where p contains
a selector we have a destructive update of the heap. The statements of the
extended language also contain a statement malloc p for creating a new cell
pointed to by p.
0: Y --o
z --o
2:
3:
4:
x--o
5:
where as usual Var* is the (finite) set of variables occurring in the program
of interest. As mentioned above the cells of the heap have multiple fields and
they are accessed using the selectors. Each field can either be an integer, a
pointer to another cell or it can be nil. We formalise this by taking
where the use of partial functions with finite domain reflects that not all
selector fields need to be defined; as we shall see later, a newly created cell
with location ~ will have all its fields to be uninitialised and hence the corre-
sponding heap H will have H(~, sel) tobe undefined for all sel E Sel.
p[x](a, H) a(x)
H(a(x), sel)
{ if a(x) E Loc and 1i is defined on (a(x), sel)
p[x.sel](a, H)
undef
if a(x) fţ Loc or H is undefined on (a(x), sel)
The first clause takes care of the situation where p is a simple variable and
using the state we determine its value - note that this may be an integer,
a location or the special Iiil-value o. The second clause takes care of the
case where the pointer expression has the form x.sel. Here we first have to
determine the value of x; it only makes sense to inspect the sel-field in the
case x evaluates to a location that has a sel-field and hence the clause is split
into two cases. In the case where x evaluates to a location we simply inspect
the heap H to determine the value of the sel-field - again we may note that
this can be an integer, a location or the special value o.
Example 2.44 In Figure 2.12 the oval nodes model the cells of the heap
H and they are labelled with their location (or address). The unlabelled edges
denote the state a: an edge from a variable x to some node labelled ~ means
that a(x) = ~; an edge from x to the symbol o means that a(x) = o. The
labelled edges model the heap H: an edge labelled sel from a node labelled
~ to a node labelled e means that there is a sel pointer between the two
cells, that is H(~, sel) =~';an edge labelled sel from a node labelled ~ to the
symbol o means that the pointer is a nil-pointer, that is H(~, sel) =o.
Consider the pointer expression x.cdr and assume that a and H are as
in row O of Figure 2.12, that is a(x) = 6 and H(6, cdr) = 6. Then
p[x.cdr](a, H) = 6. •
semantic functions A and B has tobe changed to take the heap into account:
A[p](a, H) g:J[p](a, H)
A[n](a, H) = N[n]
A[a1 opa a2](a, H) A[ai](a, H) opa A[a2](a, H)
A[nil](a, H) o
Analogously to above, the meaning opr of the binary relation operator opr
has to be suitably modified to give undefined in case the arguments are not
both integers or both pointers (in which case the equality operation tests for
the equality of the pointers). The meaning of the unary operation opP is
defined by opP; as an example:
. "l( ) =
18-lll V
{ tt if V = O
ff otherw1se
.
refl.ecting that for the assignment x: =a the state is updated as usual and the
heap is left unchanged. In the case where we assign to a pointer expression
containing a selector field we shallleave the state unchanged and update the
heap as follows:
Here the side condition ensures that the left hand side of the assignment does
indeed evaluate to a location.
The construct malloc p is responsible for creating a new cell. We have two
clauses depending on the form of p:
([malloc x]", cr, 'H)--+ (cr[x ~---+ ~], 'H)
where ~ does not occur in cr or 'H
m
([malloc (x.sel)J",cr,'H)--+ (cr,'H[(cr(x),sel) 1--+
where ~ does not occur in cr or 'Hand cr(x) E Loc
Note that in both cases we introduce a fresh location ~but we do not specify
any values for 'H(~, sel) -as discussed before we have settled for a semantics
where the fields of~ are undefined; obviously other choices are possible. Also
note that in the last clause the side condition ensures that we already have a
location corresponding to x and hence can create an edge to the new location.
Remark. The semantics only allows a limited reuse of garbage locations.
For a statement like [malloc xjl; [x:=nilj2; [malloc yj3 we will assign some
location to x at the statement with labei 1 and since it neither occurs in
the state nor the heap after the assignment labelled 2 we are free to reuse
it in the statement labelled 3 (but we do not have to). For a statement like
[malloc x]\ [x.cdr:=nilj2; [x:=nilj3; [malloc y] 4 we would not be able to
reuse the location allocated at 1 although it will be unreachable (and hence
garbage) after the statement labelled 3. •
• an abstract state, S,
• an abstract heap, H, and
• sharing information, is, for the abstract locations.
Since Var* is finite it is clear that ALoc is finite and a given shape graph
will contain a subset of the abstract locations of ALoc.
The idea is that if x E X then the abstract location nx will represent the lo-
cation t1(x). The abstract location n0 is called the abstract summary location
and will represent ali the locations that cannot be reached directly from the
state without consulting the heap. Clearly nx and n0 will represent disjoint
sets of locations when X =/= 0.
In general, we shall enforce the invariant that two distinct abstract locations
nx and ny always represent disjoint sets of locations. As a consequence, for
any two abstract locations nx and ny it is either the case that X = Y or
that X n Y = 0. To prove this assume by way of contradiction that X=/= Y
and that z E X n Y. From z E X we get that t1(z) is represented by nx and
similarly z E Y gives that t1(z) is represented by ny. But then t1(z) must be
distinct from t1(z) and we have the desired contradiction.
The invariant can be formulated as follows:
Example 2.45 Consider the state and heap in row 2 of Figure 2.12. The
variables x, y and z point to different locations (6, 6, and 6, respectively)
so in the shape graph they will be represented by different abstract locations
named n{x}' n{y} and n{z}· The two locations ~4 and 6 cannot be reached
directly from the state so they will be represented by the abstract summary
location n0. •
From Invariant 1 it follows that there will be at most one abstract location
in the shape graph containing a given variable.
We shall only be interested in the shape of the heap so we shall not distinguish
between integer values, nil-pointers and uninitialised fields; hence we can
view the abstract state as an element of
Thus the target of a selector field will be uniquely determined by the source
unless the source is the abstract summary location n0.
Example 2.46 Continuing Example 2.45 we can now see that the ab-
stract state S2 corresponding to the state of row 2 of Figure 2.12 will be
cdr
X~
1:
2: 3: y
X ---r:::EI:I
4: y 5: y
z z
2.12. The square nodes model abstract locations; the unlabelled edges from
variables to square nodes model the abstract state and the labelled edges
between square nodes model the abstract heap. If the abstract state does
not associate an abstract location with some variable then that variable does
not occur in the picture.
Note that even if the semantics uses the same locations throughout the
computation it need not be the case that the locations are associated with
the same abstract locations at all points in the analysis. Consider Figures
2.12 and 2.13: the abstract location n0 will in turn represent the locations
{6,6,~4,~5}, {6,~4,6}, {~4,~5}, {6,~5}, {6,6} and {6,6,6}. •
cdr
x~~cdr
X
y
y
cdr
X~~
X
0 y
y
0~
X
cdr
X~
y
y
Obviously, the abstract heaps themselves also contain some implicit sharing
information: this is illustrated in the bottom row of Figure 2.14 where there
are two distinct edges with target n{y}. We shall ensure that this implicit
sharing information is consistent with the explicit sharing information (as
given by is) by imposing two invariants. The first ensures that information
in the sharing component is also refl.ected in the abstract heap:
(a) (n0, sel, nx) is in the abstract heap for some sel, or
(b) there exists two distinct triples (nv,seh,nx) and (nw,seh,nx)
in the abstract heap (that is either seh =f. sel2 or V =f. W).
Case 4(a) takes care of the situation where there might be severallocations
represented by n0 that point to nx (as in the second and third rows of
Figure 2.14). Case 4(b) takes care of the case where two distinct pointers
(with different source or different selectors) point to nx (as in the bottom
row of Figure 2.14).
The second invariant ensures that sharing information present in the abstract
heap is also refl.ected in the sharing component:
This takes care of the case where nx represents a single location being the
target of two or more heap pointers (as in the bottom row of Figure 2.14).
Note that invariant 5 is the "inverse" of invariant 4(b) and that we have no
"inverse" of invariant 4(a) - the presence of a pointer from n0 to nx gives
no information about sharing properties of nx.
In the case of the abstract summary location the explicit sharing information
clearly gives extra information: if n0 E is then there might be a location
represented by n0 that is the target of two or more heap pointers, whereas
if n0 fţ is then all the locations represented by n0 will be the target of at
most one heap pointer. The explicit sharing information may also give extra
information for abstract locations nx where X i=- 0: from 4(a) alone we
cannot deduce that nx is shared- this is clearly illustrated for the node n{y}
by the top two rows of Figure 2.14.
2. V(x, nx) E 5 : x E X
3. V(nv,sel,nw),(nv,sel,nw,)EH:(V=0) V (W=W')
4. Vnx E is : (3sel : (n0, sel, nx) E H) V
(3(nv,selt,nx),(nw,sel2,nx) E H:
seh i=- sel2 V V i=- W)
5. V(nv,selt,nx),(nw,sel2,nx) E H:
((seh i=- sel2 V Vi=- W) 1\ X i=- 0)::::} nx E is
cdr
X~
Figure 2.15: The single shape graph in the extremal value L for the list
reversal program.
L if f = init(S*)
Shape0 (f) {
U{Shape.(f') 1 (f',f) E flow(S*)} otherwise
where L E P(SG) is the extremal value holding at entry to S* and JjA are
the transfer functions to be developed below.
The analysis is a forward analysis since it is defined in terms of the set
flow(S*), and it is a may analysis since we are using U as the combina-
tion operation. However, there are also aspects of a must analysis because
each individual shape graph must not contain any superfluous information.
This will be useful for achieving strong update and strong nullification; here
"strong" means that an update or nullification of a pointer expression allows
one to remove the existing binding before adding a new one. This in turn
leads to a very powerful analysis.
Example 2.47 Consider again the list reversal program of Example 2.43
and assume that x initially points to an unshared list with at least two el-
ements and that y and z are initially undefined; the singleton shape graph
corresponding to this state and heap is illustrated in Figure 2.15 and will be
the extrema} value L used throughout this development.
The Shape Analysis computes the sets Shape0 (f) and Shape.(f) of shape
graphs describing the state and heap before and after executing the elemen-
tary block labelled f. The equations for Shape. (f) are
The transfer function J[A : P(SG) ---t P(SG) associated with a labei, l, has
the form:
j{A(SG) = U{ cf>EA((5, H, is)) 1 (5, H, is) E SG}
where cf>EA : SG ---t P(SG) specifies how a single shape graph (in Shape0 (l))
may be transformed into a set ofshape graphs (in Shape.(i)) by the elemen-
tary block labelled l. We shall now inspect the various forms of elementary
block and specify cf>~A in each case. We shall first consider the boolean ex-
pressions and the skip-statement, then the different forms of assignments
and finally the malloc-statement.
Transfer function for [W~ and [ski p jl. We are only interested in
the shape of the heap and the boolean tests do not modify the heap. Hence
we take
cf>EA((5, H, is)) = {(5, H, is)}
so that the transfer function J[A will be the identity function. Similarly for
the skip-statement.
An interesting case is when (x, n{ x}) E S since this will cause the two abstract
locations n{x} and n0 tobe merged. The sharing information is then updated
to capture that we can only be sure that n0 is unshared in the updated shape
graph if both n0 and n{x} were unshared in the original shape graph. This is
illustrated in Figure 2.16: the left hand picture shows the interesting parts of
the shape graph (S, H, is) and the right hand picture shows the corresponding
parts of (S', H', is'). We shall assume that the square boxes represent distinct
abstract locations so in particular V, {x}, W and 0 are all distinct sets.
The fat boxes represent unshared abstract locations as before, the thin boxes
represent abstract locations whose sharing information is not affected by the
transfer function, and unlabelled edges between abstract locations represent
pointers that are unaffected by the transfer function.
X
X
y y
Y(n ) = { nzu{x} if y E Z
9x z nz otherwise
so that we obtain strong update. Here the second clause in the formula for
5" adds the new binding to x. Again we note that if (5, H, is) is compatible
. (5" , H" , .1s") •
t hen so 1s
The clause is illustrated in Figure 2.17 where we assume that nodes represent
distinct abstract locations; it follows from the invariants that y E Y but y fţ. V
and y fţ. W. Note that nyu{x} inherits the sharing properties of ny although
2.6 Shape Analysis 119
both x and y will point to the same cell; the reason is that the sharing
information only records sharing in the heap- not sharing via the state.
Example 2.51 The statement [y: =x] 4 of the list reversal program of Ex-
ample 2.43 is of the form considered here: each of the shape graphs of
Shape.(3) in Example 2.47 is transformed into one of the shape graphs of
Shape.(4).
Also the statement [z: =yj3 is of the form considered here: each of the shape
graphs of Shape.(2) is transformed into one of those of Shape.(3). •
!iSA = /SA
ia
0 /SA
i2
0 /SA
i1
where the transfer functions Jj2A and Jj3A follow the pattern described above.
ff
We shall therefore concentrate on the transfer function 1A, or equivalently,
JfA in the case where x -f=. y.
Example 2.52 The statement [x:=x.cdr] 5 of the list reversal program of
Example 2.43 is transformed into [t: =x.cdr] 51 ; [x: =tj5 2 ; [t: =ni1] 53 . We shall
return to the analysis of [t: =x.cdr] 51 later. •
So assume that x -f=. y and let (S, H, is) be a compatible shape graph before
the analysis of the statement. As in the previous case, the first step will be
to remove the old binding for x and again we use the auxiliary function killx:
The next step will be to rename the abstract location corresponding to y.sel
to include x in its name and to establish the binding of x to that abstract
location. We can now identify three possibilities:
3. There is an abstract location ny such that (y, ny) E S' and (ny, sel, n0)
E H'; in this case the shape graph will represent a state and a heap
where no other variable points to the location pointed to by y.sel.
Case 1. First consider the statement [x:=y.set]f (where x =/=- y) in the case
where there is no abstract location ny such that (y, ny) E S'. Then there is
no abstract location for y.sel and hence no abstract location to rename and
no binding to establish. Thus we take:
t
(5, H, is) 1 (S", J,,, is") 1
(5', H', is') (5"', H"', is111 )
which means that killx ( (5", H", is")) = (5', H', is'). It is also immediate that
(x,n{x}) E 5" and that (ny,sel,n{x}) E H".
We shall then take
4>IA((5, H, is)) = {(5", H", is") 1 (5", H", is") is compatible 1\
killx ((5", H", is")) = (5', H', is') 1\
(x, n{x}) E 5" 1\ (ny, sel, n{x}) E H"}
is'\{n0} is"\{n0,n{x}}
n0 E is' iff n0 E is" V n{x} E is"
showing that
Since both (5', H', is') and (5", H", is") are compatible shape graphs it follows
that if nu E is' then x ţf. U and if nu E is" then x ţf. U V {x} = U.
Hence is' = {kx(nu) 1 nu E is"} establishes is' \ {n0} = is" \ {n0,n{x}}
2.6 Shape Analysis 123
(5, H, is)
We shall also say that two edges (nv, sel', nw) and (n~, sel", n~) are related
if and only if kx (nv) = kx (n~), sel' = sel" and kx (nw) = kx (n~). Clearly
an external edge is related only to itself.
Reasoning as above one can show that:
• each internal edge in H' is related to an internal edge in H" and vice
versa,
• each edge going-out in H' is related to an edge going-out in H" and vice
versa, and
• each edge going-in in H' is related to an edge going-in in H" and vice
versa.
124 2 Data Flow Analysis
~
(s"3• H"3• .l53") (s "4• H"4• .l54")
X X
y y
One consideration is that the going-in edge (ny, sel, n0) E H' should be
changed into the going-in edge (ny,sel,n}xJ) E H". We clearly demanded
that (ny, sel, n{ x}) E H" and because (S' , H", i511 ) is compatible it follows
that (ny,sel,n0) ~ H".
As a more concrete illustration consider the scenario in Figure 2.19. Here
neither n0 nor nw is shared and we assume that both nv and nw are distinct
from n0; we also assume that x # y and sel2 # sel3. The result of the
2.6 Shape Analysis 125
transfer function is shown in Figure 2.20. First note that the going-in edge
(ny, sel, n0) E H is changed to (ny, sel, n{x}) E H~' in all shape graphs. Next
note that the going-in edge labelled seh can only point to n0 because n{x} is
not shared (as n0 is not) and ny points to n{x}· The going-out edge labelled
sel 2 can start at both n0 and n{x} but it cannot do so simultaneously because
nw is not shared. The interna! edge labelled sel3 can only point to n0 because
n{x} is not shared and ny points to n{x}; but it can start at both n0 and n{x}
and can even do so simultaneously. This explains why there are six shape
graphs in q)~A((S, H, i5)), all of which are clearly needed.
X X~
Figure 2.21: The effect of [x.sel: =nil]R when #into(nu, H') ::;1.
Remark. Again we shall note that the analysis does not incorporate garbage
collection: it might be the case that there is only one pointer to the abstract
location nu and that after the assignment x.sel: =nil the corresponding lo-
cation will be unreachable. However, the abstract location may still be part
of the shape graph. •
The interesting case is when x =/= y, (x, nx) E S and (y, ny) E S. The first
step will be to remove the binding for x.sel and for this we can use the killx.sel
function. The second step will be to establish the new binding. So we take
cp~A((5, H, is)) = {(5", H", is")}
2.6 Shape Analysis 127
X X
Figure 2.22: The effect of [x.sel: =y]t when #into(ny, H') < 1.
S" S' (= S)
H" H' U {(nx, sel,ny)}
is' U {ny} if #into(ny, H') ~ 1
is" {
is' otherwise
Note that the node ny might become shared when we add a new pointer to
it. The effect of the transfer function is illustrated in Figure 2.22.
for t being a fresh variable and ft, f2 and f3 being fresh labels. Thus the
transfer function JiA satisfies
! tSA = /SA
ta 0
/SA 0 fSA
t2 t1
where t is a fresh variable and €1, €2 and €3 are fresh labels. The transfer
function f'fA is then
/z /z /J
The transfer functions 1A, 2A and 3A all follow the patterns we have seen
before so this completes the specification of the transfer function.
Concluding Remarks
Data Flow Analysis for imperative languages. As mentioned
in the beginning of this chapter, Data Flow Analysis has a long tradition.
Most compiler textbooks contain sections on optimisation which mainly dis-
cuss Data Flow Analyses and their implementation [5, 55, 181]. The emphasis
in these books is often on practica! implementations of data flow analyses. A
classic textbook which provides a more theoretical treatment of the subject
is by Hecht [69]; the book contains a detailed discussion of the four example
Data Flow Analyses in Section 2.1, and also presents a more traditional treat-
ment of Monotone Frameworks based on the use of semi-lattices as well as a
number of algorithms (see Chapter 6 for a more thorough treatment of algo-
rithms). Marlowe and Ryder [103] provide a survey of data flow frameworks.
Steffen [164] and Schmidt [151] express data flow analyses using modallogic
(rather than equations) thereby opening up the possibility of using model
checking techniques for program analysis.
The examples presented in Section 2.1 are fairly standard. Alternative treat-
ments of this material can be found in any of the books already cited. The
examples may ali be represented as Bit Vector Frameworks (see Exercise 2.9):
the lattice elements may be represented by a vector of bits and the lattice op-
erations efficiently implemented as boolean operations. The method used in
Section 2.2 to prove the correctness of the Live Variables Analysis is adapted
from [112] and is expressed by means of an operational semantics [140, 130].
The notion of faint variables, introduced in Exercise 2.4, was first introduced
by Giegerich, Moncke and Wilhelm [65].
The use of semi-lattices in Data Flow Analysis was first proposed in [96]. The
notion of Monotone Frameworks is due to Kam and Ullman [93]. These early
papers, and much of the later literature, use the dual notions (meets and
maximal fixed points) to our presentation. Kam and Ullman [93] prove that
the existence of a general algorithm to compute MOP solutions would imply
the decidability of the Modified Post Correspondence Problem [76]. Cousot
and Cousot [37] model abstract program properties by complete semi-lattices
in their paper on Abstract Interpretation (see Chapter 4).
Concluding Remarks 129
of the heap by more directly approximating the access paths. Here a main
distinction is the kind of properties of the access paths that are recorded:
some focus on simple connectivity properties [62], others use some limited
form of regular expressions [101], and yet others use monomial relations [45].
The analysis presented in Section 2.6 is based on the work of Sagiv, Reps
and Wilhelm [148, 149]. In contrast to [148, 149] it uses sets of compatible
shape graphs; [148, 149] merge sets of compatible shape graphs into a single
summary shape graph and then use various mechanisms for extracting parts
of the individual compatible shape graphs and in this way an exponential
factor in the cost of the analysis can be avoided. The sharing component of
the shape graphs is designed to detect list-like properties; it can be replaced
by other components detecting other shape properties [150].
l[x := ···]···1
~
Î.J
ud-chain
~
\
()
r;
du-chain
1..1
Mini Projects
[if2] (if [bjf then SI else S2, a, tr) ----+ (S2, a, tr) if B[b]a = false
[whi] (while [bjf do S, a, tr) ----+ ((S; while [bjf do S), a, tr)
if B[b]a = true
The aim of this mini project is to prove the correctness of Reaching Definitions
with respect to the notion of semantic reaching definitions introduced in
Section 1.5. To get a precise definition of the set of traces of interest we shall
begin by introducing a so-called instrumented semantics: an extension of a
more traditional semantics that keeps track of additional information that is
mainly of interest for the program analysis.
The instrumented semantics has transitions of the forms:
where x 1 , · · · , Xn are the variables in Var* and to consider the finite derivat ion
134 2 Data Flow Analysis
sequence:
tr' E TrVar: = {tr E Trace 1 Vx E Var*: :Jl' E Lab:: (x,l') occurs intr}
Intuitively (and as can be proved formally), there should bea similar deriva-
tion sequence (S*, CT*) --t* CT 1 in the Structural Operational Semantics. Simi-
lar remarks apply to infinite derivation sequences.
As in Section 2.2 we shall study the constraint system RD~ (S*) corresponding
to the equation system RD= (S*). Let reach be a collection of functions:
?
reachentry,reachexit: Lab* --t P(Var* x Lab~)
In this mini project we shall implement one of the program analyses consid-
ered in Section 2.1. As implementation language we shall choose a functional
language such as Standard ML or Haskell. We can then define a suitable
Exercises 135
Exercises
Exercise 2.1 Formulate data flow equations for the Reaching Definitions
Analysis of the program studied in Example 1.1 of Chapter 1 and in particular
define the appropriate gen and kill functions. •
Clearly x is dead at the exits from 2 and 3. But x is live at the exit of 1 even
though its only use is to calculate a new value for a variable that turns out to
be dead. We shall say that a variable is a faint variable if it is dead or if it is
only used to calculate new values for faint variables; otherwise it is strongly
live. In the example x is faint at the exits from 1, 2 and 3. Define a Data
Flow Analysis that detects strongly live variables. (Hint: For an assignment
[x := a]e the definition of fe(l) should be by cases on whether x is in l or
not.) •
Exercise 2.5 A basic block is often taken tobe a maximal group of state-
ments such that all transfers to the block are to the first statement in the
group and, once the block has been entered, all statements in the group are
executed sequentially. In this exercise we shall consider basic blocks of the
form
Exercise 2. 7 Consider the correctness proof for the Live Variables Anal-
ysis in Section 2.2. Give a compositional definition of LV=(···) for a labei
consistent statement using
as one of the clauses and observe that a similar development is possible for
LV~(···). Give a formal definition of live f= C where C is a set of equalities
or inclusions as might have been produced by LV=(s) or LV~(S).
Prove that {live llive f= LV~(S)} is a Moare family in the sense of Appendix
A (with n being n and determine whether or not a similar result holds for
{live llive f= Lv=(s)}. •
Show that the four classical analyses of Section 2.1 are Bit Vector Frame-
works. Show that all Bit Vector Frameworks are indeed Distributive Frame-
works. Devise a Distributive Framework that is not also a Bit Vector Frame-
work. •
Exercise 2.1 O Consider the Constant Propagation Analysis of Section
2.3.3 and the program
if [W then s1 else s2
this prevents us from using the result of the test to pass different information
to S1 and S2; as an example SUppose that X is known to be positive Or negative
and that b is the test x>O, then X is always positive at the entry to sl and
always negative at the entry to S2 . To remedy this deficiency consider writing
[b]l' as [b]l' 1 ,e2 where .e1 corresponds tob evaluating to true and .e2 corresponds
to b evaluating to false. Make the necessary changes to the development in
Sections 2.1 and 2.3. (Begin by considering forward analyses.) •
Exercise 2.17 Consider the Fibonacci program of Example 2.33 and the
Detection of Signs Analysis of Exercise 2.15 and Example 2.36. Construct
the data flow equations corresponding to using large and small assumption
sets, respectively. •
Exercise 2.18 Choose one of the four classical analyses from Section 2.1
and formulate it as an interprocedural analysis based on call strings. (Hint:
Some may be easier than others.) •
so that it maps integers to integers rather than states to states. Consider the
Detection of Signs Analysis and define the transfer functions for the input
and output statements. •
Exercise 2.20 Consider extending the procedure language such that pro-
cedures can have multiple call-by-value, call-by-result and call-by-value-result
parameters as well as local variables and reconsider the Detection of Signs
Analysis. How should one define the transfer functions associated with pro-
cedure call, procedure entry, procedure exit, and procedure return? •
Exercise 2.22 In the Shape Analysis of Section 2.6 work out direct def-
initions of the transfer functions for elementary statements of the forms
[x: =x.seW, [x.sel: =x]R, [x.sel: =x.sel']R and [malloc (x.sel)jR. •
Exercise* 2.24 The Shape Analysis as presented in Section 2.6 does not
take garbage collection into account. Modify the Structural Operational Se-
mantics of the pointer language to perform garbage collection and subse-
quently modify the analysis to reflect this. •
Syntax of the FUN language. For the main part of this chapter
we shall concentrate on a small functional language: the untyped lambda
calculus extended with explicit operators for recursion, conditiona} and local
definitions. The purpose of the Control Flow Analysis will be to compute
for each subexpression the set of functions that it could evaluate to, and to
express this it is important that we are able to labei all program fragments.
We shall be very explicit about this: a program fragment with a labei is
called an expression whereas a program fragment without a labei is called a
term. So we use the following syntactic categories:
e E Exp expressions (or labelled terms)
t E Term terms (or unlabelled expressions)
We assume that a countable set of variables is given and that constants
(including the truth values), binary operators (including the usual arithmetic,
boolean and relational operators) and labels are left unspecified:
J, xE Var variables
c E Const constants
op E Op binary operators
l E Lab labels
The abstract syntax of the language is now given by:
e .. - ti
Example 3.1 The functional program (fn x => x) (fn y => y) consid-
ered in Section 1.4 is now written as:
Compared with the notation of Example 1.2 we have omitted the square
brackets. •
• Cis the abstract· cache associating abstract values with each labelled
program point.
• p is the abstract environment associating abstract values with each
variable.
V E -----
Val = P(Term) abstract values
p E Env Var --t Val abstract environments
c E -----
Cache Lab --t
-----
Val abstract caches
v
Here an abstract value is an abstraction of a set of functions: it is a set of
terms of the form fn x => eo or fun f x =>ea. We will not be recording any
constants in the abstract values because the analysis we shall specify is a pure
Control Flow Analysis with no Data Flow Analysis component; in Section
3.5 we shall show how to extend it with Data Flow Analysis components.
Furthermore, we do not need to assume that all bound variables are distinct
and that all labels are distinct, but clearly, greater precision is achieved if
this is the case; this means that semantically equivalent programs can have
different analysis results and this is a common feature of all approaches to
program analysis. As we shall see an abstract environment is an abstraction
of a set of environments occurring in closures at run-time (see the semantics
in Subsection 3.2.1). In a similar way an abstract cache might be considered
as an abstraction of a set of execution profiles: as discussed below some texts
prefer to combine the abstract environment with the abstract cache.
Example 3.3 Consider the expression ((fn x => x1)2 (fn y => y3)4)5
of Example 3.1. The following table contains three guesses of a 0-CFA anal-
ysis:
Intuitively, the guess (Ce, Pe) of the first column is acceptable whereas the
guess (C~, P:,) of the second column is wrong: it would seem that fn x =>
x 1 is never called since ~(x) = 0 indicates that x will never be bound to
any closures. Also the guess (C~, fl:) of the third column would seem to be
acceptable although clearly more imprecise than (Ce, Pe)· •
3.1 Abstract 0-CFA Analysis 145
Example 3.4 Let us consider the expression, loop, of Example 3.2 and
introduce the following abbreviations for abstract values:
One guess of a 0-CFA analysis for this program is (Cip, pjp) defined by:
Intuitively, this is an acceptable guess. The choice of Plp(g) = {f} refl.ects that
g will evaluate to a closure constructed from that abstraction. The choice of
Pip(x) = {idy, idz} refl.ects that x will be bound to closures constructed from
both abstractions in the course of the evaluation. The choice of C1p(l0) = 0
refl.ects that the evaluation of the expression will never terminate. •
We have already said that Control Flow Analysis computes the interproce-
dural fl.ow information used in Section 2.5. It is also instructive to point
out the similarity between Control Flow Analysis and Use-Definition chains
( ud-chains) for imperative languages (see Subsection 2.1.5): in both cases we
attempt to trace how definition points reach points of use. In the case of
Control Flow Analysis the definition points are the points where the function
abstractions are created, and the use points are the points where functions
are applied; in the case of Use-Definition chains the definition points are the
points where variables are assigned a value, and the use points are the points
where values of variables are accessed.
Remark. Clearly an abstract cache C : Lab --+ Val and an abstract environ-
ment p: Var --+ Val can be combined into an entity of type (Var U Lab) --+
Val. Some texts dispense with the labels altogether, simply using an abstract
environment and no abstract cache, by ensuring that all subterms are prop-
erly "labelled" by variables. This type of expression frequently occurs in the
internals of compilers in the form of "continuation passing style", "A-normal
form" or "three address code". We have abstained from doing so in order to
illustrate that the techniques not only work for compiler intermediate forms
but also for general programming languages and calculi for computation; this
fl.exibility is useful when dealing with non-standard applications (as discussed
in the Concluding Remarks). •
146 3 Constraint Based Analysis
[!un] (C,P) f= (fun f x => eo)e iff {fun f x => eo} ~ C(l)
[app] (C,P) F (til t~2)e
iff (C, P> F= til 1\ (C, p-) F= t~2 1\
(V(fn x => t~0 ) E C(lt) :
(c, ?) F= t~o 1\
C(l2) ~ p-(x) 1\ C(lo) ~ c(t)) 1\
(V(fun f x => t~0 ) E C(ll) :
(c, ?) F= t~o 1\
C(l2) ~ p-(x) 1\ C(lo) ~ c(t) 1\
{fun f X=> t~0 } ~ P(f))
[i~ (C, P) f= (if t~0 then ti1 el se t~2 )e
iff (C, P> F= t~0 1\
(C, P> F= til 1\ (c, P> F= t~2 1\
c(tl) ~ c(t) 1\ C(l2) ~ C(l)
[let] (C, P) f= (let x = ti1 in t~2 )e
iff (c, P> F= til 1\ (C, ?) F= t~2 1\
C(lt) ~ ?(x) 1\ C(l2) ~ c(t)
[ opJ (C, P> F= (til op t~2 )t iff (C, P> F= til 1\ (C, ?) F= t~2
Table 3.1: Abstract Control Flow Analysis (Subsections 3.1.1 and 3.1.2).
c p
~
'"
1 - 1 1
[ ' '
'S::: l
Hl
A
1 1t~21
A
~
and that the result of the function evaluation (labelled fo) is linked to the
result of the application itself (labelled f)
and finally, that the function body itself can be analysed using (C, P):
This is illustrated in Figure 3.2. For terms fun f x => t~0 the demands are
much the same except that the term itself additionally needs to be included
in p(f) in order to refl.ect the recursive nature of fun f x => t~0 •
Example 3.5 Consider Example 3.3 and the guesses of a 0-CFA analysis
for the expression. First we show that (Ce, Pe) is an acceptable guess:
1..-.........-.._...'"'-'"""-''...................................-.......-.................... 1 "
( fn X =>
1
t~0 ) 1 tf
A
1
1 1t~2 1----------.
A
lt~l----------~-'--'~----~
A
L - - - - -' L - -
L - - xl'~--~------------------------------------~
Figure 3.2: Pictorial illustration of the clauses [app], [fn] and [var].
All of these are easily checked using the clauses [fn] and [var].
Next we show that (C~, ?.,) is not an acceptable guess:
lhs ~ rhs
where rhs is ofthe form C(i) or p(x) and where lhs is ofthe form C(l), p(x), or
{t}. These inclusions express how the higher-order entities may flow through
the expression.
It is important to observe that the clauses [fn] and [tun] do not contain
"recursive calls" demanding that subexpressions must be analysed. Instead
one relies on the clause [app] demanding this for all "subexpressions" that
may eventually be applied. This is a phenomenon common in program anal-
ysis, where one does not want to analyse unreachable program fragments:
occasionally results obtained from these parts of the program can suppress
transformations in the reachable part of the program. It also allows us to
deal with open systems where functions may be supplied by the environment;
this is particularly important for languages involving concurrency. However,
150 3 Constraint Based Analysis
note that this perspective is different from that of type inference, where even
unreachable fragments must be correctly typed.
In the terminology of Section 2.5 the analysis is ftow-insensitive because FuN
contains no side effects and because we analyse the operand to a function call
even when the operator cannot evaluate to any function; see Exercise 3.3 for
how to improve on this. Also the analysis is context-insensitive because it
treats all function calls in the same way; we refer to Section 3.6 for how to
improve on this.
As an example we have:
and the suggested analysis result (Cip, Pip)· To show (Cip, Plp) ~ loop it is,
according to the clause [let], sufficient to verify that
because Clp(5) ~ Pip(g) and Clp(9) ~ C1p(lO). The first clause follows from
[fun] and for the second clause we use that C1p(6) = {f} so it is, according to
[app], sufficient to verify that
because C1p(8) ~ Pip(x), Clp(4) ~ Clp(9) and f E Pip(f). The first two clauses
now follow from [var] and [fn]. For the third clause we proceed as above and
since Clp(1) = {f} it is sufficient to verify
(C1p, Plp) ~ f 1
(C1p, Pip) ~ (fn y => y 2 ) 3
(Cip,Pip) ~ (fl (fn y => y2)3)4
The semantic correctness result is important since it ensures that the infor-
mation from the analysis is indeed a safe description of what will happen
during the evaluation of the program. The result about the existence of least
solutions ensures that all programs can be analysed and furthermore that
there is a "best" or "most precise" analysis result.
As in Section 2.2, the material of this section may be skimmed through on a
first reading; however, we reiterate that it is frequently when conducting the
correctness proof that the final and subtle errors in the analysis are found
and corrected!
v E Val values
p E Env environments
defined by:
V c 1 close t in p
p .. - [ ] 1 p[x t--+ v]
'Ii'ansitions. We are now ready to define the transition rules of the Struc-
tural Operational Semantics by means of judgements of the form
p 1- ie 1 ---+ ie2
3.2 Theoretical Properties 155
[bind1]
p f- (bind Pl in iel)l --+ (bind Pl in iei)l
p f- (bind Pl in vf 1 )l --+ vf
given by the axioms and inference rules of Tables 3.2 and 3.3; they are ex-
plained below. The idea is that one step of computation of the expression
ie 1 in the environment p will transform it into ie2.
The value of a variable is obtained from the environment as expressed by the
axiom [var]. The axioms [fn] and [!un] construct the appropriate closures;
they restrict the environment p to the free variables of the abstraction. Note
that in [tun] it is only recorded that we have a recursively defined function;
the unfolding of the recursion will not happen until it is called.
The clauses for application shows that the semantics is a call-by-value seman-
tics: In an application we first evaluate the operator in a number of steps
using the rule [app 1 ] and then we evaluate the operand in a number of steps
using the rule [app2]. The next stage is to use one of the rules [apptnl or
[ appfun] to bind the actual parameter to the formal parameter and, in the
case of [appfunl, to unfold the recursive function so that subsequent recursive
calls will be bound correctly. We shall use a bind-construct to contain the
body of the function together with the appropriate environment. Finally, we
156 3 Constraint Based Analysis
evaluate the bind-construct using rule [bind1] a number of times, and we get
the result of the application by using rule [bind2 ]. The interplay between
these rules is illustrated by the following example.
First [app 1] and [fn] are used to evaluate the operator, then [app2] and lfn]
are used to evaluate the operand and [aPPJn] introduces the bind-construct
containing the local environment [x ~----+ (el o se (fn y => y 3 ) in [ ]) ] needed
to evaluate its body. So x 1 is evaluated using [bind1] and [var], and finally
[bind2 ] is used to get rid of the local environment. •
3.2 Theoretical Properties 157
The semantics of the conditiona! is the usual one: first the condition is eval-
uated in a number of steps using rule [ift] and then the appropriate branch
is selected by rules [ih] and [i!J]. For the local definitions we first compute
the value of the bound variable in a number of steps using rule [let1] and
then we introduce a bind-construct using rule [let2] refl.ecting that the body
of the let-construct has to be evaluated in an extended environment. The
rules [bind1] and [bind2] are now used to compute the result. For binary ex-
pressions we first evaluate the arguments using [op 1 ] and [op2 ] and then the
operation itself, denoted op, is performed using [op3 ].
As in Chapter 2 the labels have no impact on the semantics but are merely
carried along. It is important to note that the outermost labei never changes
while inner labels may disappear; see for example the rules [ih] and [bin~].
This is an important property of the semantics that is exploited by the 0-CFA
analysis.
of Example 3.2 and see how the informal explanation of its semantics is
captured in the formal semantics. First we introduce abbreviations for three
closures:
[] f- loop
--t (let g = f 5 in (g6 (fn z => z 7)8)9)10
--t (bind [g ~ f] in (g 6 (fn z => z 7 ) 8 ) 9 ) 10
--t (bind [g ~ f] in (f 6 (fn z => z 7 ) 8 ) 9 ? 0
--t (bind [g ~ f] in (f6 id~) 9 ) 10
--t (bind [g ~ f] in
(bind [f ~ f][x ~ idz] in (f 1 (fn y => y2)3) 4)9) 10
--t* (bind [g ~ f] in
(bind [f ~ f][x ~ idz] in
(bind [f ~ f][x ~ idy] in (fl (fn y => y2)3)4)4)9)10
-+ ie' -+ ie" -+
[~ [~
(C,{J) (C,{J) (c, P)
is strictly smaller than that of the call itself; thus a simple proof by well-
founded induction (Appendix B) suffices for showing the well-definedness of
n.
Example 3.9 Suppose that:
p [x ~ close tt in Pt] [y ~ close t2 in P2]
Pt []
P2 [x ~ close t3 in P3]
P3 = []
Theorem 3.10
u P n {J and P f-ie-+ ie' then (c, PJ Fie implies (c, PJ Fie'.
To see this assume that p f-- t€ ----+* ( close to in po)f and that (C, p) f= tf as
well as p R p. Then Theorem 3.10 (and an immediate numerical induction)
gives that (C, p) f= (el o se to in Po )€. Now from the clause [clase] of Table
3.4 we get that to E C(C) as was claimed. It is worth noticing that if the
program is closed, i.e. if it does not contain free variables, then p will be [ ]
and the condition p R p is trivially fulfilled.
Note that the theorem expresses that all acceptable analysis results remain
acceptable under evaluation. One advantage of this is that we do not need to
rely on the existence of a least or "best" solution (tobe proved in Subsection
3.2.3) in order to formulate the result. lndeed the result does not say that
the "best" solution remains "best" - merely that it remains acceptable. More
importantly, the result opens up the possibility that the efficient realisation
of Sections 3.3 and 3.4 computes a more approximate solution than the least
(perhaps using the techniques of Chapter 4). Finally, note that the formula-
tion of the theorem crucially depends on having defined the analysis for all
intermediate expressions rather than just all ordinary expressions.
We shall now turn to the proof of Theorem 3.10. We first state an important
observation:
Proof We assume that p R pand (C, {i) f= ie and prove (C, {i) f= ie' by induction
on the structure of the inference tree for p 1- ie ---+ ie'. Most cases simply amount
to inspecting the defining clause for (C, {i) f= ie; note that this method of proof
applies to all fixed points of a recursive definition and in particular also to the
greatest fixed point. We only give the proofs for some of the more interesting cases.
The case [var]. Here p 1- ie---+ ie' is:
From (C, {i) f= ie we get (fn x => ea) E C(i'); from p R p it is immediate to get
pa R P., this then establishes (C, {i) f= ie'.
The case [app 1 ]. Here p r ie --+ ie' is:
The defining clauses of (C, {i) f= ie and (C, {i) f= ie' are equal except that the former
has (C, {i) f= ie1 where the latter has (C, {i) f= ie~. From the induction hypothesis
applied to
(C, {i) F iel' p R and p r iel --> ie~ p,
we get (C, {i) f= ie~ and the desired result then follows.
The case [appfnl· Here p r ie --+ ie' is:
From (C, {i) f= ie we have (C, {i) f= (close (fn x => tg 0 ) in pd 1 which yields:
and in the case where V2 = close t2 in p2 it follows from the definition of (C, {i) f=
v~ 2 • Finally, the first universally quantified formula of the definition of (C, {i) f= ie
gives:
tg
(C,{i) f= 0 , C(i'2) <:;; p(x), and C(i'a) <:;; C(i')
Now observe that V2 V (P,p(x)) since C(i'2) <:;; p(x) follows from the clause (app1n).
Since Pl R p we now have
p r (bind Pl in vf 1 )R --> vf
R
f= ie we have (C, {i) f= v11 as well as C(i'1) <:;; C(i') and the desired
~ ~
Next let (Ce, Pe) be as in Example 3.3. Clearly [ l n Pe and from Example
3.5 we have:
Using Table 3.4 it is easy to check that this is indeed the case. •
3.2.3 Existence of Solutions
Having defined the analysis in Table 3.1 it is natural to ask the following
question: Does each expression e admit a Control Flow Analysis, i.e. does
there exist (C, j5) such that (C, j5) f= e? We shall show that the answer to this
question is yes.
However, this does not exclude the possibility of having many different analy-
ses for the same expression so an additional question is: Does each expression
e have a "least" Control Flow Analysis, i.e. does there exists (Co, po) such
that (Co, Po) f= e and such that whenever (C, j5) f= e then (Co, po) is "less
than" (C, p)? Again, the answer will be yes.
Here "least" is with respect to the partial order defined by:
It will be the topic of Sections 3.3 and 3.4 (and Mini Project 3.1) to show that
the least solution can be computed efficiently for all expressions. However, it
may be instructive to give a general proof for the existence of least solutions
also for intermediate expressions. To this end we recall the notion of a Moare
family (see Appendix A and Exercise 2.7):
This property is also called the model intersection property because whenever
we take the "intersection" of a number of "models" we still get a "model" .
3.2 Theoretical Properties 163
Proposition 3.13
For all ie E IExp the set {(C, P) 1 (C, p) f= ie} is a Moore family.
Lemma 3.14
(i) For all pE Env the set {p 1 p n p} is a Moore family.
(ii) For all V E Val the set { (p, v) 1V V (p, v)} is a Moore family. •
Proof To prove (i) we proceed by well-founded induction on p (which is also the
manner in which the existence of the predicate was proved). Now assume that
ViEI: p R {i;
for some index set I and let us show that p R (n;p;). For this consider x, tx, and
Px such that:
p(x) = close tx in Px
We then know
ViEI: tx E Pi(x)
Px R Pi
1\
and using the induction hypothesis it follows that
tx E (niPi)(x) 1\ Px n (niPi)
(taking care when I = 0).
To prove (ii) we simply expand the definition of V and note that the result then
follows from (i). •
ViEI: (C;,pi) 1= ie
and let us prove that n;(C;,pi) 1= ie. We shall proceed by coinduction (see Ap-
pendix B) so we start by defining the ternary relation Q' by:
and this amounts to assuming (C', jl) Q' ie' and proving that (C', jl) ( Q(Q')) ie'.
So let us assume that
ViEI: (C;,,Pi) != ie'
and let us show that:
il;(c;, Pi) (Q(Q')) ie'
For this we consider each of the clauses for ie' in turn.
Here we shall only deal with the more complicated choice ie' = (itÎ 1 it; )e. From
2
Similarly we get:
f ~
Next consider (fn X=> ta0 ) E ni(C;(.el)) and let us prove that:
For ali i E I we have that (C;, p;) != ie' and since (fn x => t~0 ) E C; (.e!) we have
and this then gives (3.1) as desired (taking care when I = 0). The case of
(fun f x => t~0 ) E n;(C;(RI)) is similar. This completes the proof. •
Example 3.15 Let us return to Example 3.5 and consider the following
potential analysis results for ((fn x => x1)2 (fn y => y3) 4)5:
Neither (C~, ~) nor (C~, f5::) is a least solution. Their "intersection" (C~ n
c~, ~ n P::) is smaller and equals (c.,
p-.) which turns out to be the least
analysis result for the expression. •
Proposition 3.16
There exists e* E Exp such that { (C, p) 1(C, p) F=' e*} is not a
Moore family.
Proof ( sketch) This proof is rather demanding and is best omitted on a first
reading. To make the proof tractable we consider
e* t;
t* = (fn x => (xl xl)l)l (fn x => (xl xl)l)l
and take:
Labe {f}
Var. {x}
Term. {t*,fnx=> (xl xl)t,xl xt,x}
lE~. {ti 1 tE Term.}
Val. P( { fn x => (xl xl)l}) = {0, {fn x => (xl xl)l}}
166 3 Constraint Based Analysis
This is in line with Exercise 3.2 (and the development of Subsection 3.3.2) and as
we shall see the proof of Proposition 3.13 is not invalidated.
Next let Q be the functional defined by Table 3.1 and let Q be in the domain of Q.
The condition
Q = Q(Q)
is equivalent to:
(C, {i) Q (fn x => (xc xc)e)c iff {fn x => (xe xen <:;; C(P)
(C, {i) Q (fn x => (xe xf)f)f iff {fn x => (xc xc)e} <:;; C(P)
This implication can be reversed and this shows that also the conjunct of the above
four conditions is equivalent to Q = Q(Q).
Using that p(x) can only be 0 or {fn x => (xc xc)c}, and similarly for C(P), the
above four conditions are equivalent to the following:
3.2 Theoretical Properties 167
(C,P) Q (fn X=> (xf xf)f)f iff {fn X=> (xf xcn = C(R)
(C, P) Q (x€ xc)c iff p(x) = C(R) 1\
for i E {1, 2, 3, 4} it follows that Q1 and Q2 satisfy the condition whereas Q3 and
Q4 do not.
It is now straightforward to verify also the remaining three conditions and it follows
that:
Q; = Q(Q;) for i = 1,2
This means that Q1 equals f= (the greatest fixed point of Q) and that Q2 equals
f=' (the least fixed point of Q). One can then calculate that
(c, P) Q1 t! iff c(e) = ;J(x) -1- 0
(C, P) Q2 t: iff false
which cannot be a Moore family (since a Moore family is never empty). This
completes the proof. •
168 3 Constraint Based Analysis
Proposition 3.13 shows that a least solution does exist; however, the algo-
rithm that is implicit in the proof does not have tractable (i.e. polynomial)
complexity: it involves enumerating all candidate solutions, determining if
they are indeed solutions, and if so taking the greatest lower bound with
respect to the others found so far.
An alternative approach is somehow to obtain a finite set of constraints, say
of the form lhs ~ rhs (where lhs and rhs are much as described in Section
3.1), and then take the least solution to this system of constraints. The
most obvious method is to expand the formula (C, p) f= e* by unfolding
all "recursive calls", using memoisation to keep track of all the expansions
that have been performed so far, and stopping the expansion whenever a
previously expanded call is re-encountered.
Three phases. We shall take a more direct route motivated by the above
considerations; it has three phases:
If additionally
(C, P) FA e* =} (C, p) i=B e*
then we can be assured that no solutions are lost and hence the best (i.eo least)
solution to "I==B e*" will also be the best (i.eo least) solution to "I==A e*" o As
we shall see, it may be necessary to restrict attention to only solutions (C, p)
satisfying some additional properties (eogo that only program fragments of e*
appear in the range of Cand p) o
of Example 3.40 We shall verify that (Cip, /)ip) l=s loop where C1p and /)ip are
as in Example 3.40 Using the clause [let], it is sufficient to show
(Cip, /)ip) l=s (fun f x => (f 1 (fn y => y 2)3)4)5 (3o2)
(Cip, /)ip) l=s (g 6 (fn z => z 7 ) 8 ) 9 (303)
170 3 Constraint Based Analysis
since we have Clp(5) ~ Pip(g) and Clp(9) ~ C1p(lO). To show (3.2) we use the
clause [fim] and it is sufficient to show
since f E Clp(5) and f E Pip(f). Now C1p(l) = {f} so, according to clause [app]
this follows from
3.3 Synta.x Directed 0-CFA Analysis 171
since Cip(3) ~ ,Oip(x) and Cip(4) ~ Cip(4). The first clause follows from [var]
since ,Oip(f) ~ Cip(l) and for the last clause we observe that idy E Cip(3) and
(Cip, ,Oip) Fs y2 as follows from ,Oip(y) ~ Cip(2).
To show (3.3) we observe that Cip(6) = {f} so using [app] it is sufficient to
show
~
(Cip,,Oip) Fs g6
(Cip, Pip) Fs (fn z=> z )
~ ~ 7 8
since Cip(8) ~ ,Oip(x) and Cip(4) ~ Cip(9). This is straightforward except for
the last clause where we observe that idz E Cip(8) and (Cip,,Oip) Fs z 7 as
follows from ,Oip(z) ~ Cip(7).
Note that because the analysis is syntax directed we have had no need for
coinduction, unlike what was the case in Example 3.6. •
c; (c) ={ 0 Term*
if C f/:- Lab*
if CE Lab*
-p;(x) ={ 0 Term*
ifxf/:_Var*
if x E Var*
Then the claim
~ ~T T
(C,p) r;;;; (C* ,p*)
intuitively expresses that (C, p) is concerned only with subterms occurring in
the expression e*; obviously, we are primarily interested in analysis results
with that property. Actually, this condition can be "reformulated" as the
technically more manageable
where we define Cache* = Lab*--+ Val*, Env* =Var*--+ Vaî* and Vaî* =
P(Term*).
172 3 Constraint Based Analysis
We can now show that all the solutions to "F=s e/' that are "less than"
(CJ, pi) are solutions to "f= e*" as well:
Proposition 3.18 T
Fs e* and (C, p) ~ (C*, p*) then (C, PJ f= e*.
~ ~ ~T ~
If (C, p)
T
Proof Assume that (c, P) F 8 e* and that (c, P) [;;;; (c* 'p* ). Furthermore let
~ ~T
(3.4). For (fun f x => t t0°) E -C(l\) we need to show that (C, -
P) F• t 0R.° and that
(fun f x => t~0 ) E p(f); the first follows from (3.4) and the second is an immediate
consequence of (C, P) F• (fun f x => t 0°) (for some R) that again follows from
~ f f
(3.4). •
Proposition 3.19
{(C, p) E C~e* X EnV* 1(C, PJ Fs e*} is a Moore family.
This means that the properties obtained for the analysis of Table 3.1 in
Subsection 3.2.3 also hold for the analysis of Table 3.5 with the additional
restriction on the range of the analysis functions. In particular, any analysis
result that is acceptable with respect to Table 3.5 (and properly restricted to
c;clle* x ~ *) is also an acceptable analysis result with respect to Table
3.1. The converse relationship is studied in Exercise 3.11 and Mini Project
3.1.
Proof We shall write (CI' p""[) also for the greatest element of c-;clie* X ~*•
It is immediate to show that
(a) (C""[,PJH=s e
(b) if (C1, Pt) F• e and (C2, IJ2) F• e then ((Ct, {Jt) n (C2, {J2)) F• e.
and note than one can write Y = {(C;, p;) 1i E {1, · · ·, n}} for some n ~O since
c-;clie* X ~ * is finite. That
then follows from (a) and (b) because nY = (C7:, {J7:) n (C1, {Jt) n ... n (Cn, Pn)· •
where rhs is of the form C(l) or r( x), and lhs is of the form C(l), r( x), or {t},
and all occurrences of t are of the form fn x => ea or fun f x => ea. To
simplify the technical development we shall read (3.7) as
[con] C*[ct] = 0
(i.IJ C*[(if t~0 then ti1 else t~2 )t] = C*[t~0 ] U C*[tÎ 1 ] U C*[t~2 ]
u {C(fl) ~ C(f)}
U{C(f2) ~ C(f)}
where we use that fn x => x1 and fn y => y 3 are the only abstractions in
1Cerna*. •
Proposition 3.21
~ ~T T
If (C, P) ~ (C*, p*) then
(C, P) Fs e* if and only if (C, P) Fc C*[e*]
Thus the least solution (C, p) to (C, P) Fs e* equals the least solution to
(C, p) Fc C*[e*].
Proof A simple structural induction on e shows that
for all subexpressions e of e*; in the case of the function application (ti 1 t~2 )' the
assumption (C, {i) !;;;; (C~, p~) is used to ensure that C(f1) ~ Term*. •
and show that it has a least fixed point lfp( F*) that is indeed the least solution
whose existence is guaranteed by Propositions 3.18 and 3.21.
3.4 Constraint Based 0-CFA Analysis 177
Proposition 3.22
Ifp(F*) =n{(C,P) 1 (C,ţ;) f=cC*[e*]}
may be used to compute the least fixed point in at most O(n2 ) iterations.
A naive approach will need to consider all O(n 2 ) constraints to determine
the value of each of the O( n) components of the new iterant; this yields an
overall O(n5 ) bound on the cost.
INPUT: C*[e*]
OUTPUT: (C, p)
METHOD: Step 1: Initialisation
W:= nil;
for q in Nodes do D[q] := 0;
for q in Nodes do E[q] := nil;
Step 3: Iteration
while W =1 nil do
q := head(W); W := taii(W);
for ce in E[q] do
case ce of
P1 s;;; P2: add(p2, D[pl]);
{t} s;;; P =* Pl s;;; P2:
if t E D[p] then add(p2, D[p!]);
• a constraint {t} s;;; p ==? P1 s;;; P2 gives rise to an edge from P1 to P2 and
an edge from p to P2.
Having constructed the graph we now traverse all edges in order to propagate
information from one data field to another. We make certain only to traverse
3.4 Constraint Based 0-CFA Analysis 179
p D[p) E[p)
C(l) 0 [idx ~ C(2) =? C(l) ~ C(5)]
C(2) idx [idy ~ C(2) =? C(3) ~ C(5), idy ~ C(2) =? C(4) ~ r(y),
idx ~ C(2) =? C(l) ~ C(5), idx ~ C(2) =? C(4) ~ r(x)]
C(3) 0 [idy ~ C(2) =? C(3) ~ C(5)]
C(4) idy [idy ~ C(2) =? C(4) ~ r(y), idx ~ C(2) =? C(4) ~ r(x)]
C(5) 0 [l
r(x) 0 [r(x) ~ C(l))
r(y) 0 [r(y) ~ C(3)]
• a data array D that for each node gives an element of Val*; and -
• an edge array E that for each node gives a list of constraints from which
a list of the successor nodes can be computed.
The set Nodes consists of C(f) for all fin Lab* and r(x) for all x in Var*.
The first step of the algorithm is to initialise the data structures. The second
step is to build the graph and to perform the initial assignments to the data
fields. This is established using the procedure add(q,d) that incorporates d
into D[q] and adds q to the worklist if d was not part of D[q]. The third step
is to continue propagating contributions along edges as long as the worklist
is non-empty. The fourth and final step is to record the solution in a more
familiar form.
Example 3.23 Let us consider how the algorithm operates on the ex-
pression ((fn x => x 1 ) 2 (fn y => y3 ) 4 ) 5 ofExample 3.20. After step 2 the
data structure W has been initialised to
w= [C(4), C(2)],
180 3 Constraint Based Analysis
and the data structures D and E have been initialised as in Figure 3.4 where
we have written idx for {fn x => x 1 } and idy for { fn y => y 3 }. The algo-
rithm will now iterate through the worklist and update the data structures W
and D as described by step 3. The various intermediate stages are recorded
in Figure 3.5. The algorithm computes the solution in the last column and
this agrees with the solution presented in Example 3.5. •
The following result shows that the algorithm of Table 3. 7 does indeed corn-
pute the solution we want:
Proposition 3.24
Given input C*[e*] the algorithm of Table 3.7 terminates and the
result (C, p) produced by the algorithm satisfies
Proof It is immediate that steps 1, 2 and 4 terminate, and this leaves us with
step 3. It is immediate that the values of D[q] never decrease and that they can be
increased at most a finite number of times. It is also immediate that a node q is
added to the worklist only if some value of D[q] actually increased. For each node
placed on the worklist only a finite amount of calculation {bounded by the number
of outgoing edges) needs to be performed in order to remove the node from the
worklist. This guarantees termination.
Next let (C',p) bea solution to (C',p) Fc C*[e*]. It is possible to show that the
following invariant
3.4 Constraint Based 0-CFA Analysis 181
is maintained at all points after step 1. It follows that (C, {i) C (C', p) upon
completion of the algorithm.
We prove that (C, {i) Fc c* [e*] by contradiction. So suppose there exists ce E c* [e*]
such that (C, {i) Fc ce does not hold. If ce is {t} ~ p then step 2 ensures that {t} ~
D[p] and this is maintained throughout the algorithm; hence ce cannot have this
form. If ce is Pl ~ P2 it must be the case that the final value of D satisfies D[p1]
=f. 0 since otherwise (C, {i) Fc ce would hold; now consider the last time D[p1] was
modified and note that Pl was placed on the worklist at that time (by the procedure
add); since the final worklist is empty we must have considered the constraint ce
(which is in E[p1]) and updated D[p2] accordingly; hence ce cannot have this form.
If ce is {t} ~ p =} Pl ~ P2 it must be the case that the final value of D satisfies
D[p] =f. 0 as well as D[pl] =f. 0; now consider the last time one of D[p] and D[p1] was
modified and note that p or Pl was placed on the worklist at that time; since the
final worklist is empty we must have considered the constraint ce and updated D[p 2]
accordingly; hence ce cannot have this form. Thus (C,{i) Fc ce for all ce E C*[e*].
We have now shown that (C,{i) Fc C*[e*] and that (C,{i) [;;; (C',p) whenever
(C',p) Fc C*[e*]. It now follows that
(c, P'J = 1-1 {(c', P') (c', P') F=c c* [e*]}
1
as required. •
The proof showing that the algorithm terminates can be refined to show that
it takes at most O(n 3 ) steps if the original expression e* has size n. To see
this recall that C[e*] contains at most O(n) constraints of the form {t} s;; p
or P1 s;; P2, and at most O(n 2 ) constraints of the form {t} <;;; p::::} p 1 <;;; p 2 .
We therefore know that the graph has at most O(n) nodes and O(n 2 ) edges
and that each data field can be enlarged at most O( n) times. Assuming
that the operations upon D[p] take unit time we can perform the following
calculations: step 1 takes time O(n), step 2 takes time O(n 2 ), and step 4
takes time O(n); step 3 traverses each of the O(n 2 ) edges at most O(n) times
and hence takes time O(n 3 ); it follows that the overall algorithm takes no
more than O(n 3 ) basic steps.
It is not the case that any (C, P) satisfying (C, P) f= e* can be obtained using
the above approach- see Exercise 3.11 and Mini Project 3.1.
For many applications it is the ability to compute the least (C, p) satisfy-
ing (C, P) f= e* that is of primary interest, rather than the ability to check
(C, p) f= e* for a proposed guess (C, p). However, there are applications where
it is the ability to check (C, P) f= e* that is of primary concern. As an example
consider an open system e0 that utilises resources of an umpecified library
e*. Then analyses and optimisations of e0 must rely on (C, P) expressing
all properties of interest about the library e*, i.e. (C, P) f= e*. This ensures
that the unspecified library e* can be replaced by another library e:
as long
as (C, p) continues to express all properties of interest about the library e:,
e:.
i.e. (C, P) f=
for some function dop : Data x Data -+ P(Data) specifying how the
operator computes with the abstract properties of integers and booleans.
d+ tt ff - o +
tt 0 0 0 0 0
ff 0 0 0 0 0
- 0 0 {-} {-} {-,o,+}
o 0 0 {-} {O} {+}
+ 0 0 {-,o,+} {+} {+}
and similarly for the other operators. •
Acceptability relation. The acceptability relation of the combined
analysis has the form
and is presented in Table 3.8. Compared with the analysis of Table 3.1 the
clause [con] now records that de is a possible value of c and the clause [op]
makes use of the function op described above. In the case of [i.IJ we have
made sure only to analyse those branches of the conditiona! that the analysis
of the condition indicates the need for; hence we can be more precise than
in the pure Control Flow Analysis - the Data Flow Analysis component of
the analysis can influence the outcome of the Control Flow Analysis. In
the manner of Exercise 3.3 similar improvements can be made to many of
the clauses (see Exercise 3.14) and thereby the specification becomes more
flow-sensitive.
A pure 0-CFA analysis will not be able to discover that the else-branch of
the conditiona! will never be executed so it will conclude that the subterm
with labei 12 may evaluate to fn y => y4 as well as fn z => 25 6 as shown
in the first column of Figure 3.6. The second column of Figure 3.6 shows that
when we combine the analysis with a Detection of Signs Analysis (outlined
184 3 Constraint Based Analysis
[Jn] (C,P) Fd (fn x => eo)e iff {fn x => eo} ~ C(f)
[!un] (C, P) Fd (fun f x => eo)e iff {fun f x => eo} ~ C(f)
in Example 3.26) then the analysis can determine that only fn y => y4 is
a possible abstraction at labei 12. Note that the Detection of Signs Analy-
sis (correctly) determines that the expression will evaluate to a value with
property {O}. •
The proof techniques introduced in Section 3.2 should suffice for proving the
correctness of the analysis with respect to the operational semantics. A slight
extension ofthe algorithmic techniques presented in Sections 3.3 and 3.4 (and
in Mini Project 3.1) suffices for obtaining an implementation of the analysis
provided that the set Data is finite.
3.5 Adding Data Flow Analysis 185
Figure 3.6: Control Flow and Data Flow Analysis for example program.
Finally, we should stress that a solution to the analysis of Table 3.8 does
not immediately give a solution to the analysis of Table 3.1. More precisely,
(C,p) Fd e does not guarantee that (C',(J) f= e where 't:/C: C'(C) = C(C) n
Term and 't:/x : (J(x) = p(x) n Term. The reason is that the Control Flow
Analysis part of Table 3.8 is infiuenced by the Data Flow Analysis part in
the clause [i~: if for example the abstract value of the condition does not
include dtrue then the then-branch will not be analysed.
could be split into a term component and a data component and similarly
for the abstract environment p: Var -+ Vaid.
Having decoupled 'P(Term) and 'P(Data) we can now consider replacing
'P(Data) by a more general coliection of properties. An obvious possibility
is to replace 'P(Data) by a complete lattice L and perform a development
closely related to that of the (forward) Monotone Frameworks of Chapter 2.
So let us define a monotone structure to consist of:
for ali binary operators op (and where dop :Data x Data-+ 'P(Data) is as
above). •
v E Val
- P(Termj__ abstract values
p
C
E
E
Env
C;clie
=
-
Var-----+ Val abstract environments
Lab-----+ Val abstract caches
Example 3.30 Returning to the expression of Example 3.27 and the De-
tection of Signs Analysis we now get the analysis result of the last column of
Figure 3.6. So we see that the result is as before. •
The proof techniques introduced in Section 3.2 should suffice for proving the
correctness of the analysis with respect to the operational semantics. A slight
extension ofthe algorithmic techniques presented in Sections 3.3 and 3.4 (and
in Mini Project 3.1) suffices for obtaining an implementation of the analysis
provided that L satisfies the Ascending Chain Condition (as is the case for
Monotone Frameworks).
188 3 Constraint Based Analysis
[fn] (C, 5,p,8) FD (fn x => ea)f iff {fn x => eo} ~ C(i)
iff (C,
--
D, p, -8) FD tll
l
A (C,
--
D, p, -8) FD t2f 2 A
(\i(fn => t~0 ) E C(i1) :
X
i'
(C, D, p, 8) FD t 0° A
~ ~ ~
C(il) ~ C(i)A
5(i!)!::;;; 5(i)) A
f
(tfalse!::;;; D(io) =? (C,D,p,8) FD t22 A
~ ~ ~ ~
C(i2) ~ C(i)A
6(t2)!::;;; 5(t))
[let] (C, 5, p, 8) f= D (let x = tf 1 in t~2 )f
iff (C,D,p,8) FD tll
-- f_ -
A (C,D,p,8)
-- -
FD t2f_ 2 A
C(i1) ~ P(x) A 5(i!)!::;;; 8(x) A
C(i2) ~ C(i) A 5(i2) !::; ; 5(i)
[op]
(C, 6, p, 8) ti
1=~ (if t~0 then 1 else 2 ) ' tg
iff (C, D, p, 8) 1=~ t~0 A
ti
(C, 6, p, 8) 1=~ 1 A C(fl) <;;;; C(f) A D(f1) ~ D(f) A
tg
(C, 6, p, 8) 1=~ 2 A C(f2) <;;;; C(f) A D(f2) ~ D(f)
Unlike what was the case for the analyses of Tables 3.8 and 3.9, a solution to
the analysis modified in this way does give rise to a solution to the analysis
of Table 3.1; to be precise (C, 6, P, 8) 1=~ e guarantees (C, p) 1= e.
In terms of implementation this modification means that the constraints for
the control fiow component (C and p) can be solved first, and based on this
the constraints for the data fiow component (D and 8) can be solved next. If
both sets of constraints are solved for their least solution this will stiH yield
the least solution of the combined set of constraints.
0(6) = {+}
0(14) {0,+}
0(15) {0, +}
8(z) {o}
This analysis is less precise than those of Tables 3.8 and 3.9: it will only
determine that the expression will evaluate to a value with the property
{0,+}. •
Example 3.33 For the expression of Example 3.32 we could for example
consider
and then analyse the expanded expression: the 0-CFA analysis is now able
to deduce that x1 can only be bound to fn x2 => x2 and that x2 can only
be bound to fn y => y so the overall expression will evaluate to fn y => y
only. •
[In] (C,p) f=6e (fn x => eo)t iff {(fn x => eo,ceo)} <::;; C(f!,8)
where ceo = ce 1 FV(fn x => eo)
[!un] (C, p) f=6e (fun f x => eo)t iff { (fun f x => eo, ceo)} <::;; C(f, 8)
where ceo = ce 1 FV(fun f x => eo)
[ op] (C, p) f=6e (t~ 1 op t~2 )f iff (C, P) f=6e tf1 1\ (C, P) f=6e t~2
The uniform k-CFA analysis differs from the k-CFA analysis in performing
a similar development for the abstract cache that now maps a labei and a
context to an abstract value:
Given information about the context of interest we can determine the abstract
value associated with a labei. Again we indirectly get the effect of having a
cache for each possible context although it is still a global entity. (In k-CFA
one has ciclle = (Lab X CEnv) ---+ Vai.)
shall dispense with proving the correctness of the analysis and with showing
how it can be implemented.
Example 3.34 We shall now specify a uniform 1-CFA analysis for the
expression of Example 3.33:
The initial context will be A, the empty sequence of labels. In the course
of the analysis the current context will be modified at the two application
points with labels 5 and 8; since we only records call strings of length at most
one the only contexts of interest will therefore be A, 5 and 8. There are four
context environments of interest:
ceo = [] the initial (empty) context environment,
ce1 = ceo[f f-+ A] the context environment for the analysis of the body
of the let-construct,
ce2 = ceo[x f-+ 5] the context environment used for the analysis of the
body of f initiated at the application point 5, and
ce3 = ceo[x f-+ 8] the context environment used for the analysis of the
body of f initiated at the application point 8.
because C(d(7, A)~ Pfd(x, 8) and C(d(l, 8) ~ C(d(8, A). This is straightforward
except for the first clause. Proceeding as above we see that C(d(3,A) = {(fn
x => x\ceo)} and it is sufficient to verify
because C(d(4, A)~ P(d(x, 5) and c:d(l, 5) ~ C(d(5, A). This is straightforward.
The importance of this example is that it shows that the uniform 1-CFA
analysis is strong enough to determine that fn y => y6 is the only result
possible for the overall expression unlike what was the case for the 0-CFA
analysis in Example 3.32. We can also see that, since P(d(y,8) = 0 for all
8 E {A, 5, 8} it follows that fn y => y6 is never called upon a function. •
The resulting analysis will have exponential worst case complexity even for
the case where k = 1. To see this assume that the expression has size n and
that it has p different variables. Then ~ has O( n) elements and hence there
will be O(p · n) different pairs (x, 8) and O(n 2 ) different pairs (f, 8). This
means that (C, p) can be seen as an O(n 2 ) tuple of values from Vai. Since
Vai itself is a powerset of pairs of the form (t, ce) and there are O(n · nP)
such pairs it follows that Val has height O(n · nP). Since p = O(n) we have
the exponential worst case complexity claimed above.
This should be contrasted with the 0-CFA analysis developed in the previous
sections. It corresponds to letting ~ be a singleton. Repeating the above
calculations we can see (C, fi) as an O(p+n) tuple ofvalues from Vai, and Vai
will be a lattice of height O(n). In total this gives us a polynomial analysis
as we already saw in Section 3.4.
The worst case complexity of the uniform k-CFA analysis (as well as the k-
CFA analysis) can be improved in different ways. One possibility is to reduce
the height of the lattice Vai using the techniques of Chapter 4. Another
possibility is to replace all context environments with contexts, i.e. to have
Vai = P(Term x ~); clearly this will give a lattice of polynomial height.
This idea is closely related to the so-called polynomial k-CFA analysis where
the analogues of context environments are forced to be constant functions,
i.e. to map all variables to the same context. In the case of polynomiall-CFA
the analysis is of complexity O(n 6 ).
196 3 Constraint Based Analysis
(C,p) f= (fn X1, · · · ,xm => eb) iff {fn XI,··· ,Xm => eb} ~ C(f)
The third last conjunct in the clause for function application can be written
It is clear from this specification that the body of the function is analysed sep-
arately for each possible tuple of arguments and that no merging of data takes
place. To be practica} we need to implement the analysis using memoisation
so that the bodies are only analysed once. This can be dane by organis-
ing each (C, P) l=~bPA t~b into so-called templates and to maintain them in a
global pool; when a template is created it is only added to the pool if it is
not already present.
The Cartesian Product Algorithm derives its name from the cartesian product
C(J:\, 8) x · · · x C(lm, 8) over which 8b ranges. The analysis can be implemented
in a straightforward lazy manner because the product grows monotonically:
C(C1, 8) x · · · x C(lm, 8) will increase each time one of the C(Ci, 8) increases
(assuming that none is empty). A mild generalisation is studied in Exercise
3.17.
C: Lab -+ ~ -+ Vai
p: Var-+~-+ Vai
where Vai= P(Term) is the powerset of interest. Clearly D and Term can
be taken to be equal and the fact that ~ = Term then shows that ~ = D and
we may conclude that "small assumption sets" and the Cartesian Product
Algorithm are variations over a theme.
Concluding Remarks
Control Flow Analysis for functional languages. Many of
the key ideas for Control Flow Analysis have been developed within the
context of functionallanguages. The concept of k-CFA analysis seems due to
Shivers [156, 157, 158]; other works on 0-CFA-like analyses include [163, 136,
57, 56]. The ideas behind k-CFA and polynomial k-CFA analysis were further
clarified in [79] that also established the exponential complexity of k-CFA
analysis for k > O; it also related a form of Set Based Analysis [70] to 0-CFA.
The uniform k-CFA analyses were introduced in [122] as a simplification of
the k-CFA analyses; an obvious variation over this is to record the set of the
last k distinct call points (see Exercise 3.15). Yet another variation over the
Concluding Remarks 199
Only few papers [125] discuss the interplay between the choice of specification
style for the analysis and the choice of semantics. We have used a small-step
Structural Operational Semantics rather than a big-step semantics in order
to express the semantic correctness also of looping programs. We have used
an environment based semantics in order to ensure that we do not "modify"
the bodies of functions before they are called, so that function abstractions
can meaningfully be used in the value domains of our analysis (125]. As a
consequence we have had to introduce intermediate expressions (closures and
bindings) and have had to specify the abstract analysis also for intermediate
expressions; for the syntax directed specification and the constraint based
analysis this was not necessary given that semantic correctness had already
been dealt with. Alternative choices are clearly possible but are likely to
sacrifice at least some of the generality offered by the present approach.
step (iii), it is unimportant how the constraints were in fact obtained. For
this reason, it is often said that set constraints allow the separation of the
specification of an analysis from its implementation and that set constraints
are able to deal with forward analyses as well as backward analyses and
indeed mixtures of these.
Set constraints [72, 7] have a long history [143, 84]. They allow us to express
general inclusions of the form
V .. - XIYI···
C .. _ true 1 false 1 O 1 · · · 1 cons 1 nill · · ·
Set constraints allow the consideration of constructors that are not just
nullary and this allows us to record also the shape of data structures, so
for example cons(81, 82) U nil expresses possibly empty lists whose heads
come from 8 1 and whose tails come from 82. The associated projection selects
those terms (if any) having the required shape, e.g. cons- 1(8) produces the
heads that may be present in 8. We have seen conditiona! constraints before
and it turns out that projection is so powerful that it can be used to code
conditiona! constraints. Finally, it is sometimes possible to explicitly take
the complement of a set but this adds to the complexity of the development.
(It means that solutions can no longer be guaranteed using Tarski's Theorem
and sometimes a version of Banach's Theorem may be used instead.)
The complexity of solving a system of set constraints depends rather dra-
matically on the set forming operations allowed and therefore many versions
have been considered in the literature. We refer to [6, 135] for an overview
of what is known in this area; here we just mention [28] for a general result
and [70, 10] for some cubic time fragments.
However, it is worth pointing out that many of these results are worst-case
results; benchmark results of Jaganathan and Wright [80] shows e.g. that in
practice a 1-CFA analysis may be faster than a 0-CFA analysis despite the
fact that the former has exponential worst-case complexity and the latter
polynomial worst-case complexity. The reason seems to be that the 0-CFA
analysis explores most of its polynomial sized state space whereas the 1-CFA
analysis is so precise that it only explores a fraction of its exponentially sized
state space.
The basic idea behind many of the solution procedures for set constraints is
roughly as follows [9]:
202 3 Constraint Based Analysis
This is not quite the algorithm used in Section 3.4 where we were only in-
terested in solving a rather limited class of constraints for 0-CFA analysis.
When generating the constraints in Table 3.6 we were able to "guess a uni-
verse" Term* that was sufficiently large and this allowed us to generate
explicit versions of the conditiona! constraints; in fact a superset of all those
to be considered in step 1 of the above algorithm. Therefore our subsequent
constraint solving algorithm in Table 3. 7 merely needed to check the already
existing constraints and to determine whether or not they could contribute
to the solution. In practice, the above "lazy'' algorithm is likely to perform
much better than the "eager" algorithm of Tables 3.6 and 3.7.
Mini Projects
The language considered so far only includes simple data like integers and
booleans. In this mini project we shall extend the language with more general
data structures:
As before the terms of interest are fn x => eo and fun f x => eo for recording
the abstractions. The new contribution is a number of elements of the form
C(f1, · · ·, fn) denoting a data element C(v1, · · ·, vn) whose i'th component
might have been created by the expression at program point fi.
For the more ambitious: are there any difliculties in developing an abstract
analogue of the analysis in Table 3.1? •
204 3 Constraint Based Analysis
In this mini project we shall implement the pure 0-CFA analysis considered
in Section 3.3. As implementation language we shall choose a functional
language such as Standard ML or Haskell. We can then define a suitable
data type for FUN expressions as follows:
1. lmplement the constraint based control flow analysis of Section 3.4; this
includes defining an appropriate data structure constraints for (condi-
tiona!) constraints.
2. Implement the graph based algorithm of Section 3.4 for solving con-
straints; this involves choosing appropriate data structures for the work-
list and the two arrays used by the algorithm.
For the more ambitious: generalise your program to perform some of the more
advanced analyses, e.g. by incorporating data flow information or context
information. •
In Section 3.5 we showed how to incorporate Data Flow Analysis into our
Control Flow Analysis; this becomes more challenging when imperative con-
structs are added to the language:
where
• p: Var ----. Vai and p(x) describes the values that x might be bound
to,
• Sa : Lab----. (Pnt ----.Val) and Sa(f) describes the states that may be
possible before the subexpression labelled f is evaluated,
• S. : Lab ----. (Pnt ----.Val) and S.(f) describes the states that may be
possible after the subexpression labelled f is evaluated, and
• me : Var ----. Pnt and me(x) indicates the creation point for the refer-
ence variable x.
One choice of Vai is P(Term) x L where the first component tracks functions
and the second component tracks abstract values. It may be helpful to devise
a Structural Operational Semantics for the extended language and to use it
as a guide when defining the acceptability relation.
A more advanced treatment would involve context information in the manner
of Section 3.6. •
Exercises
Exercise 3.1 Consider the following expression (omitting labels):
let f = fn x => x 1
in let g = fn y => y+2
in let h = fn z => z+3
in (f g) + (f h)
Add labels to the program and guess an analysis result. Use Table 3.1 to
verify that it is indeed an acceptable guess. •
206 3 Constraint Based Analysis
Exercise 3.2 The specification of the Control Flow Analysis in Table 3.1
uses potentially infinite value spaces and this is not really necessary. To see
this choose some expression e* E Exp that is tobe analysed. Let Var* ~ Var
be the finite set of variables occurring in e*, let Lab* ~ Lab be the finite set
of labels occurring in e*, and let Term* be the finite set of subterms of e*.
Next define
v E Val* = P(Term:l_
p E Env*
C E Ciclie*
Var* - t Val*
Lab* - t Val* --
and note that these value spaces are finite. Show that the specification of
th~nalysi~ Table 3.1 stiH makes sense when (C, P) is restricted to be in
Cache* x Env *. •
Exercise 3.3 Modify the Control Flow Analysis of Table 3.1 to take ac-
count of the left to right evaluation order imposed by a call-by-value se-
mantics: in the clause [app] there is no need to analyse the operand if the
operator cannot produced any closures. Try to find a program where the
modified analysis accepts analysis results (C, P) rejected by Table 3.1. •
(3.8)
(3.9)
Show that (3.8) implies (3.9) but not vice versa. Discuss which of (3.8) or
(3.9) is the preferable definition. •
Exercise 3.6 Reconsider our decision to use Vai= P(Term) and con-
sider using Vai = P(Exp) instead. Show that the specification of the Con-
trol Flow Analysis may be modified accordingly but that then Fact 3.11 (and
hence the correctness result) would fail. •
Exercises 207
by structural induction on e. •
Exercise 3.10 Consider Proposition 3.18 and determine whether or not
the statement
holds in general. •
Exercise 3.11 Give an example showing that both of the statements
if (C, p) ~ e* then (C, p) ~ 8 e*
.- ........ .-T T ........
if (C, PJ ~ e* and (C, p) (;;;; (C*, p*) then (C, PJ ~ 8 e*
fail in general. •
208 3 Constraint Based Analysis
Exercise 3.12 Give a direct proof of the correctness of the syntax di-
rected analysis of Table 3.5, i.e. establish an analogue of Theorem 3.10. This
involves first extending the syntax directed analysis to the bind- and close-
constructs and next proving that if p R p, p f--ie --+ ie' and (C, p) Ps ie then
also (C,p) Ps ie'. •
Show that
(C, p) Pc c;[e*] implies (C, p) Pc C*[e*]
where (C, p) Pc (ls = rhs) is defined in the obvious way. Also show that
holds in the special case where (C, p) is least such that (C, p) Pc C* [e*]. •
f(x)= e
Exercises 209
e .. - t€
t .. - c 1x 1 f e 1 if eo then e1 else e2 1 e1 op e2
ce E CEnv =Var---.~
Hint: Modify Table 3.10 to deal with functions taking m arguments and
to perform the appropriate determination of new contexts in the clause for
function application. •
Chapter 4
Abstract Interpretation
The purpose of this chapter is to convey some of the essential ideas of Ab-
stract Interpretation. We shall mainly do so in a programming language
independent way and thus focus on the design of the property spaces, the
functions and computations upon them, and the relationships between them.
We first formulate a notion of correctness for a restricted class of analyses
as this will allow us to motivate better some of the key definitions in the
development. Then we cover the widening and narrowing techniques that can
be used to obtain approximations of the least fixed point and for limiting the
number of computation steps needed. Next we consider Galois connections
and Galois insertions that allow a costly space of properties to be replaced
with one that is less costly. Galois connections can be constructed in a
systematic way and can be used to induce one specification of an analysis
from another.
(4.2)
for this without committing ourselves to the method used for specification
of the analysis. However, unlike what is the case for the semantics, it is
customary to require 1> to be deterministic and thereby define a function;
this will allow us to write /p(h) = l2 to mean p f- lt 1> h.
In the rest of this section we shall show how to relate the semantics to the
analysis. We shall present two approaches based on correctness relations and
representation functions, respectively. In both cases we shall define a notion
of correctness of the analysis with respect to the semantics and we shall show
that the two notions are equivalent.
This is a mundane approach in the sense that it only applies to analyses
where properties directly describe sets of values. This is the case for the
Constant Propagation Analysis of Section 2.3, the Shape Analysis of Section
2.6 and the Control Flow Analysis of Chapter 3 but it is not the case for
the Live Variable Analysis of Section 2.1 where properties are related to
relations between values. In the literature, the terms first-order analyses
versus second-order analyses have been used to differentiate between these
classes of analyses. It is important to stress that the development of Sections
4.2 to 4.5 apply equally well to both classes.
We begin by showing how the development of Chapters 2 and 3 can be
rephrased in the style of (4.1) and (4.2).
for the reflexive transitive closure of the transition relation, i.e. for:
to mean that:
Thus for a program S* with isolated entries, â1 is the abstract state associated
with the entry point of s*' and â2 is the abstract state associated with the
exit points; we use the least upper bound operation (U) on the complete
lattice s"ta1ecp to combine the contributions from the (possibly severa!) exit
points of s*. •
Example 4.2 Consider the FuN language of Chapter 3. Recall that the
semantics is given by a Structural Operational Semantics with transitions
of the form p t- ie --+ ie' where p is an environment (an element of Env =
Var --+fin Val) and ie and ie' are intermediate expressions from IExp. Now
let e* be the closed expression of interest. We shall write
to mean that e* when given the argument v1 will evaluate to the value v2,
i.e. that
[] t- (e* vfl /2 --+* v~2
where f1 and f2 are fresh labels. Note that the set V of values now is the set
Val.
We shall next consider the pure Control Flow Analysis of Section 3.1. Recall
that the result of analysing the expression e* is a pair (C, PJ satisfying (C, p) f=
e* as defined in Table 3.1. Here C is an element of c;clle = Lab* --+ Val
and p is an element of EnV =Var* --+Vai where Vai = P(Term*). For
this analysis we shall take the properties L of interest to be pairs (p, v) of
EnV x Vai and assume that (C, p) f= (e* cl 1 )l 2 for some constant c. Then
we define
e* t- (Pi., v!) 1> (p2, v2)
to mean that when e* is given an argument with property CP1, v1) then the
result of the application will have property (ih v2):
The intention is that v R l formalises our claim that the value v is described
by the property l.
p f--
R R
p f-- [>
V Rh A h ~ l2 ::::} V R l2 (4.4)
Condition (4.4) says that the smaller the property is with respect to the
partial ordering, the better (i.e. more precise) it is. This is an "arbitrary''
decision in the sense that we could instead have decided that the larger the
property is, the better it is, as is indeed the case in much of the literature on
4.1 A Mundane Approach to Correctness 215
Data Flow Analysis; luckily the principle of duality from lattice theory (see
the Concluding Remarks) tells us that this difference is only a cosmetic one.
Condition (4.5) says that there is always a best property for describing a
value. This is important for having to perform only one analysis (using the
best property, i.e. the greatest lower bound of the candidates) instead of
several analyses (one for each of the candidates). Recall from Appendix A
that a subset Y of Lis a Moore family if and only if (nY') E Y for all subsets
Y' of Y. We can then see that condition (4.5) is equivalent to the demand
that {li v R l} is a Moore family.
Condition (4.5) has two immediate consequences:
vRT
vRh 1\ v R l2 * v R (h n l2)
The first formula says that T describes any value and the second formula says
that if we have two descriptions of a value then their greatest lower bound is
also a description of the value.
between the values (i.e. the states) and the properties (i.e. the abstract
states):
Example 4.4 For the Control Flow Analysis mentioned in Example 4.2
we shall define
Recall that we have two kinds of values v E Val, constants c and closures
close t in p, and that V is given by:
V (~ ~) .ff { true if v = c
v p,v I tE vi\ \:fx E dom(p): p(x) V (p,p(x)) ifv =closet in p
--
This will turn Env x Val into a complete lattice. By induction on v E Val
one can then easily prove that (4.4) and (4.5) are fulfilled. •
{J:V---+L
The idea is that f3 maps a value to the best property describing it. The
correctness criterion for the analysis will then be formulated as follows:
p f-
f3 1 1 f3
p f- l>
Thus the idea is that if the initial value VI is safely described by h then the
final value v2 will be safely described by the result l2 of the analysis.
4.1 A Mundane Approach to Correctness 217
Lemma 4.5
(i) Given {3: V---+ L, then the relation R(3 : V x L---+ { true, false} satisfies
conditions (4.4) and (4.5), and furthermore f3R 13 = {3.
(ii) Given R: V x L---+ {true,false} satisfying conditions (4.4) and (4.5),
then f3R is well-defined and Rf3n = R.
Hence the two formulations (4.3) and (4.6) of correctness are equivalent. •
Proof To prove (i) we first observe that condition (4.4) is immediate since [;;; is
transitive. Condition (4.5) is immediate because when (J(v) is a lower bound for L'
we have (J(v) [;;; nL'. The calculation f3R/3(v) = n{ll V R/3 l} = n{ll (J(v) [;;; l} =
(J(v) then concludes the proof of (i).
To prove (ii) we observe that from v R l we get f3R(v) [;;; l and hence v R13n l.
Conversely, from v R13n l we get f3R(v) [;;; l; writing L' = {l v R l} it is clear that
1
(4.5) gives v R (nL') and this amounts tov R (f3R(v)); we then get the desired
result v R l by (4.4). •
Example 4. 7 For the Control Flow Analysis studied in Examples 4.2 and
--
4.4 we shall define
f3cFA : Val ---+ Env x Val
if v = c
{ (Ax.0, 0)
f3cFA (v) = (f3t'FA (p), { t}) if v = close t in p
The first clause reflects that we do not collect constants in a pure 0-CFA
analysis. In the second clause we only have one closure so the abstract value
will be a singleton set and we construct the associated "minimal" abstract
environment by extending f3cFA to operate on environments. To do that
we shall "merge" all the abstract environments occurring in U{f3cFA(p(x)) 1
x E Var*}; this reflects that the 0-CFA analysis uses one global abstract
environment to describe all the possible local environments of the semantics.
So we define f3t'FA : Env ---+ ~ by:
for all Vl E vl, V2 E v2 and h E Ll. Using the concept of logica[ relations
(briefty mentioned above) this can be written as:
{3 can be defined from the representation functions {31 and {32 and it will be
denoted {31 ----* fJ2:
Note that it now follows (from Lemma 4.5) that if each R; satisfies conditions (4.4)
and (4.5) then so does RI -» R2. •
Example 4.9 Consider the program plus with the semantics given by
plus 1- (zt, z2) ~ z1 + z2
where z 1, z2 E Z. A very precise analysis might use the complete lattices
(P(Z), ~) and (P(Z x Z), ~) as follows:
fpius(ZZ) = { Z1 + Z2 (zt, Z2)1 E ZZ}
where ZZ ~ Z x Z. Consider now the correctness relations Rz and Rzxz
generated by the representation functions:
f3z(z) {z}
f3zxz(zt, z2) = {(zt, z2)}
The correctness of the analysis of plus can now be expressed by
Vz1, z2, z, ZZ: plus 1- (z1, z2) ~ z 1\ (z1, z2) Rzxz ZZ ::::} z Rz fpius(ZZ)
or more succinctly
: _.•.. [-oo,ooj
'._[-oo,lj
./~-l,oc}
O} [-2,2} . [O,ocj
[-2,0}
The above example illustrates how the abstract concepts can give a more
succinct formulation of the correctness of the analysis. In the following we
shall see several cases where we move freely between what we may call a
"concrete" formulation of a property and an "abstract" formulation of the
same property. And we shall see that the latter often will allow us to reuse
general results so that we do not have to redevelop parts of the theory for
each application considered.
Example 4.10 We shall now present a complete lattice that may be used
for Array Bound Analysis, i.e. for determining if an array index is always
within the bounds of the array- if this is the case then a number of run-time
checks can be eliminated.
The lattice (Interval, r::;::) of intervals over Z may be described as follows.
222 4 Abstract Interpretation
Zl
if int = .1
if int = [zi. z2]
Z2
if int = .1
if int = [z1, z2]
.1 ifY ~ {.1}
UY = { [ inf'{inf(int) 1 intE Y}, sup'{sup(int) 1 intE Y}]
otherwise
where inf' and sup' are the infimum and supremum operators on Z' corre-
sponding to the ordering ~ on Z'; they are given by inf'(0) = oo, inf'(Z) = z'
if z' E Zis the least element of Z, and inf'(Z) = -oo otherwise; and simi-
larly sup'(0) = -oo, sup'(Z) = z' if z' E Zis the greatest element of Z, and
su p' (Z) = oo otherwise. It is now straightforward to show that UY is indeed
the least upper bound of Y. •
Red(f)- - - -
nnr(T)
gip(f)
lip(!)
Ext(f) - - - -
.l
for all h, l2 E L, i.e. it always returns an element larger than both its ar-
guments. Note that we do not require O to be monotone, commutative,
associative, nor absorptive (i.e. that l O l = l).
Let (ln)n be a sequence of elements of L and let cp : L x L -+ L be a total
function on L. We shall now use cp to construct a new sequence (l~)n defined
by:
l ifn=O
l~ = { :
ln-l c/J ln ifn >O
The following result expresses that any sequence can be turned into an as-
cending chain by an upper bound operator:
Note that the operation is not symmetric: for int = [0, 2] we e.g. have
[1, 2]0int [2, 3] = [1, 3] whereas [2, 3]0int [1, 2] = [-oo, oo].
It is immediate that Oint is an upper bound operator. Consider now the
sequence:
[0, O], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], · · ·
If int = [O, oo], then the upper bound operator will transform the above
sequence into the ascending chain:
[0, O], [0, 1], [0, 2], [O, 3], [O, 4], [O, 5], · · ·
226 4 Abstract Interpretation
f V'm _ jm+1 _
-V' -···
Red(f) - - - - - •
lfp(f) - - - - - - - - - - - - - - -
~~
~~
~~ = _l
However, if int = [O, 2], then we will get the following ascending chain
[0, O], [0, 1], [0, 2], [0, 3], [-oo, oo], [-oo, oo], · · ·
which eventually stabilises.
•
Widening operators. We can now introduce a special class of upper
bound operators that will help us to approximate the least fixed points: An
operator \7 : L x L __, L is a widening operator if and only if:
• for all ascending chains (ln)n the ascending chain (t;;:)n eventually sta-
bilises.
Note that it follows from Fact 4.11 that (t;;:)n is indeed an ascending chain.
The idea is as follows: Given a monotone function f : L __, L on a com-
plete lattice L and given a widening operator \7 on L, we shall calculate the
sequence (!!!; )n defined by
if n =O
if n > O 1\ f(f~- 1 ) [::; ~~- 1
otherwise
4.2 Approximation of Fixed Points 227
As in Fact 4.11 it follows that this is an ascending chain and Proposition 4.13
below will ensure that this sequence eventually stabilises. From Fact 4.14
below we shall see that this means that we will eventually have f(!V') ~ fv
for some value of m (corresponding to the second clause in the definition
of fv)· This means that fis reductive at fv and from Tarski's Theorem
(Proposition A.10) we then know that fv ;;! lip(!) must be the case; hence
we take
lEpvU) =!V'
as the desired safe approximation of lip(!). This is illustrated in Figure 4.4.
We shall now establish the necessary results.
Proposition 4.13
If V' is a widening operator then the ascending chain Uv )n even-
tually stabilises.
In preparation for the proof of Proposition 4.13 we shall first show the fol-
lowing technical result:
if n =O
otherwise
if n =O
if n >O
and note that (ln)n is an ascending chain because (!!!; )n is an ascending chain (Pact
4.14) and fis monotone. We shall now prove that
where LB(zi, z3) E {zi} U K U { -oo} is the best possible lower bound and
UB(z2, z4) E {z2} U K U {oo} is the best possible upper bound. In this way
a change in any of the bounds of the interval [zi, z2] can only take place in a
finite number of steps (corresponding to the elements of K).
For the precise definition we let Zi E Z' = Z U { -oo, oo} and write:
{
ZI if ZI :::; Z3
LBK(ZI, Z3) k if Z3 <ZI 1\ k = max{k E K 1 k:::; z3}
-00 if Z3 <ZI 1\ \:fk E K : Z3 < k
{
Z2 if Z4 :::; Z2
UBK(Z2, Z4) k if Z2 < Z4 1\ k = mi n{ k E K 1 Z4 :::; k}
00 if Z2 < Z4 1\ \:fk E K : k < Z4
4.2 Approximation of Fixed Points 229
.l if int1 = int2 = .l
int1 \7 in~= { [ LBK(inf(int!), inf(int2)), UBK(sup(int!),sup(int2))]
otherwise
[O, 1], [O, 2], [O, 3], [O, 4], [0, 5], [0, 6], [0, 7], · ·.
and assume that K = {3, 5}. Then (int~)n will be the chain
[0, 1], [0, 3], [0, 3], [0, 5], [0, 5], [0, oo], [0, oo], · · ·
Without loss of generality we can assume that the second property holds.
Hence there must exist an infinite sequence n 1 < n 2 < · · · such that
where for the purpose of this definition we set max(0) = -oo. Since we also
have
it must be the case that sup( intnj+l) > sup( int~j) as otherwise sup( int~j+l)
= su p( int~J ) . But then the construction yields
n { fv if n =o
[/]~ = [/]~- 1 ~ f([/]~- 1 ) if n >O
Lemma 4.16 below guarantees that this is a descending chain where ali ele-
ments satisfy lip(!) !;:;; [!]~. Proposition 4.17 below tells us that this chain
eventually stabilises so [f]:l' = [/]~'+1 for some value m'. We shall therefore
take
lfp~(f) = [/]:l'
as the desired approximation of lfp(f). The complete development is illus-
trated in Figures 4.4 and 4.5. We shall now establish the required results.
for alin. •
Proof By induction on n we prove:
(4.7)
4.2 Approximation of Fixed Points 231
[JJ~ = fv
[/]1.
~ .............. . [/]:l'-1
~--·········· [/]~' = [/J:l' +1 = ...
lfp(f)
and together these two results estahlish the hasis of the induction proof.
For the inductive step we may apply f to the induction hypothesis (4. 7) and get
From the assumption JUV') ~ !V' it is immediate that f(rUV')) ~ rUV') for
n 2': o and hence rUv) E Red{f). But then rUV') ;;;J lfp(f). This completes the
proof. •
Proposition 4.17
If ~ is a narrowing operator and f Uv) ~ fv then the descending
chain ([f]~)n eventually stabilises.
ifn =O
ifn >O
and note that this defines a descending chain because ([f]::l..)n is a descending chain
and because f([f]~) ~ fv· Therefore the sequence (l~)n eventually stabilises. We
now prove by induction on n that:
l~ = [f]::l..
The hasis (n = O) is immediate. For the inductive step we calculate l~+I
l~ ~ ln+I [f]::l.. ~f([f]::l..) = [/]'~+ 1 . It thus follows that ([/]::l..)n eventually
stabilises. •
It is important to stress that narrowing operators are not the dual concept
of widening operators. In particular, the sequence Uv )n may step outside
Ext(f) in order to end in Red(f), whereas the sequence ([f]~)n stays in
Red(f) all the time.
where z1 < z2 < Z3 < · · ·. The idea is now to define a narrowing operator
~N that will force the sequence to stabilise when Zi ~ N for some fixed non-
negative integer N. Similarly, for a descending chain with elements of the
form [-oo, zi] the narrowing operator will force it to stabilise when Zi::; -N.
Formally, we shall define ~ = ~N by
{
.1 if int1 = .1 V in~ = .1
[z1, z2] otherwise
4.3 Galois Connections 233
where
Z1 = { inf( intl)
inf( int2)
if N < inf( in~) 1\ sup( in~) = oo
otherwise
Z2 = { sup( intl)
sup( int2)
if inf(int2) = -oo 1\ sup(int2)
otherwise
< -N
[0, oo], [1, oo], [2, oo], [3, oo], [4, oo], [5, oo], · · ·
and assume that N = 3. Then the operator will give the sequence ([n, oo]Ll)n:
[0, oo], [1, oo], [2, oo], [3, oo], [3, oo], [3, oo], · · ·
Let us show that b. is indeed a narrowing operator. It is immediate to verify
that
in~ ~ int1 implies in~ ~ int1 b. int2 ~ int1
by cases on whether in~ = ..l or in~ =1- ..l. We shall then show that if (intn )n
is a descending chain then the sequence (in~ )n eventually stabilises. So
assume that (intn)n is a descending chain. One can then show that (in~)n
is a descending chain (Exercise 4.10). Next suppose by way of contradiction
that (in~ )n never eventualiy stabilises. It follows that there exists n 1 2: O
such that:
in~ =1- [-oo, oo] for alin 2: n1
It furthermore follows for ali n 2: n1 that in~ must have:
But then in~ = in~2 for all n 2: n2 and we have the desired contradiction.
This shows that b. is a narrowing operator. •
a:L-+M
L
--
a
M
for this setup and would expect that a and "1 should be somehow related.
We shall study this relationship in the present section and then return to the
connection between p f- h 1> l2 and p f- m11> m2 in Section 4.5; in Section 4.4
we shall study the systematic construction of such relationships.
We define (L, a, "f, M) to be a Galois connection between the complete lat-
tices (L, !;;;) and (M, !;;;) if and only if
that satisfy:
(4.8)
ao'Y C .>..m.m (4.9)
Conditions (4.8) and (4.9) express that we do not Iose safety by going back
and forth between the two lattices although we may Iose precision. In the
case of (4.8) this ensures that if we start with an elementlE L we can first
find a description a(l) of it in M and next determine which element 'Y(a(l))
of L that describes a(l); this need not bel but it will bea safe approximation
tol, i.e. l!;;; 'Y(a(l)). This is illustrated in Figure 4.6.
where inf and sup are as in Example 4.10. Thus '"YZI will extract the set of ele-
ments described by the interval, e.g. '"Yzr([O, 3]) = {0, 1, 2, 3} and '"Yzr([O, oo]) =
{z E Z 1 z 2:: 0}.
The abstraction function azr : P(Z) -+ Interval is defined by
z-{.l
azr( ) - [inf'(Z),sup'(Z)]
ifZ=0
otherwise
where inf' and sup' are as in Example 4.10. Thus azr will determine the
smallest interval that includes all the elements of the set, e.g. azr( {0, 1, 3}) =
[0,3] and azr({2*z 1 z >O})= [2,oo].
Let us verify that (P(Z), azr, '"Yzr, Interval) is indeed a Galois connection. It
is easy to see that azr and '"Yzr are monotone functions. We shall next prove
that (4.8) holds, i.e. that '"YZI o azr;;;) >..Z.Z. If Z =/= 0 we have:
Finally, we shall prove (4.9), i.e. that azr o 'YZI ~ >.int.int. Consider first
int = [zi. z2] where we have:
Proposition 4.20
(L, a,')', M) is an adjunction if and only if (L, a,')', M) is a Galois
connection.
Proof First assume that ( L, a., 'Y, M) is a Galois connection and let us show that
it is an adjunction. So assume first that o.(l) ~ m; since 'Y is monotone we get
7(o.(l)) ~ 7(m); using that 7oo.;;;) >..l.l we then get l ~ 7(o.(l)) ~ 7(m) as required.
The proof showing that l ~ 7(m) implies a.(l) ~ m is analogous.
Next assume that (L, a.,"(, M) is an adjunction and let us prove that it is a Galois
connection. First we prove that 'Y o a. ;;;) >..l.l: for l E L we trivially have o.(l) ~ o.(l)
and using that o.(l) ~ m => l ~ 7(m) we get l ~ 7(o.(l)) as required. The proof
showing that a. o 'Y ~ >..m.m is analogous. To complete the proof we also have to
show that a. and 'Y are monotone. To see that a. is monotone suppose that h ~ l2 ;
we have already proved that 'Y o a.;;;) A.l.l so we have h ~ l2 ~ 7(o.(l2)); using that
l ~ 7(m) => a.(l) ~ m we then get o.(h) ~ o.(h). The proof showing that 'Y is
monotone is analogous. •
4.3 Galois Connections 237
(P(V),a,,,L)
between P(V) and L where the abstraction and concretisation functions are
defined by
a(V') = U{f3(v) vE V'} 1
r(l) = {vEVIf3(v)i;;;l}
for V' ~ V and l E L. Let us pause for a minute to see that this indeed
defines an adjunction:
a(V')!;;;; l {:} U{f3(v) 1 vE V'}!;;;; l
{:} VvEV':f3(v)i;;;l
{:} V'~ 1 (t)
It follows from Proposition 4.20 that we also have a Galois connection. It is
also immediate that a( {v}) = (3( v) as illustrated by the diagram:
'
{vE V /3ry(v)
1 ~ D'} {v 1 TJ(v) E D'}
for V' ~ V and D' ~ D. The relationship between 'TJ, (3ry, ary and /ry is
illustrated by the diagram:
238 4 Abstract Interpretation
{0,+}
{+}
P(V) P(D)
oj/;:jo 'fJ
V D
Example 4.21 Let us consider the two complete lattices (P(Z), ~) and
(P(Sign), ~) where Sign = {-,O,+}; see Figure 4.7. The extraction function
- ifz<O
sign(z)= { O ifz=O
+ if z >o
with
Gsign(Z) {sign(z) 1 z E Z}
1'sign(S) {z E Z 1 sign(z) E S}
(i) a uniquely determines 'Y by 'Y(m) = U{l 1 a(l) !;.;; m} and 'Y uniquely
determines a by a(l) = n{m Il!;.;; 'Y(m)}.
Then we have a(l) [:;; m =} l E {l' 1 a(l') [:;; m} =} l [:;; 'Y(m) where the last
implication follows from the definition of 'Y· For the other direction we first observe
that l [:;; 'Y(m) =} a(l) [:;; a('Y(m)) because a is completely additive and hence
monotone. Now
sol [:;; 'Y(m) =} a(l) [:;; m. It follows that (L, a,"(, M) is an adjunction and hence a
Galois connection by Proposition 4.20.
The proof of the claim for "( is analogous. •
The final result shows that we neither Iose nor gain precision by iterating
abstraction and concretisation:
such that (Interval, ars, 'Y!s, P(Sign)) is a Galois connection, we shall sim-
ply determine whether or not "Yrs is completely multiplicative: If it is, then
Lemma 4.23 guarantees that there does indeed exist a Galois connection and
Lemma 4.22 tells us how to construct the abstraction function. If "Yrs is
4.3 Galois Connections 241
not completely multiplicative then Lemma 4.22 guarantees that there cannot
exist a Galois connection. In order to determine whether ar not 1'Is is com-
pletely multiplicative we shall use Lemma A.4 of Appendix A: It is immediate
ta verify that P(Sign) is finite and that 'Yrs satisfies conditions (i) and (ii)
of Lemma A.4. Ta verify condition (iii) we need ta compare 'Yrs(S1 n 82) ta
'Yrs(SI) n 'Yrs(S2) for all pairs (81. 82) E P(Sign) x P(Sign) of incomparable
sets of signs, i.e. all pairs in the following list:
v S m iff v R ('Y(m))
and we shall now argue that this is a correctness relation between V and M
fulfilling conditions corresponding ta (4.4) and (4.5). We have
using that 'Y is monotone and that R satisfies condition (4.4); this shows that
S also satisfies condition (4.4). Also for all M' ~ M we have
(Vm E M': v S m) ::::} (Vm E M': v R ('Y(m)))
::::} vR(n{'Y(m)lmEM'})
* v R ('Y(nM'))
::::} v 8 (nM')
using that 'Y is completely multiplicative (Lemma 4.22) and that R satisfies
condition (4.5); this shows that S also satisfies condition (4.5). Hence S
defines a correctness relation between V and M.
Continuing the above line of reasoning assume now that R is generated by the
representationfunction {3: V-+ L, i.e. v R l {::} {3(v) ~ l. Since (L,a,"f,M)
is a Galois connection and hence an adjunction (Proposition 4.20) we may
calculate
v S m {::} R ('Y(m))
v
{::} {3(v) ~ 'Y(m)
{::} (a o {3)(v) ~ m
showing that S is generated by a o {3 : V -+ M. This shows how the Galois
connection facilitates the definition of correctness relations and representa-
tion functions and concludes our motivation for why it is natural to require
that the connection between L and M is specified by a Galois connection.
that satisfy:
ao'Y >..m.m
Thus we now require that we do not Iose precision by first doing a concretisa-
tion and then an abstraction. As a consequence M cannot contain elements
4.3 Galois Connections 243
that do not describe elements of L, i.e. M does not contain superfluous ele-
ments.
Lemma 4.27 For a Galois connection (L, a,"(, M) the following claims
are equivalent:
(i) (L, a,"(, M) is a Galois insertion;
(ii) a is surjective: '1/m E M : :Jl E L : a(l) = m;
(iii) "( is injective: 'l/m1,m2 E M: "f(m!) = "f(m2)::::} m1 = m2; and
(iv) 'Y is an order-similarity: 'l/m1. m 2 E M :
'Y(m!) !;;;; 'Y(m2) {::} m1 !;;;; m2. •
Proof First we prove that (i) => (iii). For this we calculate
Next we prove that (iii) ~ (ii). FormE M we have -y(o:(y(m)) = -y(m) by Fact
4.24 and hence a('Y(m)) = m by (iii).
Now we prove that (ii) ~ (iv). That m1 [:;:; m2 ~ -y(m1) [:;:; -y(m2) is immediate
since 'Y is monotone. Next suppose that -y(m1) [:;:; -y(m 2); by monotonicity of a this
gives a('Y(m1)) [:;:; a('Y(m2)); using (ii) we can write m1 = a(h) and m2 = a(b) for
some l1, l2 E L; using Fact 4.24 this then gives m 1 [:;:; m 2 as desired.
Finally we prove that (iv) ~ (i). For this we calculate
a('Y(m1)) [:;:; m2 <(=} -y(m1) [:;:; -y(m2) <(=} m1 [:;:; m2
where the first step is using Proposition 4.20 and the last step is using (iv). This
shows that a('Y(m1)) and m1 have the same upper bounds and hence are equal. •
c.;:M--+M
Proposition 4.29
Let (L, o:, -y, M) be a Galois connection and define the reduction
operator c.; : M --+ M by
This is illustrated in Figure 4.9: The idea is that two elements of M are
identified by ~ if they map to the same value by ')'; in particular, m and
a('Y(m)) will be identified.
In preparation for the proof we shall first show the following two results:
is a complete lattice. •
Proof Clearly o:[L]
~ M is partially ordered by the ordering l;;:M of M = (M, l;;:M ).
We now want to show for M' ~ o:[L] ~ M that
We first show that UM M' E o:[L] which means that there exists l EL such that
a(l) = UM M'. For this take l = UL 'Y[M'] which clearly exists in L. Since
a o 'Y o a = a (Fact 4.24) and a is completely additive (Lemma 4.22) we then have
then
~(m) = o{y(m))
and hence a[L) = ~[M).
•
Proof We have <;(m) ~ a(-y(m)) because -y(m) = -y(o:('Y(m))) using Fact 4.24.
That o:('Y(m)) ~ ~,;(m) is equivalent to -y(m) ~ -y(<;(m)) using Proposition 4.20 and
-y(~,;(m)) = nb(m') 1 -y(m') = -y(m)} = -y(m) follows from Lemma 4.22.
Next consider o:(l) for l E L; by Fact 4.24 we have o:(l) = o:(-y(o:(l))) and hence
o:(l) = ~,;(o:(l)); this shows o:[L] ~ ~,;[M]. Finally consider ~,;(m) for m E M; then
~,;(m) = o:('Y(m)); this shows ~,;[M] ~ o:[L]. •
signparity[Z) = {( -, odd), (-, even), (0, even), (+, odd), (+, even)}
showing that (P(Z), asignparity, 'Ysignparity, P(signparity[Z))) is the resulting Ga-
lois insertion. •
(P(V), ~). We may then decide to use a more approximate set of prop-
erties and introduce the complete lattice (Lt, ~) related to L 0 by a Galois
connection (Lo, 0:1, 1'1, Lt). This step can then be repeated any number of
times: We replace one complete lattice Li of properties with a more ap-
proximate complete lattice (Li+l, ~) related to Li by a Galois connection
(Li, ai+ 1, 'Yi+ 1, Li+ t). This process will stop when we have an analysis with
the required computational properties. So the situation can be depicted as
follows:
-
'Yk
The above sequence of approximations of the analysis could as well have been
done in one step, i.e. the "functional composition" of two Galois connections
is also a Galois connection. Formally, if (Lo, 0:1, 1'1. L1) and (L1, 0:2, '/'2, L2)
are Galois connections, then
is also a Galois connection. To verify this we simply observe that o: 2(o: 1(1 0 )) ~
12 <=? o:1(1o) ~ 1'2(12) <=? 1o ~ 'Y1b2(12)) and, using Proposition 4.20, this
shows the result. A similar result holds for Galois insertions because the
functional composition of surjective functions yields a surjective function.
Each of the Galois connections (Li, ai+ 1, 'Yi+ 1, Li+ 1) may have been obtained
by combining other Galois connections and we shall shortly introduce a num-
ber of techniques for doing so. We shall illustrate these techniques in a se-
quence of examples that in the end will give us a finite complete lattice that
has turned out tobe very useful in practice for performing an Array Bound
Ana1ysis.
(P(Z X Z),o:diff,'/'diff,P(Z))
The abstraction and concretisation functions O:diff and 1'diff will then be
for ZZ ~ Z x Z and Z ~ Z.
<-1 if z < -1
-1 if z = -1
range(z) = o if z =o
+1 if z =1
>+1 if z>1
The abstraction and concretisation functions O:range and 1'range will then be
O:range(Z) {range(z) 1 z E Z}
1'range(R) = {z 1 range(z) E R}
where O:R = O:range o O:diff and 1'R = 1'diff o 1'range, is a Galois connection. We
obtain the following formulae for the abstraction and concretisation functions:
to approximate the total function spaces and the monotone function spaces.
Alternatively, we may have several analyses of the same structure and may
want to combine them into one analysis and here the direct product and the
direct tensor product allow us to do that. Using the notion of Galois inser-
tions this leads to a study of the reduced product and the reduced tensor
product.
The benefit of having such a catalogue of techniques is that a relative small
set of "basic" analyses, whose correctness have been established, can be used
to construct rather sophisticated analyses and, from an implementation point
of view, opens up the possibility of reusing existing implementations. Finally,
it is worth stressing that the complete lattices used in program analysis ob-
viously can be constructed without using these techniques but that it often
provides additional insights to view them as combinations of simpler Galois
connections.
where:
To see that this indeed does define a Galois connection we simply calculate
and use Proposition 4.20. A similar result holds for Galois insertions (Exercise
4.17).
250 4 Abstract Interpretation
Example 4.34 The Array Bound Analysis will contain a component that
performs a Detection of Signs Analysis on pairs of integers. As a starting
point, we take the Galois connection
where
where one way of proving the equality is to use that a is completely additive.
This shows the required result.
It is instructive to see how the relational method is simplified if the Galois
connections (P(Vi), ai, '/i, P(Di)) are given by extraction functions 'T/i :Vi----+
Di, i.e. if ai(~')= {TJi(vi) 1 Vi E ~'} and 'Yi(DD ={vi 1 TJi(vi) E D;}. We
then have
which also can be obtained directly from the extraction function TJ : V1 X V2 ----+
D1 x D2 defined by ry(v1,v2) = (ryi(vi),ry2(v2)).
Example 4.35 Let us return to Example 4.34 and show how the rela-
tional method can be used to construct a more precise analysis. We will now
get a Galois connection
a'(!) a of
r' (g) 'o g
To see this we first observe that a' and 1' are monotone functions because a
and 1 are; furthermore
r' (a'(!))
a'(r'(g))
follow since (L, a,/, M) is a Galois connection. A similar result holds for
Galois insertions. The construction is illustrated by the following commuting
diagrams:
L M L M
~~)
s s
4.4 Systematic Design of Galois Connections 253
Example 4.36 Assume that we have some analysis mapping the program
variables to properties from the complete lattice L, i.e. operating on the ab-
stract states Var ---+ L. Given a Galois connection (L, a,"(, M) the above
construction will show us how the abstract states of Var ---+ L are approxi-
mated by the abstract states of Var---+ M. •
by taking
a(!) a2 o f o 'Yl
"f(g) "/2 o g o a1
To check this we first observe that the functions a and 'Y are monotone
because a2 and "/2 are; next we calculate
f "f(g)
L1 L2 L1 L2
>I 1 j a, a1 j ]~
M1 M2 M1 M2
a(!) g
same data can be combined into one analysis; this amounts to performing two
analyses in parallel. We shall consider two variants of this analysis, one "cor-
responding" to the independent attribute method and one "corresponding"
to the relational method.
Example 4.37 Let us consider how this construction can be used to com-
bine the detection of signs analysis for pairs of integers given in Example 4.35
with the analysis of difference in magnitude given in Example 4.33. We get
the Galois connection
Note that the expression (x, 3*x) in the source language has a value in
{(z,3*z) 1 z E Z} which is described by assR({(z,3*z) 1 z E Z}) =
({(-,-),(0,0),(+,+)},{0,<-1}). Thus we do not exploit the fact that ifthe
pair is described by (o, O) then the difference in magnitude will indeed be
described by O whereas if the pair is described by (-, -) or (+, +) then the
difference in magnitude will indeed be described by <-1. •
4.4 Systematic Design of Galois Connections 255
which also can be obtained directly from the extraction function 'f) : V ---->
D 1 x D 2 defined by TJ(v) = (TJI(v),TJ2(v)).
Example 4.38 Let us return to Example 4.37 and show how the direct
tensor product gives a more precise analysis. We will now get a Galois con-
nection
(P(Z x Z),assR',1'SSR', P(Sign x Sign x Range))
where
It is worth pointing out that this Galois connection is also obtainable from
the extraction function
twosignsrange: Z x Z --t Sign x Sign x Range
defined by twosignsrange(z1, z2) = (sign(z1), sign(z2), range(lz1l-lz21)).
Returning to the precision of the analysis we will now have assR' ({ (z, 3 * z) 1
z E Z}) = {(-,-,<-1),(0,0,0),(+,+,<-1)} and hence have a more precise
description than in Example 4.37.
However, it is worth noticing that the above Galois connection is not a Ga-
lois insertion. To see this consider the two elements 0 and {(O, O, <-1)} of
P(Sign x Sign x Range) and observe that
')'SSR' (0) = 0 = ')'SSR' ( {(0, 0, <-1)})
Thus 'YSSR' is not injective and hence Lemma 4.27 shows that we do not have
a Galois insertion. •
where
a(V') U{a1({v}) x a2({v}) 1 vE V'}
-y(DD) {v 1 a1({v}) x a2({v}) ~ DD}
c;(DD) n{DD' 1 -y(DD) = -y(DD')}
4.4 Systematic Design of Galois Connections 257
Again it follows from Proposition 4.29 that this is indeed a Galois insertion.
Example 4.39 Let us return to Example 4.38 where we noted that the
complete lattice P(Sign x Sign x Range) contains more than one element
that describes the empty set of P(Z x Z). The superfluous elements will
be removed by the construction of Proposition 4.29. The function (SSR' will
amount to
where SSR, SSR' <:;;; Sign x Sign x Range. In particular, <;ssR' will map the
singleton sets constructed from the 16 elements
to the empty set. The remaining 29 elements of Sign x Sign x Range are
and they describe disjoint subsets of Z x Z. Let us call the above set of 29
elements for AB (for Array Bound); then <;ssR' [P(Sign x Sign x Range)] is
isomorphic to P(AB).
To conclude the development of the complete lattice and the associated Ga-
lois connection for the Array Bound Analysis we shall simply construct the
reduced tensor product of the Galois connections of Examples 4.35 and 4.33.
This will yield a Galois insertion isomorphic to
Note that from an implementation point of view the last step of the con-
struction has paid off: if we had stopped with the direct tensor product in
Example 4.38 then the properties would need 45 bits for their representation
whereas now 29 bits suffice.
Summary. The Array Bound Analysis has been designed from three simple
Galois connections specified by extraction functions:
258 4 Abstract Interpretation
~' 1 la,
Mt M2
0:2 o /p o 'Yl
Example 4.40 Let us return to Example 4.9 where we studied the simple
program plus and specified the very precise analysis
/plus(ZZ) = {zl + Z2 1 (z1, z2) E ZZ}
using the complete lattices (P(Z), ~) and (P(Z x Z), ~). In Example 4.21
we introduced the Galois connection
(P(Z), O:sign, 'Ysign, P(Sign))
for approximating sets of integers by sets of signs. In Example 4.35 we used
the relational method to get the Galois connection
(P(Z x Z), o:sS', 'YSS', P(Sign x Sign))
operating on pairs of integers. We now want to induce a more approximate
analysis for the plus program
9plus : P(Sign X Sign) --+ P(Sign)
from the existing analysis /plus· To do so we take
9plus = O:sign O /plus O 'YSS 1
Lemma 4.41 If (Li, ai, '/'i, Mi) are Galois connections, and f3i : Vi ---+Li
are representation functions then
and that a2 o fpo'/'l ~ gp. Since (Li, ai, '/'i, Mi) are Galois connections and fv
and gp are monotone we get fv ~ 1'2 o gp o a1 as illustrated in the diagrams:
4.5 Induced Operations 261
fp fp
Ll L2 L1 L2
1' 1 l
1n
a2 a, j
1n
11'
M1 M2 M1 M2
gp gp
Lemma 4.41 may then be read as saying that if JP: L1 --+ L2 is optimal then
so is a2 o fp o 11 : M1 --+ M2.
Lemma 4.42 Assume that (L, a,"(, M) is a Galois connection and let f:
L --+ L and g : M --+ M be monotone functions satisfying that g is an upper
approximation to the function induced by J, i.e.:
To prove the second result we observe that {l(m) 1 g(m) ~ m} <;;; {l 1 f(!) ~ !}
follows from the previous result. Hence we get (using Lemma 4.22):
Here we do not demand that L satisfies the Ascending Chain Condition and
we do not specify the space F of transfer functions as we shall be taking
F tobe the entire space of monotone functions from L to L (which clearly
contains the identity function and is closed under composition of functions).
An instance A of a generalised Monotone Framework then consists of:
defined by:
-
f(Ao,A.) = ( >.f. u {A.(f)t 1 (f t ,f) E F} U tE,
R >.f.JR(Ao(f)))
We then have the following important result (in the manner of Section 4.4):
using the techniques for total function spaces and the independent attribute
method presented in Section 4.4. The assumptions 9R ;! a o fR o 'Y (for all f)
and J ;! a( t) can then be used to establish
g ;) a' o f o "( 1
as the following calculations show
where we have used that o:(j_) = j__ We can now use Lemma 4.42 to obtain
g(Bo, B.) ~ (Bo, B.) implies fb'(Bo, B.)) ~ "/(Bo, B.)
and it follows that if (Bo, B.) f= B;:::J then ('y o Bo, "(o B.) f= A;:::J as stated
above.
For the analysis B it follows from Section 4.3 that it is natural to use the
representation function
o: o {3 : State --+ M
and the correctness of all solutions to B;:::J then amounts to the claim:
Assume that (Bo, B.) f= B;:::J and (S*, a1) --+* a2; (4 12 )
then (o: o {3)(at) ~ J implies (o: o {3)(a2) ~ U{B.(f) 1 f E final(S*)}. ·
It follows that:
A Worked Example
As a concrete example we shall now consider an analysis SS for the WHILE
language that approximates how sets of states are transformed into sets of
states. First we prove that it is correct. Then we show that the Constant
Propagation Analysis of Section 2.3 is an upper approximation of an analysis
induced from 55. This will then establish the correctness of the Constant
Propagation Analysis.
Given a labei consistent statement S* in Stmt we can now specify the in-
stance as follows:
Lemma 4.43 Assume that (SSo, ss.) F 55 2 and (S*, 0"1) ---+* 0"2i then
State implies 0"2 E U{ ss. (.e) 1.e E final( s*)}.
0"1 E •
Proof From Section 2.2 we have:
(S, a-) -+ (S', a') implies final(S) :2 final(S') 1\ flow(S) :2 flow(S')
and as in Chapter 2 it is immediate that
flow(S) :2 flow(S') 1\ (SSo,SS.) 1= SS;;J(S) implies (SSo,SS.) 1= SS;;J(S')
It then suffices to show
(SSo,SS.) 1= SS;;J(S) 1\ (S,a)-+ a' 1\ a E SSo(init(S))
implies a' E U{ SS. (1!) 1! E final( S)}
1
since then an induction on the length of the derivation sequence (S*, a1) ----+ * a 2 will
give the result. The proof proceeds by induction on the inference in the semantics.
We only consider a few of the interesting cases.
The case ([x := a]f, a) ----+ a[x ..... A[a]a]. Then 55;:2 (S) will contain the equation
SS.(f) :2 {a[x ..... A[a]a]l a E SSo(f)}
and since init([x :=an= f and final([x :=an= {f} we see that that the required
relationship holds: if a E SSo(f) then a[x ..... A[a]a] E SS.(f).
The case (S1; S2, a) ----+ (S~; S2, a') because (S1, a) ----+ (S~, a'). From the assumption
a E SSo(init(S1;S2)) we get a E SSo(init(Sl)) and the induction hypothesis gives
a' E SSo(init(SD). But then a' E SSo(init(S~; S2)) as required.
The case (S1;S2,a)--+ (S2,a') because (S1,a)--+ a'. From the assumption a E
SSo(init(S1; S2)) we get a E SSo(init(Sl)) and the induction hypothesis gives a' E
U{SS.(f) 1 f E finaJ(S1)}. We have
{(f,init(S2)) f E finaJ(S1)} <:;; flow(S1;S2)
1
by f3cp(a) =a (as in Example 4.6). As in Section 4.3 this gives rise to a Galois
connection (P(State), acp, 'YCP, Statecp) where acp(~) = U{f3cp(a) 1 a E
~} and ')'cp(â') ={a f3cp(a) r;;;; â'}. One can now show that for alllabels C
1
as well as 'YCP(>.x.T) =State. Let us only consider the case where [x := a]c
occurs in s* and calculate
If 'VL turns out tobe a widening operator we then know how to approximate
the least fixed point of f : L --+ L while calculating over L. This has the
advantage that the coarser structure of M is only used to ensure convergence
and does not needlessly reduce the precision of all other operations. The
following result gives sufficient criteria for this approach to work:
268 4 Abstract lnterpretation
Proposition 4.44
Let (L, a, 'Y, M) bea Galois connection and let 'VM : M x M--+ M
be an upper bound operator. Then the formula
Proof First we prove that "\h is an upper bound operator. Since 'VM is an upper
bound operator we have a(l;) !;;;; o:(h)'VMo:(l2)· Using that a Galois connection is
an adjunction we get l; !;;;; 'Y(o:(h)'VMo:(l2)), i.e. l; !;;;; h '\1Ll2.
Assume now that condition ~i) is fuifilled and consider an ascending chain (ln)n
in L. We know that aiso (ln L )n is an ascending chain and that l;[L E 'Y[M] for
n > O. Hence (a(l;[L) )n is an ascending chain and since M satisfies the Ascending
Chain Condition there exists no ~ 1 such that a(l;[L) = a(l;[~) for all n ~ no. So
'Y(a(l;[L )) = 'Y(a(l;[~)) for ali n ~ no and using that 'Y o a o 'Y = 'Y (Fact 4.24) we
get l;[L = l;[~ for ali n ~ no. This compietes the proof.
Assume next that condition (ii) is fuifilled and consider again an ascending chain
(ln)n in L. Since a is monotone it follows that (a(ln)n) is an ascending chain in M.
Now, 'VM is a widening operator on M so there exists no such that (a(ln))vM =
(a(ln 0 ))vM for n ~ no. We shall now prove that
(a(ln))VM = a(l;[L) (4.13)
for all n ~O. The case n =O is immediate since (o:~lo))vM = a(lo) = a((ciL). For
the induction step we assume that (a(ln))vM = a(lnL). Then
(a(ln+l)) VM = (a(ln)) VM 'VM a(ln+l)
a(l;[L) 'VM a(ln+l)
and
a(l;[L \JL ln+I)
a('Y(a(l;[L) 'VM a(ln+l)))
a(l;[L) 'VM a(ln+I)
since (L, a, "f, M) is a Gaiois insertion.
Using (4.13) we thus have that there exists no such that a(l;;L) = a(z;;g) for all
n ~ n 0 • But then 'Y(a(l;[L)) = 'Y(a(l;[~)) and hence l;[L = l;[~ for all n ~ n 0 as
was shown above. This compietes the proof. •
4.5 Induced Operations 269
Lemma 4.45 If (L, a,"(, M) is a Galois insertion such that 'Y(..l-M) = .lL,
and if \lM : M x M---+ M is a widening operator, then the widening operator
\IL : L x L - t L defined by h \IL l2 = 'Y(a(h) \lM a(l2)) satisfies
(4.14)
(4.15)
)
if g (YvM
n-1) C:: n-1
- YvM
otherwise
)
n-1
YvM if g(g~;}) [;:; g~-;}
g~-;} "iJM g(g~;}) otherwise
-y(g~M)
In this calculation we have used that (4.14) and (4.15) hold for n-1, the definition
of "ilL, and that (L, a, -y, M) is a Galois insertion. •
This result then provides the formal justification for the motivating remarks
in the beginning of the subsection. To be specific let M be of finite height,
let (L, a,"(, M) be a Galois insertion satisfying "f(.lM) = .lL, and let "ilM be
the least upper bound operator UM. Then the above lemma shows that
which means that Jfp.,h (!) equals the result we would have obtained if we
decided to work with a o f o 'Y : M ---+ M instead of the given f : L ---+ L;
furthermore the number of iterations needed turn out to be the same. Since
the greater precision of L over M is available for all other operations, this
suggests that the use of widening operators is often preferable to the approach
of Subsection 4.5.1.
Concluding Remarks
In this chapter we have only been able to touch upon a few of the central
concepts of Abstract Interpretation; this has mainly been based on [37, 39,
35, 40]. Mini Project 4.1 and the series of examples leading up to Example
4.39 are based on [179]. Mini Project 4.3 is inspired by [126, 68]. Much
more can be said both about the development of the theory and about its
applications. In this section we briefly discuss some of the more important
concepts that have been omitted so far.
a subset of L and that no essential features are lost by doing this. This then
opens up the possibility for directly comparing the precision of various Galois
connections over the same complete lattice L by simply relating the closure
operators. The reiat ion P1 !;;;; P2 is naturally defined by 't:/l E L : Pl (l) !;;;; P2 (l)
but turns out to be equivalent to the condition that p2 ( L) ~ p 1 ( L) and
represents the fact that P2 is more approximate than PI·
Having defined an ordering on the set of upper closure operators on L one
can next show that the set is a complete lattice: the least element is given
by the upper closure operator >-.l.l and the greatest element by >-.l. T. The
binary greatest lower bound operator n is of special interest: it gives rise to
the construction of the reduced product [39] (see Section 4.3.)
A number of additional constructs can be explained by means of upper clo-
sure operators: we just mention reduced cardinal power [39] and disjunctive
completion [39]. By "inverting" some of these constructions it may then be
possible to find the "optimal bases" with respect to a given combination: for
reduced product the notion of pseudo-complementation has been used to find
the minimal factors [33, 34], and for disjunctive complet ion one can identify
the basic irreducible properties that are inherently necessary for the analysis
[63, 64].
element, and the dualleast upper bound is the greatest lower bound etc. The
principle of lattice duality of Lattice Theory says that if any statement about
partially ordered sets is true then so is the dual statement. (Interestingly the
concept of monotonicity is its own dual.) However, we should like to point
out that the dual of a complete lattice may of course differ from the complete
lattice itself; pictorially we represent this by drawing the complete lattice
"up-side down".
The principle of lattice duality is important for program analysis because it
gives an easy way of relating the literature on Abstract Interpretation to the
"classical" literature on Data Flow Analysis: simply dualise the complete
lattices. So in Abstract Interpretation the greatest element is trivially safe
and conveys no information whereas in "classical" Data Flow Analysis it is
the least element that has this role. Similarly, in Abstract Interpretation we
are interested in least fixed points whereas in "classical" Data Flow Analysis
we are interested in greatest fixed points.
Staying within the basic approach of Abstract Interpretation, that going up
in the complete lattice means losing information, it is still possible to dualise
much of the development: in particular we can define the notion of dual Ca-
lois connections. To see why this may be worthwhile consider the following
scenario. In program analysis we aim at establishing an element le E L for
describing the set of values that may reach a given program point f. In pro-
gram transformation it is frequently the case that a certain transformation
3 is valid only if the set of values that reach a certain point have certain
properties; we may formulate this as the condition le ~ ls. Now if we want
to be more approximate we approximate le to l~ and ls to l~ and formulate
the approximate condition l~ ~ l~. To ensure that l~ ~ l~ implies le ~ ls we
demand that le ~ l~ and that l~ ~ ls. Thus properties of program points are
approximated by going up in the complete lattice, for which Galois connec-
tions are useful, whereas enabling conditions for program transformations are
approximated by going down in the complete lattice, and for this the concept
of dual Galois connections is useful.
A final word of advice concerns the interplay between Abstract Interpretation
and Denotational Semantics. In Denotational Semantics the least element
conveys absolutely no information, and we learn more when things get larger
according to the partial order; had there been a greatest element it would
have denoted conflicting information. This is quite opposite to the situation
in Abstract Interpretation where the greatest element conveys absolutely no
information and we learn more when things get smaller according to the par-
tial order; the least element often denotes non-reachability. Hence it would
be dangerous to simply apply too many of the intuitions from Denotational
Semantics when performing Abstract Interpretation not least because both
formalisms ask for least fixed points and therefore are not duals of one an-
other.
274 4 Abstract Interpretation
Mini Projects
We shall now return to the Shape Analysis of Section 2.6 and show how it
gives rise to a Galois connection. Recall that the semantics uses configura-
tions with a state a E State and a heap component 1i E Heap and that the
analysis works on shape graphs consisting of an abstract state S, an abstract
heap H and a sharing component is.
We shall begin by defining a function vars that given a location and a state
will determine the associated abstract location:
and argue that this shows the semantic correctness of the Control and
Data Flow Analysis. •
276 4 Abstract Interpretation
O+
Exercises
Exercise 4.1 For the complete lattice (Sign', ~) of Figure 4.10 definea
correctness relation RzS' : Z x Sign' - t { true, false}. Verify that it indeed has
the properties (4.4) and (4.5). Next define a representation function f3zS' :
Z - t Sign' and show that the Rzs' constructed above is indeed generated by
f3zS'. •
Exercise 4.2 Show that if (4.4) and (4.5) hold for R and L then we also
have:
Give an example showing that v R ..l fails even though (4.4) and (4.5) are
fulfilled. •
Exercise 4.3 Show that the Control Flow Analysis of Chapter 3 is indeed
correct in the sense of condition (4.3) as claimed in Example 4.4. To do so
first show that
whenever vis a value (i.e. a closure ora constant). Next show that
is a corollary of Theorem 3.10. Finally assume the premise of (4.3) and use
the above results to obtain the desired result. •
Exercises 277
Exercise 4.4 Show that the relation RcFA defined in Example 4.4 is gen-
erated by the representation function f3cFA also defined in Example 4. 7. To
do so prove that
Show that /p is monotone. Next show that (p f-- · ~ ·)(R1 ---.. R2) fp where
vi Ri li is defined by Vi E li. Also, show that for f' : L1 -+ L2 we have
(p f-- · ~ ·)(R1 ---.. R2) f' if and only if /p ~ f'. A semantics that associates a
program p with /p as defined here is sometimes called a collecting semantics.
Finally, note that Ri is generated by f3i defined by f3i (Vi) = {Vi}; show that
/p = ((31 ____.. fJ2)(p f-- • ~ ·). •
if h = .l
if 12 ~ h 1\ h -1- .l
otherwise
• .X(l l ). { h U h if h ~~'V l2 ~ h
1' 2 T otherw1se
are upper bound operators (where l' is some element of L). Determine which
of them that are also widening operators. Try to find sufficient conditions on
l' such that the operator involving l' is a widening operator. •
• la = f(l-);
• la = P 7 ( .l);
• la E Ext(f);
• la arbitrary.
Which of these suffice for proving Fact 4.14 and Proposition 4.13? •
where int! is an interval satisfying inf( int!) > -oo and sup( int!) < oo. Show
that
\:lint1, int2 : int1 \1 int2 r:;;, int1 \lK int2
and that the inequality may be strict. Show that \1 is an upper bound
operator. Determine whether or not \1 is a widening operator. •
Exercise 4.10 Let (ln)n bea descending chain and let ~ : L x L-+ L be
a total function that satisfies l~ r:;;, l~ =? l~ r:;;, (l~ ~ l~) r:;;, l~ for alll~, l~ E L.
Show that the sequence (l:; )n is a descending chain and that l:; ;;;) ln for all
n. •
Exercise 4.11 Consider the following alternative strategy to narrowing
for improving the approximation fv
E Red(f) to the fixed point lfp(f) of
the function f : L -+ L. A descending chain truncator is a function Y that
maps descending chains (ln)n to a non-negative number such that
Show that there exists a Galois connection between Interval and Sign' with
')'IS' as the upper adjoint. •
Exercise 4.15 Assume that the Galois connections (P(Di), a'IJ;+p 'Y'IJi+P
P(Di+l)) are given by the extraction functions "li+l : Di - t Di+l· Show that
the composition of the Galois connections, (P(Do), o:,')', P(D2)), will have
a = a'IJ2 o am = a'IJ2 om and 'Y = 'Y'IJ 1 o ')'172 = 'Y'IJ2 o'IJ 1 ; i.e. the Galois connection
is given by the extraction function ry2 o ry 1. •
Exercise 4.16 Let (P(VI), a1, 'YI. P(DI)) and (P(V2), a 2, ')'2, P(D2)) be
Galois connections given by the extraction functions ry1 : V1 - t D 1 and ry2 :
V2 - t D2. Furthermore assume that V1 and V2 are disjoint and similarly for
D1 and D2. Define an extraction function
by
(v) = { "71 (V) if V E V1
"1 "72(v) ifv E V2
Show that this defines a Galois connection
a(h,l2) (a1(h),a2(h))
')'(mi, m2) ('/'1 (mI), '/'2 (m2))
and show that (L1 x L2, a,')', M1 x M2) is a Galois insertion. Then define
a(!) a2 o f o 1'1
1'(9) 1'2 o g o al
and determine whether or not (P(VI x V2), a,')', P(D1 x D2)) is a Galois
insertion.
•
Exercise 4.19 Let (L, a 1 m, MI) and (L, a 2 ,')'2 , M 2 ) be Galois insertions.
Define
(ai(l), a2(l))
'/'I(ml) n')'2(m2)
a(!) a1 o f o '/'1
')'(g) '/'l og oa1
a(>.l.l) >.m.m
a(JI o h) a(JI) o a(h)
necessarily hold? Which of the equalities hold when (L1, a1, 1'1, M1) is known
to be a Galois insertion? •
Exercises 281
{(x,y)lx=y}
{(x,y) 1 x = -y}
{(x,y) 1 x = y+ 1}
{(x,y) lx=y+3}
{(x,y) 1 x ~ y}
{(x,y) lx~y+1}
{(x, y) 1 x 2 + y2 :S 100}
o:(!)
'Y(g)
such that ((L1 x L2)--+ L3, o:,')', (M1 x M2)--+ M3) is a Galois connection.
Next let all of (Li, ai, ')'i, Mi) be the Galois connection
Next define
o:(plus) = A(int1, in~).···
and supply the details of the definition. •
Exercise 4.23 Let L be the complete lattice of sets of integers, let M
be the complete lattice of intervals of Example 4.10, let (L, a,')', M) be the
Galois insertion of Example 4.19, and let 'VM : M x M--+ M be the widening
operator of Example 4.15. Observe that the formula
defines a widening operator 'VL : L x L--+ L and developa formula for it (in
the manner of the formula for 'VM of Example 4.15). •
282 4 Abstract lnterpretation
Exercise 4.24 Suppose that (L, a, 'Y, M) is a Galois connection ora Ca-
lois insertion and that possibly M satisfies the Descending Chain Condition.
Let f:l.M : M x M--+ M bea narrowing operator and try to determine if the
formula
So far our techniques have applied equally well to typed and untyped pro-
gramming languages. This flexibility does not apply to the development to
be performed in this chapter: here we demand that our programming lan-
guage is typed because we will use the syntax for types in order to express the
program analysis properties of interest (as was already illustrated in Section
1.6).
We shall first present an Annotated Type System for Control Flow Analy-
sis in Section 5.1, demonstrate its semantic soundness and other theoretical
properties in Section 5.2, and then in Section 5.3 show how to obtain an
algorithm for computing the annotated types (and prove that it is sound and
complete). In Sections 5.4 and 5.5 we give examples of other analyses spec-
ified by Type and Effect Systems. In Section 5.4 we study Type and Effect
Systems with rules for subtyping, polymorphism and polymorphic recursion
and illustrate their use in an analysis for tracking Side Effects, an Exception
Analysis and an analysis for Region Inference. Finally, in Section 5.5 we show
that the annotations can be given more structure and we illustrate this for a
Communication Analysis.
The program points, 1r E Pnt, are used to name the function abstractions in
the program; this could also be done using the notion of labelled terms from
Chapter 3 but for our present purposes we do not need the full generality
of this machinery - the reason is that now we will use the types to record
information that was previously associated with labels. Hence our syntax
just makes use of expressions and dispenses with terms.
As in the previous chapters we shall assume that a countable set of variables
is given and that constants (including the truth values), binary operators
(including the natural arithmetic, boolean and relational operators) and pro-
gram points are left unspecified:
c E Const constants
op E Op binary operators
J,x E Var variables
7r E Pnt program points
Example 5.1 The functional program (fn x => x) (fn y => y) con-
sidered in Chapters 1 and 3 is now written as
Recall that this is a looping program: g is first applied to the identity function
fnz z => z but it ignores its argument and calls itself recursively with the
function fnv y => y. •
[con] r f-uL C: Te
Types. Let us first introduce the notions of types and type environments:
T E Type types
r E TEnv type environments
We shall assume that the types are given by
where int and bool are the only two kinds of base types and as usual we
use arrows for function types. Each constant c E Const has a type that we
shall denote Te so e.g. true has type Ttrue = bool and 7 has type T 7 = int.
Each binary operator op will expect two arguments of type T~P and T;P,
respectively, and give a result of type Top - an example is the relational
operation ~ that expects two arguments of type int and gives a result of
type bool. For the sake of simplicity we shall assume that all the constants
have base types and that all binary operators expect arguments of base types
and return values of base types.
The type environments are given by:
that says that the expression e has type 7 assuming that any free variable
has type given by r. The axioms and rules for the judgements are listed in
Table 5.1 and are explained below.
The axioms [con] and [var] are straightforward: the first uses the predefined
type for the constant and the second consults the type environment. In the
rules [fn] and [!un] we guess a type for the bound variables and determine
the types of the bodies under the additional assumptions; the rule [!un] has
the implicit requirement that the guess of the type for f matches that of the
resulting function. As a consequence of these two rules the type system is
nondeterministic in the sense that f f-uL e : 71 and f f-uL e : 72 does not
necessarily imply that 71 = 72.
The rule [app] requires that the operator and the operand of the application
can be typed and implicitly it requires that the type of the operator is a
function type where the type before the arrow equals that of the operand
- in this way we express that the types of the formal and actual parameter
must be equal.
The rules [i~, [let] and [op] are straightforward. In particular, the let-
construct let x = e1 in e2 admits exactly the same typings as the application
(fn71" x => e2 ) e 1 and regardless of the choice of 1r. In Sections 5.4 and 5.5
we shall consider a polymorphic let-construct where let x = e 1 in e2 may
admit more typings than (fn1r x => e2) e1.
of Example 5.2 has type 7 ~ 7 for each type 7. We shall first consider the
expression funF f X => f (fny y => y) where we write ffx for the type
environment [f f--t (7 ~ 7) ~ (7 ~ 7)][x f--t 7 ~ 7]. Then we get
ffx f-uL f : (7 ~ 7) ~ (7 ~ 7)
ffx f-uL fny y => y: 7 ~ 7
5.1 Control Flow Analysis 287
using the axiom [var] and the rule [Jn]. The rule [app] then gives
and we have a judgement matching the premise of the rule [!un]. Hence we
get
[] f-uL fUllf f X=> f (fny y => y): (7---* 7)---* (7---* 7)
Taking r g to be the type environment [g f---> ( 7 ---* 7) ---* (7 ---* 7)] we get
rg f-uLg (fnz Z => z): 7---* 7
using the axiom [var] and the rules [Jn] and [app]. The rule [let] can now be
used to show that the expression loop has type 7---* 7. •
lintJ int
lboolJ bool
l71 lfd ---* l72J
~ <p ~J
---t 72
[con] r f-cFA C : Te
[fn]
r f-cFA fn7r X => ea : Tx {7r }Ucp Ta
1 2
r r
~ ~
f-cFA e1 : T 0 p f-cFA e2 : T 0 p
[op]
r f-cFA e1 Op e2 : Top
of Example 5.1. Writing 7'v for int ~ int we have the following inference
tree:
[x f-t JY] f-cFA X ; JY [y f-t int] f-cFA y : int
and we are close to the information supplied by Cin Chapter 3. The infor-
mation corresponding to p can be obtained by "merging" information from
the various type environments of the inference tree (see Exercise 5.4). •
and let us write ffx for [f f-t (T ~ 7') .J.t4 (T ~ 7')][x f-t 7' ~ 7'].
Using the clause [fn] we have
~ ~ {Y,Z}
rfx f-cFA fny y => y : T ---+ T
Next let Î'g be [g f-t (T ~ 7') .J.t4 (7' ~ 7')]. We now exploit that the
annotation {Z} can be enlarged to {Z, Y} in the clause [fn] so that:
~ ~ {Z,Y} ~
r g f-cFA fnz Z => Z : T ---+ T
Since {Z, Y} = {Y, Z} we can use the clause [app] and get
[com] 'Pl u 'P2 = 'P2 u 'Pl [ass] 'Pl u (rp2 u 'P3) = (rpl u 'P2) u 'P3
[re/] rp=rp
for every annotation rp and hence we have [ ] f-cFA loop : ~ for every r r
annotation rp; clearly the judgement with rp = 0 is the most informative. •
Fact 5.6
(i) Iff f-cFA e: rthen LÎ'J f-uL e: LrJ.
(ii) If r f-uL e : T then there exists f and r such that
f f-cFA e : 7, LÎ'J = r and LTJ = T. •
Proof The proof of (i) is straightforward. For the proof of (ii) one annotates ali
arrows with the set of ali program points in e. •
This result paves the way for extending L·J to operate on entire typings:
applied to the typing f f-cFA e : rit produces a typing of the form LÎ'J f-uL
e : LTJ. As an example, the typing of Example 5.5 is transformed into the
typing of Example 5.3 (assuming that LrJ = r).
In Section 5.4 we shall study explicit inference rules for subeffecting: this is
a related technique for ensuring that the analysis is a conservative extension
of the underlying type system.
5. 2 Theoretical Properties
Having specified the analysis we shall now ensure that it is semantically
correct. Furthermore, the fact that the analysis is a conservative extension
of the underlying type system motivates the following result: whenever we
have a typing in the underlying type system then the set of typings of the
Control Flow Analysis constitutes a Moore family. So as in Section 3.2 (and
Exercise 2. 7) every acceptable expression can be analysed and it has a best
analysis.
As in Sections 2.2 and 3.2, the material of this section may be skimmed
through on a first reading; however, we re-iterate that it is frequently when
292 5 Type and Effect Systems
[con]
[fn] f- (fn1r x => eo) ---+ (fn1r x => eo)
[!un] f- (fun7r f x => eo) ---+ fn1r x => (eo[f ~ fun1r f x => eo])
conducting the correctness proof that the final and subtle errors in the anal-
ysis are found and corrected!
meaning that the expression e evaluates to the value v. We shall assume that
eE Exp is a closed expression, i.e. FV(e) = 0, meaning that e does not have
5.2 Theoretical Properties 293
v E Val values
and let us see how this looping program is modelled in the Natur al Semantics.
First we observe that the axiom [!un] gives
so we have replaced the recursive call of f with the recursive function defini-
tion itself. Turning to the body of the let-construct we have to replace the
occurrence of g with the abstraction:
fnF x => ((funF f x => f (fny y => y)) (fny y => y))
The operator will now evaluate to this value and the operand fnz z => z
will evaluate to itself. So the next step is to determine a value v such that
we have an inference tree for
and after that we are in a position to use the rule for application. The eval-
uation of the operator in (5.1) proceeds as before and so does the evaluation
of the operand and once more we are left with the obligation to construct an
inference tree for the judgement (5.1). Thus we have encountered a circular-
ity and we see that the looping of the program is modelled in the semantics
by the absence of an inference tree. In Example 3.8 we showed how the
Structural Operational Semantics deals with the expression loop. •
This ensures that when given arguments of appropriate types the operator
will return a value of the expected type.
The semantic correctness of the analysis now expresses that the annotated
type foreseen by the analysis will also be the annotated type of the value
obtained by evaluating the expression; this is expressed by the following
subject reduction result:
Theorem 5.9
If [ ] f-cFA e : 7, and f- e -----> v then [ ] f-cFA v : 7.
Fact 5.10 If Î\ f-cFA e : 7 and Î\(x) f2(x) for ali x E FV(e) then
î\ f-cFA e : 7. •
Proof The proof is by induction on the inference tree for Î\ f-cFA e : 7 using that
it is the rightmost occurrence of a variable in a type environment that determines
its type. •
The next result shows that we can safely substitute an expression of the
correct annotated type for a variable:
Lemma 5.11 Assume [ l f-cFA ea : 7o and r[x f--+ 7a] f-cFA e : 7. Then
f f-cFA e[x f--+ eo) : 7. •
Proof The proof is by structural induction on e. Most cases are straightforward
so we shall only present the cases of variables and function abstractions.
The case y. We assume that
so from the axiom [var] of Table 5.2 we have (r[x f-+ 7o])(y) = 7. If X = y then
y[x f-+ eo] = eo and 7 = 7o, and from [ ] f-cFA eo : 7o and Fact 5.10 we get the
required r
f-cFA eo : 7. If X =f- y then y[x f-+ eo] = y and it is easy to see that
r f-cFA Y: 7.
The case fn" y => e. We assume that
so, according to rule Lfn] of Table 5.2, it must be the case that 7 = Ty ~ T'
and:
r[x f-+ Ta][y f-+ Ty] f-cFA e : T'
lf X = y then it follows from Fact 5.10 that r[y f-+ Ty] f-cFA e : 7' and since
(fn7r y => e)[x r-+ ea] = fn7r y => e the result follows. So assume that x :/= y;
then (fn7r y => e)[x r-+ ea] = fn7r y => (e[x r-+ ea]). It follows from Fact 5.10 that
r[y f-+ Ty][x f-+ Ta] f-cFA e : 7' and then we get r[y f-+ Ty] f-cFA e[x f-+ ea] : 7' from
the induction hypothesis. Now the result follows using the rule [fn]. •
f- fun7r f x => ea --> fn" x => ea[/ r-+ fun" f x => ea]
and according to Table 5.2 this is because [/ r-+ Tx {1r}U<po) 7a][x r-+ Tx] f-cFA ea: 70.
Since [/ r-+ Tx {1r}U<po 1 7a][x r-+ Tx] equals [x r-+ Tx][f r-+ Tx ~ 7a] (because
we assumed that f and x are distinct) it follows from Lemma 5.11 that
because f- e1 --> fn" x => ea, f- e2 --> v2 and f- ea[x r-+ v2] -->va. Also we have
[ ] f-cFA e1 e2 : 7a
and according to Table 5.2 this is because [] f-cFA e1 : T2 ..!4 Ta and [] f-cFA e2 : T2.
The induction hypothesis applied to the inference tree for e 1 gives:
~ 'P ~
[] f-cFA fn" x => ea : T2 ---+ Ta
According to Table 5.2 this can only be the case if 1r E r.p and [x r-+ T2] f-cFA ea : 70.
The induction hypothesis applied to the inference tree for e2 gives
[ ] f-cFA v2 : T2
and by Lemma 5.11 we now get [] f-cFA ea[x r-+ v2] :Ta. The induction hypothesis
applied to the inference tree for ea[x r-+ v2] now gives
[ ] f-cFA va : Ta
5.2 Theoretical Properties 297
[ ] f-cFA v1 : r
and this is the desired result.
The case [ih] is analogous.
The case [let]. We assume that
and this is because [ ] f-cFA e1 : r1 and (x r--t Ti] f-cFA e2 T2. The induction
hypothesis now gives:
( l f- CFA Vl : Tl
From Lemma 5.11 we then get [] f-cFA e2[x r--t v1] : T2 and the induction hypothesis
gives
as required.
The case [op]. We assume that
(] f-cFA e1 op e2 : Tap
and this can only be because (] f-cFA e1 : T,!P and (] f-cFA e2 : T;P. The induction
hypothesis gives [ ] f-cFA v1 : T,!p and [ ] f-cFA v2 : T;P and the desired result that
[ ] f-cFA v : Tap then follows from the stated assumptions about the relationship
between the semantics and the types of the binary operators. •
TYPe[T]
for the set of annotated types 7 with underlying type T, i.e. such that l7J = T.
Next define Ti ~ f2 for T1, f2 E TYPe[T] to mean that whenever ~ has the
annotation '-Pi in a given position then '-Pl ~ <p2. Formally this is achieved
by:
f1~Tf r.p~r.p' f2~~
- (,[) ....... _, c.o' _,
T1 ....<....t T2 ~ T1 ........__. T2
As an example, (int ~ int) ~ int ~ ( int ~ int) ~ int will
be the case if and only if '-Pl ~ r.p3 and <p2 ~ '-P4· (Note that this ordering
is not contravariant unlike what will be the case for the subtyping to be
introduced in Section 5.4.) Clearly the least element in TYPe[T] will have 0
on all function arrows occurring in T and the greatest element will have Pnt
on all function arrows.
5.2 Theoretical Properties 299
for the set of typings f f-cFA e : 7 such that L·J maps r f-cFA e : 7 to
r f-uL e: T (and hence LfJ = r and LrJ = r). This facilitates extending the
partial ordering !;;;; to operate on the set JUDGcFA[r f-uL e : r] of typings.
Moore family result. We are now ready to state the result about
Moore families:
Proposition 5.12
JUDGcFA[r f-uL e: r] is a Moore family whenever r f-uL e: T.
Y = {(fi f-cFA e : ~) 1 i E I}
bea subset of JUDGcFA[r f-uL e: r]. Using that (TYPe[r'], ~) is a complete lattice
n
for all choices of r' E Type we get that Y exists and that it is defined in a
pointwise manner. (Note that if I = 0 then nY is obtained from r f-uL e : T by
n
placing 0 = Pnt as annotation on ali function arrows.) It remains to show that
nY E JUDGCFA[r f-uL e: r].
The case [var]. The result follows from (nifi)(x) = ni(fi(x)).
The case ffn]. We have r f-uL fn". X=> ea : Tx --+ To because r[x 1-+ Tx] f-uL ea :Ta.
For iEI we have fi f-cFA fn". x =>ea : ~ {"-}u<pi • Tii because of
~- ~ ~
and clearly r'[x ~--+ rx] f-cFA ea : r 0 is an element of JUDGcFA[r[x ~--+ Tx] f-uL ea :ro].
By the induction hypothesis we get (nifi)[x 1-+ ni~] f-cFA ea : niTii and hence
nifi r el e2 : ni7il
whenever {X, Y} ~ 'PI· The least solution therefore has <p1 = {X, Y} and
<p2 = 0. This clearly tells us that f is only applied to (fnx x => x+1)
and (fny y => y+2) and not to (fnz z => z+3). A larger solution like
<p1 = {X, Y, Z} and <p2 = {V} would not convey this information. Hence it
seems sensible to ask for the least solution with respect to ~ and the existence
of this solution is guaranteed by Proposition 5.12. •
We shall take:
e int int
e bool bool
e(T1 ___, T2) (e TI) ___, (e T2)
eo: T ifeo:=T
We shall write el o e2 for the composition of el and e2' i.e. (el o e2 )T = el (e2 T)
for all augmented types T.
The idea. The type reconstruction algorithm, called WuL, is given two
arguments: an augmented type environment r (mapping program variables
to augmented types) and an expression e. If it succeeds in finding a typing
for the expression then it will return its augmented type T and a substitution
e telling how the type environment has to be refined in order to obtain a
typing. As an example we will have
this property holds we shall say that the algorithm is syntactically sound.
In order for the algorithm to be syntactically complete it is also required
that all typings obtainable in the inference system can be reconstructed from
the results of the algorithm. We shall discuss these properties at length in
Subsection 5.3.3.
Also the clause for function application relies on the unification procedure.
In this clause we call WuL recursively on the operator and the operand and
we use unification to ensure that the type of the operator T1 (modified by
02) is a function type with an argument type that equals the type r2 of the
operand: 82 TI has to have the form T2 ---+ a. Again we have to record that
the assumptions of r have to be modified by all three substitutions that have
304 5 Type and Effect Systems
been constructed.
By now the clause for conditiona} is straightforward and similarly the clauses
for the let-construct and the binary operator. (Note that the let-construct
is not polymorphic and therefore no special action is needed to determine the
type of the bound variable; we shall return to the problem of polymorphism
later.)
where 'c is a fresh type variable. As we shall see in Example 5.15 below this
gives rise to the substitution ['a t---t 'b - t 'b] ['c t---t 'b - t 'b] and the initial call
of WuL will return 'b - t 'b and ['a t---t 'b - t 'b]['c t---t 'b - t 'b]. •
UuL(int, int) id
• two types with different top-level constructors (by which we mean int,
bool, or --+) are to be unified, or
• a type variable is to be unified with a function type containing that
type variable.
Example 5.15 In Example 5.14 we had the following call of the unifica-
tion procedure:
UuL ('a --+ 'a, ('b --+ 'b) --+ 'c)
It will first give rise to the call UuL ('a, 'b --+ 'b) which returns the substitution
['a ~-----> 'b --+ 'b]. Then we will have the call UuL ('b --+ 'b, 'c) and the substitution
306 5 Type and Effect Systems
['c ,___. 'b ----+ 'b] is returned. Thus the overall result will be the substitution
['a,___. 'b----+ 'b]['c ,___. 'b----+ 'b] as already used in Example 5.14. •
UcFA(int, int) = id
UcFA(bool, bool) id
!3 72,71
UcFA(T1 """"'--t ~ ~ L 72
~') let Bo = [,B' f-+ .8]
01 = UcFA (O o 71. Oo T;_)
02 = UcFA(01 (Oo 72), 01 (Oo ~))
in (}2 o (}1 o Oo
{
[a f-+ 7] if a does not occur in 7
UcFA(7,a) or if a equals 7
fail otherwise
UcFA(a, 7) = { [a f-+ 7]
fail
if a does not occur in 7
or if a equals 7
otherwise
Here 71 and 72 are simple types and (} will be a simple substitution: A simple
substitution is a substitution that maps type variables to simple types, and
that maps annotation variables to annotation variables only. Thus a simple
substitution applied to a simple type still gives a simple type. As for UuL
the intention is that (} unifies 71 and 72, i.e. (} 71 = (} 72. In case this is not
possible the outcome of UcFA (71, 72) will be a failure. The unification function
UcFA is defined in Table 5.7 and is explained below.
As before id is the identity substitution and (}' o (}" is the composition of two
substitutions. In the clause for unifying the two function types 71 L 72
and 7f L 72 the desired result is obtained by first ensuring that the
two annotation variables are equal and then proceeding as in the unification
algorithm for the underlying types. Note that UcFA (71, ~) will fail if and only
if UuL ( lTd, l72J) fails.
308 5 Type and Effect Systems
We construct the substitution ['3 f--+ '1] and then we perform the call
'2 1 )
UcFA (/a, 1b---+ b
which returns the substitution ['a f--+ 'b ~ 'b]. Then we make the call
1 '2 1 1
UcFA( b ---+ b, c)
and the substitution ['c f--+ 'b ~ 'b] is returned. Thus the overall result will
be ['3 f--+ '1]['a f--+ 'b ~ 'b]['c f--+ 'b ~ 'b]. •
The following fact, whose proof we leave to Exercise 5.8, expresses that the
algorithm is correct. The first part of the result says that the algorithm is
syntactically sound: if it succeeds then it produces the desired result. The
second part says that the algorithm is syntactically complete: if there is some
way of unifying the two simple types then the algorithm will succeed in doing
so.
if and only if it covers C and for each (3 ;;2 <p in C it is the case that (}A (3 is
a superset of, or is equal to, (}A <p.
A ground substitution is an annotation substitution on annotation variables
and a type substitution on type variables. A substitution (} is a ground
validation of (r, 7, C) if and only if it is a ground substitution that covers
r,7 and c c c
and such that (} F (or more precisely, (}A F where (}A is the
restriction of (} to the annotation variables).
annotation variable fh (Oo f3o) has to contain the labei rr of the function
definition.
The clauses for the remaining constructs are fairly straightforward modifica-
tions of the similar clauses for WuL· Note that the substitutions are applied
to the constraint sets as well as the types and type environments.
where 'c is a fresh type variable and '3 is a fresh annotation variable. This
gives rise to ['3 1-t '1]['a 1-t 'b ~ 'b]['c 1-t 'b ~ 'b] as shown in Example 5.16
and the initial call of WcFA will return the type 'b ~ 'b, the substitution
['3 1-t '1]['a 1-t 1 b ~ 'b]['c 1-t 'b ~ 'b] and the set {'1 2 {X}, '2 2 {Y}} of
constraints. This corresponds to the typing obtained in Example 5.4. •
of Example 5.2 and the call WcFA([ ], loop). This will first give rise to a call
WcFA([], funF f x => f (fny y => y))
that returns the type ('a ~ 'a) ~ 'b and the set {'1 2 {F}, '2 2 {Y}} of
constraints. Then we have a call of WcFA on the body of the let-construct:
The call of WcFA on fnz z => z will give the type 'c ~ 'c and the constraint
set {'3 2 {Z} }. The unification procedure will then be called with the types
('a ~ 'a) ~ 'b and ('c ~ 'c) ~ 'd and the resulting substitution
is ['1 1-t '4, '2 1-t '3, 'a 1-t 'c, 'b 1-t 'd]. Thus the application will have type 'd
and the constraint set will be {'3 2 {Z} }. The initial call of WcFA on loop
returns the type 'd and the constraint set {'4 2 {F}, '3 2 {Y}, '3 2 {Z}}.
This corresponds to the typing obtained in Example 5.5. •
312 5 Type and Effect Systems
As illustrated in the above examples, when the overall call of WcFA cr'
e) gives
(T, (), C) we are only in~erested in the effect of() on the type and annotation
variables occurring in r.
Theorem 5.20
If WcFA (f, e) = (T, (), C) and () 0 is a ground validation of () f, 7
c
and then Oa(O r) f-cFA e : Oc 7.
This theorem may be reformulated as follows: if WcFA (f, e) = (7, (), C) and
Or is a type substitution that covers () f and 7, and if ()A is an annota-
tion substition that covers () f, 7 and C and that satisfies ()A f= C, then
()A ( Or (() r)) f-cFA e : ()A ( Or 7). To see this first note that ()A o Or = Or o ()A
is a ground substitution; next note that given a ground substitution Oc we
may obtain an annotation substitution ()A by restricting Oc to annotation
variables and similarly we may obtain a type substitution Or by restricting
Oa to type variables and clearly Oa =()A o Or = Or o ()A·
Proof The proof proceeds by structural induction on e (because WcFA is defined
by structural induction on e).
5.3 lnference Algorithms 313
The case c. We have WcFA(Î\ c) =(Te, id, 0). Next let (}c bea ground validation of
Î\ clearly it also covers Te
(because Te is a base type) and it also satisfies (}G f= 0.
From the axiom [con] of Table 5.2 it is immediate that
(}c(f) f--cFA c : Te
and since (}c Te = Te this is the desired result.
The case x. We have WcFA(r, x) = (r(x), id, 0). Next let (}c bea ground validation
of r; clearly it also covers r(x) and it satisfies (}G F 0. From the axiom [var] of
Table 5.2 it is immediate that
Since (}c f= Co U {,Bo ; ;:> {71'}} we have (}G ,Bo ; ;:> {71'} so we can apply the rule [fn] of
Table 5.2 and get
~ Oa !3o ~
(}G((}o f) f--cFA fnrr X=> eo: (}G((}o ax) -------> (}G To
Since (}1 ro= (}l((}o ao) and (}c f= ((}1 Co)U{(}l((}o ,Bo) ; ;:> {7r}} we get (}c((}l((}o ,Ba));;;:>
{71'} so we can apply the rule [tun] of Table 5.2 and get
(}G((}l((}o r)) f--cFA funrr f X=> eo: (}G((}l((}o ax)) Oa(Ol(Oo f3o)) (}G((}l To)
(}~ ((}3 ((}2 ( (}1 r))) f--cFA el : (}~ ((}3 ( (}2 Tl))
314 5 Type and Effect Systems
Similarly O~ o 03 is a ground validat ion of 82 (81 f), r2 and C2. Hence we can apply
the induction hypothesis to e2 and get:
Theorem 5.21
Assume that f is a simple type environment and e' f f-cFA e : 7'
e'
holds for some ground substitution that covers f. Then there
c
exists 7, e, and ee such that
• WcFA(r, e) = (7, e, C),
where c.p' is an annotation (i.e. it does not contain any annotation variables) and
from Table 5.2 we get that also:
Let now ax bea fresh type variable and note that ax fţ. dom( O'). Define O" by
if (=ax
otherwise
where ( can either be a type variable or an annotation variable. Then we also have:
By the induction hypothesis there exists ro, Oo, Co and (}~ such that:
Then we get:
where c.p' does not contain annotation variables and according to Table 5.2 this is
because:
(O' f)[J >--t:;;, {."-}u'l'' rWx
>--t :;;,] f-cFA eo: T~
Let ax, a 0 and {30 be fresh type and annotation variables and note that they are
not in dom(O'). Define O" by:
if (=ax
~,
Tx
O" ( ~ {
{'Il'} u c.p'
To
~,
if ( = f3o
if ( = ao
(}' ( otherwise
316 5 Type and Effect Systems
By the induction hypothesis there exists Ta, Ba, Ca and B~ such that:
Since B~ (Ba aa) = B" aa = 7;; = B~ Ta it follows from Fact 5.17 that there exists B1
and Ba such that UcFA(Ba aa, 7a) = B1 and B~ =Ba o B1. Hence
By the induction hypothesis applied to e1 there exists T'1, B1, C1 and Bi; such that:
WcFA(r,el) = (7l,Bl,cl)
Bi; is a ground validation of B1 f, 71, and C1
Bi; o B1 = B' except on fresh type and annotation variables
created by WcFA(B' r, el)
Ba 71 = 72 ..........., 7a
1 ........ _,(J)' . ..........,
Then we have
Ba1 ( Bl ~
r) f-cFA e2 : ~
72
so by the induction hypothesis applied to e2 there exists T'2, B2, C2 and B~ such
that:
WcFA(Bl f, e2) = (72, B2, C2)
B~ is a ground validation of B2 (B1 r), T'2 and C2
B~ o B2 = Bi; except on fresh type and annotation variables
created by WcFA (· · · , e2)
5.3 Inference Algorithms 317
8~ 72 = 7~
if (=a
if ( = f3
otherwise
Since 8~(82 71) = 8~(82 71) = ~ ..:L.. 76 = 8~ 7:J ..:L.. 7b = 8~(72 ../!..." a) it follows
from Fact 5.17 that there exists 83 and 8a such that UcFA (82 71, 72 ../!..." a) = 83 and
8~ = 8a o 83. It follows that
is a Moore family. •
Proof Let C be a finite set of constraints of the form f3 2 r.p where r.p E SAnn
and such that X 2 AV( C) where X is a finite set of annotation variables; it will
not be of importance that C is generated by WcFA· Let Y be a possibly empty
318 5 Type and Effect Systems
subset of the set displayed in the lemma. Each element of Y will be an annotation
substitution with domain X and that satisfies C. By setting
for {3 E X
r
Proof Let (}T be given by (}T a = int for ali type variables a in (} and 7; clearly
(}T r
covers (} and 7. Next let X be the set of annotation variables in(} r, r
and C;
then Lemma 5.22 guarantees the existence of an annotation substitution (}A that
covers (}r, c
T and and such that (}A F c.
Taking 8c = (}A o (}T we obtain a
ground validation of 8 7 and r, c. •
The result obtained by choosing a type substitution is only unique in case
there are no type variables present in () f and 7. If there is at least one
type variable present then the result of using ()T displayed above will differ
from the result of using ()~ given by ()~ a = bool for all type variables a
in() f and 7. In general a type substitution ()~ may have ()~ a E TYPe[ra]
for an arbitrary underlying type 7 0 • However, in case TYPe[ra] has more
than one element one is likely to prefer the least element since it has empty
annotations on all function arrows (but see Exercise 5.9).
In a similar way one is likely to prefer the least annotation substitution
guaranteed by Lemma 5.22. Keeping in mind that all constraints in C have
the form f3 2 {1r} for f3 E AVar and 1r E Pnt, we simply set:
5.4 Effects
The Type and Effect System for Control Flow Analysis is fairly simple: it is
a syntax directed system using a form of subeffecting and the annotations are
just sets. Much more powerful Type and Effect Systems can be constructed
by allowing subtyping, let-polymorphism or polymorphic recursion; the re-
sulting analyses will be more powerful and, not surprisingly, the techniques
required for the implementation will be more demanding.
Subtyping and the various notions of polymorphism can be combined but for
the sake of simplicity we shall present them one at a time. We shall first
present a Side Effect Analysis for an extension of the FuN language with
assignments; it will use subeffecting and subtyping. Then we shall present
an Exception Analysis for an extension of FuN with exceptions; it will use
subeffecting, subtyping as well as polymorphism. Finally, we shall present
a Region Analysis for the FUN language; it will be based on polymorphic
recursion.
newR r:=O
in let fib = funF f z => if z<3 then r:=!r+1
else f(z-1); f(z-2)
in fib x; !r
So for the function fib in Example 5.24 the analysis will record that it
accesses and assigns the reference variable created at program point R.
Semantics. Before presenting the analysis let us briefly sketch the se-
mantics of the language. To distinguish between the various incarnations
of the new-construct we shall introduce locations (or references) and, as for
the imperative languages of Chapter 2, the configurations will then contain
a stare component mapping locations to their values:
c; E Store = Loc --tfin Val
The values of Val include the constants c, the (closed) function abstractions
e
of the form fn7r X => e and the locations E Loc. The semantic clauses of
Table 5.4 are now modified to trace the store in a left-to-right evaluation as
for example in the following clause for the let-construct:
f- (e1, c;1) - - t (VI , c;2) f- (e2 [X t--t VI] , c;2) - - t ( V2 , c;3)
f- (let x = e1 in e2, c;I) - - t (v2, c;3)
For the new constructs we then have the following axioms and rules (explained
below):
f- (el, c;l) (vi. c;2) f- (e2[x 1--t eJ, c;2[e 1--t vl]) - - t (v2, c;3)
--t
In the rule for new we evaluate e1, create a new location ~ which is initialised
to the value of e 1 , and then we syntactically replace all occurrences of x
with that location so that all subsequent references to x will be to ~- We
exploit this when defining the semantics for the constructs for accessing and
updating the reference. Note that the value returned by the assignment will
be the value being assigned.
~
7 ::= J.nt
o 1bool 1~71 'P ~1
----t 72 refro ~
7
Here ref ro 7 is the type of a location created at one of the program points in
ro and that will contain values of the annotated type 7. As before the type
environment f will map variables to annotated types.
newR r:=O
in let fib = funF f z => i f z<3 then r:=!r+1
else f(z-1); f(z-2)
in fib x; !r
of Example 5.24. The variable r has the annotated type ref{R} int and the
variable fib has type int {!R,R:=}> int since it maps integers to integers
and whenever executed it may, as a side effect, access and update a reference
created at the program point R. •
Typing judgements. The typing judgements for the Side Effect Anal-
ysis will be of the form:
f f-sE e : 7 & cp
322 5 Type and Effect Systems
[Jn]
[app]
r f--sE el : T2 ~ To & 'Pl r f--sE e2 : T2 & 'P2
f f--sE e1 e2 : Ta & 'Pl U 'P2 U cpo
[ifl
r f--sE eo : bool & cpo r f--sE el : T & 'Pl r f--sE e2 : T & 'P2
r f--sE if eo then el else e2 : T & 'Po u 'Pl u 'P2
[let]
r f--sE el : TI & 'Pl r[x f-t 7)] f--sE e2 : T2 & 'P2
r f--sE let X = el in e2 : T2 & 'Pl u 'P2
[op]
r f--sE e1 : T 1p
~
0 & 'Pl r f--sE e2 : T 2p & 'P2
~
0
f f--sE e1 :TI & 'Pl f[x f-t ref{1r} T1] f--sE e2 : T2 & 'P2
[new]
f f--sE new7r x : = e1 in e2 : T2 & {'Pl U 'P2 U {newrr})
[seq]
f f--sE e1 : T1 & 'Pl f f--sE e2 : T2 & 'P2
f f--sE e1 ; e2 : T2 & 'Pl U 'P2
f f--sE e: T & cp
[sub] if T ::::; T1 and <p ~ cp'
f f--sE e : T' & <p'
The idea is that under the assumption f, the expression e will evaluate into a
value with the annotated type 7 and that during computation the side effects
expressed by r.p might take place. The analysis is specified by the axioms and
rules of Table 5.9.
In the clauses [con] and [var] we record that there are no side effects so we use
0 for the overall effect. The premise of the clause [fn] gives the effect of the
body of the function and we use that to annotate the arrow of the function
type whereas we use 0 as the overall effect of the function definition itself:
no side effects can be observed by simply defining the function. A similar
explanation holds for the recursive function definition, the only difference
being that the assumption about f in the premise has to use the same effect
as the one determined from the function body. In the rule [app] we see how
the information comes together: the overall effect is what we can observe
from evaluating the argument e1, what we can observe from evaluating the
argument e2 , and what we get from evaluating the body of the function called.
The rules [i~, [let] and [op] are straightforward.
Turning to the axioms and rules involving reference variables we make sure
that we only assign values of the appropriate type to the variable. Also,
in each of the cases we make sure to record that a location at the relevant
program point has been created, referenced or assigned. The rule [seq] is
straightforward and the final rule [sub] will be explained below.
newAx:=1
in (news y:=!x in (x:=!y+1; !y+3))
+ (newc x:=!x in (x:=!x+1; !x+1))
because the reference variable that is updated is the local one. Hence
is the type and effect of the overall program. It follows from the effect that
the variable being created at B (y in the program) is never reassigned after
its creation. This might suggest transforming the news-construct into a let-
construct (i.e. let y=! x in (x: =y+1; y+3)). •
324 5 Type and Effect Systems
newAx:=1
in (fn f => f (fn y => !x) + f (fn z => (x:=z; z)))
(fn g => g 1)
where we have omitted the labels on the function definitions. The program
evaluates to 2 because each summand evaluates to 1.
The type and effect of the two arguments to f are
. {lA}
1nt ~ int & 0
. {A:=}
1nt ~ int & 0
5.4 Effects 325
{A:=} {!A,A: =}
(int ----t int) < (int int)
and may use the rule for subtyping to change the types of the arguments to
the type expected by f.
If instead we had only used the rule for subeffecting we would be obliged to
let the two arguments to f have type and effect:
{A:=, !A} & 0
int int
{A:=, !A}
int int & 0
This is indeed possible by using the rule for subeffecting just before the rule
for function abstraction. •
The combined rule [sub] for subeffecting and subtyping gives rise to a conser-
vative extension of the underlying type system. This would also have been
the case if we had adopted either the rule for subeffecting or the rule for
subtyping. However, had we incorporated no additional rule then this would
not be the case.
Semantics. Before presenting the analysis let us briefly sketch the se-
mantics of the language. An expression can now give rise to an exception so
we shall extend the set Val of values to include entities of the form raise
8. The semantic clauses of Table 5.4 then have tobe extended to take care
of the new kind of values. For function application we shall for example add
three rules
f- e1 ~ raise 8
f- e1 e2 ~ raise 8
f- e1 e2 ~ raise 8
5.4 Effects 327
if v2 -=/:- raise 8
Example 5.29 Consider the function fnF f => fnx x => f x. We can give
it the type schema
'1 0 1 1
\i 'a, 'b, 11. ('a ---+ 'b) ---t ('a ---+ 'b)
[In]
r f-Es e : 7 & cp
[gen]
f f-Ese: \i((l, · · ·, (n).7 & cp
if (1, ... , (n do not occur free in r and cp
r
where the type environment now maps variables to type schemes. As ex-
pected â denotes the type schema of e, and <p is the set of exceptions that may
be raised during evaluation of e. The analysis is specified by the axioms and
rules of Table 5.10. Most of the axioms and rules are straightforward modifi-
cations of those we have seen before; in the rule [let] the use of a type schema
for the let-bound variable is going to give us the required polymorphism.
The axiom [raise] ensures that a raised exception can have any type and
the effect records that the exception s might be raised. The rule [hand le]
then determines the effects of its two subexpressions and records that any
exception raised by e1 and any exception except s raised by e2 is a possible
exception for the construct. Formally, <p \ { s} is defined as follows: {s} \ {s} =
0, {s'} \ {s} = {s'} if s' -j. s, (<p U<p') \ { s} = (<p \ { s}) U (<p1 \ { s} ), 0\ {s} = 0
and (3 \ {s} = (3. (We shall consider alternative definitions shortly.)
The rule [sub] is the rule for subeffecting and subtyping and the ordering
7 :::; 7' on types is given by
int ~ int&0
int ~ int&0
We can now take two instances of the type schema for f: to match the first
argument of f we use the substitution ['a ~ int; 'b ~ int;' O ~ {neg}] and to
match the second argument we use ['a~ int;'b ~ int;'O ~ {pos}]. Hence
the type and effect of each summand is
int & {neg}
int & {pos}
and therefore
int & {neg, pos}
is the overall type and effect of the entire program.
If we did not avail ourselves of polymorphism we would have to rely on
subeffecting and subtyping and let f have the type
.
(J.nt {neg,pos} . ) 0 (. {neg,pos} . )
J.nt --+ J.nt J.nt
which is more imprecise than the type scheme displayed above, although we
would stiH be able to obtain int & {neg, pos} as the overall type and effect
of the entire program. •
tp::=··· 1 tp\{s}
and then to extend the axiomatisation of UCAl to deal with set difference. •
(ri,o1)
(r1,1)
(r2;o,)
(r2,1)
D (r3,o3)
(r3,1)
r1 r2 r3
Figure 5.1: The memory model for the stack-based implementation of FuN.
as follows:
rn .. - r11 r21 r31 · · ·
{! .. - "1 1"2 1"3 1...
r .. - e 1rn
The syntax of extended expressions
ee E EExp
is given by:
332 5 Type and Effect Systems
Here the value 7 is placed in the region [! 1, the value of x+y in region [!2,
the function named Y in the region [! 3 and the argument 9 in region [!4.
The final value (which is 7 + 9 = 16) is therefore to be found in region [! 2.
All other regions ([! 1 , [! 3 , and [! 4 ) no longer serve any purpose and can be
deallocated; this is made explicit by the letregion-construct. In the version
of the program that is actually run we should replace the metavariables [!1,
[! 3 and [! 4 by region variables 11 1, 11 2, and "3 and furthermore the free region
variable [! 2 should be replaced by a region name such as r1. •
p f- (ee,<;)_____, (v,<;')
domains:
Here Var* is the finite set of variables in the extended expression ee* of
interest, a stare is a stack of regions (where the stack is modelled as a finitary
mapping from region names), a region is a list of storable values (where the
list is modelled as a finitary mapping from indices called offsets), and a
storable value is given by
p f- (eeo, '>1) -----> ((rn, o), '>2) p f- (ee1, '>2) -----> (v1, '>3)
[iA]
p f- (if eeo then ee1 el se ee2, '>1) -----> (v1, '>3)
if '>2(rn, o)= true
p f- (eeo, '>1) -----> ((rn, o), '>2) p f- (ee2, '>2) -----> (v2, '>3)
p f- (if eeo then ee1 else ee2, '>1) -----> (v2, '>3)
if c;2(rn, a) =false
p f- (ee,c;1)-----> ((rn',o'),'>2)
[place]
p f- (ee[rn] at rn, c;1)
-----> ((rn,a),c;2[(rn,a) f---+ (x,eeo[Qf---+ rn],po)])
if o rţ dom( '>2 (rn)) and <:;2(rn', o') = (§, x, eeo, po)
p f- (ee[§ f---+ rn], '>1 [rn f---+ []]) -----> (v, '>2)
[region]
e
p f- (letregion in ee, '>1) -----> (v, '>2 ~rn)
if {rn} n dom(c;) = 0
consisting of an annotated type and the region where the value resides. For-
mally, annotations (or effects), annotated types and type schemes
present purposes f3.c.p can be read as f3 U c.p although the inference algorithm
will view it as the constraint f3 2 f3 U c.p.
where c.p = {get {!4, get e1, put {!2}, and the overall expression will have the
extended type int@e2· •
[fn]
f I-RI fnn x => eo ~ (fnn x => eeo) at r :
((Tx@rx ~ ro@ro)@r) & {put r}
[op]
fi-RI e1 op e2 ~ (ee1 op ee2) at r:
(Top@r) & 'Pl U 'P2 U {get r1, get r2, put r}
[sub]
f f-RI e ""' ee : (T@r) & '{J
Observe(f, 7, r')(f3)
{~ if (3 occurs in
otherwise
f, 7, or r'
One can now show that [] f---R1 e""' ee: (int@Q2) & {put Q2}· •
5.5 Behaviours
So far the effects have had a rather simple structure in that they merely
denote sets of atomic actions like accessing a value or raising an exception.
The effects have not attempted to capture the temporal order of these atomic
actions. Often such information would be useful, for example to check that
a variable is not accessed until after it has been assigned a value. In this
section we shall show how to devise a Type and Effect System where effects
(called behaviours) are ah le to record such temporal ordering in the context
of a Communication Analysis for a fragment of Concurrent ML.
Also we shall assume that the constants, c E Const, not only include integers
and booleans but also a special value called unit and denoted by () ; this is
the value to be returned by the spawn and send constructs.
Example 5.34 In this example we shall imagine that the expressions are
extended with operations on lists: isnil e tests whether or not the list e
is empty, hd e selects the first element and tl e selects the remainder of
the list. We now define a function pipe that takes a list of functions, an
input channel and an output channel as arguments; it constructs a pipeline
of processes that apply the functions to the data arriving at the input channel
and returns the results on the output channel (see Figure 5.2). It makes use
of the function node that takes a function, an input channel and an output
channel as arguments and it is defined as follows:
let node = fnF f => fn1 inp => fno out =>
spawn ((funH h d => let v = receive inp
in send (f v) on out;
h d) ())
in funp pipe fs => fn1 inp => fno out =>
if isnil fs then node (fnx x =>x) inp out
else let ch = channelc
in (node (hd fs) inp ch; pipe (tl fs) ch out)
To deal with the empty list the function produces a process that just applies
the identity function (denoted id in Figure 5.2). •
fun1r f x => e--+ (fn1r x => e)[f ~----+ (fun11" f x => e)]
v; e--+ e
v E Val values
defined by:
v ::= c 1 ch 1 fn1r x => eo
There is no need to package the function abstraction with an environment
because the semantics will treat a function application (fn11" x => eo) v by
substituting v for x in eo yielding eo[x ~----+ v].
Part of the sequential semantics is specified in Table 5.14. In the manner of
a Structural Operational Semantics it axiomatises the relation
for when the expression e 1 evaluates to e 2 in one step. However, it does not
describe how e 1 may evaluate to e2 as a result of a subcomponent e 11 of e 1
evaluating to e12 unlike what has been the case for the Structural Operational
Semantics used so far. As an example Table 5.14 cannot be used to show
that (1 + 2) + 4--+ 3 + 4 although it can be used to show that 1 + 2--+ 3.
To recover from this shortcoming we shall make use of evaluation contexts;
these are expressions containing one hole which is written [ ]. Formally,
evaluation contexts E are given by:
E .. - [ ]\ E e 1 v E \let x = E in e
if E then e1 else e2 E op e v op E 1 1
Here the syntax ensures that E is an expression containing exactly one hole
while e is an ordinary expression without any holes. The definition of E may
be read as follows: you are allowed to evaluate an expression on its own,
you may evaluate the function part of an application, you may evaluate the
argument part of an application only after the function part has been fully
evaluated etc. We shall write E[e] for the expression obtained by replacing
the hole of E with e; so for example if E is [] + 4 then E[e] is e + 4. We
dispense with the detailed definition of E[e] because it is straightforward:
since the hole in E never occurs in the scope of a bound variable there is no
risk of variable capture.
The basic idea is to stipulate that e1 evaluates to e2 in one step (e1 =} e2) if
there exists E, e10 and e2o such that e1 = E[e10], e10---+ e2o and e2 = E[e2o].
As an example, (1+2)+4 =} 3+4 by taking E tobe [ ]+4, e10 tobe 1+2 and
e20 to be 3. Note that having E op e as an evaluation context corresponds
to having an inference rule
is defined by
p ::= proc1 1 proc2 1 · · ·
and we take
CP E Pfin(Chan)
PP E Proc ---+fin Exp
where it will be the case that each P P(p) is a closed expression. We shall
write PP[p: e] for PP' given by dom(PP') = dom(PP) U {p}, PP'(p) = e
and PP'(q) = PP(q) for q =1- p.
The concurrent semantics is specified in Table 5.15. It is a Structural Oper-
ational Semantics that axiomatises the relation CP1, PP1 =} CP2, PP2 for
when one configuration CP1, PP1 in one step evolves into another configu-
ration CP2, PP2. Thanks to the use of evaluation contexts all clauses can
5.5 Behaviours 343
[spawn] CP, PP[p: E[spawn eo]] =?- CP, PP[p: E[O]][po: eo]
if Po (/. dom(PP) U {p}
Behaviour variables, (3 E AVar, are as before and the behaviour A is used for
atomic actions that do not involve communication; in a sense it corresponds
to the empty set in previous annotations although it will be more intuitive to
think of it as the empty string in regular expressions or as the silent action
in process calculi. The behaviour 'Pl; 'P2 says that 'Pl takes place before 'P2
whereas 'Pl + 'P2 indicates a choice between 'Pl and 'P2 (as will be the case
for the conditiona!); this is reminiscent of constructs in regular expressions
as well as in process algebras. The construct rec(3.cp indicates a recursive
behaviour that acts as given by cp except that any occurrence of (3 stands
for rec(3.cp itself; it is typically used whenever there is explicit or implicit
recursion in the program.
The behaviour 7 chan r indicates that a new channel has been allocated over
which entities of type 7 can be communicated; the region r indicates the set
of program points (see below) where the creation could have taken place.
The behaviour spawn cp indicates that a new process has been generated and
that it operates as described by cp. Next r!f indicates that a value is sent
over a channel of type 7 chan r and r?r indicates that a value is received
over a channel of that type; this is reminiscent of constructs in most process
algebras (in particular CSP).
Regions have a bit more structure than in Subsection 5.4.3 because now we
shall also be able to take the union of regions:
r ::= { 1r} 1 (} 1 r1 U r2 1 0
Example 5.35 Returning to the program of Example 5.34, the node func-
tion is intended to have the type schema
V'a, 'b, '1, 111, 112. ('a ~ 1b) ~ ('a chan 111) ~ ('b chan 112) ~ unit
where cp = spawn(rec 12. ("1?'a; '1; "2!'b; '2))
corresponding to the function argument having type 'a ~ 1b, the input
channel having type 'a chan 111 and the output channel having type 'b chan 112.
When supplied with these arguments the node function will spawn a process
that recursively will read on the input channel, execute the function supplied
as its parameter, and write on the output channel - this is exactly what is
expressed by cp.
The pipe function is intended to have the type schema
V'a, 11, "1, 112.(('a ~ 'a) list ~ ('a chan ("1 U {C} ))
~ ('a chan "2) ..:i..., unit
where cp' = rec '2. (spawn(rec 13. (("1 U {C})?'a; A; "2!'a; '3))
+'a chan C; spawn(rec '4. (("1 U {C})?'a; 11; C!'a; 14)); 12)
where the first summand in the body of cp' corresponds to the then-branch
where node is called with the identity function (which has behaviour A) and
the second to the else-branch of the conditiona!. Here we see that a channel
is created, a process is spawned and then the overall behaviour recurses. We
shall return to these types schemes after presenting the typing rules. •
where the type environment f maps variables to type schemes (or types), &
is the type scheme (or type) for the expression e, and cp is the behaviour that
may arise during evaluation of e. The analysis is specified by the axiom and
rules of Tables 5.16 and 5.17 and have many points in common with those
we have seen before; the differences are explained below.
The axioms [con] and [var] for constants and variables differ from the similar
axioms in Table 5.10 in that A is used instead of 0. A similar remark holds
for the two rules [fn] and [fun] for functions; note that [!un] does not explic-
itly demand cp 0 to be of the form rec(J'.cp' although this will frequently be
necessary in order to satisfy the demands (using the ordering on behaviours
to be introduced below). In the rule [app] for function application we now
use sequencing to express that we first evaluate the function part, then the
argument and finally the body of the function. In the rule [i.fl for conditiona!
we additionally use choice to express that only one of the then- and else-
branches are taken. Then the rules [let] and [op] should be straightforward.
346 5 Type and Effect Systems
[fn]
[app]
r f--cA el : T2 ~ To & 'Pl r f--cA e2 : T2 & 'P2
r f--cA el e2: To & 'Pli 'P2i 'Po
(i~
r f--cA eo : bool & 'Po r f--cA el : T & 'Pl r f--cA e2 : T & 'P2
r f--cA i f eo then el else e2: T & cpo; ('Pl + 'P2)
[let]
f f--cA e1 : â1 & 'Pl Î'(x ~----> â1] f--cA e2 : :J='2 & 'P2
r f--cA let X =el in e2 : T2 & 'Pli 'P2
[op]
r f--cA e1 : Tap1
~
& 'Pl r f--cA e2 : Tap2
~
& 'P2
r f--cA el op e2: Tap & 'Pli'P2;A
[spawn]
r reA eo : To & i.po
r reA spawn eo : unit & spawn i.po
[send]
r reA el : T & 'Pl r reA e2 : T chan r2 & 'P2
f reA send e1 on e2: unit & i.p1;i.p2;r2!T
[receive]
r reA eo : T chan ro & i.po
r reA recei ve eo : T & i.po; ro ?T
[seq]
r reA el : TI & 'Pl r reA e2 : T2 & 'P2
f reA e1; e2: T2 & i.p1; i.p2
[sub]
r reA e : T & i.p if T :::; T1 and i.p !;;; cp'
f reA e : T 1 & i.p1
[gen]
r reA e : T & i.p
r reA e: V((I, ... ,(n).T & i.p
if (I, · · · , (n do not OCCUI free in f and i.p
[ins]
r reA e: V((I, ... '(n).T & i.p
f reA e : (() T) & i.p
if() has dom(B) ~ {(I,···,(n}
ch can "only" be given the type 'a chan {A} and therefore the program fails
to type check (because 'a cannot be equal to int and bool at the same time).
However, if the generalisation rule [gen] were only to require that ( 1 , · · ·, (n
do not occur free in r,
then it would be possible to give the channel ch the
type schema V'a.'a chan {A}; as a consequence we would be able to give types
to the two occurrences of send in the body of the let construct and the type
system would not be semantically correct. •
The rule [sub] for subeffecting and subtyping looks like the one in Table 5.10
but involves a few subtleties. The ordering T :::; T1 on types is given by
'P1 ~ 'P2
spawn 'P1 ~ spawn 'P2
r1 r;;, r2 71 5: 72 r1 r;;, r2 72 5: 71
r1 !71 ~ r2 !72 r1 ?71 ~ r2 ?72
is covariant in r (just as was the case for the type 7 chan r); r!f is covariant
in both r and 7 (because a value is sent) whereas r?r is covariant in r and
contravariant in 7 (because a value is received). There is no explicit law for
renaming bound behaviour variables as we shall regard rec(3.<p as being equal
to rec(3' .<p' whenever they are a-equivalent.
The Communication Analysis in Tables 5.16 and 5.17 differs from the Region
Inference Analysis of Tables 5.12 and 5.13 in that there is no analogue of the
rule [region] where an Observe function is used to reduce the annotation to
what is visible from the outside. The reason is that the Communication Anal-
ysis is intended to record all the side effects (in the form of communications)
that take place during computation.
We use the rules [!un] and [sub] of Tables 5.16 and 5.17 together with the
axiom <p[(3 ~ rec(3.<p] !;;:; rec(3.<p of Table 5.18. Then the rule [gen] allows us
to obtain the type schema given in Example 5.35.
Thrning to the pipe function we first note that it can be given a type of the
form
(('a ~ 'a) list) ~ ('a chan (111 U {C} )) ~ ('a chan 112) ~ unit
where the regions for the input and local channels are "merged" because they
can both be used as input channels in a call to pipe whereas the region for
the output channel is always kept separate. The behaviour <p 1 is of the form
because in the then-branch the input channel for node has type 'a chan ("1 U
{ C}) and the out put channel has type 'a chan 112, whereas in the else-branch
the input channel for node has type 'a chan ("1 U{ C}) and the output channel
has type 'a chan {C} (as well as 'a chan ("1 U { C})). The rule [gen] allows us
to obtain the type schema displayed in Example 5.35. •
Concluding Remarks
Control Flow Analysis. The literature [49, 50, 71, 18] contains many
formulations of non-standard type systems aiming at performing Control
350 5 Type and Effect Systems
[88, 102, 89, 90]. At the same time we illustrated a number of design consid-
erations tobe taken into account when devising a Type and Effect System:
whether or not to incorporate subeffecting, subtyping, polymorphism, poly-
morphic recursion, whether or not types are allowed to be influenced by
effects (which is not the case in Sections 5.4 and 5.1), and whether or not
constraints are an explicit part of the inference system (as is implicitly the
case in Subsection 5.4.3). However, it would be incorrect to surmise that the
selection of components are inherently linked to the example analysis where
they were illustrated. Rather, the techniques needed for semantic correctness
and for syntactic soundness and completeness depend heavily on the partic-
ular selection of components; some are straightforward to deal with whereas
others are beyond state-of-the-art.
The Side Effect Analysis presented in Subsection 5.4.1 illustrated the use
of subeffecting and subtyping, but did not incorporate polymorphism, there
were no constraints in the inference system, and the effects did not influence
the types. This system is sufficiently simple that semantic soundness may
be established using the techniques of Section 5.2. If the rule for subtyping
was omitted then also the techniques developed in Section 5.3 would suffice
for obtaining a sound and complete inference algorithm. The presence of
the rule for subtyping naturally leads to a two stage implementation process:
first the underlying types are inferred and next the constraints on effects
(or annotations) are determined [169, 174]. This works because we restrict
ourselves to shape conformant subtyping where effects do not infiuence the
type information. However, adding polymorphism to this development would
dramatically increase the complexity of the development (see below).
The Exception Analysis presented in Subsection 5.4.2 illustrated the use of
subeffecting, subtyping and polymorphism, but there were no constraints in
the inference system, and the effects did not infl.uence the types. Semantic
soundness is a bit more complex than in Section 5.2 because of the polymor-
phism but the techniques of [173, 167, 168, 15] suffice. For the development
of a syntactically sound and complete inference algorithm one may take the
two stage approach described above [169, 174]; as before, it works because
we restrict ourselves to shape conformant subtyping where effects do not
influence the type information. Alternatively, one may use more powerful
techniques [183, 167, 168, 127] that even allow to include type information
inside effects; this amounts to an extension of the approach of Section 5.3
and will be explained below.
The Region lnference analysis presented in Subsection 5.4.3 illustrated the
use of polymorphic recursion as far as the effects are concerned, there were
implicitly constraints in the inference system (via the dot notation on func-
tion arrows), but still the effects cannot influence the types. The presentation
is mainly based on [175] but adapted to the FUN language and the style of
presentation used elsewhere in this chapter. To obtain effects that are as
352 5 Type and Effect Systems
Mini Projects
Consider a Type and Effect System for Call-Tracking Analysis: it has judge-
ments of the form
r f--cT e: T & cp
354 5 Type and Effect Systems
where cp denotes the set of functions that may be called during the evaluation
of e (and similarly for the annotations on function arrows).
2. Modify the Natural Semantics of Table 5.4 such that semantic correct-
ness can be stated for the analysis and prove that the result holds.
3. Devise an algorithm for Call-Tracking Analysis and prove that it is
syntactically sound (and complete).
For the more ambitious: can you also deal with subtyping and/or polymor-
phism? •
As in Mini Project 3.2 we shall now extend the language with more general
data structures and consider how to modify the Control Flow Analysis (Table
5.2) so as to track the creation points.
1. Modify the Control Flow Analysis of Table 5.2 to track the creation
points of pairs.
2. Extend the Natural Semantics of Table 5.4 and augment the proof of
semantic correctness (Theorem 5.9).
3. Extend the algorithms WcFA and UcFA and augment the proof of syn-
tactic soundness and completeness (Theorems 5.20 and 5.21).
Mini Projects 355
In this mini project we shall implement the Control Flow Analysis consid-
ered in Sections 5.1 and 5.3. As implementation language we shall choose a
functional language such as Standard ML or Haskell. We can then define a
suitable data type for FuN expressions as follows:
1. Define data types for simple types and simple substitutions and imple-
ment the function UcFA of Table 5.7.
2. Define data types for simple type environments and constraint sets and
implement the function WcFA of Table 5.8.
3. Define data types for types and type environments and implement a
function that pretty prints the result in the manner of Subsection 5.3.4:
type variables must get instantiated to int and annotation variables to
the least solution to the constraints.
How much of the development in Sections 5.2 and 5.3 can be adapted to
this scenario? (Feel free to impose further conditions if needed, e.g. that the
.
property space satisfies the Ascending Chain Condition.)
For the more ambitious: can you deal with both subtyping and polymor-
~~
Even the most advanced programming languages, allowing user defined data
types and enforcing strong typing, hardly ever record units of measure (for
example meters, degrees Centigrade, US dollars). Each unit of measure may
involve two ingredients: the scaling factor and the base of origin:
There are many reasons for why one would like to extend the programming
language to be precise about the units of measurement; perhaps one needs
to port the software to a new environment where a different unit of mea-
surement is used, perhaps one is operating on data presented in a mixture of
units of measurement, perhaps one wants to reduce the likelihood of inear-
rect computations (like adding US dollars and European currency units), or
perhaps one wants to prepare for a change in data formats.
Let us assume that we are given a program that is correctly typed in some un-
derlying type system. One approach to incorporating units into the program
proceeds as follows:
5. Possibly data formats are changed based on the extended type infor-
mation.
In this mini project we focus on the first task and for our purposes it suffices
to let the syntax of the underlying types be given by:
7 ::= num 1 7 -t 7
A suitable extended type system might be based around the following types
and annotations:
f .. - num'f' 1 f -t f
cp ··- scale BASE base
scale unit 1 scale.scale 1 scale- 1 1
(]" 1
* V'a1,a2,a[with a= a1.a2]:
numu1 BASEnone ---t numu2 BASEnone ---t numuBASEnone
This reflects the fact that multiplication and division works on relative units
of measurement (i.e. having no base). One needs tobe careful about the the
laws imposed upon annotations so as to ensure that e.g. meter.feet is treated
in the same way as feet.meter in accordance with the usual conventions of
Physics; this amounts to an axiomatisation that is more refined than the
UCAl axiomatisation.
Thrning to the operations for addition and substraction:
+ .
V'a, /31, /32, f3 [w1th f3 =
{ (3fJ1 if /32 = none ]
2 1'f (3 1 = none
l
numu BASE /31 ---t numu BASE {32 ---t numu BASE {3
This reflects the fact that addition can be used on units of measurement
where at most one has a base and that subtraction can be used to find the
relative unit of measurement between two based measurements as well as to
decrease a based measurement by a relative unit.
Define an annotated type system for a version of the FUN language using
a selection of the ideas above. It may be helpful to allow polymorphism
only for the scale annotations (using the scale variables a) and the base
annotations (using the base variables (3). Try to establish a notion of semantic
correctness for the annotated type system. Develop an inference algorithm
and prove it syntactically sound. Finally investigate whether or not the
inference algorithm is syntactically complete.
Exercises 359
In some cases it is possible to extend this scheme with explicit rules for
conversion between units; e.g. x FT = x * 30.48 CM which involves both a
conversion between feet (FT) and meters (M) and between meters (M) and
centimeters (CM). This is not always feasible (e.g. in the case of currency)
and we shall leave the extension to the interested reader. •
Exercises
Exercise 5.1 Consider the following expression:
let f = fnx x => x 1;
g = fnv y => y+2;
h = fnz z => z+3
in (f g) + (f h)
Use Table 5.1 (the underlying type system) to obtain a type for this expres-
sion; what types do you use for f, g and h? Next use Table 5.2 (Control Flow
Analysis) to obtain the annotated type of this expression; what annotated
types do you use for f, g and h? •
Exercise 5.2 Consider the following variation of FUN where function def-
initions explicitly involve type information as in fn1r x : 7x => eo and fun1l" f :
(7x ~ 7o) x => eo. Modify Table 5.1 accordingly and prove that the resulting
type system is deterministic: r f-uL e : 71 and r 1-uL e : 72 imply 71 = 72. •
Exercise 5.3 Consider the inclusion 'Pl ~ 'P 2 between effects that was
discussed in Subsections 5.4.1 and 5.2.3. Give an axiomatisation of 'Pl ~ 'P 2
such that 'Pl ~ 'P2 holds if and only if the set of elements mentioned in 'Pl is
a subset of the set of elements mentioned in 'P2· •
• C(f) = 7l ensures that all judgements p, C; r' 1--cFA tl : T' in (5.2) have
T' = 7l, and
• p(x) = 7x ensures that all judgements p, C;
~, ~
r' 1--cFA xl : 7' in (5.2) have
7 = 7x.
Check that (5.2) holds for the expression in Example 5.4 when we take C(1) =
rv, C(2) = rv {x} rv,
C(3) = int, C(4) = C(5) = p(x) = rv,
and rv, rv
p(y) = int. •
Exercise 5.5 Consider adding the following inference rule to Table 5.2
(Control Flow Analysis)
where 1_;-o is the least element of TYPe[7o] and 7o = l7oJ. Explain what this
rule does and determine whether or not Theorem 5.9 (semantic correctness)
continues to hold. •
Exercise 5.6 We shall now extend the Control Flow Analysis of Section
5.1 to include information about where functions are called. To do so modify
the syntax of expressions by adding labels to all application points (in the
manner of Chapter 3):
e ::= · · · 1 (el e2/
Also define a syntactic category of labei annotations 1/J E LAnn by
tobe interpreted modulo UCAl. Finally modify the syntax of annotated types
as follows:
7~ ::= · · · 1 71
~ </> ~
----t 72
'ljJ
In this system it should be possible to type the expression
((fnx x => x) (fny y => y)) 1
(see Example 5.4) such that fnx x => x gets the type
(int 22.
0
int) ~ ( int
{1}
22.
0
int)
for all c/Jx, cf>y E Ann, 1/Jx, 1/Jy E LAnn, and 7 E TYPe. Modify the analysis
of Table 5.2 so as to specify this analysis. •
Exercises 361
Exercise 5.9 Consider the partial ordering f s T' on the annotated types
of Section 5.1 that is defined by:
7{sf1 f2s~ cpc;;cp'
......... c.o_ .-. --::::::1 (/)'_ -:::::1
71 --'-t 72 s 71 --'--t 72
We shall say that this ordering treats 7) -'1!...; ~ covariantly in <p and f2 but
contravariantly in 7); this is in line with the subtype ordering considered in
Section 5.4 but differs form the partial ordering f!;;; f' considered in Section
5.2.
Show that (TYPe[7], s) is a complete lattice for all choices of the underlying
type 7 E Type. Next investigate whether or not an analogue of Proposition
5.12 holds for this ordering.
Finally reconsider the decision to let ()~(o:) in Subsection 5.3.4 be the least
element of (TYPe[7], !;;;); would it be preferable to let ()~(o:) be the least
element of (Type[7J, s) or (TYPe[7J, ~)? •
Exercise 5.11 Formulate what it means for the Side Effect Analysis of
Table 5.9 to be semantically correct; this involves modifying the Natural
Semantics of Table 5.4 to deal with the store and to record the side effects.
(Proving the result would require quite some work.) •
362 5 Type and Effect Systems
Exercise 5.12 Suppose that the language of Subsection 5.4.1 has a call-
by-name semantics rather than a call-by-value semantics. Modify the Side
Effect Analysis of Table 5.9 accordingly. •
Here s E C,02 means {s} ~ <p2 and A V (<p2) is the set of annotation variables
in 'P2· •
Exercise 5.15 Consider the Exception Analysis of Table 5.10 and change
the rule [let] to
and perform similar changes in [i~, [raise], [hand le] and [sub]; do not forget to
define the meaning of 7 ~ T'. Clearly the new system is at least as powerful
as Table 5.10 but is it more powerful? (Hint: Consider the places where [gen]
and [ins] can be used in Table 5.10.) •
Chapter 6
Algorithms
Here Xt = { (x, l)} and we also allow a string of subscripts on X; for example,
x356? = {(x, 3), (x, 5), (x, 6), (x, ?)}.
have the same solutions: any solution of the former is also a solution of the
latter and vice versa. Furthermore, the least solution of the above systems is
also the least solution of
x=hU···Utn
(where the x component has been removed on the right hand side). Given
these observations, it should be clear that it does not much matter whether
our algorithms are supposed to salve inequation or equation systems - but see
Exercise 6.5 for the flexibility of inequation systems in obtaining efficient al-
gorithms. Throughout this chapter we will concentrate on constraint systems
with multiple inequations for the same left hand side.
6.1 Worklist Algorithms 367
for N ~ 1 where the left hand sides are not necessarily distinct.
• The terms are interpreted with respect to solutions, 'ljJ : X --+ L, and
we write [t]'lj; E L to represent the interpretation of t with respect to
'lj;.
• The interpretation [t]'lj; of a term t is monotone in 'ljJ and its value only
depends on the values {'lj;(x) 1 x E FV(t)} of the solution on the flow
variables occurring in the term.
In the interest of generality, we leave the nature of the right hand sides, ti,
unspecified.
Example 6.2 The constraints used in this chapter may appear tobe sim-
pler than those used in Chapter 3 in that apparently we do not allow con-
ditiona} constraints. However, conditiona} constraints can be dealt with by
allowing the terms to contain conditionals. Consider the following expression
(from Example 3.20):
Using the notation above, the constraints generated by the Constraint Based
0-CFA Analysis are:
In its most abstract form the worklist could be viewed as a set of constraints
with the following operations:
empty = 0
function insert((x;;;;:) t),W)
return WU {x;;;;:) t}
function extract(W)
return ((x;;;;:) t),W\{x;;;;:) t}) for some x;;;;:) tin W
Initially the worklist contains ali constraints from S, the influence sets are
generated and the Analysis array has every flow variable set to .l. During
the iteration a constraint x ; ; ;) t is selected from the worklist using the extract
function. If the Analysis array is assigned during the iteration, ali constraints
influenced by the updated variable are added to the worklist using the insert
function. The algorithm terminates when the worklist equals the value empty.
Example 6.3 Consider the simplified equation system from Example 6.1.
After step 1 of the abstract worklist algorithm of Table 6.1, the worklist W
contains ali equations and the influence sets are as foliows (where we identify
the equations with the flow variables on the left hand side):
X1 X2 X3 X4 X5 X6
Additionaliy Analysis is set to 0 for ali flow variables. We shali continue this
example later. •
by:
Fs('l/J)(x) = UHt]'I/J 1 x;;;;;) tin S}
This defines a monotone function over the complete lattice X ---+ L and it
foliows from Tarski's Fixed Point Theorem (see Proposition A.10) that Fs
has a least fixed point, J.Ls, which is the least solution to the constraints S.
Since L by assumption satisfies the Ascending Chain Condition and since X
is finite it foliows that also X ---+ L satisfies the Ascending Chain Condition;
therefore J.Ls is given by
Lemma 6.4 Given the assumptions, the algorithm of Table 6.1 computes
the least solution of the given constraint system, S. •
6.1 Worklist Algorithms 371
Proof We write Analysisi[x] to represent the value of Analysis[x] after the i-th
iteration of the loop considered.
First we prove termination. The loops in step 1 are ali for-loops; thus termination
of step 1 is trivialiy proved. The body of the while-loop of step 2 removes an
element from the worklist, it then either adds at most N elements to the worklist
(if Analysis[x] is assigned to) or else it leaves the worklist unchanged (if Analysis[x]
is not assigned to ). If Analysis[x] is assigned to it gets a strictly larger value. Since
L satisfies the Ascending Chain Condition, Analysis[x] (for each of the finitely many
x E X) can only change a finite number of times. Thus the worklist will eventualiy
be exhausted.
The correctness proof is in three parts: (i) first we show that on each iteration the
values in Analysis are less than or equal to the corresponding values of J.Ls, (ii) then
we show that J.Ls is less than or equal to Analysis at the termination of step 2, and
(iii) finaliy we combine these results.
Part (i). We show that
Vx E X: Analysisi[x] G J.Ls(x)
The third step follows from the induction hypothesis and the monotonicity of [t]
and the fourth step foliows because x ;:;:1 t is one of the constraints considered in
the definition of Fs. So each iteration of the while-loop preserves the invariant.
Part (ii). On termination of the loop, the worklist is empty. By contradiction we
establish that
Fs(Analysis) G Analysis
thereby showing the reductiveness of Fs on Analysis. For the proof by contradiction
suppose that Analysis[x]:;?) Fs(Analysis)(x) for some x E X and further suppose that
one reason is because the constraint x ;;::) t is not fulfilied. Consider the last time
that Analysis[y] was assigned to, for any variable y E FV(t).
If this was in step 1 then, since ali constraints are in W at the beginning of step 2,
for some i :::: 1 it is ensured that
where Analysisi_ 1 [y] contains the final value for y; hence [t] (Analysis 1 ) remains
stable for j :::: i - 1 showing that this case cannot apply.
372 6 Algorithms
It follows that Analysis[y] was last assigned in step 2. This must have been in the
context of dealing with a constraint y;;;] t'. But then, since (x ;;;] t) E infl[y], the
constraint x ;;;] t was added to the worklist and then we re-established, for some
later i ~ 1, that
As before, [t](Analysisj) remains stable for j ~ i - 1 showing that this case cannot
apply either. This completes the proof by contradiction.
Thus Fs is reductive on Analysis and by Tarski's Fixed Point Theorem (see Propo-
sition A.10):
p,s = lfp(Fs) [::; Analysis
Part (iii). That p,s = Analysis on termination of step 2 follows from the combination
of parts (i) and (ii). •
Assume that the size of the right hand sides of constraints is at most M ~ 1
and that the evaluation of a right hand side takes O(M) steps; further assume
that each assignment takes 0(1) step. Each constraint is influenced by at
most M flow variables and therefore the initialisation of the influence sets
takes O(N + N · M) steps. Writing Nx for the number of constraints in
infl[x] we note that LxEX Nx ::::; M · N. Assuming that L is of finite height
at most h ~ 1, the algorithm assigns to Analysis[x] at most h times, adding
Nx constraints to the worklist, for each flow variable x E X. Thus, the total
number of constraints added to the worklist is bounded from above by:
N + (h · L Nx) :S N + (h · M · N)
xEX
Since each element on the worklist causes a call to eval, the cost of the calls
is O(N · M + h · M 2 · N). This gives an overall complexity of O(h · M 2 · N).
empty =nil
function insert((x;;;) t),W)
return cons((x;;;) t),W)
function extract(W)
return (head(W), taii(W))
since each C uniquely identifies the transfer function fe. The influence sets
were indirectly represented through the flow, F; tobe more precise, infi[C'] =
{(C',C'') E F [ C" E Lab}.
The number N of constraints in a system generated from Intraprocedural
Data Flow Analysis is proportional to the number b of elementary blocks.
Furthermore, it is usual to take M = 1. Thus the upper bound on the
complexity of the abstract algorithm specialises to O(h · b); for an analysis
such as Reaching Definitions Analysis where h is proportional to b this gives
O(b 2 ). This agrees with the bound obtained in Example 2.30. •
w X1 X2 X3 X4 xs X6
[xl, X2, X3, X4, Xs, X5] 0 0 0 0 0 0
[x2, X4, X2, X3, X4, Xs, X5] X? - - - - -
[xs,x6] - - - - - -
[x6] - - - - - -
[l - - - - - -
Example 6. 7 Continuing Examples 6.1 and 6.3, the LIFO worklist algo-
rithm obtained from Tables 6.1 and 6.2 operates as shown in Figure 6.1. The
first column is the worklist where we identify the equations by the variables
on the left hand side as in Example 6.3. The remaining columns are the
values of Analysis[xi]; "-" means that the value is unchanged and hence is
equal to that in the previous row. The first row of the table is the result
of the initialisation of step 1; each of the remaining rows corresponds to one
iteration through the loop of step 2. The improved strategy, where a con-
straint is never inserted when it is already present, is considered in Exercise
6.3. •
• there is a directed edge from the node for X; ;;;;) t; to the node for Xj ;;;;) tj
if x; appears in tj (i.e. if Xj;;;) tj appears in infl[xi]).
This constructs a directed graph. Sometimes it has a root, i.e. a node from
which every other node is reachable through a directed path (see Appendix
C). This will generally be the case for constraint systems corresponding to
forward analyses of WHILE programs; in the case of Example 6.1 the root is
x1 . It will not in general be the case for constraint systems corresponding
to backward analyses for WHILE programs nor for constraint systems con-
structed for Constraint Based Analysis. We therefore need a generalisation of
the concept of root. One obvious remedy is to add a dummy root and enough
dummy edges from the dummy root to ordinary nodes that the dummy root
in fact becomes a root. A more elegant formulation, that avoids cluttering
the graph with dummy nodes and edges, is to consider a handle, i.e. a set of
nodes such that each node in the graph is reachable through a directed path
starting from one of the nodes in the handle (see Appendix C). Indeed, a
graph G has a root r if and only if G has {r} as a handle. In the case of Ex-
ample 6.2 a minimal handle is {x2, X4}. One can take the entire set of nodes
of a graph as a handle but it is more useful to choose a minimal handle: a
handle such that no proper subset is also a handle; as discussed in Appendix
C, minimal handles always exist (although they need not be unique).
We can then construct a depth-first spanning forest from the graph Gs and
handle Hs using the algorithm of Table C.l. This also produces an array,
rPostorder, that associates each node (i.e. each constraint x ;;;;) t) with its
number in a reverse postorder traversa! of the spanning forest. To lighten
the notation, particularly when presenting the Round Robin Algorithm later,
we sometimes demand that a constraint system (x; ;;;) t;)f:, 1 is listed in reverse
376 6 Algorithms
,--------1 XI f - - - - ,
(a)
empty = (ni1,0)
function insert((x:;;;) t),(W.c,W.p))
return (W.c,(W.p U {x:;;;) t}))
function extract( (W.c,W.p))
if W.c = nil then
W.c := sort_rPostorder(W.p);
W.p := 0
return ( head(W.c), (taii(W.c),W.p))
Example 6.8 Figure 6.2( a) shows the graphical representation of the con-
straints of Example 6.1; again we use the left hand side of the equation as
the name of the equation. The node for XI is the root of the graph. Figure
6.2(b) shows a depth-first spanning tree for the graph; the associated reverse
postorder is XI, · · · , X6. •
Extraction based on reverse postorder. Conceptually, we now
modify step 2 of the worklist algorithm of Table 6.1 so that the iteration
amounts to an outer iteration that contains an inner iteration that visits the
nodes in reverse postorder.
To achieve this, without actually changing Table 6.1, we shall organise the
worklist W as a pair (W.c,W.p) of two structures. The first component, W.c,
is a list of current nodes to be visited in the current inner iteration. The
6.2 Iterating in Reverse Postorder 377
W.c W.p Xl X2 X3 X4 X5 X6
[J {xl, ... ,x6} 0 0 0 0 0 0
[x2, X3, X4, X5, X6] {x2, x4} X? - - - - -
[x3, X4, X5, X6] {x2,X3,X4,X6} - X37 - - - -
[x4, x5, x6] {x2,x3,x4,x6} - - x3? - - -
[x5,x6] {x2, ... 'x6} - - - x5? - -
Example 6.9 Returning to Examples 6.1 and 6.3, we shall consider the
reverse postorder XI,···, X6 of Example 6.8. Using the algorithm of Table
6.3, we get the iterations shown in Figure 6.3. Note that we perform fewer
iterations than when using the LIFO strategy (see Example 6.7). Improved
versions of Table 6.3 are considered in Exercises 6.6 and 6.7. •
Example 6.10 Using the reverse postorder XI,··· ,x6 of Example 6.8 to
solve the equations of Example 6.1, the Round Robin Algorithm of Table 6.4
operates as shown in Figure 6.4. The lines marked * record the assignment
of false to change at the beginning of the body of the while-loop. •
Lemma 6.11 Given the assumptions, the algorithm of Table 6.4 com-
putes the least solution of the given constraint system, S. •
6.2 Iterating in Reverse Postorder 379
change XI X2 X3 X4 X5 X6
true 0 0 0 0 0 0
* false
true X? - - - - -
true - x3? - - - -
true - - X3? - - -
true - - - X57 - -
true - - - - x5? -
true - - - - - x35?
* false
false - - - - - -
false - - - - - -
false - - - - - -
false - - - - - -
false - - - - - -
false - - - - - -
Proof Since the algorithm of Table 6.4 is obtained in a systematic manner from
that of Table 6.1, the correctness proof can be obtained in a systematic manner
from that of Lemma 6.4; we leave the details to Exercise 6.9. •
We shall say that the constraint system (xi ;;;J ti)k 1 is an instance of a Bit
Vector Framework when L = P(D) for some finite set D and when each right
hand side ti is ofthe form (x]inY? )uY? for sets Y;k ~ D and variable Xj, E X.
Clearly the classical Data Flow Analyses of Section 2.1 produce constraint
systems of this form (possibly after expansion of a composite constraint Xi ;;;J
t7i
t} U · · · U t7i ).
into the individual constraints Xi ;;;J t}, · · ·, Xi ;;;J
Consider a depth-first spanning forest T and a reverse postorder rPostorder
constructed for the graph Gs with handle Hs. We know from Appendix C
that the loop connectedness parameter d( G s, T) 2 Ois defined as the largest
number of so-called back edges found on any cycle-free path of Gs. We also
know that the back edges are exactly those edges that are not topologically
sorted by rPostorder, i.e. for which the target of the edge does not have an
rPostorder number that is strictly larger than that of the source.
Let us say that the algorithm of Table 6.4 has iterated n 2 1 times if the
loop of step 1 has been executed once and the while-loop of step 2 has been
executed n- 1 times. We then have the following result:
Lemma 6.12 Under the assumptions stated above, the algorithm of Table
6.4 halts after at most d(Gs, T) + 3 iterations. It therefore performs at most
O((d(Gs, T) + 1) · N) assignments. •
Proof If a path contains d back edges, the while-loop of step 2 takes at most d + 1
iterations to propagate a change throughout the path. To be specific, it takes one
iteration for the value to arrive at the source of the first back edge; this follows
since up to this point the nodes in the path are numbered in increasing sequence.
After that, it takes one iteration of the while-loop for the value to reach the source
of the next back edge, and so on. So the algorithm needs at most d + 1 iterations
to propagate the information.
One more iteration of the while-loop suffices for detecting that there are no further
changes. We also have to count one for the iteration of the for-loop in step 1. This
gives an upper bound of d + 3 .:; d( G s, T) + 3 on the number of iterations.
Clearly each iteration can perform at most O(N) assignments or evaluations of right
hand sides. This gives an upper bound of O((d(Gs, T) + 1) · N) on the number of
assignments. •
For WHILE programs we know from Appendix C that the loop connectedness
parameter is independent of the choice of depth first spanning forest, and
hence of the choice of the reverse postorder recorded in rPostorder, and that
it equals the maximal nesting depth d of while-loops. It follows that Lemma
6.12 gives an overall complexity of O( (d + 1) · b) where b is the number of
elementary blocks. We would normally expect this bound to be significantly
smaller than the O(b 2 ) obtained in Examples 2.8 and 6.5.
6.3 Iterating Through Strong Components 381
Example 6.13 The WHILE program of Example 6.1 has a loop connect-
edness parameter of 1. According to Lemma 6.12 the Round Robin Algorithm
will perform at most 4 iterations (1 for step 1 and 3 for step 2 of Table 6.4).
However, Example 6.10 managed to succeed in only 3 iterations (1 for step
1 and 2 for step 2). •
Example 6.14 Consider once again the equation system of Example 6.1.
Its strong components and the reduced graph are shown in Figure 6.5. There
are two possible topologica! orderings of the strong components. One is
{x1}, {x2,x3}, {X4,X5}, {x5} and the other is {xl}, {x4,x5}, {x2,x3}, {x5}.
382 6 Algorithms
§]
(a) (b)
OUTPUT: srPostorder
METHOD: sec:= 1;
for each sec in topologica! order do
rp := 1;
for each x ;;;! t in the strong component sec
in local reverse postorder do
srPostorder[x ;;;! t] := (scc,rp );
rp := rp + 1
sec := sec + 1;
In this example each strong component was an outermost loop. This holds
in general for both forward and backward flow graphs for programs in the
WHILE language. •
For each constraint we need to record both the strong component it oc-
curs in and its number in the local reverse postorder for that strong com-
ponent. We shall do so by means of a numbering srPostorder that to each
constraint x ;;;! t assigns a pair (sec, rp) consisting of the number sec of the
strong component and the number rp of its reverse postorder numbering in-
side that strong component. When srPostorder[x ;;;! t] = (sec, rp) we shall
write fst(srPostorder[x ;;;! t]) for sec, and snd(srPostorder[x ;;;! t]) for rp. One
way to obtain srPostorder is using the algorithm of Table 6.5.
6.3 Iterating Through Strong Components 383
The Algorithm. The basic method of the new algorithm is that strong
components are visited in topologica! order with nodes being visited in reverse
postorder within each strong component.
Conceptually, we now modify step 2 of the worklist algorithm of Table 6.1
so that the iteration amounts to three levels of iteration; the outermost level
deals with the strong components one by one; the intermediate level performs
a number of passes over the constraints in the current strong component; and
the inner level performs one pass in reverse postorder over the appropriate
constraints.
To achieve this, without actually changing Table 6.1, we shall again organise
the worklist W as a pair (W.c,W.p) of two structures. The first component,
W.c, is a list of current nodes to be visited in the current inner iteration.
The second component, W.p, is a set of pending nodes to be visited in a
later intermediate or outer iteration. Nodes are always inserted into W.p
and always extracted from W.c; when W.c is exhausted the current inner
iteration has finished and in preparation for the next we must extract a
strong component from W.p, sort it and assign the result to W.c. The details
are provided by the operations of Table 6.6.
Intuitively, an inner iteration ends when W.c is exhausted, an intermediate
iteration ends when sec gets a higher value than last time it was computed,
and the outer iteration ends when both W.c and W.p are exhausted.
Again the lists can be organised as priority queues. This time the priority of
x ;:;) t is the srPostorder information about the strong component, sec, and the
local reverse postorder number, rp; rather than directly using pairs, (sec, rp),
it may be helpful to use a linearised representation with numbers such that
the lexicographic ordering on the pairs corresponds to the arithmetic ordering
on the numbers.
Example 6.15 Consider the ordering {x!}, {x2,x3}, {x4,x5}, {x6} of the
strong components of Example 6.14. The algorithm iterating through strong
components (Table 6.6) produces the walkthrough of Figure 6.6 when solving
the system. Note that even on this small example the algorithm performs
slightly better than the others (ignoring the cost of maintaining the worklist).
Improved versions of Table 6.6 are considered in Exercises 6.11 and 6.12. •
It is also possible to split the set W.p of pending constraints into two col-
lections: W.pc for those that relate to the current strong component and
W.pf for those that relate to future strong components. (Since the reduced
graph is a DAG there cannot be any constraints relating to earlier strong
components.) We leave these details to Exercise 6.12.
Once more the overall correctness of the algorithm carries over from Lemma
6.4; also the estimation of the complexity carries over from Section 6.1.1.
384 6 Algorithms
empty = (ni1,0)
function insert({x:;;;) t),{W.c,W.p))
return (W.c,(W.p U { x :;;;) t}))
function extract{{W.c,W.p))
local variables: sec, W_scc
if W.c = nil then
sec := min{fst(srPostorder[x:;;;) t]) 1 (x :;;;) t) E W.p };
W_scc := {(x:;;;) t) E W.p 1 fst(srPostorder[x:;;;) t]) =sec};
W.c := sort_srPostorder(W_scc);
W.p := W.p \ W_scc;
return ( head{W.c), (taii{W.c),W.p))
W.c W.p X1 X2 X3 X4 xs X6
[l {x1, ... ,x6} 0 0 0 0 0 0
[l {x2,· .. ,x6} X? - - - - -
[x3] {x3,· ··,x6} - X37 - - - -
[l {x2, · · · ,x6} - - X37 - - -
[x3] {x4,xs,x6} - - - - - -
[l {x4,xs,x6} - - - - - -
[xs] {xs,x6} - - - Xs? - -
[l {X4,X5,X6} - - - - Xs? -
[x5] {x6} - - - - - -
[l {x6} - - - - - -
[l 0 - - - - - x3s?
Concluding Remarks
Iterative solvers. Worklist algorithms have a long history; an early
presentation of a general worklist algorithm for Data Flow Analysis may be
found in [96]. A number of special purpose iterative algorithms are pre-
sented in [69]; this covers versions for forward Data Flow Analysis problems
(based on reverse postorder traversa! of the depth-first spanning forest) and
backward Data Flow Analysis problems (based on postorder traversa! of the
depth-first spanning forest); it also covers versions in so-called "integrated
form" (roughly meaning that constraints are placed on the worklist) and
"segregated form" (roughly meaning that flow variables are placed on the
Concluding Remarks 385
worklist); it also deals with the Round Robin Algorithm. A theoretical study
of Round Robin Algorithms may be found in [92]. The empirica! study of
the loop connectedness parameter for Fortran programs, giving rise to the
Folk Theorem that the Round Robin Algorithm operates in linear time in
practice, is contained in [98].
The use of strong components to speed up iterative algorithms has been
considered by a number of authors (e.g. [77, 87]). The treatment in Section
6.3 and Exercise 6.12 is based on [77] where the worklists W.c, W.pc, and
W.pf are represented by priority queues currentQ, pendingQ, and futureQ,
respectively. We refer to [77] for examples for which iteration over strong
components performs better than either the basic worklist algorithm or the
Round Robin Algorithm. There are many ways to implement priority queues
as balanced search trees; the use of 2-3 trees is explained in [4].
Local solvers. Suppose that the set of constraints is very large but that
we are only interested in the values of a small subset of the flow variables.
In this situation the classical worklist solvers may compute information in
which we have no interest; hence it may be better to perform a local fixed
point computation where one finds a partial solution for the flow variables of
interest (and of course all flow variables which directly or indirectly influence
them). For best performance the local fixed point solvers perform a dynamic
determination of what is essentially the strong components of the constraints
of interest.
One such algorithm is the top down solver [29]; this is a general fixed point
algorithm that was originally developed for program analysis tools for logic
programming languages. The algorithm proceeds top down; as it meets a
new flow variable, it attempts to produce a solution for that flow variable.
Its practica! performance is very good and is competitive with the more
sophisticated versions of the worklist algorithm.
Another algorithm is the worklist based local fixed point solver [51, 54] that
uses time stamps to impose an ordering on the elements of the worklist. The
worklist is now a maximum priority queue - the priority of an entry being
given by its time stamp. Elements are selected from the worklist in an order
which gives a run-time approximation to strong components (as with the top
down solver).
A further refinement is the differential worklist based local fixed point solver
[52, 53]. The differential worklist algorithm aims to minimise the amount
of recomputation that occurs when a value changes; it does this by only
recomputing the actual sub-terms influenced by the changed value and by
only computing the incremental change due to the difference between the old
and new values. This algorithm compares well in practice with a number of
special purpose algorithms reported in the literature. (See Exercise 6.5 for a
key insight.)
386 6 Algorithms
cert ain classes of analyses (e.g. they may not support interprocedural anal-
yses). lnternally, some of the systems work on abstract syntax trees, others
work on flow graphs and yet others work directly on the equations or con-
straints.
The specification languages of tools like Spare [177], System Z [185] and PA G
[104] are based on eager functional languages and they support high-level
definitions of complete lattices and the associated analysis functions; Spare
and System Z are inspired by a denotational approach to program analysis
(see e.g. [117]) whereas PAG is closer to the traditional Data Flow Analysis
approach. The above tools build on the ideas of Abstract Interpretation
by allowing the user to control the complexity of the analyses by specifying
operations related to widening operators (see Section 4.2 and Exercise 6.13).
Other program analysis tools are based on other specification mechanisms
like graph rewriting [17] or modal logic [97].
To improve the efficiency of the generated analysers, some of the tools can
take advantage of special properties of the analyses. An example is PAG
[104] which supports several implementations of sets including bit vectors,
AVL trees (see [4]) and BDD's [26] (binary decision diagrams). Another ex-
ample is Sharlit [172] which provides an equation solver based on elimination
techniques for Bit Vector Frameworks.
BANE [8] is a general constraint solver for a very general class of set con-
straints [7] (see the Concluding Remarks to Chapter 3). The constraints
are sufficiently general that the system has been used successfully for imple-
menting both Constraint Based Analyses and Type Systems. It is written in
Standard ML but also admits a simple textual interface in which a system of
constraints can be specified.
Mini Projects
Recall that a Bit Vector Framework (see Section 6.2 and Exercise 2.9) is a
special case of a Monotone Framework with
direct the system to use (i) the Round Rabin Algorithm (see Table 6.4), or
the worklist algorithm of Table 6.1 based on (ii) last-in first-out (see Table
6.2 and Exercise 6.3), (iii) reverse postorder (see Table 6.3 and Exercise 6.6)
and (iv) strong components with local reverse postorders (see Table 6.6 and
Exercise 6.11).
Design suitable experiments to allow an empirica! comparison of the worst-
case and average-case performance of the four algorithms.
For the more ambitious: Generalise your system to accept descriptions of
Monotone Frameworks and more general instances of constraint systems.
Apply the system to the equations and constraints generated in Chapters
2 and 3. •
Consider the following simplified form of set constraints (as discussed in the
Concluding Remarks to Chapter 3) where the terms on the right hand sides
of the constraints are given by:
t x 1 _i 1 c 1 t1 u t2 1 t1 n t2 1
Here x stands for a flow variable, c for a primitive constant (like {fn x => x 1 }
of Example 6.2), and if · · · then · · · denotes a conditiona! constraint as con-
sidered in Example 6.2.
1. Verify that the assumptions of this chapter stiU hold; in particular, that
the evaluation of terms is stiU monotone.
2. Observe that once a condition becomes true, it remains so. Modify the
LIFO worklist algorithm (see Tables 6.1 and 6.2) such that it simplifies
constraints as conditions become true.
3. State and prove a correctness result for the new algorithm. What can
you say about its complexity?
4. Modify the new algorithm to implement the optimisation suggested by
Exercise 6.3.
5. Investigate the application of these ideas to the material of Chapter 3.
For the more ambitious: Explore the extent to which similar optimisations
are possible for the other worklist algorithms of this chapter. Give particular
attention to the maintenance of the ordering on the constraints; what are the
implications for the complexity of the modified algorithms? •
Exercises 389
Exercises
Exercise 6.1 In Example 6.7 the result of step 1 produced the worklist
W = [x 1 ,• ·,x6 ]. Redo the example assuming that step 1 instead produced
the worklist W = [x6, · ·,x1]. Compare the number of iterations. •
Exercise 6.2 Consider the program ((fn x => x) (fn y => y)) from
Example 6.2. Give a walkthrough of the worklist algorithm of Table 6.1 to
solve these constraints using the LIFO extraction strategy of Table 6.2. •
Exercise 6.3 Modify the LIFO extraction strategy of Table 6.2 so that a
constraint is never inserted into the worklist W when it is already present.
Then redo Example 6. 7 using the modified strategy and compare the number
of iterations. •
ifn E H
Dom(n)- { {n}
- {n} u n(n',n)EA Dom(n') otherwise
where each t{ has size 0(1) and can be evaluated in 0(1) steps. Show that
the worklist algorithm can solve this system in O(h · M 2 • N) steps. Next
show that the constraint system is equivalent to the constraint system
in the sense that they have the same solutions. Finally show that the worklist
algorithm can solve this system in O(h · M · N) steps. •
X1 0 X6 x1 U {a+b}
X2 X6 X7 x2 U {a*b}
X3 x1 n xw xs X3 U {a+b}
X4 Xs Xg x4\{a+b,a*b,a+1}
X5 Xg xw x5 U {a+b}
Exercise 6.9 Complete the details of the proof of Lemma 6.11 concerning
the correctness of the Round Robin Algorithm. •
Exercise 6.10 Draw the graph of the constraint system in Exercise 6.8.
Identify the strong components of the graph (see Appendix C) and write
down a srPostorder numbering for the nodes in the graph. Use the algorithm
of Section 6.3 to salve the system. •
Exercise 6.12 In Section 6.3 we discussed using two priority queues for
the worklist algorithm based on strong components: W.c for the constraints to
be considered in the current "inner" iteration, W.pc for those tobe considered
in the next "inner" iteration, and W.pf for those to be considered in future
"outer" iterations.
Develop a concrete algorithm based on these ideas so that as few operations
as possible need to be performed. Explore the idea of adding a constraint
x';;;) t' to W.c, W.pc or W.pf depending on how srPostorder[x';;;) t'] compares
to srPostorder[x ;;;) t] where x ;;;) t is the constraint just considere_d in step
2 of Table 6.1. The objective should be to keep W.c and W.pc as large as
possible without destroying the iteration order. Then reda Example 6.15 and
compare the number of iterations with the result from Exercise 6.11. •
Exercise 6.13 Modify the worklist algorithm of Table 6.1 to use a widen-
ing operator (see Section 4.2) over L. Does the widening operator over L give
rise to a widening operator over X ---t L? Prove that the resulting worklist
algorithm computes an upper approximation to the least solution of the con-
straint system. Also prove that it always terminates when L is a complete
lattice (even when it does not satisfy the Ascending Chain Condition). •
holds for n ~ O. Conclude that if fis the fastness closure of f then Jfp(F) = f.
This shows that for fast Distributive Frameworks (including all Bit Vector
Frameworks) there is a simple non-iterative way of solving data fiow equations
for programs with a simple loop structure. •
Appendix A
Partially ordered sets and complete lattices play a crucial role in program
analysis and in this appendix we shall summarise some of their properties.
We review the basic approaches for how to construct complete lattices from
other complete lattices and state the central properties of partially ordered
sets satisfying the Ascending Chain and Descending Chain Conditions. We
then review the classical results about least and greatest fixed points.
{2,3} {3}
{3} {2,3}
(a)
are equivalent. •
Proof Clearly (i) implies (ii) and (iii). To show that (ii) implies (i) let Y ~ L and
define
(A.l)
and let us prove that this indeed defines a greatest lower bound. All the elements
of the set on the right hand side of (A.l) are lower bounds of Y so clearly (A.l)
defines a lower bound of Y. Since any lower bound of Y will be in the set it follows
that (A.l) defines the greatest lower bound of Y. Thus (i) holds.
A.l Basic Definitions 395
To show that (iii) implies (i) we define UY = n{l E L 1 Vl' E y : l' ~ l}.
Arguments analogous to those above show that this defines a least upper bound
and that (i) holds. •
Example A.3 Consider the complete lattice (P(S), ~)of Figure A.l (a).
The subsets
{{2}, {1, 2}, {2, 3}, {1, 2, 3}} and {0, {1, 2, 3}}
are. •
Properties of functions. A function f : L1 ~ L 2 between partially
ordered sets L1 = (L1, ~1) and L2 = (L2, ~2) is surjective (or onto or epic)
if
Lemma A.4 If L = (L, !;::;, u, n, 1_, T) and M = (M, !;::;, u, n, 1_, T) are
complete lattices and M is finite then the three conditions
1-lh(m) m E M'}
1
Proof Suppose that f is of the form displayed and let Y be a non-empty set; Then
UU(Y) y E Y}
1 u{u{'P(d) 1 dE Y} U 'P0 1 Y E Y}
U{U{'P(d) 1 dE Y} 1 Y E Y} U 'P0
f(UY)
J(Y) J(UY)
u({f({d}) 1 dE Y} U {!(0)})
u({'P(d)) 1 dE Y} U {'P0})
u{'P(d) 1 dE Y} U 'P0
so f can be written in the required form. The additional statement about com-
pletely additivity is straightforward. •
and
(ln, l21) ~ (h2, l22) iff ln ~1 h2 1\ h1 ~2 b
It is then straightforward to verify that L is a partially ordered set. If ad-
ditionally each Li = (Li, ~i, Ui, ni, .li, Ti) is a complete lattice then so is
L = (L, ~, U,n, .l, T) and furthermore
398 A Partially Ordered Sets
and ..l = (..l1, ..l2) and similarly for nY and T. We often write L1 x L2 for
L and call it the cartesian product of L1 and L 2 .
A variant of the cartesian product called the smash product is obtained if we
require that all the pairs (h, b) of the lattice satisfy h = ..l1 {:} l2 = ..l2.
UY = .>.s.Ul{J(s) 1 f E Y}
and ..l = .>.s ...l1 and similarly for nY and T. We often write S ---t L 1 for L
and call it the total function space from S to L 1 .
UY = .Xh.Ud!(h) 1f E Y}
and ..l = .Xh ...l2 and similarly for nY and T. We often write L1 ---t L2 for L
and call it the monotone function space from L1 to L2.
A.3 Chains
The ordering ~ on a complete lattice L = (L, ~) expresses when one prop-
erty is better (or more precise) than another property. When performing a
program analysis we will typically construct a sequence of elements in L and
it is the general properties of such sequences that we shall study now. In the
next section we will be more explicit and consider the sequences obtained
during a fixed point computation.
A.3 Chains 399
Writing (ln)n also for {ln 1 nE N} it is clear that an ascending chain also is
a chain. Similarly, a sequence (ln)n is a descending chain if
3no E N : Vn E N : n ~ no =} ln = ln 0
Lemma A.6 A partially ordered set L = (L, [;;;) has finite height if and
only if it satisfies both the Ascending and Descending Chain Conditions. •
Proof First assume that L has finite height. If (ln)n is an ascending chain then it
must be a finite chain and hence eventually stabilise; thus L satisfies the Ascending
Chain Condition. In a similar way it is shown that L satisfies the Descending Chain
Condition.
Next assume that L satisfies the Ascending Chain Condition as well as the Descend-
ing Chain Condition and consider a chain Y ~ L. We shall prove that Y is a finite
chain. This is obvious if Y is empty so assume that it is not. Then also (Y, !;;;;) is
a non-empty partially ordered set satisfying the Ascending and Descending Chain
Conditions.
As an auxiliary result we shall now show that
• o 1• 00
• -1
• -2 • 2
• 1
1._00 • o
(a) (b)
To see this we shall construct a descending chain (l~)n in Y' as follows: first let l~
be an arbitrary element of Y'. For the inductive step let l~+ 1 = l~ if l~ is the least
element of Y'; otherwise we can find l~+ 1 E Y' such that l~+ 1 !;;; l~ 1\ l~+ 1 "Il~.
Clearly (l~)n is a descending chain in Y; since Y satisfies the Descending Chain
Condition the chain will eventually stabilise, i.e. 3n~ : Vn ~ n~ : l~ = l~, and the
o
construction is such that l~, is the least element of Y'.
o
Returning to the main proof obligation we shall now construct an ascending chain
(ln)n in Y. Using (A.3) each ln is chosen as the least element of the set Y \
{lo, · · ·, ln-1} as long as the latter set is non-empty, and this yields ln-1 !;;; ln 1\
ln-1 "lln; when Y \ {lo, · · ·, ln-1} is empty we set ln = ln-1, and since Y is non-
empty we know that n > O. Thus we have an ascending chain in Y and using the
Ascending Chain Condition we have 3no : \In ~ no : ln = ln 0 • But this means that
Y \ {lo, · · ·, ln 0 } = 0 since this is the only way we can achieve that lno+1 = ln 0 • It
follows that Y is finite. •
Example A. 7 The partially ordered set of Figure A.2 (a) satisfies the
Ascending Chain Condition but does not have finite height; the partially
ordered set of Figure A.2 (b) satisfies the Descending Chain Condition but
does not have finite height. •
One can show that each of the three conditions finite height, ascending chain,
and descending chain, is preserved under the construction of cartesian prod-
uct: if L1 and L2 satisfies one of the conditions then L1 x L2 will also satisfy
that condition. The construction of total function spaces S --+ L only pre-
serves the conditions of L if S is finite and the construction of monotone
function spaces L1 --+ L2 does not in general preserve the conditions.
An alternative characterisation of complete lattices satisfying the Ascending
Chain Condition is given by the following result:
A.3 Chains 401
are equivalent. •
Proof It is immediate that (i) implies (ii) so let us prove that (ii) implies (i). Using
Lemma A.2 it suffices to prove that all subsets Y of L have a least upper bound
UY. If Y is empty clearly UY = .l. If Y is finite and non-empty then we can
U
write Y = {y1, · · ·, Yn} for n ~ 1 and it follows that Y = (· · · (y1 Uy2) U · · ·) Uyn.
If Y is infinite then we construct a sequence (ln)n of elements of L: let la be an
arbitrary element yo of Y and given ln take ln+l = ln in the case where Vy E Y : y [;;;;
ln and take ln+l = ln U Yn+l in the case where some Yn+l E Y satisfies Yn+l lb ln.
Clearly this sequence is an ascending chain. Since L satisfies the Ascending Chain
Condition it follows that the chain eventually stabilises, i.e. there exists n such that
ln = ln+l = · · ·. This means that Vy E Y : y [;;;; ln because if y lb ln then ln =1- ln U y
and we have a contradiction. So we have constructed an upper bound for Y. Since
it is actually the least upper bound of the subset {yo, · · ·, Yn} of Y it follows that
it is also the least upper bound of Y. •
(i) fis additive, i.e. Vh, 12 : /(h U 12) = f(h) U J(l2), and
(ii) f is affine, i.e. VY ~ L, Y =1- 0 : /(U Y) = U{!(l) Il E Y}
for the set of fixed points. The function f is reductive at l if and only if
f(l) ~ l and we write
Red(f) = {l f(l) ~ l}1
for the set of elements upon which f is reductive; we shall say that f itself
is reductive if Red(f) = L. Similarly, the function f is extensive at l if and
only if f(l) ;;::1 l and we write
for the set of elements upon which f is extensive; we shall say that f itself is
extensive if Ext(f) = L.
Since L is a complete lattice it is always the case that the set Fix(!) will
have a greatest lower bound in L and we denote it by lfp(f):
lfp(f) = n Fix(!)
Similarly, the set Fix(!) will have a least upper bound in L and we denote it
by gfp(f):
gfp(f) = U
Fix(!)
We then have the following result, known as Tarski's Fixed Point Theorem,
showing that lfp(f) is the least fixed point of f and that gfp(f) is the greatest
fixed point of f:
Proposition A.lO
Let L = (L, ~' U,n, .l, T) bea complete lattice. If f: L--+ Lis
a monotone function then lfp(f) and gfp(f) satisfy:
Proof To prove the claim for Jfp(f) we detine lo = nRed(f). We shall first show
that f(lo) [;;; lo so that lo E Red(f). Since l0 [;;; l for ali l E Red(f) and f is
monotone we have
f(lo) [;;; f(l) [;;; l for alll E Red(f)
A.4 Fixed Points 403
r(T)
Red(f)- - - -
• nnr(T)
gfp(f)
lfp(f)
Un rCl)
Ext(f) - - - -
- ...... -
r(l-)
l_
and hence f(lo) r;;;; lo. To prove that lo r;;;; f(lo) we observe that f(f(lo)) r;;;; f(lo)
showing that f(lo) E Red(f) and hence lo r;;;; f(lo) by definition of lo. Together this
shows that lo is a fixed point of f so lo E Fix(!). To see that lo is least in Fix(!)
simply note that Fix(!) t;;; Red(f). It follows that lfp(f) = lo.
The claim for gfp(f) is proved in a similar way. •
In denotational semantics it is customary to iterate to the least fixed point by
taking the least upper bound of the sequence (fn (l_)) n. However, we have not
imposed any continuity requirements on f (e.g. that f (Un ln) = Un (f (ln))
for all ascending chains (ln)n) and consequently we cannot be sure to actually
reach the fixed point. In a similar way one could consider the greatest lower
bound of the sequence (r(T))n· One can show that
and note that for a natural number n we have Jln = r+l(..L). Then lfp(f) =
Ji"' whenever K, is a cardinal number strictly greater than the cardinality of
L, e.g. K, may be taken tobe the cardinality of P(L). A similar construction
allows to obtain gfp(f) as the limit of a descending (transfinite) chain. •
Concluding Remarks
For more information on partially ordered sets consult a text book (e.g. [43]).
Appendix B
Q(O)
't:/n: Q(n) =? Q(n + 1)
and conclude
't:/n: Q(n)
Formally, the correctness of mathematical induction can be related to the fact
that each natural number is either O or the successor, n + 1, of some other
natural number, n. Thus the proof principle reftects the way the natural
numbers are constructed.
dED
d ::= Base 1 Con1(d) 1 Con2(d, d)
406 B Induction and Coinduction
where Base is a base case, Con 1 is a unary constructor and Con 2 is a binary
constructor. To prove that a certain property, Q(d), holds for all elements,
d, of D we can define a size measure:
size(Base) o
size( Con 1(d)) 1 + size(d)
size(Con2(d1, d2)) 1 + size( d1) + size( d2)
and then proceed by mathematical induction on size(d) to prove Q(d).
Alternatively we can conceal the mathematical induction within a principle
of structural induction: we must then show
Q(Base)
\:/d: Q(d) =:;. Q(Con 1(d))
\:/d1, d2: Q(dl) A Q(d2) ==? Q(Con2(d1, d2))
d-+n
Con1(d)-+ n+ 1
d1 -+ n1 d2 -+ n2
Con2(d1, d2)-+ n1 + n2
Q(Base-+ O)
V V d-+n
V )
\:/(d-+ n): Q(d-+ n) =:;. Q ( Con1(d)-+n + 1
B.2 Introducing Coinduction 407
Qt(O)
't:/n: Qt(n) =} Qt(n + 1)
and then concluding
't:/n: Qt(n)
from which the desired Qt(27) follows.
An alternative presentation of essentially the same idea is to establish the
validity of the axiom and rule
fo(O,m) true
fo(n + l,m) fo(n,m)
/3(0,m) h(O,m)
h(n + l,m) h(n+ l,m)
B.2 Introducing Coinduction 409
Least fixed point. Let us begin by looking at the least fixed points.
It follows from Appendix A that
and given that the clauses for Qo(Q) only use a finite number of Q's on the
right hand sides (in fact zero or one), Q0 satisfies a continuity property that
ensures that
because Q3(..l) = ..l so that ..l is a fixed point. This explains why we have
Jfp(Q3)(27) =false and why an inductive proofwill not work for establishing
Q3(27). Somewhat similar arguments can be given for Ql and Q2.
Greatest fixed point. Let us next look at the greatest fixed points.
Here
dED
d ::= Base 1 Con1(d) 1 Con2(d, d)
with one base case, one unary constructor and one binary constructor. Next
consider the definition of a predicate
Q(Base) iff
Q(Con1 (d)) iff · · · Q(d') · · ·
Q(Con2(d1, d2)) iff · · · Q(d~) · · · Q(d~) · · ·
We can use this as the hasis for defining a functional Q by cases as in:
Q(Q')(Base)
Q(Q')(Con1(d)) = ... Q'(d') .. .
Q(Q')(Con2(d1, d2)) ... Q'(d~) ... Q'(d~) ...
We note that
(D --+ { true,false}, !::;;)
is a complete lattice under the ordering given by Q1 !::;; Q2 if and only if
\/d: Q1 (d) :::} Q 2(d). We also
assume that Q is monotone
and this means that e.g. a clause like "Q(Con1(d)) iff •Q(d)" will not be
acceptable. From Proposition A.lO it follows that Q has a least as well as a
greatest fixed point.
Induction (or least fixed point). Consider first the inductive def-
inition:
Q = lfp(Q) (B.4)
This is more commonly written as:
.. ·Q(d') ... . .. Q(d~) ... Q(d;) ...
(B.5)
Q(Base) Q(Con1(d)) Q(Con2(d1, d2))
It is often the case that each rule only has a finite number of calls to Q
and then the two definitions are equivalent: the predicate in (B.5) amounts
412 B lnduction and Coinduction
Q = gfp(Q)
as follows from the formula for g.tp(Q) given in Proposition A.lO. So to prove
Q(d) one needs to
Such a proof has a very optimistic fiavour: we can assume everything we like
as long as it cannot be demonstrated that we have violated any facts. It is
commonly used for checking that a specification holds because the specifica-
tion should not forbid some behaviour unless explicitly called for.
It sometimes saves a bit of work to use the derived proof rule
To see that this is a valid proof rule suppose that Q' [;;;; Q(Q U Q'). By
definition of Q we also have Q [;;;; Q(Q) and by monotonicity of Q this gives
Q [;;;; Q(Q U Q'). Hence
QuQ' [;;;; Q(QuQ')
and Q U Q' [;;;; Q follows by definition of Q. It is then immediate that Q' [;;;; Q
as required.
Clearly this explanation can be generalised to algebraic data types with ad-
ditional base cases and constructors, and from predicates to relations.
B.3 Proof by Coinduction 413
defined by:
R1 = R1 (R1. R2)
R2 = R2(R1, R2)
This is intended to define
and write R' ~ R" for the truth value defined by:
(c )n+m ak ifk::;n
(ai)i=l (bj)'j=l k k=l where Ck = {
bk-n if k > n
ak ifk::;n
(ai)i=l (bj )'t=l (ck)~l where Ck = {
bk-n if k > n
(ai)~l (bj)j·~l (ck)~l where Ck = ak
S--+OI1IOSI1S
xES xES
OES 1E S
Ox E S 1x E S
S--+S
When interpreted inductively this does not change the set S; it is stiH {0, 1} +.
However, when interpreted coinductively the set S changes dramatically; it is
now {0, 1, 2} 00 and thus includes the empty string as well as strings containing
2. To see this simply note that any string x in {0, 1, 2} 00 can be written as
x = x and hence no string can be excluded from being inS. •
Concluding Remarks
For more information on induction principles consult a text book (e.g. [16]).
It is harder to find good introductory material on coinduction; one possibility
is to study the work on strong bisimulation in CCS (e.g. Chapter 4 of [106]).
Appendix C
Example C.l Let S bea statement from the WHILE language of Chapter
2. The forward flow graph (Jabels(S), How(S)) and the backward flow graph
(labels(S),Ho~(S)) are both directed graphs. Next let Lab bea finite set
of labels and F ~ Lab x Lab a flow relation in the manner of Section 2.3.
Then G = (Lab, F) is a directed graph. •
a set of constraints of the form considered in Section 3.4 and let A* contain
the edge (PI.P2) for each constraint P1 ~ P2 in C and the edges (p,p2) and
(p1,P2) for each constraint {t} ~ p::::} P1 ~ P2 in C. Then G = (N*,A*) is a
directed graph. •
Cycles. A cycle is a non-trivial path from a node to itself (and may take
the form of a self-loop); the set of cycles from a node n to itself is defined as
follows:
cycles(n) = {[no, · · ·, nm] 1 m 2:: 1 1\ no = n 1\ nm =n 1\
Vi< m: (ni, ni+1) EA}
from observing that if P12 is a path from n1 to n2 and P23 is a path from n2 to n3
then P12P23 is a path from n1 to n3. •
The equivalence classes of SC are called the strong components (or strongly
connected components) of the graph G = (N, A). A graph is said to be
strongly connected whenever it contains exactly one strongly connected com-
ponent.
A directed graph G = (N, A) always has a handle since one can use H =Nas
the handle. A handle H is minimal if no proper subset is a handle, that is, if
H' C H then H' is not a handle. One can show that minimal handles always
exist since the set of nodes is finite. The minimal handle is a singleton if and
only if the graph has a root; in this case we say that the graph is rooted.
Example C.6 Let S bea statement from the WHILE language of Chap-
ter 2. Then init( S) is a root for (labels( S), flow( S)) and {init( S)} is a min-
imal handle. Furthermore, fi.nal(S) is a handle for the backward fl.ow graph
(labels( S), flo~ (S)). •
METHOD: i := k;
mark ali nodes of N as unvisited;
let Ar be empty;
while unvisited nodes in H exists do
choose a node h in H;
DFS(h);
{n} ifn E H
Dom(n) = {
{n} U n{Dom(n') j (n', n) EA} otherwise
The node n' properly dominates the node n if the two nodes are distinct
and n' dominates n. The node n' directly dominates the node n if it is
the "closest" proper dominator of n; that is n' E Dom( n) \ {n} and for all
n" E Dom(n)\{n} we have n" E Dom(n').
• Forward edges: edges that are not tree edges and that go from a node
to a proper descendant in the tree.
• Cross edges: edges that go between nodes that are unrelated by the
ancestor and descendant relations.
The algorithm of Table C.l ensures that cross edges always go from nodes
visited later (i.e. with lower numbers in the reverse postorder) to nodes visited
earlier (i.e. with higher numbers).
in Figure C.2. The reverse postorder of the nodes is b1 , b3, 8 3, b2, 8 1 ,82 and
the second number annotating each node in Figure C.2 reflects this ordering.
The edges from 82 to b2 and from 83 to b3 in Figure C.l are back edges. The
edge from b3 to 81 is a cross edge. •
Example C.8 Let 8 bea uniquely labelled statement from WHILE and
consider the forward flow graph (labels(8), flow(8)) with root init(8). First
consider a while loop while b' do 8' in 8. It gives rise to one tree edge
and one or more back edges; more precisely as many back edges as there are
elements in fina1(8'). Next consider a conditiona! i f b' then 8~ else 8~ in 8
that is not the last statement of 8 and not the last statement of the body of a
while loop in 8. It gives rise to three tree edges and one or more cross edges;
more precisely there will be one fewer cross edges than there are elements in
fina1(8n U finaJ(8~). Note that no forward edges can arise for statements in
WHILE; however, an extension of WHILE with a one-branch conditiona! i f b
then 8 can give rise to forward edges. •
Next, we establish that every cycle in a directed graph must contain at least
one back edge.
Proof By Lemma C.9, for any edge (n, n'), rPostorder[n] < rPostorder[n'] if and
only if the edge is not a back edge, that is, if and only if the edge is a tree edge in
T or a forward edge or a cross edge. Thus rPostorder topologically sorts T as well
as the forward and cross edges. •
Loop connectedness. Let G = (N, A) bea directed graph with handle
H. The loop connectedness parameter of G with respect to a depth-first
spanning forest T constructed by the algorithm of Table C.l, is the largest
number of back edges found in any cycle-free path of G; we write d(G, T),
or d( G) when T is clear from the context, to denote the loop connectedness
parameter of a graph. The value of d(G) for the graph in Figure C.l is 1.
Let a dominator-back edge be an edge (n1, n2) where the target n2 dominates
the source n1. A dominator-back edge (n1, n2) is a back edge regardless of
the choice of spanning forest T because the path from H to n1 in T must
contain n2 (given that n2 dominates n1). The graph shown in Figure C.3
shows that there may be more back edges than there are dominator-back
edges; indeed, it has root r and no dominator-back edges, but any spanning
forest T will characterise one of the edges between n1 and n2 as a back edge.
The literature contains many definitions of the concept of reducible graph.
We shall say that a directed graph G = (N, A) with handle H is reducible
Co2 Reverse Postorder 425
if and only if the graph (N, A\ Adb) obtained by removing the set Adb of
dominator-back edges is acyclic and stiH has H as handleo The simplest
example of an irreducible graph is shown in Figure Co3o
Reducible graphs are of interest because for a reducible graph G with handle
Hand an arbitrary depth-first spanning forest T, it will be the case (as shown
below) that an edge is characterised as a back edge with respect to T if and
only if it is a dominator-back edgeo It follows that the loop connectedness
parameter is then independent of the choice of depth-first spanning foresto
Example C.13 Once more let S bea uniquely labelled statement from
WHILE and consider the fiow graph (labels(S),flow(S)) with root init(S)o It
is reducible: Each while loop can be entered only at the test node which then
dominates all the nodes for the body of the loop and hence all the back edges
introduced by the while loop are also dominator-back edgeso Clearly the
graph consisting of tree edges, forward edges and cross edges will be acyclico
The loop connectedness parameter equals the maximum number of back edges
(and hence dominator-back edges) of a cycle-free path in the fl.ow graph; it
426 C Graphs and Regular Expressions
follows that it is equal to the maximum number of nested while loops in the
program. •
Another interesting property of rPostorder for reducible graphs is the following
result.
2. If R1 and R2 are regular expressions then (R1 + R2), (R1 · R2) and
(R1 )* are (so-called compound) regular expressions.
.C[A] {A}
.C[0] 0
.C[a] = {a} for all a E E
.C[R1 + R2] .C[R1] U .C[R2]
.C[R1 · R2] .C[Rd · .C[R2]
.C[R~] u 00
k=O
(.C[Rl])k
h(0) = 0
h(A) A
Concluding Remarks
There are a number of good books on graph theoretic concepts; aur presen-
tation is based on [48], Chapter 5 of [4], Chapters 3 and 4 of [69], Chapter
10 of [5] and Chapter 7 of [110]. For more information on regular languages
consult a text book such as [76].
Index of N otation
-- 214, 219 220
341
fl [VJ, 246
r , 22, 285
- r re: T , 22
-fin VIII
17, 1 • 20 r 1 x , 286
$ ' 324 329, 347 f r e : r & lţ), 24
1= • 309 r, 24 287, 307, 321
'V 226 ")', 15
1 154 2 6 "Y" 237
( 1 ' 367 L, 69
• 62
!;;;; • 29 34
tb 66 69
L. , 16
;;;;) • 367
î 96 , 91, 99 100
~ • 29 A, 344
2 • 30 ~ VIII
r : • 22 t/J~A , 116
r : & ' 24 lţ), 24 287 306, 321 327 335, 343
r '"'-+ • 211 , 219
lţ) !;;;; cp' ' 348
r
-
27, 212 219
r lţ) 1 !;;;; '-P2' 29
' 154
IPI ~ '{)2, 29
' 86
1T , 284
"" 367
p, 86, 153, 333
O' 15, 300 306 p r . (S , c;) - (S' <;'), 86
a" , 237 p r . {S, c;) - ' 6
{J, 216 306 p 1 X 154
fJ 2 cp, 30 p 11 , 144, 187 191
{J, - !32. 220 p. 86
fJR, 217 (! , 331
{J" 237 lY 332
ll. 91 , 96, 97 99, 100, 191 230 E , 17
6 91 , 191 (1 , 54, 105
246
<;.,/> c, 11, 144, 187, 193
T, 285, 300 c*, 174
r ~ T', 324, 329, 347 c, 142, 284
1'1 G ~' 298 C~e, 144, 187, 193
7', 24,287,306,321,327,335,343 ce, 191
(), 301 CEnv, 191
()A F C, 309 ch, 339
~' 86, 105, 320 Chan, 339
(, 327 clear, 52
close-construct, 153
A, 55, 108 Const, 142, 284
Acp, 73 CP, 102
a, 3 CP, 342
AEentry, 40 cs, 14
AEexit, 40 cycles, 418
AExp*' 39 .Q*, 82
AExp, 3
~' 187
AExp(·), 39 d, 187
AHeap, 111, 114 de, 182
ALoc, 110
d 0 p, 183
ALoc(H), 111
Data, 182
ALoc(S), 111
Analysis=, 69
D--;-~ 187
Analysis!;;;, 69 DCache, 187
Analysis, 369 def, 52
Analysis, 65 DEnv, 187
AnncA, 343 DOM, 13
Ann, 287 doin, 86, 154, 286, 301
AnnEs, 327 DU, 54
AnnRI, 335 du, 53
AnnsE, 321 E, 69, 341
AState, 110, 114 e1 ---+ e2, 341
AType, 300 e, 142, 283, 339
AV, 101, 317 ee, 331
AVar, 306 EExp, 331
B, 55, 108 empty, 368
b, 3 Env, 86, 153, 333
BExp, 3 ~' 144, 187, 191
BExp(·), 39 EVal, 333
bind-construct, 87, 154 Exp, 142, 283, 339
[B]t, 37 Ext, 223, 402
Blocks*, 38 extract, 368
blocks*, 84 :F, 68
blocks, 36, 83 J, 91
c, 308 :Fcp, 73
Index of Notation 431
[9] A. Aiken and E. Wimmers. Type inclusion constraints and type infer-
ence. In Proc. FPCA '93, pages 31-41. ACM Press, 1993.
[10] A. Aiken, E. Wimmers, and T.K. Lakshman. Soft typing with condi-
tiona! types. In Proc. POPL '94, pages 163-173. ACM Press, 1994.
440 Bibliography
[14] T. Amtoft, F. Nielson, and H.R. Nielson. Type and behaviour recon-
struction for higher-order concurrent programs. Journal of Functional
Programming, 7(3):321-347, 1997.
[49] K.-F. Faxen. Optimizing lazy functional programs using flow inference.
In Proc. SAS '95, volume 983 of Lecture Notes in Computer Science,
pages 136-153. Springer, 1995.
Bibliography 443
[51] C. Fecht and H. Seidl. An even faster solver for general systems of
equations. In Proc. SAS '96, volume 1145 of Lecture Notes in Computer
Science, pages 189-204. Springer, 1996.
[54] C. Fecht and H. Seidl. A faster solver for general systems of equations.
Science of Computer Programming, 35(2):137-161, 1999.
[57] C. Flanagan and M. Felleisen. The semantics of future and its use in
program optimization. In Proc. POPL '95, pages 209-220. ACM Press,
1995.
[58] Y.-C. FUh and P. Mishra. Polymorphic subtype inference: Closing the
theory-practice gap. In Proc. TAPSOFT '89, volume 352 of Lecture
Notes in Computer Science, pages 167-183. Springer, 1989.
[59] Y.-C. FUh and P. Mishra. Type inference with subtypes. Theoretical
Computer Science, 73:155-175, 1990.
[92] J. B. Kam and J. D. Ullman. Global data fiow analysis and iterative
algorithms. Journal of the ACM, 23:158-171, 1976.
[120] F. Nielson and H. R. Nielson. From CML to its process algebra. The-
oretical Computer Science, 155:179-219, 1996.
448 Bibliography
[133] H. R. Nielson and F. Nielson. Flow logics for constraint based analysis.
In Proc. CC '98, volume 1383 of Lecture Notes in Computer Science,
pages 109-127. Springer, 1998.
Bibliography 449
[161] G. S. Smith. Principal type schemes for functional programs with over-
loading and subtyping. Science of Computer Programming, 23:197-226,
1994.
Bibliography 451
[164] B. Steffen. Generating data flow analysis algorithms from modal spec-
ifications. Science of Computer Programming, 21:115-239, 1993.
[166] J.-P. Talpin and P. Jouvelot. Polymorphic Type, Region and Effect
Inference. Journal of Functional Programming, 2(3):245-271, 1992.
[167] J.-P. Talpin and P. Jouvelot. The type and effect discipline. In
Proc. LICS '92, pages 162-173, 1992.
[168] J.-P. Talpin and P. Jouvelot. The type and effect discipline. lnformation
and Computation, 111(2):245-296, 1994.