ASM - Abstract StateMachines Theory and Applications (Lecture Notes in Computer Science) PDF
ASM - Abstract StateMachines Theory and Applications (Lecture Notes in Computer Science) PDF
13
Series Editors
Volume Editors
Yuri Gurevich
Microsoft Research
One Microsoft Way, Redmond, WA 98052, USA
E-mail: [email protected]
Philipp W. Kutter
ETH Zürich, Institute TIK
Gloriastr. 35, 8092 Zürich, Switzerland
E-mail: [email protected]
Martin Odersky
EPFL Lausanne, DI
IN-Ecublens, 1015 Lausanne, Switzerland
E-mail: [email protected]
Lothar Thiele
ETH Zürich, Institute TIK
Gloriastr. 35, 8092 Zürich, Switzerland
E-mail: [email protected]
Cataloging-in-Publication Data applied for
ISSN 0302-9743
ISBN 3-540-67959-6 Springer-Verlag Berlin Heidelberg New York
This work is subject to copyright. All rights are reserved, whether the whole or part of the material is
concerned, specifically the rights of translation, reprinting, re-use of illustrations, recitation, broadcasting,
reproduction on microfilms or in any other way, and storage in data banks. Duplication of this publication
or parts thereof is permitted only under the provisions of the German Copyright Law of September 9, 1965,
in its current version, and permission for use must always be obtained from Springer-Verlag. Violations are
liable for prosecution under the German Copyright Law.
Springer-Verlag Berlin Heidelberg New York
a member of BertelsmannSpringer Science+Business Media GmbH
c Springer-Verlag Berlin Heidelberg 2000
Printed in Germany
Typesetting: Camera-ready by author, data conversion by PTP-Berlin, Stefan Sossna
Printed on acid-free paper SPIN: 10722735 06/3142 543210
Preface
The ASM 2000 workshop was held in the conference center of the Swiss Federal
Institute of Technology (ETH) at Monte Verità, Canton Ticino, March 19-24,
2000.
The ASM formalism was proposed together with the thesis that it is suitable
to model arbitrary computer systems on arbitrary abstraction levels. ASMs have
been successfully used to analyze and specify various hardware and software
systems including numerous computer languages.
The aim of the workshop was to bring together domain-experts, using ASMs
as a practical specification method, and theorists working with ASMs and related
methods. In addition the workshop served as a forum on theoretical and practical
topics that relate to ASMs in a broad sense. Three tutorials including hands-on
experience with tools were organized by U. Glässer and G. del Castillo (on the
topic “Specifying Concurrent Systems with ASMs”), H. Rüss and N. Shankar
(on the topic “A Tutorial Introduction to PVS”), M. Anlauff, P.W. Kutter, and
A. Pierantonio (on the topic “Developing Domain Specific Languages”).
In response to the organization committee’s call for papers, 30 papers were
submitted, each of which was independently reviewed by four members of the
program committee. This volume presents a selection of 12 of the refereed papers
and two reports on industrial ASM application at Siemens AG and Microsoft
Research, together with contributions based on the invited talks given by A.
Blass (University of Michigan), E. Börger (University of Pisa), G. Goos (Univer-
sity of Karlsruhe), M. Odersky (Swiss Federal Institute of Technology (EPFL),
Lausanne), W. Reisig (Humboldt University Berlin), and N. Shankar (SRI In-
ternational). The introduction written by E. Börger gives an overview on ASM
research from the beginning to the present.
On behalf of the program committee, we would like to express our apprecia-
tion to the six lecturers who accepted our invitation to speak, to the tutorial
organizers, to all the authors who submitted papers to ASM 2000 and to the
Centro Stefano Franscini (CSF) at Monte Verità and the local organizers who
made the workshop possible.
ASM 2000 took place from March 19th to 24th at the conference center Monte
Verità of the Swiss Federal Institute of Technology (ETH).
Organization Committee
Program Committee
Local Organizers
Bernhard Bron (ETH Zürich, Switzerland)
Samarjit Chakraborty (ETH Zürich, Switzerland)
Christoph Denzler (ETH Zürich, Switzerland)
Felix Etter (ETH Zürich, Switzerland)
Monica Fricker (ETH Zürich, Switzerland)
Philipp W. Kutter (ETH Zürich, Switzerland)
Milan Tadjan (ETH Zürich, Switzerland)
Sponsoring Institutions
Swiss Federal Institute of Technology, Zürich, Switzerland
Introduction
Abstract State Machines at the Cusp of the Millennium . . . . . . . . . . . . . . . . . 1
E. Börger (Univ. of Pisa)
Mathematical Foundations
Abstract State Machines and Pure Mathematics . . . . . . . . . . . . . . . . . . . . . . . 9
A. Blass (Univ. of Michigan)
Industrial Applications
Report on a Practical Application of ASMs in Software Design . . . . . . . . . . 361
E. Börger (Univ. of Pisa), P. Päppinghaus, and J. Schmid (Siemens
AG, Munich)
Using Abstract State Machines at Microsoft: A Case Study . . . . . . . . . . . . . . 367
M. Barnett, E. Börger, Y. Gurevich, W. Schulte, and M. Veanes
(Microsoft Research)
Egon Börger
Abstract. The ASM’2000 Workshop marks for the ASM method the
transition from its adolescence to the maturation period. The goals which
have been achieved open new frontiers and put us into the position to
embark on new challenges.
We went a long way since the Spring of 1987 when Yuri Gurevich visited Pisa
and, in a series of lectures on the fundamental problem of semantics of program-
ming languages, presented the world première of the concept of ASMs (then
called dynamic/evolving structures/algebras). He gave the main motivation: re-
consider Turing’s thesis in the light of the problem of semantics of programs. He
illustrated his ideas with examples, in particular specifications of Turing machi-
nes, stack machines and some Pascal programs. He gave also proofs of simple
properties of these programs. This material appeared a year later in [22]. It was
preceded by the first appearance of the ASM Thesis, in embryo in a 1984 tech-
nical report [20], and fully spelled out in a notice presented on May 13 of 1985
to the American Mathematical Society [21]. It was accompanied by the first
real-world application, namely the dynamic semantics of MODULA-2 [26], and
shortly afterwards followed by the ASM treatment of concurrency used to de-
fine the semantics of OCCAM [27], which was presented by Gurevich in another
series of lectures in Pisa in May 1990. Since then the concept of Abstract State
Machines essentially remained stable [23,24]1 and triggered hundreds of publica-
tions in various domains including finite model theory, complexity theory and
numerous areas of applied computer science, in particular programming langu-
ages, database query languages, protocols, architectures and embedded control
software [1].
The first attempts to put the bold ASM thesis to the test were focussed
on the problem of the dynamics of programming languages known to us, and
we came from a purely theoretical background and had no practical, let alone
1
The initially present construct to shrink domains, which was motivated by concerns
about resource bounds, was abandoned because it belongs to garbage collection
rather than to high-level specification. Some technical variation was later introduced
concerning the treatment of non determinism and of inconsistent update sets.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 1–8, 2000.
c Springer-Verlag Berlin Heidelberg 2000
2 E. Börger
industrial, experience. What came out of that is a practical method which ex-
ploited ASMs for the development of a full-fledged refinement approach which
enabled us to rigorously define and analyse the dynamic semantics of real-life
programming languages and their implementation on virtual or real machines.
By now, the covered programming paradigms include the paradigms of all the
major modern programming languages. The method of constructing ground mo-
dels, described in [5] where the name primary model (rather than ground model)
was used, proved to be mature and was chosen for standardization purposes by
the International Standards Organization, see [8,9,11,12,3], and by the Interna-
tional Telecommunication Union, as reported in [19] and in these Proceedings
[18].
At the next step, the method was tried out for the specification and verifi-
cation of machine architectures and protocols. Eventually this was followed by
applications to software engineering. Here one starts by constructing a ground
model for a proposed (or, in the case of reverse engineering, for an existing)
software system. Through the entire design process, one refines and/or coarsens
the models linking the high level models in a traceable and inspectable way to
executable code; see the survey in [6].
The key for the surprisingly fast success of the method lies in (a) the two
constituents of the notion of ASM, namely being abstract (Abstract State) and
operational (Abstract Machine) (see the section Abstract Machines + Abstract
State = ASM in [6] for the historical reconstruction of the confluence of these
two concepts2 ) and (b) in the systematic way it offers for practical software
development to separate different concerns.
The abstract character of ASMs allows one, on one side, to tailor the mo-
dels to the needs or purposes of the design, and, on the other side, to make their
rigorous analysis feasible. The latter is due to the freedom to use those proof
methods which are appropriate for the present discourse. In other words, the ab-
straction mechanism, built into the notion of ASM, permits one to make real the
old dream of well documented and controllable hierarchical system development.
Use ASMs to do the following:
– Make the faithfulness of the models, with respect to the design intentions,
checkable by direct inspection (falsifiable in the Popperian sense). This holds
in particular in requirements engineering for the faithfulness of the ground
model with respect to the informally given requirements. The faithfulness
becomes checkable by the application domain expert once an ASM model is
there (see [16]).
– Link, by hierarchies of stepwise refinements, the high-level definition in a
transparent way to its implementation. Here each refinement step is supposed
to reflect design decisions one wants to document for future use, e.g. for
maintenance purposes or for changes by extensions and modifications.
2
Notice that the ASM abstract machine is different from the familiar abstract machi-
nes. It has built-in parallelism which allows one to abstract from irrelevant sequen-
tialization.
Abstract State Machines at the Cusp of the Millenium 3
Experience shows that it is not only easier to prove properties for complex
systems in this way, but this splitting of proof obligations often is the only
known way to show that a run-time system works the way it is supposed
to. A characteristic example are the ASM-based proofs of the correctness of
compilation schemes [13,10,17] and the implementation of those schemes by
provably correct real-life compilers, a problem which is addressed in these
Proceedings. By the way, collecting such justificational evidence yields an
interesting byproduct: a detailed analysis of the design itself.
Most of these lines of research and principles were present, in embryo, already
at the first international ASM workshop held as early as 1994, as part of the IFIP
World Computer Congress in Hamburg, Germany [30]. Although the applications
which appeared there were limited by our academic experience and were largely
motivated by an effort to critically test the ASM thesis, it is not incidental that
right from the beginning, by making good use of the freedom of abstraction
offered by the ASM concept, we were naturally led to “separate and combine”
design levels, design and analysis, verification and validation, and degrees of
detail in verification. Concerning validation, let us note that the first tools for
making ASMs executable date back to 1990; see the Prolog-based compiler for
ASMs Angelika Kappel developed in [29], to execute my ASMs for PROLOG
[8], and the Michigan interpreter mentioned in [23]3 .
Only 5 years later, at the ASM workshop which was held as part of the
International Formal Methods Conference FM’99 in Toulouse, France4 , one can
observe (see again the survey [6] for details) that
All of these themes are reflected in the rich program of ASM’2000. These
Proceedings, which constitute the first book entirely devoted to ASMs, docu-
ment what has been achieved in the first decade after the formulation of the
ASM concept. We see confirmation of our conviction expressed already at the
first ASM workshop in 1994, namely that (paraphrased) “the extraordinary po-
tential of the ASM method will change drastically the industrial future of formal
specifications” [5, pg.393].
2 New Frontiers
The experience accumulated with the ASM concept, and with the method which
has been developed for its use, did change the way we think about high-level
software design and analysis. Now we have to actualize this vision to make
it work for established software development disciplines, at a large scale. The
achievements of the last decade open new frontiers and put us into the position
to face the new challenges.
Through the extensive ASM modeling, validation and verification work of
the past decade, the ASM thesis was experimentally confirmed. But this year
brought us a theoretical explanation of the observed phenomenon, namely via
a proof [25] that the sequential version of the thesis follows from three funda-
mental system theory axioms. Once established, the thesis allows one to draw
conclusions of practical importance, as is illustrated by an example in these
Proceedings [7]: the thesis guarantees that there is no loss of generality in sub-
stituting the fundamental but vague UML concepts of action and activity by the
mathematically rigorous concepts of ASM step and ASM run. It seems that in
UML [31,2] the meanings of action/activity were intentionally left unspecified,
namely to leave the space of possible implementations as open as possible. But
this was achieved at the price of making it difficult to control the implications
the concepts have in the context of the event-driven run-to-completion scheme,
in particular concerning the possibly numerous and nested exit/entry actions,
coming through interrupts, and concerning the launch and abortion of internal
activities.
On the practical side we have to take advantage of the experience, acquired
with building tools for executing ASMs, to develop an entire tool environment
which is also industrially satisfactory. It has to support the different activities
of defining, transforming (by refinements and by code generation) and analysing
ASM models (by testing, via visualization supported simulation, and by verifi-
cation). The tool environment has to enable us to capture the design knowledge
in a rigorous, electronically available and reusable way, and to achieve this goal
it must be integrated into established design flows and their tool environments.
The integration potential of ASMs, as a universal model of computation which
5
When I was working on this introduction, a message from Prof. Igor Soloviev, of
St.Petersburg State University, arrived: “One of my students, Andrew Usov, has
written an ASM interpreter. It is implemented as an java-applet designed to run
under an internet-browser (it has been tested under IE 5.0 and NS 4.7).”
6 E. Börger
of overwhelming proof details. We need to find the right way to exploit the no-
tion of monitored (real-valued) function for connecting the discrete ASM world
to the continuous world of control theory. We need to help in building models
for mobile computing. We badly need to extract the inherent object oriented
features of ASMs, which are visible in the concept of ASM agents and of their
state, to make them explicitly and syntactically available, adapted to established
object-oriented programming techniques.
These Proceedings contain numerous contributions where the mentioned is-
sues are raised and thus constitute a good point of departure to help solving the
challenging problems which are waiting for us.
References
1. Abstract State Machines. http://www.eecs.umich.edu/gasm/.
2. Rational Software Corporation, Unified Modeling Language UML, version 1.3,
1999.
3. ISO/IEC 13211-1. Prolog-Part 1: General Core. In Information Technology-
Programming Languages. International Standards Organization, 1995.
4. M. Barnett, E. Börger, Y. Gurevich, W. Schulte, and M. Veanes. Using ASMs at
Microsoft: A Case Study. In This volume.
5. E. Börger. Logic Programming: The Evolving Algebra Approach. In B. Pehrson
and I. Simon, editor, IFIP 13th World Computer Congress, number I (Techno-
logy/Foundations), pages 391–395. Elsevier, 1994.
6. E. Börger. High Level System Design and Analysis using Abstract State Machines.
In D. Hutter and W. Stephan and P. Traverso and M. Ullmann, editor, Current
Trends in Applied Formal Methods (FM-Trends 98), number 1641 in LNCS, pages
1–43. Springer-Verlag, 1999.
7. E. Börger, A. Cavarra, and E. Riccobene. Modeling the Dynamics of UML State
Machines. In This volume.
8. E. Börger and K. Dässler. PROLOG. DIN Papers for Discussion. Report 58,
ISO/IEC JTC1 SC22 WG17, April 1990.
9. E. Börger and B. Demoen. The view on database updates in Standard Prolog: a
proposal and a rationale. Report 74, ISO/IEC JTC1 SC22 WG17 , February 1991.
10. E. Börger and I. Durdanovic. Correctness of Compiling Occam to Transputer
Code. Computer Journal, (39(1)):52–92, 1996.
11. E. Börger and D. Rosenzweig. An Analysis of Prolog Database Views and Their
Uniform Implementation. In K. Dässler and R. Scowen, editor, Prolog. Paris
Papers–2, number 80, pages 87–130. National Physical Laboratory, Middlesex, July
1991.
12. E. Börger and D. Rosenzweig. The Mathematics of Set Predicates in Prolog. In K.
Dässler and R. Scowen, editor, Prolog. Copenhagen Papers–2, number 105, pages
33–42. National Physical Laboratory, Middlesex, 1993.
13. E. Börger and D. Rosenzweig. The WAM–Definition and Compiler Correctness.
In Ch. Beierle and L. Plümer, editors, Logic Programming: Formal Methods and
Practical Applications, pages 20–90. Elsevier Science B.V./North–Holland, 1995.
14. E. Börger and J. Schmid. Composition and Submachine Concepts for Sequential
ASMs. In P. Clote and H. Schwichtenberg, editor, Gurevich Festschrift CSL 2000,
LNCS. Springer-Verlag, 2000. (In print).
15. E. Börger, J. Schmid, and P. Päppinghaus. Report on a Practical Application of
ASMs in Software Design. In This volume.
8 E. Börger
Andreas Blass ?
1 Introduction
The purpose of this paper is to describe some connections between the theory
of abstract state machines (ASM’s) and concepts from pure mathematics. These
connections are of three general sorts.
First, there are direct uses of mathematical concepts in ASM’s. A well known
instance of this is the use of structures, in the sense of first-order logic, in the
very definition of ASM’s. A less well known instance is the use of interpretations,
also in the sense of first-order logic, to describe transitions of ASM’s as well as
certain sorts of simulations.
Second, there are modifications of mathematical concepts, adapting them
to the purposes of computation theory and ASM’s. As an example of this, we
discuss the ASM analog of the set-theoretic concept of permutation model.
Finally, there are analogies between ASM’s and some aspects of pure ma-
thematics. In this connection, we discuss the multifaceted philosophical issue
of “universality”: Do ASM’s provide a universal framework for descriptions of
algorithms in the same sense that set theory provides a universal framework for
mathematical proofs? In this connection, we also discuss the value of explicit
formalization and the role of definitions. We comment briefly on possible alter-
native “foundations” based on, for example, ordered lists or multisets instead of
sets.
We also discuss the issue of “objects whose identity doesn’t matter,” which
arises in connection with the import rules of ASM’s and also in various contexts
in pure mathematics.
?
Preparation of this paper was partially supported by a grant from Microsoft Corpo-
ration. The opinions expressed here are, however, entirely my own.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 9–21, 2000.
c Springer-Verlag Berlin Heidelberg 2000
10 A. Blass
no strict counterpart in the other machine, states that depend on the details of
how the machine does its work.) The interpretation tells how what one machine
does is to be seen from the perspective of the other.
Finally, since interpretations are usually (in mathematical logic) regarded
primarily as syntactic transformations rather than semantical ones, I should
add a few words about the syntactic aspect. An interpretation of a vocabulary
Υ in a theory T provides a translation of terms and formulas from Υ to the
vocabulary of T . The translation consists of replacing each function symbol F
or predicate symbol P in the given term or formula with FI or PI and restricting
all quantifiers to UI . Then the translation ϕI of a sentence ϕ is true in a model
M of T if and only if the sentence ϕ itself is true in the Υ -structure MI obtained
from M via the interpretation.
Thinking of interpretations primarily on the semantic level, transforming mo-
dels M of T into Υ -structures MI , we can describe the syntactic transformation
ϕ 7→ ϕI as producing weakest preconditions. That is, ϕI expresses, in a structure
M , exactly what is needed in order that MI satisfy ϕ. Here is a toy example to
indicate what goes on in general.
Consider the update rule c := d. The corresponding interpretation has PI =
P and FI = F for all predicate and function symbols other than c, and cI = d
(and UI is the identically true predicate). If M is a structure, then MI for this
interpretation is the same as M except that it gives c the value that d had in
M , i.e., it is the sequel of M with respect to the rule c := d. Now consider a
formula ϕ, the simplest relevant example being P (c) for a unary predicate P .
Its translation ϕI under our interpretation is obtained simply by replacing every
occurrence of c by d, so in our simple example P (c)I is P (d). And this ϕI is
exactly what M must satisfy in order to guarantee that MI satisfies ϕ.
3 Permutation Models
In this section, I’ll describe a technique for describing and analyzing the idea
of “not permitting arbitrary choices,” both in set theory and in computation
theory.
In 1922, Fraenkel [5] constructed models of set theory (with infinitely many
urelements, also called atoms) in which the axiom of choice is false. The central
idea is that all the atoms in a model of set theory “look alike”; more precisely, any
permutation of the atoms induces an automorphism of the whole set-theoretic
universe. Fraenkel formed the subuniverse of “sufficiently symmetric” sets, and
showed that it satisfies all the usual axioms of set theory except the axiom of
choice. (“Usual” is an anachronism here, since Fraenkel himself had just recently
introduced the replacement axiom and given a precise formulation of Zermelo’s
separation axiom. But the axiom system, known as Zermelo-Fraenkel set theory,
ZF, is certainly the usual one nowadays.) More precisely, Fraenkel’s model con-
sisted of those sets x which depend on only a finite subset F of the atoms, in the
sense that any permutation of the atoms fixing those in F will, when extended to
an automorphism of the whole universe, also fix x. (Subsequently, other authors
Abstract State Machines and Pure Mathematics 13
sense) as soon as one gets above the bottom level; the higher levels are formalized,
if at all, in first-order theories obtained (formally) by adding definitions as new
axioms. Second, the refinement process is a serious object of study in the ASM
world, whereas in the set-theoretic world the process of definition is usually
swept under the rug — so much so that many logicians would be taken aback
by my comment a moment ago that differential equation theory is formalized
not in ZFC but in an extension by definitions. They would probably say “what’s
the difference?” and accuse me of splitting hairs. The only authors I know of
who have made a serious study of the role of definitions in set theory are Morse
[12], who carefully analyzes the permissible syntax (far more general than what
is usually considered), and Leśniewski [11] who actually uses definitions in a
non-conservative way, as an elegant formulation of existence axioms.
This suggests a question for ASM theory: Can one push the refinement pro-
cess as far into the background as set-theorists have pushed definitions (and if
not then how far can one push it)? That is, can one arrange things so that all
one has to write is a high-level specification and the sequence of “definitions” of
its functions in terms of lower levels? The idea would be that the corresponding
low-level ASM would either be produced automatically or would (as in the set-
theoretic situation) be entirely irrelevant. And of course the high-level ASM and
the definitions together should be significantly shorter than the low-level ASM
they describe.
A third difference between ASM’s and ZFC is the use of ASM’s for detecting
and correcting errors in intuitive specifications, programs, etc. As far as I know,
formalization in ZFC has not played a role in detection and correction of errors
in mathematical proofs. Errors certainly occur, and they are detected and cor-
rected. (For particularly embarrassing examples, see the footnotes on page 118
of [9] and the introduction of [16]. I emphasize that the authors of [9] and [16]
did not commit the errors but conveniently summarized them.) But the detec-
tion and correction seems to proceed quite independently of formalization. It is
based on intuitive understanding and is therefore not very systematic. There are,
of course, projects to formalize and systematically check, by computer, various
parts of mathematics. But these seem to be of more interest, at the moment,
to computer scientists than to typical mathematicians. The latter seem to be
generally quite willing to rely on intuitive understanding rather than formal
checking.
A fourth difference between the role of ASM’s in formalizing algorithms and
the role of ZFC in formalizing proofs is that in the latter context there is (to
the best of my knowledge) no analog of [8]. That is, there is no general criterion
guaranteeing that any proof, subject to some natural constraints, is formalizable
in ZFC. Mathematicians other than set theorists generally take on faith that
anything they would consider a correct proof can be formalized in ZFC. Set
theorists are aware of possible difficulties (with universe-sized sets), but we are
confident that we could recognize any such difficulty and determine whether
a proposed argument really goes beyond ZFC (without actually writing out a
formalization).
16 A. Blass
The title of this section is intended to refer to the computational (ASM) side
of the picture, not the pure mathematical (ZFC) side. Of course, sets are basic
in ZFC, but this is to some extent the result of an arbitrary choice. Indeed,
Cantor, the father of set theory, seems to have regarded sets as intrinsically
equipped with an ordering, so perhaps some sort of (generalized) lists would be
more fundamental.
On the computational side, too, there is a tendency to view interdefinable
concepts, like (finite) sets and lists, as having equal claim to being fundamental.
But it is shown in [3] that this tendency is not always appropriate. When we
do not allow arbitrary choices (or, equivalently, an ordering) but do allow paral-
lelism, and when we impose polynomial bounds on the total computation time
of all processors, then a set-theoretic environment as in [2] is strictly stronger
than one using tuples or lists instead. (The polynomial time bound is essential
here. Without it, parallelism could compensate for the absence of an ordering
by exploring all orderings.)
Abstract State Machines and Pure Mathematics 17
6 Unidentified Objects
In this final section, I’d like to comment on an issue that comes up in many
contexts, throughout computer science and mathematics, but is almost always
ignored because it’s trivial. My main point is that, if it’s so trivial, we should be
able to give a clear explanation very close to our trivializing intuition.
The issue arises in the theory of ASM’s in connection with the importing or
creating of new elements. It doesn’t matter what the new element is, as long as
it’s new. So we refuse to worry about the exact choice of the new element. (If two
or more elements are to be imported simultaneously, then we make sure they’re
distinct, but beyond this avoidance of “clashes” we refuse to worry.) A mathe-
matician’s immediate reaction is that we work, not with a particular structure
in which a particular element has been imported, but with an isomorphism class
of structures, a different but isomorphic structure for each choice of imported
element. This works, but it doesn’t quite correspond to intuition; intuition is
still dealing with one structure, not a whole class of them. In some sense, intui-
tion deals with a “generic” member of the isomorphism class, but what exactly
(mathematically) is that?
The same issue and the same mathematical solution occur in connection with
the syntax of just about any formalized language. One uses bound variables,
but one doesn’t care what particular variable is used (as long as clashes are
avoided). It has become customary to annihilate the issue by saying “we work
modulo α-conversion,” meaning that expressions differing only in the choice of
bound variables are to be identified. This amounts to the “isomorphism class”
viewpoint of the preceding paragraph. Another approach was taken by Bourbaki
[4]. Their official syntax for logic and set theory (and thus for all of mathematics)
has no bound variables. In their places are occurrences of the symbol . To
18 A. Blass
indicate which occurrences correspond to the same variable, these are joined to
each other (and to the binding operator) by lines. This seems to me to be closer
to intuition, but rather unreadable. And it applies only to syntactic situations; I
don’t see how to adapt it to a semantic situation like ASM’s. (I find it somewhat
reassuring that the highly respected mathematicians of Bourbaki — or at least
some of them — found this issue to be worth thinking about to the extent of
producing a rather unorthodox solution.)
Another approach to this issue, in the syntactic context of bound variables,
is the use of de Bruijn indices. These indices link a variable-binder, such as
a quantifier, with the occurrences of the variables it binds, by specifying the
difference between them in quantifier depth. That is, in place of a Bourbaki box
with a line joining it to a quantifier, one would have a number indicating that
that the relevant quantifier is to be found so and so many levels less deep in the
parse tree. This notation strikes me as farther from intuition than Bourbaki’s
boxes but closer than isomorphism classes. In terms of human readability, it
seems no better than the boxes, but I understand computers read it quite well.
(Perhaps we humans just need to be wired differently.) I see no way to adapt this
approach to non-syntactic situations, like the choice of new elements created in
an ASM.
A variant of the isomorphism class approach is suggested by the topos-
theoretic view of generic or variable objects [10]. In the present context, this
amounts to regarding the isomorphism class not simply as a class but as an in-
dexed or parametrized family, the parameters being the individual choices. Thus,
for instance, an ASM that imports two elements would be viewed as a family
of ASM’s indexed by ordered pairs of distinct elements. The ASM indexed by
the pair (x, y) is the one in which first x and then y were imported. An elegant
framework and notation for this approach (applied to bound variables) is given
in [6].
The issue of unidentified objects arises in many other contexts. Indeed, one
can claim that the exact identity of mathematical objects never matters; all one
cares about is the structure relating them. For example, it never matters whether
real numbers are Dedekind cuts or equivalence classes of Cauchy sequences, as
long as they form a complete ordered field. The following quotation from [10,
page 119], though intended to emphasize the topos theorist’s view of sets in
contrast to the set theorist’s cumulative hierarchy, seems to accurately describe
how most mathematicians view sets.
This comes very close to saying that the actual elements don’t matter as long
as there are some (and equality is determined). But I claim that we have no
Abstract State Machines and Pure Mathematics 19
entirely satisfactory semantics for dealing with the concept of an arbitrary ob-
ject whose actual identity doesn’t matter (but whose distinctness from other
arbitrary objects may be important).
Notice that the problem I am raising is a semantical one. Syntactically, we
can handle objects whose identity doesn’t matter: just add constant symbols
to the language to serve as names for the desired objects. This process is well
understood in the context of first-order logic (see for example [15, Chapter 4]; it
plays a crucial role in the proof of Gödel’s completeness theorem). But passing
from syntax to semantics requires choosing elements to serve as the denotati-
ons of the new constant symbols, and that brings us right back to the original
problem: We need to choose an element but without caring which element it is.
Finally, let me mention that this problem threatens to become more acute if
one considers quantum computation or indeed any quantum phenomena. In the
quantum world, actual physical objects, like elementary particles, have identities
in only a very limited sense. It makes sense to talk about an electron and another
electron, but not to talk about this electron and that one — interchanging the
two electrons in some physical process yields not another process but another
channel for the same process, and two channels may interfere (constructively or
destructively).
In this appendix, we collect some observations and questions about ASM’s whose
connection to pure mathematics is even more tenuous than in the last section.
Indeed, most of them are based on a contrast rather than a connection between
the computational and mathematical worlds.
A major difference between the viewpoints of ASM’s (together with most of
computer science) and pure mathematics is that in the former a dynamic aspect
is always present. An ASM doesn’t just sit there, it undergoes transitions —
as the old name “evolving algebra” emphasizes. Of course, pure mathematics
is also capable of dealing with dynamic situations, but this is always explicitly
emphasized, not part of a universal background as in ASM’s.
There is more to the dynamic aspect of ASM’s than just that their dynamic
functions can change their values. It is also important that dynamic functions
retain their previous values unless explicitly changed. Ironically, this apparently
simple default assumption, favoring persistence of values, can increase the logical
complexity of what an ASM does. For example, the action of a parallel rule
do for all v
R(v)
enddo
could be described in existential logic (or in the existential fixed point logic
advocated in [1]) provided its body R(v) could be so described. The parallel rule
executes an update if and only if there exists a value of v for which R(v) executes
that update. But the next state cannot be described existentially, because the
20 A. Blass
inaction of the rule, the persistence of dynamic functions when not updated,
requires a universal quantifier for its description.
The fact that parallel ASM’s can be limited to quantifier-free guards, using do
for all to simulate quantifiers, is ultimately due to the presence of an implicit
universal quantifier in the default requirement that values persist unless expli-
citly changed. (In the last two paragraphs, I’ve pretended for simplicity that the
ASM’s under consideration never attempt conflicting updates. The possibility
of clashes would introduce additional universal quantifiers, because an update is
executed if some R(v) would execute it (ignoring clashes) and there are no w1
and w2 for which R(w1 ) and R(w2 ) would execute conflicting updates.)
Another sort of default adds complexity not in the logic but in the computa-
tional aspects of ASM’s. This default is the assumption that, when new elements
are created (or imported from the reserve), they arrive with no structure: All
Boolean functions, except equality, produce false when one of their arguments
is a freshly created element; all non-Boolean functions produce undef under
these circumstances. Thus, for example, if P is a binary Boolean function and
x is a newly created element, then P (x, y) miraculously has the value false for
all y. If we ask how it got that value, there are several possible viewpoints. One
is that creating an element really means importing it from the reserve, and that
the appropriate default values were already there while x was in the reserve —
so the default value of P (x, y) for newly imported x is just a matter of the per-
sistence default discussed above. But of course this viewpoint requires that the
initial state of a computation include the appropriate default values for reserve
elements, an assumption that is appropriate only at a sufficiently high level of
abstraction. At a lower level, one would have to ask how this initialization is to
be performed. Another viewpoint is that the defaults are (at least implicitly) set
by the ASM at the time the new element is created. This amounts to a fairly
large scale parallel operation, not available in the sequential situation; it may be
the approach closest to what happens in actual computers when new memory
locations are allocated to a computation. A third viewpoint is that the setting of
the defaults is, like the choice of which element to import, the responsibility of
the environment. This seems to be the simplest approach, but it strikes me as a
bit unfair to the environment. Making the environment choose the new element
is reasonable, because this cannot be accomplished algorithmically (unless an
ordering or some similar structure is available); but setting the defaults could
be done algorithmically (in the case of parallel ASM’s) so the justification for
turning the job over to the environment seems to be only that it’s more work
than the ASM wants to do.
Let me close with a brief comment, related to the preceding only because it’s
about the environment. The environment of an ASM is used to model a variety of
things: the choice of elements to import (or create), the arbitrary choices involved
in non-determinism, input-output operations, and, in distributed computing,
all the agents other than the one under consideration. How similar are these,
really? There is of course one similarity, which caused them to all be called
“environment” in the first place: They are not part of the algorithm (ASM)
Abstract State Machines and Pure Mathematics 21
under consideration, but they interact with it. Is there further similarity among
some (or all) of these aspects of the environment? Are there useful distinctions
to be made? (“Useful” means, at a minimum, more useful than just listing the
various items as I did above.)
One rather imprecise but perhaps useful distinction is obtained by singling
out those aspects of the environment’s activity that one would expect to be
included in a system for executing ASM programs. Such a system should not be
expected to provide input or to perform the actions of distributed agents other
than the one being simulated. But it could reasonably be expected to execute
import rules on its own, or at most with the help of the operating system under
which it runs.
References
1. Andreas Blass and Yuri Gurevich, “Existential fixed-point logic,” in Computa-
tion Theory and Logic, ed. by E. Börger, Lecture Notes in Computer Science 270,
Springer-Verlag (1987) 20–36.
2. Andreas Blass, Yuri Gurevich, and Saharon Shelah, “Choiceless polynomial time,”
Ann. Pure Appl. Logic, 100 (1999) 141–187.
3. Andreas Blass, Yuri Gurevich, and Jan Van den Bussche, “Abstract state machines
and computationally complete query languages,” this volume.
4. Nicolas Bourbaki, Elements of Mathematics. Theory of Sets, Hermann (1968).
5. Abraham Fraenkel, Der Begriff “definit” und die Unabhängigkeit des Aus-
wahlaxioms, Sitzungsberichte der Preussischen Akademie der Wissenschaften,
Physikalisch-Mathematische Klasse (1922) 253–257.
6. Murdoch J. Gabbay and Andrew M. Pitts, “A New Approach to Abstract Syn-
tax Involving Binders,” in Proceedings 14th Annual IEEE Symposium on Logic in
Computer Science, Trento, Italy, July 1999, IEEE Computer Society Press (1999)
214-224.
7. Yuri Gurevich, “Evolving Algebra 1993: Lipari Guide”, in Specification and Vali-
dation Methods, ed. by E. Boerger, Oxford University Press, 1995, 9–36.
8. Yuri Gurevich, “Sequential abstract state machines capture sequential algorithms,”
ACM Transactions on Computational Logic, to appear.
9. Thomas Jech, The Axiom of Choice, North-Holland (1973).
10. F. William Lawvere, “Variable quantities and variable structures in topoi,” in Al-
gebra, Topology, and Category Theory (A Collection of Papers in Honor of Samuel
Eilenberg), ed. by A. Heller and M. Tierney, Academic Press (1976) 101–131.
11. Eugene C. Luschei, The Logical Systems of Leśniewski, North-Holland (1962).
12. Anthony P. Morse, A Theory of Sets, Academic Press (1965).
13. Anand Pillay, Geometric Stability Theory, Oxford University Press (1996).
14. Saharon Shelah, Choiceless polynomial time logic: Inability to express, paper num-
ber 634, to appear.
15. Joseph Shoenfield, Mathematical Logic, Addison-Wesley (1967).
16. Edward E. Slaminka, “A Brouwer translation theorem for free homeomorphisms,”
Trans. Amer. Math. Soc., 306 (1988) 277–291.
17. Alfred Tarski, Andrzej Mostowski, and Abraham Robinson, Undecidable Theories,
North-Holland (1953).
Abstract State Machines and Computationally
Complete Query Languages
1 Introduction
Abstract state machines (ASMs) were introduced as a new computation model,
accompanied by the “ASM thesis” stating that any algorithm, or more bro-
adly, any computational system, at any level of abstraction, can be simulated in
lockstep by an ASM [7,13,14,15]. Recently, Blass, Gurevich, and Shelah (BGS)
introduced an instance of the ASM model for expressing queries to relational
databases [8].
Roughly, a BGS program is a complex rule, changing the values of certain
dynamic functions at various arguments during the run of the program. Rules are
built up from elementary updates by conditionals and parallel composition. The
program is iterated until a halting condition is reached. A powerful sublanguage
of terms provides set-theoretic operations on arbitrarily nested sets over the
input data elements. Once “activated,” these sets are incorporated in the run
of the program, and can become arguments and values of dynamic functions.
While any computable query can be expressed in BGS, the actual motivation
of BGS to introduce their model was to study the complexity class denoted by
e
CPTIME, corresponding to BGS programs under a polynomial time restriction.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 22–33, 2000.
c Springer-Verlag Berlin Heidelberg 2000
Abstract State Machines and Computationally Complete Query Languages 23
1
Abiteboul and Vianu used the name while invent in their paper [6], but use the name
while new in their book with Hull [1], so we use the latter name.
2
A program separates two classes K0 and K1 if it outputs ‘false’ on all structures in
K0 and ‘true’ on all structures in K1 .
24 A. Blass, Y. Gurevich, and J. Van den Bussche
seems to go without saying.3 New to our result are the programming with sets
and the added focus on polynomial time.
We conclude this introduction by mentioning some other related work. The
very first computationally complete query language was QL, introduced by
Chandra and Harel [9]. Because QL can be simulated in while new with only
a polynomial time overhead [6,19], our negative result concerning while new ap-
plies as well to QL. We also should note that the well-known object-creating
query language IQL, introduced by Abiteboul and Kanellakis [2], was set in a
complex-object data model with set values, where the distinction between tuples
and sets is blurred as one can always have a tuple with a set as a component. In-
deed, IQL is polynomial-time equivalent to while sets
new [19] and thus also to BGS.
Finally, we point out that interest in object creation in query languages has re-
cently resurged in the context of Web databases [12]. Current proposals in this
field introduce new data elements by constructing terms, and thus essentially
employ tuple-based invention.
2 Preliminaries
X := {(x1 , . . . , xj ) | ϕ}
Y := tup-new{(x1 , . . . , xj ) | ϕ}
{(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2), (3, 3)},
By adding set-new statements to the language while new , we obtain the lan-
guage while sets
new .
BGS takes a functional point of view: computing means updating the values of
certain user-defined, named, “dynamic” functions at various arguments. Argu-
ments and values can be elements of the domain D of the input structure, as
well as hereditarily finite sets built over D during the execution of the program.
Formally, the set HF(D) of hereditarily finite sets over D is the smallest set
such that if x1 , . . . , xn are in D ∪ HF(D), then {x1 , . . . , xn } is in HF(D). Every
dynamic function name has an associated arity r, and thus has, at any stage
of the computation, an interpretation (which can be updated in later stages) as
a function from (D ∪ HF(D))r to D ∪ HF(D). The extent of such a function f
is the set {(x̄, f (x̄)) | x̄ ∈ (D ∪ HF(D))r and f (x̄) 6= ∅}. At any stage of the
computation, the extent of the interpretation of any dynamic function will be
finite.
A number of static functions, which cannot be updated, are predefined: The
relations of the input structure are given as boolean functions. The usual logical
4
In SQL terminology this corresponds to grouping by the first column.
Abstract State Machines and Computationally Complete Query Languages 27
constants5 and functions (true, false, and, or, not, equality) are provided. Finally,
some set-theoretic constants and functions are provided: the empty set; the input
domain; set membership; set union; singleton extraction, and pairing. The input
domain is called ‘Atoms’. Union is unary, working on a set of sets.
Terms can now be built up from variables, constants, functions, and the set
constructor {t : v ∈ r : g}, where v is a variable that does not occur free in term
r but can occur free in term t and boolean term g. Variable v becomes bound
by the set constructor. The semantics is the obvious one of {t : v ∈ r and g}.
Finally, rules express transitions between states by updating the dynamic
functions. Elementary update rules are of the form f (t1 , . . . , tj ) := t0 , where f
is a dynamic function name (of arity j) and t1 , . . . , tj are terms. The semantics
is obvious. From elementary update rules more complex rules can be built by
conditionals and parallel composition. More specifically:
2.5 Examples
if Mode = 0 then
forall x ∈ Atoms do
Reached (x) := {x},
Paths(x, x) := {{x}},
Frontier (x) := {x}
enddo,
Mode := 1
endif,
if Mode = 1 then
forall x ∈ Atoms do
Old Frontier (x) := Frontier (x),
Frontier (x) := {y : y ∈ Atoms : y 6∈ Reached (x)
and {z : z ∈ Frontier (x) : E(z, y)} =
6 ∅}
enddo,
Mode := 2
endif,
if Mode = 2 then
forall x ∈ Atoms do
forall y ∈ Frontier (x) do
Paths(x, y) := {(p, y) : S
p ∈ {Paths(x, z) : z ∈ Old Frontier (x) : E(z, y)} : true}
enddo,
Reached (x) := Reached (x) ∪ Frontier (x)
enddo, S
Halt := {Frontier (x) : x ∈ Atoms : true} = ∅,
Mode := 1
endif.
In this section, we define what it means for two classes of structures over the
same vocabulary to be separable in polynomial time by BGS programs, or by
while new programs. We then prove that there exists a pair that is separable in
polynomial time by a BGS program, but not by any while new program.
During the run of a BGS program on a structure with domain D, a certain
number of sets in HF(D) are activated, meaning that at some point they appear
in the extent of some dynamic function. Elements of active sets are also con-
sidered to be active, and this holds recursively. Similarly, during the run of a
while new program on a structure, a certain number of new elements are inven-
ted. Activated sets and invented elements yield measures of space usage by BGS
and while new programs, which are quite rough, but sufficient for our purposes.
Equally rough measures of time spent by BGS and while new programs can be
defined as follows: the time spent by a BGS program on a structure is the num-
ber of times the program is iterated until the halting condition is reached; the
time spent by a while new program on a structure is the number of times an FO
or tuple-new statement is executed during the run of the program.
In the following two paragraphs fix two disjoint classes K0 and K1 of struc-
tures over a common vocabulary.
Let Π be a BGS program using a boolean dynamic constant Output for
output. We say that Π separates K0 from K1 if for any structure A ∈ K0 ∪ K1 ,
the value of Output in Π(A) is false if A ∈ K0 , is true if A ∈ K1 . We say
that Π separates K0 from K1 in polynomial time if moreover, there exist two
polynomials p(n) and q(n) such that for any A ∈ K0 ∪ K1 , Π runs on A for at
most p(n) time, and activates at most q(n) sets, where n is the cardinality of
the domain of A.
Similarly, let Π be a while new program having some relation variable Output.
We say that Π separates K0 from K1 if Π(A) is defined for any structure A ∈
K0 ∪ K1 , and relation Output in Π(A) is empty if A ∈ K0 , and is not empty if
A ∈ K1 . We say that Π separates K0 from K1 in polynomial time if moreover,
6
Recall that ordered pairs (x, y) are by definition in HF(D), as {{x}, {x, y}} [16].
30 A. Blass, Y. Gurevich, and J. Van den Bussche
there exist two polynomials p(n) and q(n) such that for any A ∈ K0 ∪ K1 , Π
runs on A for at most p(n) time, and invents at most q(n) elements, where n is
the cardinality of the domain of A.
Since we do not care what the programs do on structures outside K0 and K1 ,
the above notion of separation is quite liberal. Still, we will be able to obtain a
negative result regarding the separating power of while new in polynomial time.
Also, in our definition, it is important to polynomially restrict the space used as
well as the time, because in BGS or while new it is possible to use an exponential
amount of space even in an only linear amount of time.
We can prove (proof omitted):
(S(a), λm ),
sets, together with the elements of D, generate a finite subgraph of this graph.
The simulating while sets
new program will maintain a copy of that subgraph, where
the active sets are represented by invented elements, and the elements of D
are represented by themselves. The membership relation ∈ will be stored in a
relation variable Epsilon.
We now say that a while sets 0
new program Π simulates a BGS program Π if
for every input structure I, if Π(I) is defined then so is Π 0 (I), and for every
dynamic function name f of Π, say of arity r, there is an (r + 1)-ary relation
variable fb of Π 0 , such that fb in Π 0 (I) equals exactly the extent of f in Π(I),
under a representation of the active hereditarily finite sets by invented elements
as given in relation Epsilon in Π 0 (I). Moreover, we say that the simulation is
linear-step, polynomial-space if there exist a constant c and a polynomial p such
that for every input structure I where Π(I) is defined, the following holds. Let
the time for which Π runs on I be t, and let the number of sets activated during
the run be s. Then Π 0 runs on I for at most ct time, inventing at most p(s)
elements.8
We can show (proof omitted):
References
1 Introduction
The goal of this paper is to define asynchronous timed algorithms and their re-
finements. To illustrate our system of notions we study a concrete algorithm,
namely a real-time version of Lamport’s Bakery, within this framework. (In fact,
the notions we introduce, are intended to model more complex algorithms, com-
posed from many modules with non trivial asynchronous interaction.) Though
the classical notion of asynchronous algorithm does not have any metric time
constraints, practical implementation of such an algorithm usually cannot have,
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 34–49, 2000.
c Springer-Verlag Berlin Heidelberg 2000
On Verification of Refinements of Timed Distributed Algorithms 35
say, unlimited delays, and to be practical, some time constraints may be neces-
sary, at least at some level of refinement of such an algorithm. Moreover, the
time is very intuitive, and this is one of the reasons why time arguments are
largely used in reasoning about distributed algorithms.
What is more important concerning the time, is that it often appears in
initial requirements specification. So if we rewrite directly the initial specification
using time one can hardly require formal justification of this passage, but if
we eliminate time then, clearly, we modify the initial specification, and this
modification is to be justified. But formal justification will demand an adequate
notion of time to express what we are modifying. For example, Lamport in [7]
writes, giving a requirements specification of the critical section problem : ”At
any time, at most one computer may be in its critical section”. Clearly, we
can directly rewrite it as ∀ t (CS(t, p) → ∀ q 6= p ¬ CS(t, q)), where t is a time
variable, p and q are variables for processes and CS(t, r) is a predicate which
states that ”process r is in critical section at the moment t”. And that is how
we proceed here.
The paper [2] by E. Börger, Yu. Gurevich and D. Rozenzweig was a star-
ting point of our work. We use the methodology of Gurevich Abstract State
Machines (GASM) and not the exact notion from, say, [6]. More precisely, we
assemble in the style of GASM a minimal notion sufficient for our goal from
simple programming constructions starting from assignments as used in GASM.
The GASM methodology gives basic principles how to define semantics of such
algorithms. To be adequate to Lamport’s text [7] we introduce asynchronous
executions of our distributed algorithms with delays. To give to the algorithm
a real-time flavor we consider here restricted delays, and thus, slightly deviate
from the original Lamport’s Bakery.
Within our semantics the Lamport’s Bakery algorithm rewritten in our sy-
stem of notations can be proved to be correct following Lamport’s original proof.
However, our main goal is to define a notion of refinement of practical sig-
nificance and to study whether the verification proof for the non-refined initial
algorithm can be preserved for the verification of the refined one. In our defini-
tion of refinement we follow the idea of observational equivalence related to some
structural properties of the program. And for this notion the proof preservation
mentioned above takes place. Constructing a correct refinement remains a non-
trivial problem, and we illustrate it by giving a refinement of communications
in real-time Lamport’s Bakery. We also remark that a straightforward ‘local’
replacement of operators that we wish to refine by their refinements does not
give a correct refinement.
Though the notion of refinement was studied in many papers (e. g. see
[9,8,3,1,5]), our notion treats real-time algorithms, where the time can be used
without any restrictions and the refinement itself is being done with respect to
chosen pieces of the program. These properties are motivated by the analysis of
practical refinements and the verification problem, and all this differs our notion
from the notions of refinement considered in other papers. For example, in the
paper [8] the time is represented only as ticks. The properties of such systems are
36 J. Cohen and A. Slissenko
Introducing explicit time we pursue two goals: first, to give a unified and intuitive
view on semantics and verification proofs and, second, to give possibility to
introduce time constraints in specifications of algorithm when refining it or when
making precisions of initial formulation.
can be changed by algorithm. External functions that depend on time are input
functions. Internal functions of zero arity, also called nullary functions, play the
role of identifiers, as well as terms f (X) for a fixed value of its arguments X.
The mentioned arguments X can be viewed as parameters. Identifiers f (X) and
f (Y ) are different if X 6= Y .
Syntax.
T erm ::= V ariable | F unction(T erm1 , ..., T ermn ),
here and in the next line F unction is an n-ary function symbol.
InternalT erm ::= InternalF unction(T erm1 , ..., T ermn ).
F ormula ::= standard first order formula.
Guard ::= Formula without time or real variables. It must be either closed or
may have free variables ω bounded by ForAll ω ∈ Ω, see below the definition
of the operator.
Assignment ::= InternalT erm := T erm.
Asynchronous timed distributed algorithm over a vocabulary V is a pair of
the form (IniState, {P1 , . . . , PN }), where IniState is a formula defining initial
state, i. e. the values of all the abstract functions of V (at time 0 if a function has
a time argument), and each Pi is a program or process that is defined below. (We
use the word ”operator” where in programming languages one uses ”statement”
and where Yu. Gurevich [6] usually uses the word ”rule”.)
P rogram ::= Prog Operator EndProg
Operator ::= Assignment – defined above
| Seq Operator; . . . ; Operator EndSeq – sequential block
| Par Operatork . . . kOperator EndPar – parallel block
| If Guard Then Operator EndIf – conditional operator
| Repeat Operator Until Guard EndRepeat – repeat operator
Extensions of the syntax like
(1) Par ForAll ω ∈ Ω Do Op1 (ω)k . . . kOpk (ω) EndPar, where Ω is a finite
set, (2) Repeat Operator EndRepeat, have obvious meaning.
that the process has to send a query to another processes to get the necessary
information, that implies an external delay, and then to receive this information,
that contributes again to the external delay. Similar for guards.
Remark that we can eliminate Zero runs if we impose a positive lower bound
on delays.
The semantics is defined by a propagation of time moments of execution
of operators and by defining simultaneously the values of internal functions.
To simplify the presentation we will give first the rules of propagation of time
though, strictly speaking, some of them involve results of function evaluation.
Below P rog stands for P rogram, Op stands for Operator.
Time Propagation Rules.
Intuitive meaning of vertical arrows below is as follows: “Op ↓ (t)” means “t is
the termination time of the operator”, “↑ (t)Op” means “t is the start time of
the operator”. Double arrow is used when the algorithm takes values or change
values. So ↑ (t) goes down to an assignment, is changed to ↓ (t), and the latter goes
up to the end of the operator. Symbol ` means the expression at the left can be
rewritten as an expression at the right of `. Below t0 is arbitrary moment such
that t0 ≥int t, and t0 , t1 are arbitrary moments such that t1 ≥delay t0 ≥delay t.
• Given an algorithm (IniState, {P rog 1 , . . . , P rog N }) its initial time distribu-
tion is ` {↑ (0)P rog 1 , . . . , ↑ (0)P rog N }.
• The following transition says that the execution is finite and halts:
{P rog 1 ↓ (∞), . . . , P rog N ↓ (∞)} ` {P rog 1 , . . . , P rog N } ↓ (∞).
• ↑ (t)Prog ` Prog ↑ (t0 ). • ↓ (t)EndProg ` EndProg ↓ (∞).
• ↑ (t)Seq ` Seq ↑ (t0 ). • ↓ (t); ` ; ↑ (t0 ). • ↓ (t)EndSeq ` EndSeq ↓ (t0 ).
• ↑ (t)Par Op1 k . . . kOpn EndPar ` Par ↑ (t1 )Op1 k . . . k ↑ (tn )Opn EndPar
for any t1 , . . . , tn ≥int t.
• Par Op1 ↓ (t1 )k . . . kOpn ↓ (tn ) EndPar
` Par Op1 k . . . kOpn EndPar ↓ (t0 ) for any t0 ≥int max{t1 , . . . , tn }.
• ↑ (t)If ` If ↑ (t0 ). • ↓ (t)EndIf ` EndIf ↓ (t0 ).
• If ↑ (t)Guard Then Op EndIf ` If Guard (t0 ) Then Op EndIf .
• If Guard
(t) Then Op EndIf
If Guard Then ↑ (t0 )Op EndIf if Guard(t) is true,
`
If Guard Then Op EndIf ↓ (t0 ) otherwise.
• ↑ (t)Repeat ` Repeat ↑ (t0 ). • ↓ (t)Until ` Until ↑ (t0 ).
• Repeat Op Until ↑ (t)Guard EndRepeat
` Repeat Op Until Guard (t0 ) EndRepeat.
• Repeat
Op Until Guard (t) EndRepeat
Repeat ↑ (t0 ) Op Until Guard EndRepeat, if Guard(t) is false,
`
Repeat Op Until Guard EndRepeat ↓ (t0 ), otherwise.
• ↑ (t)θ := θ0 ` θ (t0 ) := θ0 . • θ (t0 ) := θ0 ` θ := (t1 )θ0 .
0 0 0
• θ := (t1 )θ ` θ := θ ↓ (t ).
Let θ = f (η1 , . . . , ηn ), where f is an internal function. To change the value of
θ we are to find the values of arguments η1 , . . . , ηn of f and the value of θ0 to
assign. We take all these values at t0 and change the value of θ at t1 ≥delay t0 .
On Verification of Refinements of Timed Distributed Algorithms 39
and the predicate T m[f, X, Tk ] which says that Tk is a moment to change the
value of f (X). By default, every element of the set V al is deleted from the set
after having been used.
– for each occurrence Assign of an assignment of the form f (η) := θ0 we
define a function Arg[Assign, Tk ] that gives X for which this assignment is to
change f (X) at Tk or later.
Time Propagation Rules are being applied to each occurrence of Tk in P(k).
To simplify the recursive definition, extend T to the left of 0 by, say, some
ε < 0, and set T0 = 0, take ρ(t) for t ∈ [ε, 0] as defined by the initial state, set
ρ+ (t) = ρ(0) for t ∈ [ε, 0) and P(0) = {↑ (0)P1 , . . . , ↑ (0)PN }. All the mentioned
sets are empty for T0 and predicate T m is false.
Suppose that Tk−1 < ∞, and all the mentioned functions and sets, have
been defined for Tk−1 for some k > 0. Let Tk = M inT m(P(k − 1)) be the
minimum of arrowed occurrences of time in P(k − 1). If Tk−1 < Tk then first
apply (UpDate) and then (Propagation) as described below, otherwise apply
directly (Propagation). The procedure (UpDate) is applied when all minimal
time occurrences with the same value have been eliminated – in this case the
time advances, and we make updates and extend all the values to the next
minimal time.
(UpDate): For all internal function f : X → Z and for all X ∈ X such that
T m[f, X, Tk−1 ] do: if #V al[f, X, Tk−1 ] > 1 then run is undefined for τ >
Tk−1 ; if V al[f, X, Tk−1 ] = {v} then set f ◦+ (τ ) = v, otherwise set f ◦+ (τ ) =
Lef tLimt→Tk−1 f ◦+ (t) for τ ∈ [Tk−1 , Tk ). Thus ρ+ (τ ) is extended for τ <
Tk . Now set ρ(τ ) = ρ+ (Tk−1 ) for τ ∈ (Tk−1 , Tk ]. After that, each used set
V al[f, X, Tk−1 ] becomes empty at Tk , and the others are extended to Tk .
(Propagation): If there is an occurrence of the form θ := (Tk )θ0 take it,
otherwise take any arrowed occurrence of Tk , and act according to the case:
(A.1) ↑ (Tk ) θ := θ0 . Replace this occurrence by θ (t0 ) := θ0 choosing arbitrary
t0 ≥delay Tk . The point Tk is the left end of interval of execution of the assignment
under consideration.
(A.2) θ (Tk ) := θ0 , where θ = f (η1 , . . . , ηn ). Let X0 = (η1 , . . . , ηn )[ρ(Tk )]
and v0 = θ0 [ρ(Tk )]. Add v0 to V al[f, X0 , Tk ] and set Arg[Assign, Tk ] = X0 ,
where Assign is the occurrence of θ := θ0 under consideration. Then choose any
t0 ≥delay Tk and replace (A.2) by θ := (t0 ) θ0 .
(A.3) θ := (Tk ) θ0 . Set T m[f, X0 , Tk ] and V al[f, X0 , Tk ] = V al[f, X0 , Tk−1 ]
for X0 = Arg[Assign, Tk−1 ]. Replace expression (A.3) by θ := θ0 ↓ (t0 ) for any
t0 ≥int Tk . The point t0 is the right end of interval of execution of the assignment
under consideration.
(B) Occurrences of ↑ (Tk ) and ↓ (Tk ) different from that of (A). Apply Time
Propagation Rules in a evident way. Set V al[f, X, Tk ] = V al[f, X, Tk−1 ] for all
X and Arg[Assign, Tk ] = Arg[Assign, Tk−1 ] for all Assign. The intervals of
execution of operators are defined also in a straightforward way, say, ↑ (t)Seq
gives the left end for the operator starting with this Seq, and EndSeq ↓ (t0 )
corresponding to this Seq gives the right end of the interval of execution.
On Verification of Refinements of Timed Distributed Algorithms 41
Lemma 1 In a total run the values of internal functions are piecewise constant
on intervals of the form (t, t0 ], t < t0 .
that it does not change. So we continue writing the formula: Guard◦ (T ) and
T ≤delay t and ∆ = U If Guard Then ↑ (t)Op EndIf V implies T AE(∆) and
Γ ` ∆ and the extension of V al, T m, Arg to t.
(S2) Γ = U θ (T ) := θ0 V , where θ = f (η1 , . . . , ηn ). Let v0 = θ0◦ (T ) and
X0 = (η1 , . . . , ηn )◦ (T ). Here, in addition to the discourse concerning ∆, T AE
and `, we set V al(f, X0 , T, v0 ) and Arg(Assign, T ) = X0 .
(S3) Γ = U θ := (T )θ0 V . Here in addition to defining T AE(∆) and Γ ` ∆
we say that for all v such that V al(f, X0 , T, v), where X0 = Arg(Assign, T ) and
Assign is the assignment under consideration, there take place T m(f, X0 , T )
and V al(f, X0 , T, v).
3 Refinement of Communication
3.1 Refinements
A refinement of a distributed algorithm is not a simple replacement of an opera-
tor by another one which is, in a way, more detailed. Though such a replacement
can take place, there may be other changes in the algorithm being refined. We
will see it later for Lamport’s Bakery. We define refinement semantically, in terms
of runs.
Assume that the runs under consideration are total, and the functions of the
vocabulary do not have udef as their value.
Let V ⊆ V1 be two vocabularies of the type described above. A run over
a vocabulary W is an interpretation of sorts and a mapping that give for any
t ∈ T a value of f (X ∗ ) for each f ∈ V : X → Z for every X ∗ ∈ X ∗ , where
X ∗ denotes the interpretation of sorts X . Now suppose that an interpretation of
sorts is fixed, that means in particular, that every sort in V is interpreted in V1
as in V .
Projection of a run ϕ1 over V1 onto V (notation: projV (ϕ1 )) is the run that
is the result of deleting from ϕ1 all identifiers of V1 \ V .
Let A and A1 be algorithms respectively over V and V1 .
We wish to compare runs of A and its refinement A1 modulo refined opera-
tors. As the latter are only in A but not in A1 we are to use some abstraction of
runs of A and that of A1 modulo some sequence of intervals (corresponding to
the intervals of execution of operators to refine) supplied with sets of identifiers
(corresponding to the identifiers being changed by these operators).
Operation W eed takes as its argument an interpretation ϕ of vocabulary
W (in particular, a run of A or that of A1 ) and a finite set of pairs Ω =
{(αi , Fi )}1≤i≤m , where each αi is a sequence of disjoint time intervals (in in-
creasing order) and each Fi is a finite set of identifiers of W different from input
or output ones. The value W eed(Ω, ϕ) is a mapping ϕ0 from time to values of ϕ
extended with udef obtained by the following procedure: for each interval αi (j)
set the values of each f (X) ∈ Fi equal to udef inside αi .
Consider a set S = {Op1 , . . . , Opm } of disjoint operators of A. Denote by
αi the sequence of intervals of execution of Opi and by Fi the set of identifiers
changed by Opi except the output ones. This gives the set Ω.
44 J. Cohen and A. Slissenko
Theorem 1 Strongly refined algorithm verifies the requirements iff the non-
refined one verifies them.
BakeryAsynp :
Initial values (IniState): numberp = 0, ¬ CS, xp [q] = 0.
Repeat
Seq
-- Doorway:
1: numberp := 1; xp [p] := 1;
2: Par ForAll q 6= p Do xp [q] := numberq ; EndPar
3: xp [p] := 1 + maxq {xp [q]};
-- Bakery:
4: numberp := xp [p];
5: Par ForAll q 6= p Do
Repeat xp [q] := numberq
Until xp [q] = 0 ∨ (xp [p], p) < (xp [q], q)
EndRepeat;
EndPar
-- Critical Section:
6: CSp ;
7: ¬CS p ;
8: numberp := 0
EndSeq
EndRepeat
So we wish to refine N umbersp and P riorityp for all p. In the algorithms descri-
bed above we may have simultaneous reads or writes. But simultaneous reads or
writes implicitly involves solving the problem of critical section, so we are in a
vicious circle which we wish to avoid. In Lamport’s algorithm there are two ty-
pes of parallelism, one concerns the interaction between processes, and the other
is the internal parallelism of one process. To manage the interaction between
the processes we will use a standard communication medium of asynchronous
distributed algorithms (based on send/receive messages).
Firstly, we introduce a new sort, namely Message. Each message µ is a
quadruple (message#, addressee, sender, contents), each component being of
46 J. Cohen and A. Slissenko
BakeryRf nPp :
Repeat
Seq
-- Doorway:
1: numberp := 1; xp [p] := 1;
2: Par ForAll q 6= p Do
2.1: send p,q := (q, p, ?);
2.2: Repeat zp,q := F irstQueue p,q Until Contents(zp,q ) ∈ N EndRepeat
2.3: xp [q] := Contents(zp,q );
EndPar;
3: xp [p] := 1 + maxq {xp [q]};
-- Bakery:
4: numberp := xp [p];
5: Par ForAll q 6= p Do
Repeat send p,q := (q, p, ?);
Repeat zp,q := F irstQueue p,q Until Contents(zp,q ) ∈ N EndRepeat
xp [q] := Contents(zp,q );
Until xp [q] = 0 ∨ (xp [p], p) < (xp [q], q)
EndRepeat;
EndPar
6–7: CSp ; ¬CSp ;
8: numberp := 0
EndSeq
EndRepeat
k
9: Par ForAll q 6= p Do
Repeat
9.1: wp,q := F irstQueue p,q ;
9.2: If Contents(wp,q ) =? Then send p,q := (q, p, numberp ) EndIf
EndRepeat
EndPar
where the intervals of execution of operators 2 and 5 for process p are respectively
[1, 2) and, say, [2.5, 3).
An execution of the ‘refined’ algorithm we consider is supposed to have the
same values for numberp , numberq , CSp and CSq except during these two
intervals of time. Therefore we must have numberq (t) = 0 and ¬CSq (t) for
t ∈ [0, 1) ∪ [2, 2.5) ∪ [3, ∞). To answer to the query of p during [1, 2), q must be
running operator 2 or operator 5. So q will execute operator 2 during [1, 2). In
order to have its number back to value 0, q must execute operators 3 − −8 before
time 2 : it is not possible because during that interval of time, numberp = 1 and
the loop in operator 5 cannot end.
References
1. M. Abadi and L. Lamport. The existence of refinement mappings. Technical
Report 29, DEC Systems Research Center, Palo Alto, California, August, 14
1988.
2. E. Börger, Y. Gurevich, and D. Rozenzweig. The bakery algorithm: Yet
another specification and verification. In E. Börger, editor, Specification and
Validation Methods, pages 231–243. Oxford University Press, 1995.
3. M. Broy. Compositional refinement of interactive systems. J. of the Assoc.
Comput. Mach, 44(6):850–891, 1997.
4. D. Beauquier and A. Slissenko. A first order logic for specification of timed
algorithms: Basic properties and a decidable class. 37 pages, 1999. To appear
in J. of Pure and Applied Logic.
5. G. Goos, A. Heberle, W. Loewe, and W. Zimmermann. On modular de-
finitions and implementations of programming languages. In Proc. of the
Intern. Workshop on Abstract State Machines (ASM’2000), March 20–24,
2000, Switzerland, Monte Veritá, Ticino, pages 174–208. ETH, Zürich, 2000.
6. Y. Gurevich. Evolving algebra 1993: Lipari guide. In E. Börger, editor,
Specification and Validation Methods, pages 9–93. Oxford University Press,
1995.
7. L. Lamport. A new solution of Dijkstra’s concurrent programming problem.
Communications of ACM, 17(8):453–455, 1974.
8. J. Ostroff. Composition and refinement of discrete real-time systems. ACM
Trans. on Software Engineering and Methodology, 8(1):1–48, 1999.
9. M. Wirsing. Algebraic specification. In J. van Leeuwen, editor, Handbook of
Theoretical Computer Science. Vol. B: Formal Models and Sematics, pages
677–788. Elsevier Science Publishers B.V., 1990.
Objects + Views = Components?
Martin Odersky
1 Introduction
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 50–68, 2000.
c Springer-Verlag Berlin Heidelberg 2000
Objects + Views = Components? 51
component will be used. As a simple example, consider the task of writing a class
for lists. Which methods should this class offer? A fairly small implementation
could be:
class List[a] = {
def head: a = ...
def tail: List[a] = ...
def isEmpty: Boolean = ...
}
Of course there are many more useful functions to be included in the List class.
Functions to select elements inside the lists, to form sublists, to concatenate
lists, to filter elements according to some criterion, or to map functions over all
elements come to mind. Furthermore, one might want to treat lists as represen-
tations of sets, in which case membership, union, intersection and set difference
also should be supported. And one can think of many other useful operations on
lists. The hard question is where to stop. Meyer [20] recommends a “shopping
list approach” to class design where essentially all useful methods one can think
of should be added to an abstraction. In the case of lists, this would probably
lead to many hundreds of methods, and even then clients of the list abstraction
will likely miss a method they require. It seems preferable to provide a more
compact list definition and let clients customize this definition as they require.
The criterion for a complete class design would then be that every useful method
for the class can be defined in terms of the public interface, not that the method
already is defined. The “can”-completeness criterion is much easier to fulfill; for
instance our simple implementation of lists above is already complete.
val SymTab {
type Scope
type Symbol = {
def name: String
def tp: Type
def location: Scope
...
}
def lookup (scope: Scope, name: String): Symbol = ...
def enter (scope: Scope, sym: Symbol): Boolean = ...
def newScope (outer: Scope): Scope = ...
}
a generic implementation. However, it’s not clear what attributes should go into
a symbol, as is illustrated by the ... in the Symbol type. Clearly, symbols will
have a “name” attribute. One might also settle on fields that contain a symbol’s
type and location. But there are also other potential attributes which depend on
the situation in which a symbol table is used. For instance, if our compiler has a
code generator, symbols will need a field in which to store their address. On the
other hand, if the compiler is coupled with a source code browser, we might need
an additional attribute in a symbol which contains the list of all usage points
of that symbol. Again, the precise definition of some aspect of the symbol table
component depends on the contexts the component is used.
In classical structured programming, where data structures and code are sepa-
rated, symbol tables would be adapted by changing their code, filling in the ...
in the Symbol type as required by the application. But source code adaptation
is impossible or at least undesirable in a component setting. Furthermore, the
necessary adaptations to the symbol table module also violate the encapsulation
principle, since fields needed only by the code generator module are placed in the
common Symbol data structure which is accessible to all. Hence, better solutions
for adaptation are called for.
The other requirement for components has to do with how flexibly they can
be composed. One usually regards the run-time embodiments of a component’s
interfaces as its “plugs”. In object-oriented languages plugs are often realized as
objects which have methods which are accessible to clients. A plug is first-class
if it can be treated like a normal value. In particular, one should be able to
pass plugs as parameters to functions, and it should be possible to construct
data structures with plugs as elements. As a very simple example for this kind
of flexibility, consider the task of displaying the information associated with a
symbol. Since there are so many different devices on which this information
could be displayed, it makes sense to split the task in two. Symbols only provide
a method toString, which converts the information associated with the symbol
to a string. In other words, symbols support the interface
type Printable = {
def toString: String
}
Then, a display device could provide a general display service for objects that
“know” how to turn their information into strings:
If dev was such a device and sym was a symbol it would then be possible to
write dev.print(sym). Of course, this assumes that symbols are values that can
be passed to the print function. In particular, the type Symbol must be compatible
with the type Printable.
Objects + Views = Components? 53
adaptable
first-class
OOP ASM
Objects + Views
Since adaptability and first-class plugs are natural requirements for software
components, one might expect that mainstream languages would provide sup-
port for both. Maybe surprisingly, this has not been the case so far. Figure 2
summarizes the state of the art, which will be further discussed in the next sec-
tions. Even though structured programming languages such as Ada or Modula-2
provide support for modules, which can be seen as an embodiment of compo-
nents, the components thus defined are neither adaptable, nor do they have
first-class plugs. Object-oriented programming leads to components with first-
class plugs, but these components are not adaptable. Conversely, the approach
taken by abstract state machines lends itself to the construction of components
which are adaptable, but these components do not have first-class plugs. More
advanced type systems and design patterns can in each case provide some of
the missing functionality. Refining object-oriented programming with bounded
genericity can provide adaptability to some degree, whereas refining functional
programming and ASM’s with a construct such as Haskell’s type classes can
provide some of the benefits of first-class plugs.
This paper presents views as a simple technique to provide both adaptability and
first-class plugs. The technique uses standard object-oriented modeling techni-
ques to model first-class plugs. A view is a syntactic construct which provides
new methods for objects of an existing class, thus providing implementations for
additional object interfaces. Views that extend a component can be distributed
over arbitrary client components.
54 M. Odersky
The abstract state machine approach reverses the usual relationship between
functions and data. Rather than having mutable data structures which are ope-
rated on by immutable functions, we have immutable data, which are operated
on by both mutable and immutable functions. This makes use of the following
analogy between function application and selection.
f (x) ≡ x.f
In other words, a data selector can be seen as a single argument function over
the selected data domain and vice versa. This equivalence makes sense on the
left-hand sides of assignments as well:
f (x) := E ≡ x.f := E
class List[a] = {
case Nil
case Cons (head: a, tail: List[a])
}
All operations over lists can be expressed in terms of pattern matching. Hence, a
list operation can be placed in an arbitrary module, and new list operations can
be added in clients of the list abstraction as needed. In a word, lists designed as
algebraic data types are adaptable. This might be too trivial to mention except
for the fact that lists represented as objects which export all their methods are
not adaptable in the same way, as clients cannot add new methods to an existing
list. This point will be further discussed in the next section.
The abstract state machine approach also ensures adaptability for mutable func-
tions. For instance, consider the situation where a symbol table module as given
in Figure 1 is adapted to work with a code generator client. The code generator
needs to maintain with each symbol its address. This could be achieved by de-
fining a mutable function from symbols to their addresses in the code generator
module:
The := 0 part in the declaration above defines the initial value of the mutable
function to be 0 over all of its domain. The code generator module could then
enter a symbol’s address by an indexed assignment such as
This states that a type T belongs to the type class Printable if there is a function
toString, which takes a value of type T and yields a value of type String. Types
become members of type classes by explicit instance declarations. Example:
print :: Printable a ⇒ a → ()
print x = ... toString (x) ...
This defines a function print which takes as parameters values of type a, where a
is an arbitrary instance of type class Printable. The call to toString in print will
invoke the method which is appropriate for the concrete type to which the type
variable a is instantiated. For example, if sym is a Symbol then the type variable
a is instantiated to Symbol and print (sym) invokes the toString method given in
1
We use Haskell syntax except for writing type class where Haskell uses class.
Objects + Views = Components? 57
3 Object-Orientation
class SymTab = {
class Symbol <: Printable = {
... (fields as before) ...
def toString: String = name ++ ”:” ++ tp.toString
}
}
Are objects also adaptable? One might think so, because with inheritance it
is possible to define a subclass which adds new fields to an existing class. For
instance, it is possible to define in the code generator class CodeGen a new
class Symbol which inherits from the original symbol class as defined in module
SymTab:
class CodeGen = {
class Symbol <: SymTab.Symbol = {
var adr: Int
}
...
}
Instance objects of class CodeGen.Symbol have all the fields of original symbols,
plus the mutable adr field. So it seems we have managed to adapt symbol ta-
ble modules to a code generator client. But closer inspection shows us that this
is not really the case. There are two problems with adaptation through inhe-
ritance. One concerns types, the other object creation. Looking at types first,
we notice that function lookup in our symbol table module still returns objects
of type SymTab.Symbol, not objects of type CodeGen.Symbol. So the added adr
information does not show up in the type.
class Symtab {
...
class Symbol ...
def lookup (scope: Scope, name: String): Symbol
...
}
To access the adr field of a code generator symbol stored in the symbol table,
we need a dynamic type cast. In other words, we can gain adaptability only
by subverting the type system, and running the risk of dynamic type errors.
A safer alternative makes use of type parameterization. For instance, here is a
re-formulation of the symbol table class using bounded genericity:
The class for symbol tables is now parameterized by a type variable s which
represents the actual type of symbols stored in the table. The only requirement
Objects + Views = Components? 59
The other problem with achieving adaptation with inheritance has to do with
object creation. Taking our symbol table example, we note that we have not
looked yet at code which creates symbols to be stored in the table. Let’s say
symbols are created during tree attribution, in class Attr:
class Attr = {
...
new Symbol(name, tp)
...
}
This is not yet precise, because we have not yet specified which symbol should
be created during tree attribution. If it is the original SymTab.Symbol that is
created, we are missing an adr if we want to couple tree attribution with code
generation. On the other hand, if it is a CodeGen.Symbol which is created, we
have hardwired the relationship between tree attribution and code generation.
Now it would be the tree attribution part which lacks adaptability, because it
would not be able to deal with different kind of symbols, for instance those
required by a source code browser. To overcome this problem, we can make use
of the Factory design pattern [7]. Factory objects would have an interface of the
form:
type Factory [t] = {
def make (...): t
}
There would have to be an appropriate factory for every subtype of symbol which
is being defined. E.g. in class CodeGen:
class CodeGen = {
...
val SymFactory: Factory[Symbol] = {
def make(name: String, tp: Type): Symbol =
new CodeGen.Symbol(name, tp)
}
...
}
The Attr class will be parameterized with the factory which is to be used for
symbol creation. We also have to parameterize Attr with the actual type of
symbols to be created. This yields:
60 M. Odersky
The preceding discussion has shown that it is possible to obtain a certain de-
gree of adaptability in object-oriented designs, but this requires planning. If we
had not foreseen the possible later extension of the symbol type, we would not
have provided the necessary parameters for subtypes and factories. The hooks
necessary for enabling future extensions can clutter programs considerably, in
particular if we envisage multiple coexisting extensions. For example, the current
compiler framework would enable a single extension of Symbol, say for a code
generator or a browser, but it would not yet enable multiple coexisting exten-
sions, say where a symbol has both an adr field for a code generator and a uses
field for a browser. Multiple extensions can be supported by stacking extensions
on top of each other but this requires even more complex protocols.
4 Views
In the previous two sections, we have discussed two program structuring me-
thodologies which have complementary strengths. Each methodology supports
one of our two criteria of adaptability and first-class plugs, but supports the
other only incompletely and at the price of complex language features and de-
sign patterns. In this section, we propose a more symmetric combination which
can address both criteria equally well. This combination arises by translating
concepts first developed for type classes into an object-oriented setting.
When comparing type classes in functional programming and object-oriented
type systems, we find some strong analogies:
A view takes the form of an unnamed function, which takes a single parameter
of some type A and yields an object of type B. The view establishes a subtype
relationship between A and B by giving declarations of all fields of B which are
not yet present in A. All fields of A are inherited; i.e. they form implicitly part of
the resulting object. It is only possible to add new fields to a class with a view,
not to change existing fields.
Subtype Clauses
Subtype clauses can be regarded as syntactic sugar for view declarations with
empty bodies. For instance, our previous class declaration
would be equivalent to
62 M. Odersky
class Symbol = {
...
def toString: String = name ++ ”:” ++ tp.toString
}
view (this: Symbol): Printable = {}
Variables in Views
Besides methods, views can also define mutable variables. Here is an example:
The only difference concerns selection syntax – we use the object-oriented version
sym.adr instead of the previous adr(sym).
Encapsulation
type Adr = {
def setAdr (x: Int): ()
def getAdr: Int
}
view (sym: SymTab.Symbol): Adr = {
var adr: Int
def setAdr (x: Int) =
if (x % 2 == 0) adr := x
else error (”bad address”)
def getAdr = adr
}
Objects + Views = Components? 63
This view defines a variable adr along with setter and getter methods. Since the
variable is not part of the view’s target type Adr, it is not accessible to clients
of the view.
Conditional Views
type Eq [a] = {
def equals (other: a): Boolean
}
The [a <: Eq [a]] construct introduces a type parameter a for the view which is
bounded by Eq[a]. The type variable can hence be instantiated to any type T
which is a subtype of the bound Eq[T]. Note that this gives us a parameterized
type which has or lacks method equals, depending on whether or not the type of
the listed elements has the same method.
Semantics
The rest of this section discusses detailed design and implementation choices for
views. The most fundamental design choice concerns name resolution of fields
defined by views. Such resolution can be static, based on compile-time types or
it can be dynamic, based on run-time types. To see the difference, consider a
variant of our symbol table code. Assume that we want to further refine the type
of symbols into variable symbols and function symbols:
64 M. Odersky
type Symbol = {
... (fields as before) ...
}
type VarSymbol <: Symbol = { ... }
type FunSymbol <: Symbol = { ... }
In the code generator, one might need a function loadAdr which returns the
instruction for loading a given symbol’s address. The result of this function
will depend on the kind of symbol encountered. It is therefore reasonable to
implement loadAdr as a method of type Symbol, to be overridden in subtypes.
But as before, we want to keep the source code in Symtab unchanged. Again as
before, we resort to views for implementing the new functionality in the code
generator client. Instead of a single view we now need three views, one for each
kind of symbol:
type Loadable = {
def load: Code
}
view (sym: Symbol): Loadable = {
def loadAdr: Code = error (”can’t load”)
}
view (sym: VarSymbol): Loadable = {
def loadAdr: Code = LLA (sym.adr)
}
view (sym: FunSymbol): Loadable = {
def loadAdr: Code = LCA (sym.adr)
}
Can this work? Assume that the code generator calls the loadAdr method of
a symbol it retrieved (via lookup) from the symbol table. The static return
type of lookup is Symbol. So if we perform name resolution based on the static
type, the loadAdr will always result in error(”can’t load”). In an object-oriented
language such behavior would be counter-intuitive. We therefore pick dynamic
name resolution, where the loadAdr method is chosen according to the run-time
type of the symbol returned from lookup. If the returned symbol represents a
variable, we invoke the loadAdr method of the view for VarSymbol. If the symbol
represents a function, we invoke the loadAdr method of the view for FunSymbol
instead. Only if the returned symbol is neither a variable nor a function, the
loadAdr method of the view for the base class Symbol is invoked.
In the last example the call to loadAdr was resolved as if loadAdr was a regular
method in Symbol, which was overridden in VarSymbol and FunSymbol. We can
indeed often view a program with views as equivalent to a program with multiple
inheritance where all views are inlined in their base classes. View methods then
Objects + Views = Components? 65
become regular methods and target types of views become elements of subtyping
clauses. The correspondence with multiple inheritance programs serves as a guide
in the definition of the semantics of views and the context-dependent syntax rules
governing them.
The context-dependent syntax rules are based on the concept of the view graph.
The view graph V G of a project has as nodes the set of types defined in all
source files of the project and as edges the set of views between types. Subtyping
clauses are also interpreted as view edges. Furthermore, we regard the methods
defined in a class as defined in an additional view which goes from a new node
representing a class implementation to the proper class type itself. A field which
occurs textually in a record type T is said to be defined by T . A field which
occurs textually in a view V is said to be implemented by V . The subtyping
ordering ≤ on classes induces an ordering on views as follows: A view from S to
T precedes a view from S 0 to T 0 if S ≤ S 0 . We then require the following:
It is worth noting that some of these rules are global in that they require know-
ledge of the complete view graph. To check such global requirements in a system
with separate compilation, one could delay some type checking until link time.
As an alternative one could also maintain a project-wide repository in which
all current view declarations are maintained. Consistency of the view graph can
then be checked incrementally, as views are added and deleted.
5 Related Work
case that different aspects of a program are defined in separate source files which
are then merged using a “weaver” tool. Binary component adaptation [16] provi-
des mechanisms to perform similar changes on Java class files. Both weavers and
binary component adaptation are possible approaches for implementing views.
Compared to these approaches, views are more restrictive in that they support
only strict extensions of existing classes. One advantage of this restriction is that
strict extensions commute, so that the semantics of the weaver tool becomes a
non-issue.
Multi-methods [1,4] are another approach to extend the limitations of pure
object-oriented programming. Here, a method is no longer attached to a single
class, and method selection takes the dynamic type of all method arguments
into account. Like views, multi-methods can be defined independently of their
argument classes. But multi-methods are more general than views in that they
abandon the concept of a distinguished receiver argument and in that they re-
quire multiple dispatch. Open objects [19] are an idiom of multi-methods which
is comparable to views.
Like views, Haskell’s type classes allow a distributed definition of functionality
over data types. Qualification of type variables in a qualified type corresponds
to F-bounded qualification of views [3]. A number of different designs for type
classes have been developed [8,22,12,13,14]. Our view proposal is most closely
related to parametric type classes [5], in that the bound of a type variable a may
have additional parameters other than a itself. Type classes are usually studied
in systems without subtyping. Where subtyping is added [15], name resolution
is based on static types, whereas views are based on dynamic types.
A language construct called “view” has also been proposed for functional pro-
gramming [28]. These views serve as alternative decompositions of sum types
such as Haskell’s algebraic data types. By contrast, the views presented here
provide alternative accesses to product types such as records or objects. Both
designs have in common that a view can be defined in program parts other than
the one which defined its base type.
6 Conclusion
Do objects plus views equal components? This paper has argued that truly reus-
able component libraries require the ability to adapt components by extending
them with new functionality, and to compose components via first-class plugs.
Object-oriented programming and abstract state machines each provide one of
these two keys to successful composition. The view construct aims at providing
both of these keys.
Work is currently underway on a complete design and implementation of views
in the context of Funnel, an implementation language for functional nets. Only
future experience will tell whether the two keys are sufficient for the construction
of truly reusable component libraries. That’s why the title of this paper still ends
in a question mark.
Objects + Views = Components? 67
References
1. D.G. Bobrow, K. Kahn, and G. et al. Kiczales. CommonLoops — merging Lisp
and object-oriented programming. In Proc. OOPSLA’86, pages 17–29, 1986.
2. Don Box. Essential COM. Addison Wesley, 1998.
3. Peter Canning, William Cook, Walter Hill, Walter Olthoff, and John C. Mitchell.
F-bounded polymorphism for object-oriented programming. In Functional Pro-
gramming Languages and Computer Architecture, pages 273–280, September 1989.
4. Craig Chambers. Object-oriented multi-methods in Cecil. In Proc. ECOOP, 1992.
5. Kung Chen, Paul Hudak, and Martin Odersky. Parametric type classes. In Proc.
ACM Conf. on Lisp and Functional Programming, pages 170–181, June 1992.
6. Robert Englander. Developing Java Beans. O’Reilly & Associates, 1997.
7. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns
: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994.
8. Cordelia Hall, Kevin Hammond, Simon Peyton Jones, and Philip Wadler. Type
classes in Haskell. In Proc. 5th European Symposium on Programming, pages 241–
256, 1994. Springer LNCS 788.
9. William Harrison and Harold Ossher. Subject-oriented programming (a critique
of pure objects). In Proc. OOPSLA, pages 411–428, September 1993.
10. Urs Hölzle. Integrating independently-developed components in object-oriented
languages. In Proc. ECOOP, volume 707 of Springer Verlag, Lecture Notes in
Computer Science, pages 36–56, 1993.
11. Paul Hudak, Simon Peyton Jones, and Philip Wadler. Report on the programming
language Haskell: a non-strict, purely functional language, version 1.2. SIGPLAN
Notices, 27(5), May 1992. more recent versions at www.haskell.org/definition.
12. Mark P. Jones. A theory of qualified types. In Proc. 4th European Symposium on
Programming, pages 287–306, February 1992. Springer LNCS 582.
13. Mark P. Jones. A system of constructor classes: Overloading and implicit higher-
order polymorphism. In Proc. ACM Conf. on Functional Programming Languages
and Computer Architecture, pages 52–61, June 1993.
14. Mark P. Jones. Type classes with functional dependencies. In Proc. European Sym-
posium on Programming, number 1782 in LNCS, pages 230–244. Springer Verlag,
March 2000.
15. Stefan Kaes. Type inference in the presence of overloading, subtyping, and re-
cursive types. In Proc. ACM Conf. on Lisp and Functional Programming, pages
193–204, June 1992.
16. Ralph Keller and Urs Hölzle. Binary component adaptation. In Proc. European
Conference on Object Oriented Programming, Lecture Notes in Computer Science.
Springer Verlag, 1998.
17. Gregor Kiczales, John Lamping, Anurag Mendhekar, Chris Maeda, Cristina Videira
Lopes, Jean-Marc Loingtier, and John Irwin. Aspect-oriented programming. In
Proc. of the European Conference on Object-Oriented Programming, Finland, June
1997.
18. Konstantin Läufer. Type classes with existential types. Journal of Functional
Programming, May 1996.
19. Todd Millstein and Craig Chambers. Modular statically typed multimethods. In
Proc. ECOOP, number 1628 in LNCS, pages 279–303. Springer Verlag, June 1999.
20. Betrand Meyer. Object-Oriented Software Construction. Prentice-Hall, 1988.
21. Mira Mezini, Linda Seiter, and Karl Lieberherr. Component integration with plug-
gable composite adapters. In Software Architectures and Component Technology.
Kluwer, 2000.
68 M. Odersky
22. Tobias Nipkow and Christian Prehofer. Type checking type classes. In Proc. 20th
ACM Symposium on Principles of Programming Languages, pages 409–418, 1993.
23. Martion Odersky. Programming with variable functions. In Proc. International
Conference on Functional Programming, September 1998.
24. Martin Odersky. Functional nets. In Proc. European Symposium on Programming,
number 1782 in LNCS, pages 1–25. Springer Verlag, March 2000.
25. Alan Pope. The Corba Reference Guide: Understanding the Common Object Re-
quest Broker Architecture. Addison Wesley, 1998.
26. Clemes Szyperski. Component Software : Beyond Object-Oriented Programming.
Addison-Wesley, 1998.
27. P. Tarr, H. Ossher, W. Harrison, and S. ”Sutton Jr.”. N degrees of separation:
Multi-dimensional separation of concerns. In Proc. ICSE, pages 107–119, 1999.
28. Philip Wadler. Views: a way for pattern matching to cohabit with data abstrac-
tion. In 14’th ACM Symposium on Principles of Programming Languages, Munich,
Germany, January 1987.
29. Philip Wadler and Stephen Blott. How to make ad-hoc polymorphism less ad-hoc.
In Proc. 16th ACM Symposium on Principles of Programming Languages, pages
60–76, January 1989.
30. Gio Wiederholt, Peter Wegner, and Stefano Ceri. Towards megaprogramming.
Communications of the ACM, 1992. November.
Xasm– An Extensible, Component-Based
Abstract State Machines Language
Matthias Anlauff
Abstract. The Abstract State Machine (ASM) [16] approach has al-
ready proven to be suitable for large-scale specifications of realistic sy-
stems [18,9,22,34]. Due to the fact that the ASM approach defines a
notion of executing specifications, it provides a perfect basis for a langu-
age, which can be used as a specification language as well as a high-level
programming language. However, in order to upgrade to a realistic pro-
gramming language, such a language must – besides other features – add
a modularization concept to the core ASM constructs in order to pro-
vide the possibility to structure large-scale ASM-formalizations and to
flexibly define reusable specification units. In this paper, the language
Xasm, which stands for Extensible ASM, is presented. Xasm realizes a
component-based modularization concept based on the notion of exter-
nal functions as defined in ASMs. This paper also briefly describes the
support environment of Xasm consisting of the Xasm-compiler transla-
ting Xasm programs to C source code, and the graphical debugging and
animation tool.
1 Introduction
The Abstract State Machine approach has been and is successfully used to model
a large number of case studies including industry-relevant ones. The simplicity of
the basic data and execution model of ASMs makes them perfectly suitable as the
basis for a language that on the one hand can be used as specification language
and on the other hand as a high-level programming language. In this paper,
the Xasm (Extensible ASM )1 language is presented which aims at providing
support for using ASMs as a programming language for producing efficient and
reusable programs. There exists a number of other ASM implementations which
all implement most of the ASM constructs as defined in the Lipari-Guide [16].
While the realization of the ASM constructs can be seen as the core functionality
which must be present in each ASM support system, the difference of an ASM
system compared to all others can be characterized by
1
formerly known as “Aslan”; the name has been changed because of a name conflict
with another tool.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 69–90, 2000.
c Springer-Verlag Berlin Heidelberg 2000
70 M. Anlauff
– its efficiency,
– the functionality of its support environment,
– its rule abstraction concept, and
– its interoperability with other languages and systems.
For example, all ASM implementations – including Xasm– define some macro
structures on top of the core ASM language in order to provide some kind of rule
abstraction concept. These additional features are indispensable for managing
large formalizations. In the ASM-Workbench [13], for instance, the a special
“Rule” construct is introduced being used to assemble ASM specifications from
smaller pieces.
Concerning these features, Xasm combines the advantages of using a formally
defined method with the features of a full-scale, component-based programming
language and its support environment.
The paper is organized as follows: In Section 2 an overview of Xasm is given.
Section 3 introduces the component-based module concept of Xasm, in Section 4
the external language interface of Xasm is described. In Section 6 the possibility
to specify the syntax of input languages using context-free grammar definitions
is presented, which is followed by the description of non-standard language con-
structs defined in Xasm in Section 5. Section 7 sketches the support environment
of Xasm; Section 8 contains concluding remarks and points out future work.
2 Overview of Xasm
Xasm is an implementation of sequential ASMs focusing on the generation of
efficient executable programs simulating the run of the specified ASM. In general,
the main design goals of Xasm can be given as follows:
– full support of the ASM language as defined in the Lipari-Guide;
– efficient execution of generated executables;
– comfortable animation and debugging of ASM specifications;
– component-based library concept for managing large-scale specifications;
– external language interface for integrating ASM specifications in other sy-
stems.
The scenario of building ASM-based programs using Xasm is depicted in Fi-
gure 1. Xasm source files are translated into C source by the Xasm-compiler.
Additionally, the user can integrate C-sources and -libraries using the external
language interface. As described below, Xasm introduces a notion of compo-
nents being stored in a special repository. During the translation process, the
Xasm-compiler retrieves registry information from the component in order to in-
tegrate pre-compiled Xasm-components in the current build process. The result
of such a build process is a binary being either an executable or a new element of
the component library. In either case, the binary contains the ASM algorithms
specified in the Xasm source files.
Basically, Xasm-programs are structured using “asm . . . endasm” con-
structs each of which containing a list of local function and universe declarations
Xasm– Extensible, Component-Based ASMs 71
Xa
sm
-S
ou Xasm-
rc Compiler
e
Registry
Information
ge -S
C
ne ou
ra rc
te e
d
C-
So
Xasm Component
ur
Library
ce
C-Compiler&
Linker
C-libs&
obj.files
Executable
and a list of ASM rules representing a certain part of the overall specification.
In general, the structure of an Xasm-asm is shown in Figure 2. The meta infor-
mation part contains information concerning the role of the asm as a reusable
component; this part is described in more detail below.
As defined in the Lipari-Guide, types are not part of the core ASM language.
However, because typing has been proven to be very useful to avoid many kinds
of errors, in Xasm types can be supplied to the declaration of a function and
are used to detect static semantics inconsistencies of the formalization.
asm A(a1 : T1 , . . . , an : Tn ) → a0 : T0
hasm meta informationi
is
huniverse, function, and subasm declarationsi
hinitialization rulesi
hasm rulesi
endasm
to provide a basis for writing ASM formalizations that can be re-used in other
formalizations.
Therefore, Xasm uses a more powerful modularization concept which is based
on the notion of a component as it is used in component-based systems (e.g. [28,
31]).
any step; from A’s point of view asm step call as function
B’s run happens in zero time. As
depicted in the figure, B behaves like a “normal” asm, the iterations shown here
are caused by the steps of the B-asm itself.
In each of the above cases, we call A the parent-asm of B, if A uses B as
sub-asm or as function. In any case, the asm must be declared in the parent
asm. As part of its meta information, an asm can be marked as a function or
as a sub-asm, so that it can only be used by other asms in the specified way.
For example, if B and C are asms defined as follows
then B can only be used as function and C as sub-asm in other asms. This
is reflected by corresponding declarations of B and C:
asm A
is
subasm C(x : Int)
external function B(x : Int) → Int
...
endasm
Example
A typical situation for using sub-asms is given, when the specification can be
split up naturally into several sub-specifications each of which modeling a certain
aspect of the overall specification.
74 M. Anlauff
In this case, the specification introduces the notion of a mode which can
be used to structure the formalization. In the example, it is assumed, that the
sub-asms update the value of the mode function to some value.
Note, that these function works correctly without recursively calling itself; it
iterates until no update changes the internal state of the asm.
The “accesses” construct is used to specify the functions the asm expects
from its parent asm. Now, with this additional meta information, the asm can
be regarded as a component, because its provides information necessary to be
processed as stand-alone unit. The asm can be separately compiled and put into
the Xasm component library; other formalization can reuse it provided that they
declare the required functions.
Besides the “accesses” construct, which allows to read the locations of the
corresponding functions provided by the parent asm, the Xasm “updates”
construct marks the corresponding function as readable and writable for the
sub-asm or ASM function. In the previous example, the mode function must be
marked as “updated” the two sub-asms, because it is updated in the body of
each of them:
Like the accessed functions, the updated functions must be declared in the
parent asm. In order to avoid repetitions in the source code, the notation
“used as subasm in A” can be used as an abbreviation of accessing all fun-
ctions and universes declared in A except those that are explicitly marked as
“updated” by the sub-asm (analogously for asms that are used as functions).
76 M. Anlauff
asm A is
...
function currentmodule → Str
function SymT able(mod : Str, block : Str, v : Str) → Int
external function check blockvar(b : String, v : Str) → Bool
with DeclT able(b : Str, v : Str) ==
SymT able(currentmodule, b, v)
...
endasm
– For each function f being marked as “updated” a local function with the
same name is (internally) declared in B;
– this local function is initialized with the values of the original function in A;
– during the run of B, the functions marked as “updated” can be accessed like
any other local function in B;
– on termination of B the updated locations of these function are propagated
to the original function declared in the parent asm A.
This ensures, that all updates of an “updates” function are accessible in B, and
that only the last updates are forwarded to the parent A. In general, the updates
of functions declared in A and updated in B are treated as being part of the
update set of A’s current step. As a consequence, multiple invocations of B in
the same step of A do not influence each other.
Consider the following – somewhat artificial – example:
asm B → Int
used as function
updates function f (x : Int) → Int
asm A is is
function f (x : Int) → Int function i ← 0
function v → Int function r
external function B → Int if i < 3 then
... r := f (0)
v := B f (0) := i
... i := i + 1
endasm else
return r
endif
endasm
In each step of the run of asm B the value of the updated function f (0) is
updated with a new value. The semantics of the “updates” declaration in Xasm
ensures that all updates of f are accessible in B and that only the last update of
f in B is propagated to the parent asm A. In this example, the update f (0) := 2
78 M. Anlauff
asm A is
relation checkok
external [output] function error → Str
with ok f lag == checkok
...
error := ”...”
...
endasm
The value that is used for updating the external function can be accessed
using the named result parameter msg. The use construct includes pre-defined
header files containing function declaration that are in this case used to declare
the external functions stdout, stdin, and stderr.
3
in step 1: f (0) := 0; in step 2: f (0) := 1
4
that means that in the second step the update r := 0 is performed in B, in the third
step r := 1
Xasm– Extensible, Component-Based ASMs 79
3.6 Scoping
In the context of use-relations between asms, Xasm distinguishes between the
parent-asm and the caller-asm:
In the easiest case, parent and caller are the same, as in the above example: asm
error is declared in and called by asm A. In the following example, this is not
the case:
asm A is asm error → msg : Str
relation checkok used as function
external [output] function error → Str updates
with ok f lag == checkok relation ok f lag
subasm B is
... use stdio
B stderr := msg
... ok f lag := f alse
endasm endasm
asm B
updates function error → Str
is
error := ”...”
endasm
The arguments to this function are given as strings which are parsed and
transformed to corresponding “ASMOBJs”. The actual invocation of the main
asm can be made using the C-function “run mainasm”:
ASMOBJ run mainasm();
This kind of embedding is actually used in the implementation of the Xasm-
compiler itself: the static semantics check is carried out by an algorithm specified
in Xasm.
A number of external C-functions are already integrated into the runtime
system. For example, external functions to communicate using UNIX-Sockets,
string manipulation functions, file access functions etc. .
introduces the constructors nil, cons, empty, and children, where terms con-
structed using the latter two constructors are elements of the universe BinT ree.
Xasm also provides pattern matching functionality like it is used in many
other languages. Syntactically, pattern matching terms are used as condition
terms in conditionals, e. g.: 5
s =∼ ’ˆ[A-Z]’
5
Xasm uses a special syntax for pattern matching variables and equality symbol
84 M. Anlauff
In this example, the regular expression contains two sub-matches, the first one
matches the string "A", the second one the string "nyString".6 The submatches
can be accessed in the then-part of the conditional rules as values of the pattern
matching variables &hd and &tl.
8 Conclusion
In this paper, the ASM based language Xasm has been presented focusing on
the additional features provided by the language with respect to the ASM core
concepts as defined in the Lipari Guide. A novel concept for structuring ASM
specifications based on the notion of components has been presented. This con-
cept perfectly fits into the basic model of the ASM approach, because it allows
to choose the level of abstraction for describing that fits best to a given pro-
blem without regarding technical constraints. Furthermore, this concept allows
Xasm– Extensible, Component-Based ASMs 87
in the repository system, so that they can be access the next time the Xasm
is executed. This kind of extension is currently part of work carried out in an
industry-based research project running at GMD In this project, it is currently
considered to use Xasm for formulating certain consistency check algorithms
occurring in the context of this project.
Additional applications outside the ASM area are possible, since ASMs can
be considered as an instance of so called transitions system models, which form
as well the basis for other popular formalisms such as UNITY [11], TLA [23],
SPL [24] and the SAL intermediate language [29]. As mentioned in Section 6 the
Xasm system is integrated with the Montages method so that new or alternative
constructs can be easily supported by Xasm. Using Montages, both syntax and
semantics of new or alternative Xasm constructs can be developed in an inte-
grated development environment called Gem-Mex. Such an extensible system
architecture allows the Xasm tool to be tailored to one of the above-mentioned
formalisms based on transition systems.
An adapted version of Xasm would be especially useful in a context like
the SAL architecture [7], where various other tools are integrated for tasks like
theorem proving and model checking. The development cycle supported by the
tools integrated with SAL could be extended with a tool like Xasm for debugging
and animation of the transition system under consideration. After the developer
gains confidence in his specification by debugging and animating them with
Xasm, other tools can be used to further analyze the specification. As a result of
the further analysis, the original specification typically undergoes major changes.
To avoid simple errors introduced by these changes, the specification can again
be debugged and animated using Xasm before it is passed to the other tools.
We plan to adapt Xasm to architectures like SAL in order to make it ac-
cessible to a large base of users. We can thus adapt the Montages method to
formalisms like SAL by providing a language semantics with SAL. A specific
language may then be developed so that the SAL semantics of programs written
in that language can be easily analyzed using the available tools. The concrete
syntax of such a language can be adapted to the terminology of domain experts
using the language to describe their system. Examples of using Montages to
develop languages for special domains are given in [20,1,4].
References
1. M. Anlauff, A. Bemporad, S. Chakraborty, P.W. Kutter, D. Mignone, M. Morari,
A. Pierantonio, and L. Thiele. From ease in programming to easy maintenance:
Extending DSL usability with Montages. Technical Report 84, Institute TIK, ETH
Zürich, December 19999.
2. M. Anlauff, P. Kutter, and A. Pierantonio. Formal Aspects of and Development
Environments for Montages. In M. Sellink, editor, 2nd International Workshop
on the Theory and Practice of Algebraic Specifications, Workshops in Computing,
Amsterdam, 1997. Springer.
3. M. Anlauff, P. Kutter, and A. Pierantonio. Enhanced control flow graphs in Monta-
ges. In A.Zamulin D.Bjoerner, M.Broy, editor, Perspective of System Informatics,
volume 1755 of LNCS, pages 40 – 53, 1999.
4. M. Anlauff, P.W. Kutter, A. Pierantonio, and Asuman Sünbül. Using domain-
specific languages for the realization of component composition. In Proceedings
Formal Approaches in Software Engineering FASE00, LNCS, 2000.
5. M. Anlauff and A. Sünbül. An ASM specification of an elevator control system.
1999.
6. M. Anlauff and A. Sünbül. Software architecture based composition of components.
In GI-Workshop Sicherheit und Zuverlässigkeit software-basierter Systeme, 1999.
7. S. Bensalem, V. Ganesh, Y. Lakhnech, C. Muñoz, S. Owre, H. Rueß, J. Rushby,
V. Rusu, H. Saı̈di, N. Shankar, E. Singerman, and A. Tiwari. An overview of SAL.
In C. Michael Holloway, editor, LFM 2000: Fifth NASA Langley Formal Methods
Workshop, June 2000. to appear.
8. J. A. Bergstra and P. Klint. The ToolBus coordination architecture. In Ciancarini
and Hankin [12], pages 75–88.
9. E. Börger and D. Rosenzweig. A Mathematical Definition of Full Prolog. In Science
of Computer Programming, volume 24, pages 249–286. North-Holland, 1994.
10. E. Börger and J. Schmid. Composition and Submachine Concepts for Sequential
ASMs. In P. Clote and H. Schwichtenberg, editor, Gurevich Festschrift CSL 2000,
LNCS. Springer-Verlag, 2000. to Appear.
11. M. Chandy and J. Misra. Parallel Program Design: A Foundation. Addison-Wesley,
Reading, MA, 1988.
12. Paolo Ciancarini and Chris Hankin, editors. Coordination and models, Proceedings
of the first international conference, Cesena, Italy, number 1061 in LNCS. Springer
Verlag, 1996.
13. G. Del Castillo. The ASM Workbench: an Open and Extensible Tool Environment
for Abstract State Machines. In Proceedings of the 28th Annual Conference of the
German Society of Computer Science. Technical Report, Magdeburg University,
1998.
14. Michel Goossens, Frank Mittelbach, and Alexander Samarin. The LATEX Compa-
nion. Tools and Techniques for Computer Typesetting. Addison-Wesley, Reading,
MA, USA, second edition, 1994.
15. F. Griffel. Componentware. dpunkt.verlag, 1998.
16. Y. Gurevich. Evolving Algebras 1993: Lipari Guide. In E. Börger, editor, Specifi-
cation and Validation Methods, pages 9–36. Oxford University Press, 1995.
17. Y. Gurevich. May 1997 Draft of the ASM Guide. Department Technical Report
CSE-TR-336-97, University of Michigan, 1997.
18. Y. Gurevich and J. Huggins. The Semantics of the C Programming Language. In
E. Börger, H. Kleine Büning, G. Jäger, S. Martini, and M. M. Richter, editors,
Computer Science Logic, volume 702 of LNCS, pages 274–309. Springer, 1993.
90 M. Anlauff
19. J.K. Huggins and W. Shen. The static and dynamic semantics of C. In Local
Proceedings of ASM2000, TIK Report Nr. 87, 2000.
20. P. W. Kutter, D. Schweizer, and L. Thiele. Integrating formal domain-specific
language design in the software life cycle. In Current Trends in Applied Formal
Methods, LNCS. Springer, October 1998.
21. P.W. Kutter and A. Pierantonio. Montages: Specifications of Realistic Program-
ming Languages. Journal of Universal Computer Science, 3(5):416–442, 1997.
22. P.W. Kutter and A. Pierantonio. The Formal Specification of Oberon. Journal of
Universal Computer Science, 3(5):443–503, 1997.
23. L. Lamport. The temporal logic of actions. ACM TOPLAS, 16(3):872–923, 1994.
24. Z. Manna and A. Pnueli. The Temporal Logic of Reactive and Concurrent Systems,
Volume 1: Specification. Springer-Verlag, New York, NY, 1992.
25. W. May. Specifying complex and structured systems with evolving algebras. In
M. Bidoit and M. Dauchet, editors, Proceedings of TAPSOFT’97: Theory and Prac-
tice of Software Development, number 1214 in LNCS, pages 535 –549, 1997.
26. John K. Ousterhout. Scripting: Higher level programming for the 21st century.
IEEE Computer, 31(3):23–30, March 1998.
27. J.-G. Schneider and O. Nierstrasz. Scripting: Higher-level programming for
component-based systems. In OOPSLA 1998, 1998. Tutorial.
28. Jean-Guy Schneider and Oscar Nierstrasz. Components, scripts and glue. In Leonor
Barroca, Jon Hall, and Patrick Hall, editors, Software Architectures – Advances and
Applications, pages 13–25. Springer, 1999.
29. N. Shankar. Symbolic Analysis of Transition Systems. In This volume.
30. Asuman Sünbül. Architectural Design of Evolutionary Software Systems. PhD
thesis, Technical University Berlin, 1999. in preparation.
31. Clemens Szyperski. Component Software: Beyond Object-Oriented Programming.
ACM Press and Addison-Wesley, New York, N.Y., 1998.
32. J. Teich, P.W. Kutter, and R. Weper. Description and simulation of microprocessor
instruction sets using asms. In This volume.
33. Larry Wall and Randal L. Schwartz. Programming Perl. O’Reilly Associates, Inc.,
Sebastopol, CA, 1990.
34. C. Wallace. The Semantics of the Java Programming Language: Preliminary Ver-
sion. Technical Report CSE-TR-355-97, EECS Dept., University of Michigan, De-
cember 1997.
?
Generic Facilities in Object-Oriented ASMs
A. V. Zamulin
Abstract. Facilities for defining generic object types, generic type ca-
tegories, generic functions and generic procedures in an object-oriented
ASM are described in the paper. These facilities permit one to specify
algorithms over complex data structures abstracting both from the type
of the structure components and the structure itself. The use of the fa-
cilities is demonstrated by the specifications of some important parts of
Standard Template Library for C++.
1 Introduction
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 91–111, 2000.
c Springer-Verlag Berlin Heidelberg 2000
92 A.V. Zamulin
in STL: vectors, lists, deques, sets, multisets, maps, and multimaps. Other con-
tainer classes can be defined if needed. Each container class is parameterized by
the component type. Thus, for each data structure one can write an algorithm
abstracting from the component type. This provides the first level of genericity
typical of C++.
To abstract from the container’s structure, STL introduces a notion of ite-
rator which is a generalization of the pointer notion. Iterators are grouped into
different iterator categories providing abstract data-access methods. There are
categories of input iterators, output iterators, forward iterators, bidirectional ite-
rators, and random-access iterators. Iterator categories build a hierarchy. This
means that a forward iterator is also an input iterator and an output iterator, a
bidirectional iterator is also a forward iterator, and a random-access iterator is
also a bidirectional iterator. Algorithms can now be written in terms of iterators
abstracting from a concrete data structure. Most important here is the fact that
an algorithm requiring, say, an input iterator can also use a forward or bidirec-
tional or random-access iterator. This provides the second level of genericity.
One of the ways of the formal STL definition is the use of classical alge-
braic specifications whose advantages are sound mathematical foundation and
existence of specification languages and tools. Taking into account the generic
nature of the data structures and iterator categories, a specification language
like Spectrum [5] providing parameterized sorts and sort classes can be used for
this purpose. However, the notions of iterator and container subsume a notion of
state which can change when containers are searched or updated. The modeling
of the state by classical algebraic specifications involves extra data structures
representing the state which must be explicitly passed to a function as argument
and yielded as result. Algebraic specifications are also a poor tool for describing a
data structure with an arbitrary order of component insertion and deletion. This
leads to very complex specifications with problems of describing the differences
between different types of containers.
At the same time it seems very natural to consider containers and iterators
as objects possessing state and to define container classes and iterator classes as
generic object types parameterized by the component type. To define formally
iterator categories and thus represent the hierarchy of iterator classes, we need
a notion of type category similar to that of sort classes of Spectrum and a notion
of generic type category absent in conventional algebraic specifications. Thus,
the adaptation and extension of generic facilities of algebraic specifications for
enhancing object-oriented ASMs are the main tasks of this paper.
The paper is organized in the following way. Concrete object types defining
sets of states of potentially mutable objects and object-oriented dynamic systems
generalizing the communities of object states and their transitions are introdu-
ced in Section 2. Unconstrained generic object types are defined in Section 3.
Generic vector type and generic list type as typical representatives of Standard
Template Library are specified in Section 4 and Section 5, respectively. Object
type categories permitting the classification of object types on the base of their
operations are introduced in Section 6. Generic object types, generic type cate-
Generic Facilities in Object-Oriented ASMs 93
gories and generic functions constrained by object type categories are defined in
Section 7. Some related work is discussed in Section 8, and some conclusions are
given in Section 9.
It is assumed that the reader is familiar with the basic ASMs notions which
can be found in [1,2]. The familiarity with the formal aspects of object-oriented
ASMs [3] is desirable, but not obligatory.
2 Basic Notions
We distinguish between data and objects and, respectively, between data types
and object types. A data identifies itself and is immutable. An object possesses
a unique identifier and state which can be mutable. A data type defines a set
of immutable values and operations over them. An object type defines a set
of states of a potentially mutable object and a number of methods capable to
observe or update the object’s state. This distinction between data types and
object types requires different methods of their specification.
We consider that data types are specified by means of one of the algebraic
specification languages like Spectrum [5], Ruslan [6] or CASL [7]. Let (Σ0 , Ax),
where Σ0 is a signature and Ax is a set of axioms, be the specification of a number
of data types. Σ0 is called the data signature in the sequel. An algebra A0 of this
signature is called a data algebra. An object-structured signature, Σobj , extends
Σ0 with a set of object type signatures [3]. Respectively, an object-structured spe-
cification, (Σobj , Axobj ), extends (Σ0 , Ax) with a set of object type specifications.
Constructing the object-structured specification, we accompany each object type
signature with the corresponding axioms. Therefore, the notions of object type
signature and object type specification correspond, respectively, to the notions
of class declaration and class definition of C++.
The object-structured specification is defined in the following way. Let OT Y P E
be a set of (object type) names and OΦ a set of object type specifications.
The object-structured specification is then a triple hOT Y P E, OΦ, into i, where
into is a function mapping OT Y P E into OΦ. If T ∈ OT Y P E, ots ∈ OΦ, and
into (T ) = ots, then the maplet hT 7→ otsi is the specification of the object type
T (in this case we sometimes write that ots is marked with T ).
The specification of an object type T in this paper has the following form:
class T = spec
[object-type-signature]
{axioms},
where object-type-signature is a triple set-of-attribute-declarations; set-of-mutator-
declarations; set-of-observer-declarations.
An attribute or observer declaration has the following form: operation-name:
operation-profile. The operation-profile is either T or T1 , . . . , Tn −→ T where
T, Ti are data/object type names indicating the types of attribute/observer pa-
rameters (if any) and the result type. A mutator declaration is either just a
94 A.V. Zamulin
then atAT (id) is an attribute of id. Note that, like in object-oriented languages, the
object type is always the first parameter type of its attribute function although
it is never indicated in the attribute declaration. An object identifier is used as
argument when the corresponding object type is a function parameter type, and
an object identifier is produced when the corresponding object type is the result
type of a function.
An object is a pair (id, obs) where id is an object identifier and obs is a tuple
of its attributes called object’s state. For example, if id is an object identifier of
type VecIt in algebra A, then the pair hid, value storedA (id)i is an object with the
state value storedA (id).
To define the interpretation of observer and mutator names, a notion of
dynamic system is introduced.
We discussed above only the functions defined inside the frames of object types.
In a more general case, an algebra can possess a number of ”stand-alone” fun-
ctions and constants defined outside the frames of object types. In addition to
this, higher-order functions for observing and transforming algebras can be de-
fined. Therefore, we introduce a notion of dynamic system possessing a number
of states and a number of operations for observing and updating the states.
Generally, the specification of an object-oriented dynamic system is repre-
sented by the following tuple:
h(Σ0 , Ax), (Σobj , Axobj ), Σdin , (Σdep , Axdep ), (Σproc , Axproc )i.
Its first component is the specification (the signature and axioms) of a number of
data types and related functions, and the second component is the specification
of a number of object types discussed above. The third component is the signa-
ture of a set of dynamic functions/constants which can be different in different
states of a dynamic system.
A dynamic function is declared as follows:
dynamic function function-name: T1 , ..., Tn −→ T ;
where T1 , ..., Tn are parameter types and T is a result type. A function without
parameters is called a constant and declared as follows:
dynamic const constant-name: T ;
where T is the type of the constant.
Examples.
dynamic const an iterator: VecIt;
dynamic function matrix: Nat, Nat −→ VecIt;
2.4 Axioms
Axioms accompanying the declarations of dependent functions are data equations
of the form t1 == t2 where t1 and t2 are two terms of the same type. The
98 A.V. Zamulin
We cannot be satisfied with only concrete object types because many object
types can have a similar structure and it would be tiresome to specify them
again and again. Therefore a notion of generic object type is introduced. We
start with the simplest case, unconstrained generic object types.
Generic Facilities in Object-Oriented ASMs 99
Now we can define the generic list type. Due to space limitations, the spe-
cification of some mutators is left to the reader. Note that a transition rule of
the form seq R1 , ..., Rn end indicates the sequential execution of the transition
terms R1 , ..., Rn , and the transition rule of the form while b do R end indicates
the repetition of the execution of the transition term R. The formal semantics
of these rules can be found in [3].
Any object type possessing the methods eq and neq specified as above is
the type of the category Equal. We also consider that T Y P E is the name of
the category with the empty specification and, therefore, any data/object type
belongs to this category.
Constructing a type category, we can inherit the specification of an existing
type category producing the union of the specifications as result.
104 A.V. Zamulin
Like an object type, an object type category can be generic, i.e., it can
use type parameters in its specification. The definition of a generic object type
category is the same as the definition of a generic object type. A generic object
type belongs to a generic type category if their parameters match and it includes
the specification of the type category as its subspecification. Iterator categories
serve as examples.
1. Each iterator type of the following category has methods advance and
get value in addition to the methods of the category ”Equal”.2
2. Each iterator type of the following category has methods advance and
put value in addition to the methods of the category ”Equal”.3
3. Each iterator type of the following category has a mutator put value in
addition to the methods of the category ”InputIterator”.
4. Each iterator type of the following category has the method retreat in
addition to the methods of the category ”ForwardIterator”.
5. Each iterator type of the following category has several methods in addition
to the methods of the category ”BidirectionalIterator”.
According to the definitions, an object type VecIt(T) belongs to the type cate-
gories RandomAccessIterator(T), BidirectionalIterator(T), ForwardIterator(T),
OutputIterator(T), InputIterator(T), and Equal. An object type ListIt(T) be-
longs to the type categories BidirectionalIterator(T), ForwardIterator(T), Ou-
tputIterator(T), InputIterator(T), and Equal (it does not belong to the type
category RandomAccessIterator(T), however). Thus, a vector iterator can be
used in any algorithm requiring either a random access iterator or bidirectional
iterator or forward iterator or input iterator or output iterator. In the same way
a list iterator can be used in any algorithm except that one which requires a
random access iterator.
7 Constrained Genericity
Now, we can call the function with vector iterators like the following:
binary find(VectIt, Nat)(vec.begin, vec.end, 7)
because vector iterators belong to the class RandomAccessIterator, and we can-
not call it with list iterators. Note the use of the type category Ordered (not
defined here) which is needed to make sure that the type of the components
contains the operation ”<”.
The above examples show us in which way the functions abstracting both
from the structure and the type of its components can be specified.
Generic Facilities in Object-Oriented ASMs 107
Generic procedures are declared similarly to generic functions and are speci-
fied similarly to ordinary procedures. The vector allocator, a special procedure
which allocates memory for a particular vector (according to STL, such a pro-
cedure is associated with each container class), can serve as an example. The
import rule of Gurevich ASMs [2] is used in the specification for creating a new
object identifier.
8 Related Work
We are not going to discuss here the approaches representing object states as
elements of the same algebra. The work along this approach is heavily based on
traditional algebraic specification methods. We can only repeat after F. Parisi-
Presicce and A. Pierantonio that ”the algebraic framework so far has been ina-
dequate in describing the dynamic properties of objects and their state transfor-
mation as well as more complex notions typical of the object oriented paradigm
such as object identity and persistency of objects” [10]. The interested reader
can refer to [11,12,13,14].
We review here several works considering object states as algebras. Object-
oriented extensions of the prominent specification methods VDM [15] and Z [16]
are the first of them.
VDM++ [17] introduces a notion of class definition as a template for a collec-
tion of objects possessing a number of instance variables (internal state) and
methods (external protocol). The definitions of the methods of existing classes
can be inherited when a new class is defined (multiple inheritance). Object’s
initial state and invariant can be specified. A set of statements typical of impe-
rative programming languages is provided. Unfortunately, the description of the
semantics of the language is done rather informally, in the way the semantics
of programming languages is usually described. As a result, the user gets an
impression that VDM++ is a programming language provided with some speci-
fication facilities (pre- and post-conditions) rather than a specification language.
No genericity is provided in the language.
Object-Z [18] practically has the same features as VDM++ with the dif-
ference that it is based on Z. A class is here a set of attributes and a set of
operations acting upon these attributes. In contrast to VDM++, the semantics
108 A.V. Zamulin
The specification language Troll [25] should be mentioned as one of the main
practical achievements in the field. Troll is oriented to the specification of ob-
jects where a method (event) is specified by means of evaluation rules similar
to equations on attributes. Although the semantics of Troll is given rather in-
formally, there is a strong mathematical foundation of its dialect Troll-light [26]
with the use of data algebras, attribute algebras and event algebras. A relation
constructed on two sets of attribute algebras and a set of event algebra, called
object community, formalizes transitions from one attribute algebra to another.
Although Troll possesses generic facilities, non of them is formalized in [26].
The problem of constrained genericity in object-oriented class libraries is di-
scussed in [27]. Abstract classes resembling our object type categories are used
there for constraining the generic parameters, and a notion of syntactical con-
formance is introduced for checking whether a concrete class is an instance of a
given abstract class. There is an important difference between an abstract class
and a concrete class since one cannot create an object of an abstract class. In
fact, an abstract class specifies some methods common for a community of con-
crete classes. For this reason me make a clear difference between object types
and object type categories independently specified. An additional feature of our
approach is generic object type categories standing for communities of generic
object types.
Finally, a fundamental work [28] formalizing bounded parametric polymor-
phism similar to our constrained genericity should be paid attention. Here gene-
ricity is constrained by allowing only those type arguments which are subtypes
of the parameter type. In this respect, this approach is very similar to the pre-
vious one. Another peculiarity of the work is that an object does not possess a
unique identifier, it is just a tuple of methods, and object updates are simulated
by method overrides generally producing new objects.
9 Conclusion
The mechanisms for the specification of generic object types and type categories
are introduced in the paper. With the use of these mechanisms, many generic
algorithms abstracting from the type of the data structure being manipulated can
be easily specified. Although the general technique of generic type specification
is well-known and can be found in the literature, the novelty of this work is the
extension of the technique to the case of object-oriented ASMs. Another novelty
is the definition and use of generic type categories for the specification of generic
algorithms.
The described technique has permitted us to specify some components of the
Standard Template Library for C++. The library thus specified can be easily
adapted to another object-oriented language. The experience obtained in the
process of the specification has proved the power of the technique. Its main
features can be summarized as follows:
1. We represent immutable values by data types and specify them algebrai-
cally.
110 A.V. Zamulin
References
1. Y. Gurevich. Evolving Algebras 1993: Lipary Guide. In: Börger, E., ed., Specifica-
tion and Validation Methods, Oxford University Press, 1995, pp. 9-36.
2. Y. Gurevich. May 1997 Draft of the ASM Guide. University of Michigan EECS
Departmental Technical Report CSE-TR-336-97 (available electronically from
http://www.eecs.umich.edu/gasm/).
3. A.V. Zamulin. Object-Oriented Abstract State Machines. Proc. ASM workshop,
Magderburg, Germany, 21-22 September, 1998, pp. 1-21 (available electronically
from http://www.eecs.umich.edu/gasm/).
4. D.R. Musser and A. Saini. STL Tutorial and Reference Guide. Addison-Wesley,
1996.
5. Broy, M., Facchi, C., Grosu, R., ea. The Requirement and Design Specification Lan-
guage Spectrum, An Informal Introduction, Version 1.0. Technische Universitaet
Muenchen, Institut fuer Informatik, April 1993.
6. A.V. Zamulin, The Database Specification Language RUSLAN: Main Features.
East-West Database Workshop (proc. Second International East-West Database
Workshop, Klagenfurt, Austria, September 25-28, 1994), Springer (Workshops in
Computing), 1994, 315-327.
7. P.D. Mosses. CoFI: The Common framework Initiative for Algebraic Specification
and Development. In: M. Bidoit and M. Dauchet, eds., TAPSOFT’97: Theory and
Practice of Software Development, LNCS, vol. 1214, pp. 115-137.
8. P. Wadler and S. Blott. How to make ad-hoc polymorphism less ad-hoc. Conf.
Record of the 16th ACM Annual Symp. on Principles of Progr. Lang., Austin,
Texas, January 1989.
9. Nakajima, R., Honda, M., and Nakahara, H. Hierarchical Program Specification: a
Many-sorted Logical Approach. Acta Informatica 14, pp. 135-155 (1980).
10. F. Parisi-Presicce and A. Pierantonio. Dynamic behavior of Object Systems. In:
Recent trends in Data Type Specification. LNCS, vol. 906, 1995, pp. 406-419.
11. J.A. Goguen and R. Diaconescu. Towards an Algebraic Semantics for the Object
Paradigm. Recent Trends in Data Type Specification, LNCS, 1994, vol. 785, pp.
1-29.
12. H.-D. Ehrig and A. Sernadas. Local Specification of Distributed Families of Sequen-
tial Objects. In: Recent Trends in Data Type Specifications. LNCS, vol. 906, 1994,
pp. 219-235.
13. F. Parisi-Presicce and A. Pierantonio. An Algebraic Theory of Class Specification.
ACM Transactions on Software Engineering and Methodology, April 1994, vol. 3,
No. 2, pp. 166-169.
Generic Facilities in Object-Oriented ASMs 111
14. C. Cristea. Coalgebra Semantics for Hidden Algebra: Parameterised objects and
Inheritance. in: Recent Trends in Algebraic development Techniques. LNCS, vol.
1374, 1997, pp. 174-189.
15. C. B. Jones. Systematic Software Development using VDM. Prentice Hall, 1990.
16. J. M. Spivey. understanding Z. A specification language and its formal semantics.
Cambridge University Press, 1988.
17. E.H. D,rr and J. van Katwijk. A Formal Specification Language for Object Oriented
Designs. In: Computer Systems and Engineering (Proceedings of CompEuro’92).
IEEE Compute Society Press, 1992, pp. 214-219.
18. R. Duke, P. King, G.A. Rose, and G. Smith. The Object-Z specification language.
In: T. Korson, V. Vaishnavi, and B. Meyer, eds., Technology of Object-Oriented
Languages and Systems: TOOLS 5, Prentice hall, 1991, pp. 465-483.
19. K. Lano and H. Houghton, eds., Object-Oriented Specification case Studies. Pren-
tice Hall (object-oriented series), 1994.
20. K. Lano and H. Houghton. The Z++ manual. October 1994. Available from
ftp://theory.doc.ic.ac.uk/theory/papers/Lano/z++.ps.Z
21. A. Pierantonio. Making Statics Dynamic. In: G. Hommel, editor, Proc. Interna-
tional Workshop on Communication based Systems, Kluwer Academic Publishers,
1995, pp. 19-34.
22. J. Meseguer. Conditional rewriting logic as a unified model of concurrency. Theo-
retical Computer Science, vol. 96, No 1 (April 1992), pp. 73–155.
23. R. Diaconescu and K. Futatsugi. CafeOBJ Report.World Scientific Publishing Co.
Pte. Ltd, AMAST series in Computing”, vol. 6,1998.
24. J. Meseguer. A logical theory of concurrent objects and its realization in the Mode
language. In: Research Directions in Concurrent Object-Oriented Programming.
The MIT Press, Cambridge, Mass., 1993, pp. 314-390.
25. T. Hartmann, G. Saake, R. Jungclaus, P. Hartel, and J. Kush. Revised Ver-
sion of the Modelling Language TROLL. Technishe Universitaet Braunschweig,
Informatik-Berichte 94-03, 1994.
26. M. Gogolla and R. Herzig. An Algebraic Semantics for the Object Specification
Language TROLL-light. In: Recent Trends in Data Type Specifications, LNCS,
vol. 906, 1995, pp. 290–306.
27. A. Frick, G. Goos, R. Newmann, W. Zimmermann. Construction of Robust Class
Hierarchies. Software—Practice and Experience, 2000 (to be published).
28. M. Abadi and L. Cardelli. A Theory of Objects. Springer-Verlag, 1996.
Towards an ASM Thesis for Unconventional
Algorithms
Wolfgang Reisig
Humboldt-Universität zu Berlin
[email protected]
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 112–130, 2000.
c Springer-Verlag Berlin Heidelberg 2000
Towards an ASM Thesis for Unconventional Algorithms 113
This yields a number of consequences: Firstly, a run may wait for fresh input.
‘Waiting’ is a really new aspect, not present in any conventional theories of com-
putation. Secondly, a run of such an algorithm is sensible also if it diverges.
Different diverging runs may execute different interactions with their environ-
ment.
Each run of this kind of algorithm transforms a finite or infinite sequence of
input data into a finite or infinite sequence of output data: The algorithm reacts
to each new input item with fresh output items; hence the term of reactive
algorithms.
Typical examples of such algorithms are technical control algorithms, such
as e.g. lift control.
become pending, but must also be able to remain quiet forever; the step from
quiet to pending is “quiescent”. The algorithm furthermore must guarantee that
pending will eventually lead to critical and back to quiet. Hence all other actions
are “progressing”.
Algorithms are frequently composed with other algorithms along quiescent
actions. Quiescence then hints at additional preconditions that are not explicitely
stated in the algorithm itself.
It may come as a surprise that we conceive reading the actual value of variables
the first issue to be considered here. Reading is a quite obvious, simple operation
in conventional algorithms.
Unconventional algorithms, however, frequently consist of local agents that
share variables: A shared variable can be addressed by two or more agents. If they
do so coincidently, the outcome is debatable. There are two main approaches to
tackle this problem.
The idea of this approach is one distinguished agent who controls the variable
and may both read (test) and write (change) it. All other agents are only allowed
to read. Reading is assumed not to really interfere with writing. Lamport in [13]
Towards an ASM Thesis for Unconventional Algorithms 119
nicely exemplifies this approach by some kind of a flag that can be risen and
lowered by one person only. Others may observe the scenery.
The observer (reader) of the flag does not affect the flag in any respect. In
particular, the observer of the flag can not prevent the flag’s owner from rising
and lowering.
Lamport’s above described approach properly reflects our everyday physical en-
vironment. It includes myriads of light waves that are reflected by items’ surface,
e.g. flags, and later on catched by humans eyes. This is why observation does
not affect the observed.
Not all physical universes are structured like this. An example is the universe
of computer hardware.
Reading a variable, i.e. copying the actual value of a register, is physically
realized as a sequence of actions that indeed affect the register’s contents. This
contents may be unreliable, inpredictable or in fact different from the expected
value at intermediate states. While being read, the register can not be written
at the same time.
A register like this behaves like a notebook, used by a group of agents. Both
actions of reading and writing require exclusive control. It is the agents’ access
to the notebook that counts for the purpose of writing and reading alike.
2.3 Conclusion
3.2 An Example
What are the sequential runs of the above algorithm? Apparently, each infi-
nite sequence w ∈ {A, . . . , E}∞ ! On the given level of abstraction, neighbored
philosophers, never eating together, can not be distinguished from detached phi-
losophers who very well might eat together.
x
Writing y to denote concurrent events x and y, one may construct lockstep runs
x1 x2
··· ···
y1 y2
Towards an ASM Thesis for Unconventional Algorithms 121
3.4 Refinement
To overcome the shortcomings of sequential and lockstep runs, one may refine
the actions A, . . . , E. For example, A may be replaced by three actions A1 , A2
and A3 , denoting the philosopher a to pick up his adjacent forks, to eat and to
release his forks, respectively. A sequence beginning A1 A2 B1 was then ruled
out, for obvious reasons.
Interleaved and lockstep runs consisting of refined actions could more truly
express causal relations than the above runs of unrefined actions can. But ASMs
insist in the specifiers right to choose an abstraction level at his convenience. A
proper way to solve the problem was the use of concurrent runs, to be considered
next.
& %
A
% &
represent the above described action A, with ingoing arcs denoting the forks of
A being available to A, and the outgoing arcs denoting the forks of A being
released by A.
Fig.8 then shows one (out of infinitely many) concurrent runs of the philoso-
phers’ algorithms: Philosophers a and c start concurrently, followed by b twice
in a row. d acts after c, and e after a and d.
A B B
C D E
glue glue
1 2
B B B B
A A A A A
E E E E
D D D D D
C C C C
B B B B
glue glue
1 2
glue glue
1 2
B B B B
C C C C C
D D D D
E E E E E
A A A A
B B B B
glue glue
1 2
A A
D C
C D
Local states in concurrent runs are frequently much more prominent: There
are concurrent runs that are essentially characterized by their local states. The
runs of the sorting algorithm of Chapter 1.7 illustrate this.
With xi denoting the pair (x, i) consisting of ‘fish x with sticker i’, Fig. 12
and Fig. 13 show two runs of the sorting algorithm.
An event is represented as
& %
% &
124 W. Reisig
Each event is an instance of the action of two fishes swapping their stickers.
The involved fishes and stickers are indicated by the local states presented at
the arcs starting and ending nodes, respectively.
Vice versa, can the concurrent runs be derived from the sequential runs? This
depends on knowledge about the involved actions’ scope, the topic of the next
chapter.
Research into concurrent runs started with [10] as well as [9] , suggesting acyclic
graphs with nodes denoting events and arcs denoting causal precedence. Petri
in [18] describes how to construct the concurrent runs of condition - event sy-
stems, the most elementary class of Petri nets. Lamport advocates concurrent
runs in [12] , but renounces them in [14]. Concurrent runs have been suggested
for several versions of Petri Nets [5] , [3] , and other system models [7]. They
are extensively employed in [20]. The issue has been debated in an electronic
discussion on the concurrency mailing list, reprinted in [19].
The scope of an action includes all variables that the action addresses. This
notion is not too important for conventional algorithms. It is however decisive
when unconventional algorithms are to be specified, constructed and analyzed.
4.1 An Example
init ≡ x := 0; z := true; y := 0
Furthermore, let
X≡ while true do x := x + 1;
Y ≡ while true do y := y + 1;
program ≡ init ; X kY
x=0 X → X → X → ···
z = true
y=0 Y → Y → Y → ···
4.2 A Variant
As a variant of the above program, assume the variables x, y, z and the initia-
lization init as above.
Furthermore, let
X≡ while z do x := x + 1;
Y ≡ while z do y := y + 1;
and
program ≡ init; X kY
x=n f x = f (n)
y=m
In this figure, g denotes an event that transforms the local states of both x
and y, actually x = n and g = m, respectively, into the local states x = f (n)
and y = m.
Summing up, f (x) and g(x, y) would cause equal effects in sequential runs,
but different effects in concurrent runs.
f (a) := b
f (a) := c
with b 6= c. [8] requires computation to stall in this case. [7] suggests nondeter-
ministic choice.
Both alternatives assume a runtime system or a global controller or whate-
ver means to detect and to manage this case instantly. Distributed algorithms
prevent this kind of regime, however: If f (a) := b and f (a) := c are concur-
rently executed by two different agents, there is no means to detect inconsistency
instantly.
References
1. Hagit Attiya and Jenifer Welch. Distributed Computing. McGraw Hill, 1998.
2. Jean-Pierre Banâtre and Daniel Le Métayer. Programming by multiset transfor-
mation. CACM, 36(1):98–111, 1993.
3. Eike Best and Cesar Fernandez. Nonsequential Processes. Springer-Verlag, 1988.
4. W. P. de Roever et al. Concurrency verification: Introduction to compositional
and noncompositional proof methods. to appear 2000.
5. U. Goltz and W. Reisig. The non-sequential behaviour of Petri nets. Information
and Control, 57(2-3):125–147, 1983.
6. Yuri Gurevich. Evolving algebras–a tutorial introduction. Bulletin of the EATCS,
(43):264–284, 1991.
7. Yuri Gurevich. Evolving algebras 1993 : Lipari guide. In E. Börger, editor, Speci-
fication and Validation Methods, pages 9–36. Oxford University Press, 1995.
8. Yuri Gurevich. The sequential asm thesis. Bulletin of the EATCS 67, pages 93–124,
1999.
9. Anatol Holt. Introduction to occurrence systems. In Associative Information
Techniques, pages 175–203. American Elsevir, New York, 1971.
10. Anatol Holt, H. Saint, R. Shapiro, and S. Warshall. Final report on the information
systems theory project. Technical Report RADC-TR-68-305, Rome Air Develope-
ment Center, Griffis Air Force Base, New York, 1968. Distributed by Clearinghouse
for Scientific and Technical Information, US Department of Commerce, 352 pages.
11. Ekkart Kindler and Rolf Walter. Mutex needs fairness. Information Processing
Letters 62, pages 31–39, 1997.
12. Leslie Lamport. Time, clocks, and the ordering of events in a distributed system.
CACM, 21(7):558–565, 1978.
130 W. Reisig
13. Leslie Lamport. Solved problems, unsolved problems and non-problems in concur-
rency. In Proceedings of the 3rd Symposium on Principles of Distributed Computing,
1983, pages 34–44, 1984.
14. Leslie Lamport. The temporal logic of actions. ACM Transactions on Programming
Languages and Systems, 16(3):872–923, 1994.
15. Nancy Lynch. Distributed Algorithms. Kaufman, Morgan, Los Altos, CA, 1996.
16. Zohar Manna and Amir Pnueli. The Temporal Logic of Reactive and Concurrent
Systems. Springer-Verlag, Berlin, 1992.
17. Robin Milner. Elements of interaction. CACM, 36:78–89, 1993.
18. C.A. Petri. Non-sequential processes. Technical Report Internal Report, GMD-
ISF-77-5, Gesellschaft für Mathematik und Datenverarbeitung, Bonn(Germany),
1977.
19. Vaugham Pratt (ed.). Debate ’90: An electronic discussion on true concurrency.
In Peled, Pratt, and Holzmann, editors, Partial Order Methods in Verification,
volume 29 of DIMACS Series, pages 359–403. 1997.
20. Wolfgang Reisig. Elements of Distributed Algorithms. Springer-Verlag, 1998.
21. Peter Wegner. Interactive foundations of computing. Theoretical Computer
Science, 192:315–351, 1998.
Partially Ordered Runs: A Case Study
Introduction
Distributed ASMs [4] is a general concurrent model of multi–agent computation.
It was intended, in generalization of its more limited precursors [5,3], to allow
as much concurrency as logically possible. Although the definition has been in
print for several years, its notion of partially ordered runs has remained largely
unexploited in its generality—most of its uses in the literature have recoursed
to some kind of specialization to linear time, discrete or continuous. The general
partially ordered runs seem to be somehow difficult to handle and to reason
about 1 .
Apart from deeply engrained intuitions of linear time, we feel that some
rather technical sources of this difficulty can be detected.
Sequential runs [4] have two properties which greatly facilitate reasoning.
1. Every move is executed in a well–defined state.
2. ‘External’ (or ‘monitored’, cf. below) changes can be located in time, and
thought of as actions by the environment. The environment thus becomes
just another (typically implicit) agent, whose behaviour can be specified in
declarative terms. The judicious splitting of dynamics to a part given by the
program and a part that can be specified declaratively is a natural way to
separate concerns and to abstract in ASMs.
In a partially ordered run neither of the above properties hold in general—a
(global) state in which a move is executed is in general not uniquely defined,
and it is not at all clear how to locate external changes in a partial order. These
seem to be important sources of insecurity and difficulty in reasoning about
partially ordered runs.
1
This remark is not limited to the ASM context—most formal methods modelling
concurrency tend to fall back, one way or another, to some kind of interleaving,
sequential semantics
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 131–150, 2000.
c Springer-Verlag Berlin Heidelberg 2000
132 Y. Gurevich and D. Rosenzweig
1 Preliminaries
We presume that the reader is familiar with [4]. Consider a one-agent program
π and let f be a basic function of π which is dynamic so that the values of f
can change in runs of π. Egon Börger suggested to use for ASMs the following
terminology borrowed from Parnas. f is controlled if only π can change it. f is
monitored if only the environment can change it. f is shared if both π and the
environment can change it. (In [4], controlled functions were called internal, and
monitored functions were called external.)
Partially Ordered Runs: A Case Study 133
Statement 3 may need an argument. We prove that (a) implies (b) implies
(c) implies (d) implies (a).
(a) implies (b). Assume that s is concurrent with t and let I = min Pre(s) ∪
min Pre(t). Clearly I is an initial segment. Check that min Pre(t) ⊆ I ⊆ max Pre(t),
so that I ∈ Pre(t). By symmetry, I ∈ Pre(s).
(b) implies (c). Trivial.
(c) implies (d). Assume that Pre(s) ∩ Pre(t) 6= ∅ and let J be any member
of Pre(s) ∩ Pre(t). Set I = J ∪ {s}. Clearly, I ∈ Pre(t).
(d) implies (a). Suppose that I, J ∈ Pre(t) and s ∈ I \ J. If s < t then
s ∈ min Pre(t) ⊆ J so that s ∈ J which is impossible. The dual argument shows
that s > t is impossible as well.
We shall say that a move t (in a given partially ordered run) may change
the value of term u if, for some I ∈ Pre(t), we have Valσ(I) (u) 6= Valσ(I∪{t}) (u)
(equivalently, if t changes the value of u in some linearization of the run). If the
above holds for all I ∈ Pre(t) (equivalently, if t changes the value of u in all
linearizations), we shall say that t must change the value of u.
Recall that a linearization of a partially ordered run is a run with the same
moves, and a linear order extending the given partial order. It was noted in [4]
that, in view of the coherence condition, all linearizations of a finite run have
the same final state.
Example 1. Take for instance two agents a, b, such that a executes the program
x := 1
Now assume that x = y = 0, mode = first initially and consider a run with a
move s of a, concurrent with two consecutive moves t1 , t2 of b. Then both s and
t1 may but not must change the value of max(x, y), while t2 must change it.
In these terms, we have
Lemma 1. If uPre(t) is not indisputable, then there is a move s concurrent with
t which may change u.
Proof. Assume the conclusion is false, that no move concurent with t may
change u. To go from σ(min Pre(t)) to σ(I), by Fact 1, we have to execute only
some moves concurrent to t, none of which may change u. Thus Valσ(I) (u) =
Valσ(min Pre(t)) (u) for all I ∈ Pre(t), and uPre(t) is indisputable, in contradiction
to the premise. t
u
Fact 3. If the values of uPre(t) and uPre(s) are both indisputable but different,
then s < t or t < s.
Proof. Assume uPre(t) and uPre(s) are both indisputable, and s and t are con-
current. Then there is an initial segment I ∈ Pre(s) ∩ Pre(t), and uPre(s) =
Valσ(I) (u) = uPre(t) . t
u
3 Lamport’s Algorithm
For arbitrary but fixed N let P1 , . . . , PN be processes (we shall also talk about
‘customers’) that may want from time to time to access a ‘critical section’ CS
of code. Any mutual exclusion protocol—which each Pi is supposed to execute
in order to enter the critical section—has to prevent two processes from being
in the critical section simultaneously. The Bakery Algorithm provides each Pi
with a (shared) register Ri and a (private) array n[1], . . . , n[N ] holding natural
numbers. Only Pi is allowed to write to Ri but every process can read the
register. We assume each register to be initialized with value 0.
The algorithm was presented by Lamport with the following piece of pseu-
docode.
Partially Ordered Runs: A Case Study 137
Start
n[i] := 1
write(Ri ,n[i])
Doorway
for all j6=i, read(Rj ,n[j])
Ticket
n[i] := 1 + maxj n[j]
write(Ri ,n[i])
Wait
for all j6=i, repeat
read(Rj ,n[j]) until
n[j]=0 or n[j]>n[i] or (n[j]=n[i] and j>i)
Critical Section
Finale
Ri := 0
The Bakery Algorithm is divided into six consecutive phases: start, doorway,
ticket assignment, wait section, critical section and finale.
To declare its interest in accessing the critical session, a process Pi writes 1
into array variable ni and then posts the written value in its register.
In the doorway section, Pi copies all the other registers into its array. It
then computes a ticket, which is the least integer greater than all integers in its
private array, writes the ticket into ni and posts the written value in its register.
During the subsequent wait section, process Pi keeps reading, into its array,
the registers of each other process Pj , until the resulting array value n[j] = 0 or
n[j] > n[i] or n[j] = n[i] ∧ j > i.
The meaning of the condition is the following: if n[j] = 0, then Pj is not
interested in entering the critical section, and it has no right to block Pi . If
n[j] > n[i] > 0, then Pi has a smaller ‘ticket’ and has the right to go before Pj .
The last clause resolves the case of two customers obtaining the same ‘ticket’:
then one with smaller identifier goes first. Note that by ordering pairs of positive
integers lexicographically:
(i, j) < (k, l) ←→ [i < k or (i = k and j < l)]
one can write the until condition as follows: n[j]=0 or (n[j],j)>(n[i],i).
Once permitted to go, Pi enters the critical section. Upon leaving CS, as
finale, Pi sets its register to 0.
Note also that the for-all commands in the doorway and the wait section may
be executed in many ways, in various sequences, all at once, concurrently etc.
It may be worth mentioning the following. The process first writes into n[i]
and then posts the written value at Ri . Obviously it could do the two actions in
the reverse order. Intuitively, the order between the two actions is immaterial,
but the sequential character of the pseudo-code imposes one.
138 Y. Gurevich and D. Rosenzweig
The doorway section in Lamport’s program does not give us any indication how
customer i is supposed to perform reading. Should it read the registers Rj in
the order given by the indices, in the reversed order? Should it get help and use
vassal agents, one per each Rj ? There are many other possibilities. To reflect the
situation in proper generality, our primary ASM model B1 includes no reading
instructions whatsoever. Instead, we will require that runs of B1 satisfy certain
provisos that guarantee that reading is performed.
The ASM has only one program, used by all customers, which has five rules. The
array A(X, Y ) represents the array n[Y ] of the program, private to customer X.
We assume that initially all registers have value 0, all customers are in mode
satisfied, and all elements of the array A(X, Y ) are undef. We assume that the
identifiers of the N customers are distinct natural numbers < N . Variables X, Y
will range over customers.
Start
if mode(me) = satisfied then
A(me,me) := 1, R(me) := 1, mode(me) := doorway
Ticket
Entry
Exit
if mode(me) = CS then
mode(me) := done
Finale
4.2 Semantics of B1
We would like to assume that, in any mode different from Satisfied, no custo-
mer stalls forever; eventually it makes a move (provided a move is continuously
enabled from some time on).
Since in ASMs we have no explicit notion of a move (or program) being
enabled, and in partially ordered runs we have no explicit notion of time, both
‘enabled’ and ‘continuously from some time on’ need definitions.
There are two obvious candidates for the notion of a program being enabled
in a state. One is based on the intuition that a program is enabled if it ‘gets
down to updates’, i.e. if in the given state it generates a nonempty set of updates.
The other possibility is that it really changes the state, i.e. that the updateset is
nonempty and also nontrivial. We are happy to sidestep the issue here, since for
all programs of this paper the two notions will coincide—whenever a nonemtpy
set of updates is generated, it will also be nontrivial. Thus we can say that a
program is enabled in state σ if it produces a nonempty set of updates in σ.
We say that an agent X stalls forever in a run if (a) X has a last move, say
t, and (b) after t a move by X (the program of X) is eventually always enabled
(in all σ(J) for J ⊇ I, for some initial segment I 3 t).
We thus assume
Corollary 1. R(Y )Pre(t) is indisputable iff t compares to all Start, Ticket and
Finale moves of Y.
Start
Ticket
if mode(me) = doorway and Ready(me) then
R(me) := T(me), mode(me) := wait
Entry
if mode(me) = wait and Go(me) then
mode(me) := CS
Exit
if mode(me) = CS then
mode(me) := done
Finale
if mode(me) = done then
mode(me) := satisfied, R(me) := 0
5.2 Semantics of B2
The ASM B2 is similar to that of B1 except for the fact that the array is gone.
In particular we assume the Progress Proviso (for known agents, i.e. customers).
The role of the array is taken over by three monitored functions, Ready, T
and Go. Looking at B1 , Ready(X) and T (X) can be seen as standing for the
abbreviations used there, while Go(X) can be interpreted as the guard of the
Entry rule, ∀Y 6= X(A(X, Y ) = 0 or (A(X, Y ), id(Y )) > (A(X, X), id(X))).
The ASM B2 provides however no means to compute Ready, T and Go.
Our first requirement says that every interested customer eventually obtains
his ticket:
C0 Each execution of Start, by a customer X, completes to a doorway x. For
each x the value T (X)Pre(Ticket(x)) is indisputable.
The indisputable value of T (X)Pre(Ticket(x)) will be, like before, denoted by
T (x). In order to express the rest of our conditions on the array in terms of T
and Go, we need some additional notation and terminology.
For open intervals in a partial order we also use (a, b) < (c, d) if b ≤ c,
and say that the two intervals are concurrent if neither b ≤ c nor d ≤ a. Note
that concurrency does not necessarily imply overlap, i.e. existence of common
elements; it in general just allows it.3
Sometimes we shall also compare elements with intervals: c < (a, b) if c ≤ a,
likewise for >.
This ordering will help us to formalize the idea that tickets increase together
with doorways (see C2 below). This should also apply in a way to concurrent
3
Note however that, if intervals are interpreted as intervals on the partial order of
initial segments, with (a, b) containing all segments containing a but not b, then
concurrent intervals indeed overlap.
Partially Ordered Runs: A Case Study 143
doorways; these are ordered by the following relation ≺, borrowed from its linear
order analog of [1].
Let X 6= Y , and let x, y range over doorways of X, Y respectively.
Definition 2. xy if x and y are concurrent and T 0 (x) < T 0 (y). Further, x ≺ y
if x y or x < y.
Lemma 4. x ≺ y or y ≺ x.
Proof. Note that T 0 (y) 6= T 0 (x) for X 6= Y , while two doorways of the same
customer can never be concurrent. t
u
Proof. Assume the premise is satisfied and the conclusion is false, i.e. that there
is no move Finale(y) < Entry(x). Take b as given by C3.
Claim 1 : T 0 (y) < T 0 (x).
Claim 2 : Ticket(y) < b.
144 Y. Gurevich and D. Rosenzweig
Given the claims, we have T 0 (y) < T 0 (x) < R0 (Y )Pre(b) 6= undef, and thus Y
must be writing to R(Y ) by a move in (Ticket(y), b). But the first such write
after Ticket(y) must be a Finale move, which contradicts the assumption that
the conclusion of the lemma is false.
Claim 1 follows immediately from definition of ≺ in case of concurrency, and
from C2 otherwise.
To prove Claim 2, we first note that b is comparable to both ends of y, and,
in view of y ≺ x, b ≤ Start(y) is impossible. It also impossible that Start(y) <
b ≤ Ticket(y), since then R(Y )Pre(b) = 1, which contradicts the choice of b. ut
Lemma 6. ≺ is transitive.
C4. By contradiction, suppose that the premise is satisfied but the conclusion
is false, i.e. W (x) is incomplete but W (y) is complete for all y ≺ x. Let Y and
b1 < b2 < · · · be the customer and the sequence of moves as given by W2.
Claim: There is a move b ∈ W (x), with R(Y )Pre(b) 6= undef, such that the
following two properties hold for each y:
First we derive the desired contradiction from the claim, and second we prove
the claim.
So suppose that the claim is true and let b be as in the claim. Then R(Y )Pre(b)
has an indisputable value, and b thus compares to all moves of Y that change
R(Y ). What is the value of R(Y ) in Pre(b)? We have two possible scenarios.
Scenario A: all y ≺ x; then b succeeds every Finale(y) and thus R(Y )Pre(b) = 0.
Scenario B: there is some y with Ticket(y) < b ≤ Finale(y); then R(Y )Pre(b) =
T (y). To summarize, if b is as in the claim, then R(Y )Pre(b) is either 0 or T (y),
so that R0 (Y )Pre(b) ≥ T 0 (y).
The values of R(Y )Pre(b) and of R(Y )Pre(bn ) for every n are indisputable.
Moves b and bn thus all compare with every move of Y which changes R(Y ).
It is easy to see that any bn 6< b satisfies (i) and (ii) in the claim. But then, as
shown above, R0 (Y )Pre(bn ) ≥ T 0 (y), which contradicts the property of bn in W2.
Thus every bn < b, which contradicts finite history.
It remains to prove the claim.
To prove the claim, note that, for y ≺ x, CS(y) is defined and complete, by
the assumption that W (y) is complete and the Progress Proviso. It suffices to
prove that there is at most one y > x. The sequence of doorways of Y is then
finite: by finite history, it has finitely many elements < x, by corollary 2 finitely
many elements concurrent to x. Thus Y has a last move, say eY . By Progress
Proviso, eY can only be a Ticket or a Finale move. Since all bn , by corollary 1,
compare to eY , by finite history, for sufficiently large n we have bn > eY . We
can then, for claimed b, take any bn > eY .
It remains to prove that Y has at most one doorway > x. Suppose x < y.
Then, by C2 (with x, y playing y, x respectively), T 0 (x) < T 0 (y) (since W (x)
is incomplete). If W (y) were complete, by C3 there would be a c ∈ W (y) such
that R0 (X)Pre(c) > T 0 (y). But since x < y < c, we also have c ∈ W (x) and
R(X)Pre(c) = T (x), so T 0 (x) > T 0 (y), which is impossible. Thus W (y) is incom-
plete, and y is the last doorway of Y .
We have thus verified that C0–C4 hold of arbitrary runs of B1 . It follows that
the results of the previous subsection, summarized in Theorem 1, hold of B1 as
well.
146 Y. Gurevich and D. Rosenzweig
Until now the universe of known agents was populated by customers only. Now we
also have another kind of agents. They present one way of making the unknown
agents of B1 known.
Formally the universe of agents splits into two disjoint universes: Customer
and Reader. Customers and readers are related by several functions. If X and
Y are distinct customers, then in each state there is at most one reader–agent
r(X, Y ), and there are no other readers.
If r is the reader r(X, Y ), then Lord(r) = X and Subject(r) = Y . The
readers will be created on the fly, when needed (at Start and Ticket moves), and
will self–destruct when their task is completed.
Customer Start
Ticket
Entry
Exit
if mode(me) = CS then
mode(me) := done
Finale
create r
agent(r) := true, Reader(r) := true
program(r) := reader-program
Lord(r) := X, Subject(r) := Y
mode(r) := m
endcreate
Reader
A(Lord(me),Subject(me)) := R(Subject(me))
if mode(me) = doorway then
destroy-reader(me)
if mode(me) = wait then
if R(Subject(me)) = 0
or (R(Subject(me)),id(Subject(me)))
> (A(Lord(me),Lord(me)),id(Lord(me))) then
destroy-reader(me)
where destroy-reader(a) abbreviates the rule
6.2 Semantics of B0
Semantics of B0 is like that of B1 , but considerably simpler, since all locations
are controlled by the known agents, and there are no moves monitored by the
known agents, to put constraints on—it is all in the programs for B0 . The reader
agents are one way to realize the requirement that those ‘for-all commands in
the doorway and the wait section of Lamport’s pseudocode may be executed in
many ways, in various sequences, all at once, concurrently etc.’ In fact the reader
agents capture all ways to realize that requirement, see below.
The only assumption we have to make, outside of the program, is the Progress
Proviso, applying here to all agents, both customers and readers:
Progress Proviso. No reader, and no customer in mode other than Satisfied,
stalls forever.
The reader–agents are created on the fly, and destroyed upon completion of
their task: the effect of destroy-reader(a), if a is a reader–agent, is returning a
to the reserve.
6.3 B0 Realizes B1
The constraints D, W1, W2 can be read as a rather direct description of what the
reader–agents do for their customers in B0 . The fact that every run of B0 satisfies
148 Y. Gurevich and D. Rosenzweig
D, W1, W2 follows from the programs and the Progress Proviso (together with
the semantics described above or in [4]).
D is satisfied in B0 since, for every move t of X executing Start, for every Y 6=
X, there is a reader r(X, Y ) at Post(Start(x)). By programs and the Progress
Proviso each of these readers makes a single self destructive move, which is the
b required by D; by programs and the Progress Proviso X eventually executes
Ticket.
By programs and Progress Proviso, for every Y 6= X there is a reader r(X, Y )
at Post(Entry(x)). That reader makes a move in W (x). For W1, W2 it then
suffices to note
Fact 4. A move t by r(X, Y ) in W (x) is the last such move iff it is successful,
i.e. T 0 (x) < R0 (Y )Pre(t) .
We can actually claim more. The requirements D, W1, W2 allow many dif-
ferent behaviours. Is there a behaviour, allowed by B1 , which is not captured by
the reader–agents of B0 ? Not really. This is the content of the following lemma,
expressing a kind of completeness property.
7 Concluding Remarks
Corrollary 2 implies the following cofiniteness property for the partial runs of the
Bakery algorithm: for each move t the set {s|s 6> t} is finite. This is a property
of the Bakery Algorithm, and not of the modelling framework: in spite of finite
history, it is easy to concoct legitimate partially ordered runs of some algorithm
which violate the cofiniteness property. In the case of the Bakery Algorithm, the
cofiniteness property implies that any two infinitely active customers have to
synchronize infinitely many times.
The cofiniteness property is an example of a property of partial runs that
is obfuscated in linear runs (since for linears runs it amounts to finite history).
This indicates that concurrent computations may have significant properties
which cannot be discovered by studying only their linearizations. Concurrent
computations should be analyzed directly.
References
1. Uri Abraham. Bakery algorithms. Unpublished manuscript, pp. 35, 1993.
2. Egon Börger, Yuri Gurevich, and Dean Rosenzweig. The bakery algorithm: Yet ano-
ther specification and verification. In E. Börger, editor, Specification and Validation
Methods, pages 231–243. Oxford University Press, 1995.
3. Paola Glavan and Dean Rosenzweig. Communicating evolving algebras. In
E. Börger, H. Kleine Büning, G. Jäger, S. Martini, and M. M. Richter, editors,
Computer Science Logic, number 702 in Lecture Notes in Computer Science, pages
182–215. Springer, 1993.
4. Yuri Gurevich. Evolving algebra 1993: Lipari guide. In E. Börger, editor, Specifica-
tion and Validation Methods, pages 9–36. Oxford University Press, 1995.
5. Yuri Gurevich and Larry Moss. Algebraic operational semantics and Occam. In
E. Börger, H. Kleine Büning, and M. M. Richter, editors, CSL’89, 3rd Workshop on
Computer Science Logic, number 440 in Lecture Notes in Computer Science, pages
176–192. Springer, 1990.
6. Leslie Lamport. A new solution of Dijkstra concurrent programming problem. Com-
munications of the ACM, 17(8):453–455, 1974.
Investigating Java Concurrency
Using Abstract State Machines
1 Introduction
The Java programming language [7,13] provides sophisticated support for con-
currency. The fundamental operations of concurrent programs are implemented
as built-in features of the language. Many of the notoriously complicated details
of concurrent programming are hidden from the programmer, simplifying the de-
sign of concurrent applications. Furthermore, a platform-neutral memory model
is included as part of the language. The incorporation of such intricate, subtle
operations into Java calls for a precise specification. As interest in the language’s
concurrency model increases, Java developers are examining and exploiting its
details [15,17,16,14]. The popularity of Java and, more importantly, its empha-
sis on cross-platform compatibility make the need for such a specification even
stronger.
We present a model of the concurrent features of Java, using the formal
operational specification method of Abstract State Machines (ASMs) 1 [9,22,23].
We use the Java Language Specification manual (JLS) [7], as our reference for the
language. The JLS is an informal specification, and due to the ambiguity which
pervades natural language, it can be interpreted in different ways. Our model
gives an unambiguous specification which reflects our interpretation of the JLS.
Throughout the paper, we indicate where ambiguities and omissions in the JLS
give rise to other interpretations. The formal specification process also uncovers
many subtle but important issues which the JLS does not bring to light. Our
goal is a specification that is not only precise but accessible to its readers, even
those not familiar with Java, concurrent programming, or ASMs. As part of this
1
ASMs were formerly known as Evolving Algebras.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 151–176, 2000.
c Springer-Verlag Berlin Heidelberg 2000
152 Y. Gurevich, W. Schulte, and C. Wallace
JLS, and then to discuss its consequences. As we shall see, there are several
issues within the JLS which make this initial understanding difficult.
We introduce in §2 the basic rules by which agents (threads) interact with
regard to shared variables, and in §3 the special considerations for variables
marked as volatile. In §4 we introduce locks, which are used to limit concurrency
within a program. In §5 we discuss prescient store actions. In §6 we describe
thread objects. In §7, we cover waiting and notification.
Our technical report [11] provides a complete presentation of our ASM speci-
fication. In it, we also prove that our specification is as general as possible while
obeying all the correctness criteria of the JLS.
bles). The function var maps each access message to the variable that sent it,
and maps each update message to the variable that is its intended recipient. The
function thread maps each update message to the thread that sent it, and maps
each access message to the thread that is its intended recipient. The function
value returns the value contained in a given message.
In any state, the members of the universe AccMsg that have been sent from
a variable v to a thread t form a “subuniverse” of AccMsg, which we call
AccMsg(v, t). Likewise, the members of the universe UpdMsg that have been
sent from t to v form a subuniverse of UpdMsg, which we call UpdMsg(t, v).
While the JLS does not dictate a specific policy on the access of shared va-
riables, it does impose some rules on the concurrent behavior of threads and
variables. We present these rules as they appear in the JLS and give our inter-
pretations of them.
JLS rules 1
1. [p. 403] “The actions performed by any one thread are totally ordered; that
is, for any two actions performed by a thread, one action precedes the other.”
2. [p. 403] “The actions performed by the main memory for any one variable are
totally ordered; that is, for any two actions performed by the main memory
on the same variable, one action precedes the other.”
3. [p. 403] “It is not permitted for an action to follow itself.”
JLS rules 2
1. [p. 403] “Each load action by a thread is uniquely paired with a read action
by the main memory such that the load action follows the read action.”
2. [p. 403] “Each store action by a thread is uniquely paired with a write action
by the main memory such that the write action follows the store action.”
3. [p. 405] “For every load action performed by any thread t on its working
copy of a variable v, there must be a corresponding preceding read action by
the main memory on the master copy of v, and the load action must put into
the working copy the data transmitted by the corresponding read action.”
4. [p. 405] “For every store action performed by any thread t on its working
copy of a variable v, there must be a corresponding following write action
by the main memory on the master copy of v, and the write action must
put into the master copy the data transmitted by the corresponding store
action.”
156 Y. Gurevich, W. Schulte, and C. Wallace
5. [p. 405] “Let action A be a load or store by thread t on variable v, and let
action P be the corresponding read or write by the main memory on variable
v. Similarly, let action B be some other load or store by thread t on that
same variable v, and let action Q be the corresponding read or write by the
main memory on variable v. If A precedes B, then P must precede Q.”
These rules restrict the ways in which a thread’s load and store actions may
be interleaved with a variable’s read and write actions. First, we note that the
term “uniquely paired” in Rules 2.1 and 2.2 is ambiguous. A statement of the
form “Each element x of X is uniquely paired with an element y of Y such that
φ(x, y)” has a lenient interpretation: “For all x in X there is a unique y in Y
such that φ(x, y)”. It also has a strict interpretation: “For all x in X there is
a unique y in Y such that φ(x, y), and for all y in Y there is a unique x in X
such that φ(x, y)”. The lenient interpretation allows spurious y’s; that is, y’s
that are not paired with any x. Which interpretation shall we use? The strict
interpretation is what the authors of the JLS intended [21], so we adopt it here.
However, we find that a lenient interpretation of the term in Rule 2.1 has some
advantages that make it worth discussing.
Read/load order. For every load action L by a thread t on a variable v, there
is a read action R < L by v such that L loads in the value read out at R
(Rules 2.1 and 2.3). This supports our conception of access messages: every load
action must load in the value of an access message issued by a previous read
action. Furthermore, a thread may not load in the same access message twice
(Rule 2.5).
The choice of interpretation of “uniquely paired” determines whether every
access message must be loaded in. The strict interpretation, under which every
access message is loaded in, is simpler and more appealing from a logical perspec-
tive, which is why it is the intended interpretation [21]. But note that spurious
read actions are innocuous: failing to load in a given access message has only
the effect of making a thread’s working memory less up-to-date than possible.
Furthermore, the lenient interpretation allows a higher degree of independence
of threads and variables: a variable is free to issue access messages without con-
cern for whether they are eventually loaded in. For instance, updated master
values of an important variable could be broadcast to threads, with each thread
deciding whether to load the value in; or blocks (pages) of master values could
be sent to a thread, with the thread selecting which values to load in.
Store/write order. For every store action S by a thread t on a variable v,
there is a write action W > S by v such that W writes in the value stored out
at S (Rules 2.2 and 2.4). Using our message-passing parlance, every store action
sends an update message which must be written in by a following write action.
As with the case of read and load actions, how we interpret “uniquely paired”
determines whether every write action is an action of writing in an update mes-
sage. Here, adopting the strict interpretation seems less controversial. It is not
clear what value a spurious write action writes. At best, it simply rewrites the
value that is already in main memory, in which case it is useless. But if it writes
Investigating Java Concurrency 157
interpretation of “uniquely paired” for the pairing of read and load actions. In
our example, t could simply ignore the access message m0 and still have its
update message m written.
To represent the relative age of messages, we introduce the relation <, and
the term oldest? to determine whether a given message has no predecessors.
term oldest?(m): (not ∃m0 : Msg) m0 < m
The JLS does not impose any particular means of ordering messages, so we
avoid doing so here by making the function external. We restrict attention to
runs in which the function < behaves in the expected way.
The terms readOK? and writeOK? determine whether the given action is
allowed by the JLS rules. Rules 2.5 and 3.2 allow a variable v to read out to a
thread t only if every assign action by t on v has been followed by corresponding
store and write actions. Rule 2.5 allows t to write a message in from v only if
the message is the oldest pending message from t to v.
term readOK?(t): not (freshAssign?(t, Self) or (∃m: UpdMsg(t, Self)))
term writeOK?(m, t): oldest?(m)
We define rules for load, store, use and assign actions, as well as the actions
of creating a variable and creating a thread.
rule Load m in from v: workingValue(Self, v) := value(m)
usableValue?(Self, v) := true
AccMsg(m) := false
rule Store out to v: freshAssign?(Self, v) := false
extend UpdMsg(Self, v) with m
value(m) := workingValue(Self, v)
Investigating Java Concurrency 161
The terms loadOK?, storeOK?, useOK? and assignOK? determine whether the
given action is allowed by the JLS rules. Rule 2.5 allows a thread t to load a
message in from a variable v only if the message is the oldest pending message
from v to t. Rules 3.3–5 allow t to store out to v only if there has been an assign
action by t on v without a following store action. Rules 3.4–5 allow t to use v
only if there has been an assign or load action that put a value in t’s working
copy of v. Rules 2.5 and 3.2 allow t to assign to v only if every access message
from v to t has been loaded in.
term loadOK?(m, v): oldest?(m) and not freshAssign?(Self, v)
term storeOK?(v): freshAssign?(Self, v)
term useOK?(v): usableValue?(Self, v)
term assignOK?(v): (not ∃m: AccMsg(v, Self))
3 Volatile Variables
The basic model for threads and shared variables, as presented in §2, permits
optimizations that reduce the amount of communication between agents and
thus enhance the performance of multithreaded programs.
Communication between threads and the main memory can be lessened
through caching: keeping values in working memory without transmitting them
to main memory or getting new values from main memory. Instead of consulting
the main memory every time it uses a variable, a thread may service several use
actions on the same variable with a single load action. It may also service several
assign actions with a single store action, sending only the last of these assigned
values to main memory.
Caching may have undesirable results, particularly for variables that are as-
signed to and used frequently by different threads. Cached values may become
outdated as other threads store out to the main memory. Also, caching assigned
values prevents other threads from viewing the newly assigned values as they
arise.
Communication between variables can be avoided altogether, allowing them
to operate independently of one another. While Rule 2.5 dictates that the order
of a thread’s actions on a single variable is also followed by the variable, the order
162 Y. Gurevich, W. Schulte, and C. Wallace
of its actions over different variables need not be followed in the main memory.
Consequently, variables need not coordinate their actions among themselves.
Hence for certain variables, a stricter discipline is necessary. Java allows the
programmer to declare variables volatile at the time of their creation. Volatile
variables follow a policy that disallows the optimizations described above.
We modify the ASM to model operations on volatile variables. We add a uni-
verse VolVar, representing the set of volatile variables. Every member of VolVar
is also a member of Var. The rule Create var requires a slight change: a new
variable may be marked as volatile.
The JLS imposes the following additional conditions on volatile variables.
JLS rules 4 [p. 407] “Let t be a thread and let v and w be volatile variables.”
1. “A use action by t on v is permitted only if the previous action by t on v
was load, and a load action by t on v is permitted only if the next action by
t on v is use. The use action is said to be “associated” with the read action
that corresponds to the load.”
2. “A store action by t on v is permitted only if the previous action by t on
v was assign, and an assign action by t on v is permitted only if the next
action by t on v is store. The assign action is said to be “associated” with
the write action that corresponds to the store.”
3. “Let action A be a use or assign by thread t on variable v, let action F be
the load or store associated with A, and let action P be the read or write of
v that corresponds to F . Similarly, let action B be a use or assign by thread
t on variable w, let action G be the load or store associated with B, and let
action Q be the read or write of v that corresponds to G. If A precedes B,
then P must precede Q.”
(W < R). In other words, if t sends a volatile update message m before using a
volatile access message m0 , then m must be written in before m0 is sent.
Thus sending m0 is allowed only after the point in time when m and all other
volatile update messages from t have been written. If m0 is sent but not used
before this point in time, it can never be used, hence violating Rule 4.1. To avoid
this, v must not issue an access message to t as long as there is a pending volatile
update message from t, and t must not issue a volatile update message as long
as there is a pending volatile access message to t. Furthermore, volatile access
and update messages must not be sent to and from t concurrently.
We define some additional functions to help enforce these conditions. Rule 4.1
requires a thread t to use a volatile variable v if and only if it has loaded in a
message from v without using its value. If a thread has loaded in a message from
a volatile variable but not used its value, the function msgToUse returns this
message. If a thread has assigned a value to a volatile variable but not stored
the value out, the function msgToStore returns the volatile update message.
The terms readOK? and writeOK? include extra conditions on volatile va-
riables. Rule 4.3 allows a volatile variable v to read out to a thread t only if
all assign actions by t on volatile variables have been followed by correspon-
ding store and write actions. To readOK? we add the conjunct VolVar(Self) ⇒
(∀v: VolVar) (not ∃m: UpdMsg(t, v)). Rule 4.3 allows v to write an update mes-
sage from t in only if the message has been stored out and is the oldest pen-
ding volatile message from t. To writeOK? we add the conjunct VolVar(Self) ⇒
m 6= msgToStore(t, Self).
We modify the rules for load and store actions. A volatile access message is
removed when it is used, rather than when it is loaded in. A volatile update
message is created through an assign action, rather than through a store action.
The term loadOK? includes an extra condition on volatile variables. Rule 4.1
allows a thread t to load a message in from a volatile variable v only if it has used
all previous loaded access messages from v. Rule 4.2 allows t to load a message
in from v only if it has stored out all previously assigned values to v. To loadOK?
we add the conjunct VolVar(v) ⇒ not usableValue?(Self, v).
We modify the rules for use and assign actions. A use action on a volatile
variable removes the volatile access message from the AccMsg universe. An assign
action on a volatile variable generates a UpdMsg.
Investigating Java Concurrency 165
The terms useOK? and assignOK? include extra conditions on volatile varia-
bles. Rule 4.1 allows a thread t to use its working value of a variable v only
if it has loaded in a message from v without using its value. Rule 4.3 requires
this message to be the oldest pending volatile access message to t. To useOK?
we add the conjunct VolVar(v) ⇒ oldest?(msgToUse(Self, v)). Rule 4.2 allows t
to assign a value to v only if all of t’s previous assigns to t have been stored
out, and Rule 4.3 requires that there be no pending volatile access messages
to t. To assignOK? we add the conjunct VolVar(v) ⇒ not (freshAssign?(Self, v)
or usableValue?(Self, v) or (∃m: AccMsg(v 0 , Self))).
If two volatile variable variables read out to t concurrently, the corresponding
load actions will be ordered, since t can only perform one load action at a time.
But the read actions will not be ordered, thereby violating Rule 4.3. We restrict
attention to runs in which this does not occur.
Rules 4.1 and 4.2 ensure that if a load or assign action on a VolVar occurs,
a corresponding use or store action will occur sometime in the future. Similarly
to Rules 2.1 and 2.2, these rules can be flouted by delaying a use or store action
indefinitely. We restrict attention to runs that avoid this situation.
4 Locks
Certain situations require that a thread be able to perform a series of operations
without interference from other threads. In general, the programs of threads
t1 . . . tn may have critical regions that they should avoid executing concurrently.
It seems reasonable to try to prevent any ti and tj from entering their critical
regions simultaneously. A natural way to solve this problem in an object-oriented
framework is to have one critical object related to all these critical regions. All
the threads may refer to the critical object, but only one thread may own the
object at any given time, and only the owner can execute its critical region. Java
provides support for this sort of mutual exclusion. The critical regions are called
synchronized regions.
In Java, each object has a unique lock associated with it. A thread gains
entry into a synchronized code region of its program by performing a lock action
166 Y. Gurevich, W. Schulte, and C. Wallace
on the lock of the critical object. The thread then holds the lock until it exits
the synchronized code region and performs an unlock action on the lock.
Various threads may issue lock or unlock actions on the same lock. The lock
and unlock actions on a single lock are performed in conjunction with an arbiter,
which restricts the number of threads holding a single lock to one at a time. The
JLS speaks of main memory as this arbiter. As with variable master copies, we
prefer to think of each lock as controlled by a master agent, rather than by the
(possibly distributed) main memory.
We modify the ASM to model operations on locks. The agents are threads,
variable masters, and lock masters. We represent locks as members of the uni-
verse Lock. As with variables, we identify lock master agents with the locks they
control, so a member of the universe Lock is both a lock and a lock master.
Objects are members of the universe Object. A LockActionType is either lock or
unlock, and a LockAction is a pair consisting of a LockActionType and a Lock on
which to perform the given action. The function lock maps each object to its
lock.
To see how the introduction of locks ensures that only one thread enters its
synchronized region, we must consider the JLS rules for the concurrent behavior
of locks.
JLS rules 5
1. [p. 403] “The actions performed by the main memory for any one lock are
totally ordered; that is, for any two actions performed by the main memory
on the same lock, one action precedes the other.”
2. [p. 403] “Each lock or unlock action is performed jointly by some thread
and the main memory.”
Rule 5.1 supports our view of independent lock master agents. The actions
on a single lock are ordered linearly, but actions on different locks may occur
concurrently.
1. [p. 406] “A lock action by T on L may occur only if, for every thread S
other than T , the number of preceding unlock actions by S on L equals the
number of preceding lock actions by S on L.”
2. [p. 406] “An unlock action by thread T on lock L may occur only if the
number of preceding unlock actions by T on L is strictly less than the number
of preceding lock actions by T on L.”
Rule 6.1 ensures that a thread’s hold on a lock is exclusive, and Rule 6.2
ensures that for every unlock action there is a unique preceding lock action.
But note that the rules allow several lock actions on the same lock to occur in
succession, with no intervening unlock action. We find it convenient to think of a
thread as building up a number of claims on a lock. A lock action adds a claim,
and an unlock action takes one away. Rule 6.1 states that only one thread may
Investigating Java Concurrency 167
have a positive number of claims on the lock; furthermore, a thread may acquire
multiple claims on a lock and surrenders the lock when and only when it has
given up all claims. Rule 6.2 states that a thread may not release a claim on a
lock it does not hold; the number of claims it has on a lock cannot be negative.
These rules ensure that the synchronization mechanism restricts synchronized
code regions to one thread at a time. At most one thread at a time may have a
positive number of claims on a given lock, and so if several threads need to lock
the same lock, they must compete for it. Only the one with a positive number of
claims on the lock is allowed into its synchronized region; the others must wait.
The function claims returns the number of claims a given thread has on
a given lock. The function synchAction returns the action that the thread is
requesting (if any).
A thread may only continue the execution of its program if it is active;
i.e., not synchronizing. We change the thread module accordingly, by guarding
the rule Execute program with the term active?(Self).
term active?(t): undef?(synchAction(t))
To the rule Execute program, we add the options Synch and Create object. We
add rules for threads’ synchronization actions with locks. A thread synchronizes
with a lock for either a lock action or an unlock action.
rule Synch on ` to perform act: synchAction(Self) := (act, `)
rule Synch: choose obj: Object
choose act: LockActionType
Synch on lock(obj) to perform act
The rule Create object creates an object and associates it with a new lock.
We also modify Create thread to associate each new thread with a new lock.
rule Create object: extend Object with obj
extend Lock with `
lock(obj) := `
The following JLS rules place some guarantees on the contents of a thread’s
working memory before and after synchronization actions.
JLS rules 7 [p. 407] “Let T be any thread, let V be any variable, and let L be
any lock.”
1. “Between an assign action by T on V and a subsequent unlock action by T
on L, a store action by T on V must intervene; moreover, the write action
corresponding to that store must precede the unlock action, as seen by main
memory.”
2. “Between a lock action by T on L and a subsequent use or store action by
T on a variable V , an assign or load action on V must intervene; moreover,
if it is a load action, then the read action corresponding to that load action
must follow the lock action, as seen by main memory.”
168 Y. Gurevich, W. Schulte, and C. Wallace
The terms lockOK? and unlockOK? determine whether a given action is allo-
wed. A lock action for a thread t on a lock ` is allowed only if t is synchronizing
for a lock action on `. Rule 6.1 allows such a lock action only if all threads other
than t have no claims on `. Rules 3.2 and 7.2 require that all previously assigned
values have been stored out. Rules 2.1, 2.3 and 7.2 require that all access messa-
Investigating Java Concurrency 169
ges have been loaded in, and Rules 4.1 and 7.2 require that all access messages
from volatile variables have been used.
term lockOK?(t):
synchAction(t) = (lock, Self) and ((∀t0 : Thread: t0 6= t) claims(t0 , Self) = 0)
and (not ∃v: Var) freshAssign?(t, v) or (∃m: AccMsg(v, t))
and (not ∃v: VolVar) usableValue?(t, v)
term unlockOK?(t):
synchAction(t) = (unlock, Self) and claims(t, Self) > 0
and (not ∃v: Var) freshAssign?(t, v) or (∃m: UpdMsg(t, v))
Prescient store actions must obey the following rules set out in the JLS:
Notice that t may issue a use action U on v between P S and RA. If the
presciently stored value were to appear in t’s working memory before t put it
there at RA, t could end up using the presciently stored value prematurely. To
prevent this, Rule 8.3 prohibits t from loading in from v between P S and RA.
For the same reason, albeit less obviously, Rule 8.4 prohibits any lock action for
t between P S and RA. This is needed because if a lock action for t were to occur
and then t were to use v between P S and RA, Rule 7.2 would require a load
action between P S and U , and such a load action is forbidden by Rule 8.3.
Investigating Java Concurrency 171
6 Thread Objects
and cannot be restarted. It is possible to stop a thread that has not started; if
this happens, the thread will never become alive. It is not stated explicitly in
the JLS what happens if start is invoked upon a thread that is already alive.
However, the intent seems to be that the invoker of start throws an exception
and nothing happens to the invokee [12].
We define functions started? and stopped? to represent the status of each
thread. We also define rules Start and Stop, which update them appropriately.
The JLS rules impose some obligations on threads which they may not be
able to fulfill after they have stopped. By Rules 2.1 and 2.3 (following the strict
interpretation of “uniquely paired”), every access message must be loaded in.
By Rule 4.1, every load action on a volatile variable must be followed by a
corresponding use action, and by Rule 4.2, every assign action to a volatile
variable must be followed by a corresponding store action. The JLS does not
make it clear whether these obligations extend to threads that have stopped.
Furthermore, it is not clear which choice is the more reasonable one. For instance,
in some cases it may be desirable to have the last assign actions of a thread stored
out, while in other cases it may be clearly undesirable; for example, consider a
thread which is stopped because it is computing erroneous results. Officially, this
is still an open issue [21]. For simplicity, we take the view that stopped threads
are not obligated to do any further actions.
The JLS rules impose some obligations on variables which may not be fulfilled
by the time the execution of all threads terminates; in particular, by Rule 2.4,
every store message must be written. Thus it may still be necessary for variables
to write some update messages in even after all threads have terminated.
A thread that is alive can be suspended, in which case it remains alive but its
execution engine does nothing. A thread is suspended when some thread (pos-
sibly itself) invokes the method suspend upon it. A suspended thread resumes
(becomes unsuspended) when some other thread invokes the method resume
upon its Thread instance. It may be useful to suspend a thread when it can-
not make progress by itself, freeing resources for other threads that can make
progress. Can a suspended thread issue load or store actions? Officially, this is
another unresolved issue [21]. Here we choose the more permissive interpretation,
allowing suspended threads to issue read and load actions.
We add the function suspended? to represent the suspended status of each
thread. We also define rules Suspend and Resume, which update this function.
A thread may be marked as a daemon. Such threads are intended to exe-
cute only in conjunction with non-daemon threads, performing “housekeeping”
actions which assist non-daemon threads, but no actions useful in themselves.
For this reason, once all non-daemon threads have stopped, execution of all
threads halts. A program starts with a single non-daemon thread. A new thread
starts with the daemon status of the thread that creates it, but its status can
be changed via the setDaemon method.
To our ASM we add the function daemon?, which determines a thread’s
current daemon status, and the rule Set daemon status, which updates this fun-
ction. We also modify the rule Create thread . A thread is added to the Object
Investigating Java Concurrency 173
We modify the modules for variables and locks. If all non-daemon threads
have terminated, the only action a variable may take is to write in update mes-
sages, so in the Var module we guard the read option with not HaltAllThreads?.
There are no actions for locks to take once all non-daemon threads have termi-
nated, so in the Lock module we guard the lock and unlock options with the
term not HaltAllThreads?.
While a thread holds a particular lock, other threads in need of that lock are
not allowed to proceed. In certain cases, a thread holding a lock may reach a
state from which it cannot progress by itself; in such a case, suspending the
thread may not be a solution, as the thread would still hold the lock even when
suspended. It would be desirable for the thread to give up control and release
its locks temporarily, so that other threads may acquire them.
Java has built-in support for this, through the mechanisms of waiting and
notification. If a thread has a claim on the lock of an object, it may release
all its claims on the lock and disable itself by invoking the method wait of
class Object on the object. Another thread may signal to the waiting thread
that further progress is possible through the notify or notifyAll methods. In
this way, a waiting thread may resume execution when it is possible, without
repeatedly checking its state. Each object has a wait set, empty when the object
is created, which contains all threads waiting for the lock on the object.
The method wait is invoked by a thread t on an object obj. The thread t must
hold the lock of obj; if it does not, an exception is thrown. Let k be the number
of claims t has on the lock of obj; then k unlock actions are performed, and t is
added to the wait set of obj and disabled. When t is re-enabled, it attempts to
regain k claims on the lock; it may need to compete with other threads to do
this. Once the lock claims are restored, the wait method terminates.
We modify the ASM to model waiting and notification actions. A thread goes
through several phases while it waits on a lock. First it synchronizes with the
174 Y. Gurevich, W. Schulte, and C. Wallace
lock to release all claims on the lock. Then it waits to be notified by another
thread. Once it is notified, it synchronizes with the lock to regain all the claims it
released. For a thread that is waiting on a lock, the function waitMode gives the
current phase of the thread, the function waitLock gives the lock on which it is
waiting, and claimsToRegain gives the number of claims that it has (temporarily)
released.
We introduce a rule for a thread’s actions while waiting on a lock. Waiting
involves synchronization with the lock during the unlock and relock phases. Note
that the waiting thread does not change its own mode from wait to relock; this
is done by another thread, through a notification.
rule Start to wait: choose obj: Object: claims(Self, lock(obj)) > 0
waitMode(Self) := unlock
waitLock(Self) := lock(obj)
claimsToRegain(Self) := claims(Self, lock(obj))
rule Continue to wait:
if waitMode(Self) = unlock then
if claims(Self, waitLock(Self)) > 0 then
Synch on waitLock(Self) to perform unlock
else waitMode(Self) := wait
elseif waitMode(Self) = relock then
if claims(Self, waitLock(Self)) < claimsToRegain(Self) then
Synch on waitLock(Self) to perform lock
else waitMode(Self) := undef
When a thread stops, the method notifyAll is invoked upon its object.
This allows a straightforward implementation of a technique called thread joins.
Consider a scenario where a thread t0 is expected to start executing only after
another thread t has stopped. One way of implementing this is to have t0 join
with t by waiting until t has stopped before proceeding. Since notifyAll is
invoked when a thread stops, t0 may join with t by simply waiting on t’s object.
Since stopping a thread involves invoking notifyAll on its representative object,
we change our ASM rule for stopping threads accordingly.
We point out a subtle issue. It is not clear from the JLS whether notifica-
tion precedes stopping or vice versa when a thread stops. However, it seems
reasonable that if stopping and notification do not occur as a single action, then
notification must precede stopping (at least in the case where a thread stops
itself). This leaves open the possibility that other threads may be notified of the
thread’s stop action before the thread has actually stopped, particularly if the
notification process takes a relatively long time.
Threads may execute waiting or notification actions. Execution of a Java pro-
gram does not proceed while a thread is waiting, so to the term active? we add the
conjunct waitMode(t) 6= wait. To the rule Execute program, we add the options
Start to wait, Notify and NotifyAll . We also add the guard def?(waitMode(Self));
if this evaluates to true, then the rule Continue to wait fires; otherwise, one of
the other options is chosen.
8 Conclusion
The Java concurrency model is currently in a state of flux. Researchers are pro-
posing modifications to the model, to allow common compiler optimizations and
programming practices that are currently prohibited by the model [19]. It is our
belief that a satisfactory successor to the current model must start with a firm
foundation. The specification of the model must not be subject to multiple in-
terpretations, as is the description in the JLS. It is equally important that the
specification be accessible to programmers who wish to use concurrency. While
multithreaded programming is inherently complicated, the method of documen-
tation should not exacerbate the problem.
We feel that this work has several things to offer the Java community. Our
alternative view of Java concurrency brings to light some issues that might other-
wise remain hidden in the JLS description. As the ASM model is mathematically
precise, it can stand as an unambiguous cross-platform standard for the langu-
age. Our account of Java concurrency has an imperative flavor that is familiar to
programmers, yet we are not restricted to a purely imperative approach; we are
free to use a declarative style wherever it is more natural to do so. By implemen-
ting the concurrency model (albeit in a completely abstract way) we can readily
see how the various constraints of the JLS definition interact with one another.
This is much less obvious if constraints are presented in isolation, as they are in
the JLS. Finally, programmers interested in multithreaded Java can explore the
model by using the ASMGofer prototype. We believe that ASMs are a useful
specification tool for both the current and future models of Java concurrency.
176 Y. Gurevich, W. Schulte, and C. Wallace
References
1. I. Attali, D. Caromel, and M. Russo. A formal executable semantics for Java. In
Proceedings of the OOPSLA ’98 Workshop on the Formal Underpinnings of Java,
1998.
2. E. Börger. Why use Evolving Algebras for hardware and software engineering? In
Proceedings of SOFSEM, 1995.
3. E. Börger and W. Schulte. A programmer friendly modular definition of the seman-
tics of Java. In J. Alves-Foss, editor, Formal Syntax and Semantics of Java. Springer,
1998.
4. P. Cenciarelli, A. Knapp, B. Reus, and M. Wirsing. ¿From sequential to multi-
threaded Java: An event-based operational semantics. In M. Johnson, editor, Alge-
braic Methodology and Software Technology, pages 75–90. Springer, 1997.
5. E. Coscia and G. Reggio. A proposal for a semantics of a subset of multi-threaded
“good” Java programs. In Proceedings of the OOPSLA ’98 Workshop on the Formal
Underpinnings of Java, 1998.
6. A. Gontmakher and A. Schuster. Java consistency: Non-operational characterizati-
ons for Java memory behavior. Technion/CS Technical Report CS0922, 1997.
7. J. Gosling, B. Joy, and G. Steele. The Java Language Specification. Addison-Wesley,
1996.
8. Y. Gurevich. Evolving Algebras: an attempt to discover semantics. In G. Rozenberg
and A. Salomå, editors, Current Trends in Theoretical Computer Science, pages 266–
292. World Scientific, 1993.
9. Y. Gurevich. Evolving Algebras 1993: Lipari guide. In E. Börger (editor), Specifica-
tion and Validation Methods, Oxford University Press, 1995, 9–36.
10. Y. Gurevich. May 1997 draft of the ASM guide. Available at [22], 1997.
11. Y. Gurevich, W. Schulte and C. Wallace. Investigating Java concurrency using
Abstract State Machines. Technical Report 2000-04, Department of Computer &
Information Sciences, University of Delaware, 1999.
12. C. Horstmann and G. Cornell. Core Java 1.1, volume II: Advanced Features. Sun
Microsystems Press, 1998.
13. Sun Microsystems. Java technology home page. http://java.sun.com/.
14. A. Jolin. Java’s atomic assignment: The key to simpler data access across threads.
Java Report 3(8), 27–36, 1998.
15. D. Lea. Concurrent Programming in Java. Addison-Wesley, 1997.
16. M. MacBeth, K. McGuigan and P. Hatcher. Executing Java threads in parallel in
a distributed memory environment. In Proceedings of IBM CASCON, 1998.
17. S. Oaks and H. Wong. Java Threads. O’Reilly and Associates, 1997.
18. G. Plotkin. Structural Operational Semantics (Lecture notes). Technical Report
DAIMI FN-19, Aarhus University, 1981.
19. W. Pugh. Fixing the Java memory model. In Proceedings of ACM Java Grande,
1999.
20. Joachim Schmid. ASMGofer home page. http://www.tydo.de/AsmGofer/.
21. G. Steele. Personal communication.
22. Univ. of Michigan. ASM home page. http://www.eecs.umich.edu/gasm/.
23. Univ. of Paderborn. ASM home page. http://www.uni-paderborn.de/cs/asm/.
Verifying Compilers and ASMs
or
ASMs for Uniform Description of
Multistep Transformations
Universität Karlsruhe
Fakultät für Informatik
{ggoos,zimmer}@ipd.info.uni-karlsruhe.de
1 Introduction
Many statistics attribute more than 50% of all software failures to problems in
requirement engineering whereas programming errors account for a much smal-
ler percentage, cf. [23,49]. But what if everything in software development was
correct except that errors were introduced during compilation? This case is not
as infrequent as some people think, cf. [52,37,12,13,14]. Especially programs of-
ten show different behavior with and without optimization. The generated code
should exactly behave like the source program; this is of utmost importance
especially in safety-critical applications. The requirement can only be ensured
by a compiler which verifiably produces correct code. But despite many efforts,
cf. e. g. the efforts for validating Ada-compilers, no compiler on the market for
a realistic programming language such as Ada, C, C++ or Java is fulfilling
this requirement. It is therefore no surprise that safety surveillance institutions
such as the ‘‘Technische Überwachungsvereine’’ (TÜV) in Germany often do not
accept software written in high-level languages but instead check the resulting
assembly code and require the use of non-optimizing compilers.
McCarthy and Painter, [36], were first considering correctness of compi-
lation but for arithmetic expressions only. Many people dealt with the problem
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 177–202, 2000.
c Springer-Verlag Berlin Heidelberg 2000
178 G. Goos and W. Zimmermann
2 Correctness of Compilation
We consider a pair (PL,ML) consisting of an imperative or object-oriented pro-
gramming language PL such as Ada, C, C++, Sather-K, [28], or Java and
a microprocessor such as the DEC Alpha represented by its machine language
ML. For simplicity we restrict ourselves to sequential programming languages.
The translation of a source program π in PL to a target program π 0 = C(π)
in ML is certainly correct if π 0 shows the same behavior as π, i. e. on all inputs
π 0 is producing the same outputs as π.
A state of a computation is called observable if it is the initial or final state or,
if the operation leading into this state is an input or output operation. Observa-
ble states are the only states representing an effect noticeable in the environment
of a computation.1 If we consider states q, q 0 of the execution of π and π 0 res-
pectively as sets of state variables then the requirement same behavior asks for
1
The programmer or compiler writer may, at his discretion, designate further states,
e. g. procedure entries or exits, as observable.
Verifying Compilers and ASMs 179
x := ?
x := 1 x := 0
x := ?
x := 1 x := 0
x := ?
an arbitrary path:
do true -> x := 1
od
is a correct translation of this program. For this reason we can only require that
each sequence of observable states of the translated program π 0 = C(π) possesses
a corresponding state sequence of the source program π; the converse is not true.
Ideally we want to preserve the termination behavior of the source program.
For reasons discussed in the next section this is not possible in a realistic envi-
ronment: we must admit that the target program π 0 is prematurely terminating
due to excessive resource demands. Thus, during compilation we cannot pre-
serve total but only partial correctness. Altogether this leads to the following
definition of correct translation:
Definition 1. A target program π 0 is a correct translation of a source program
π, π 0 = C(π), if for all inputs one of the following conditions applies:
180 G. Goos and W. Zimmermann
4 Compiler Architecture
larger phrases and the program as a whole is then derived by substituting the
semantics of subphrases into the semantics of larger ones. This property is called
horizontal compositionality (of language elements).
There may be syntactic and semantic limitations for the admissibility of sub-
phrases. A program is termed statically correct if these conditions are fulfilled.
Compilers check these conditions during semantic analysis and refuse to gene-
rate code for incorrect programs. When discussing verifying compilers we may
restrict attention to statically correct programs.
Horizontal compositionality permits to deal with one language element at
a time during the vertically decomposed translation steps. It thus adds to the
structuring of the compiler. The method is, of course, not applicable before we
have determined the phrase structure and during optimization steps which on
purpose consider several language elements together.
This raises the question how we can certify the correctness of the abstract
syntax tree for a given program text. The requirement same behavior does not
help since the dynamic behavior is only defined for the phrases visible in the
syntax tree whereas the program text is only serving as a vehicle for deriving
this tree. Similarly, as soon as we have decided which instruction sequences cor-
rectly represent the intended behavior of phrases in the source program we have
established the correctness of the target program. The remaining assembly and
linking phase only decides about the proper encoding of the resulting instruc-
tions but does not change the semantic meaning. So to say, the assembly phase
acts like the inverse of the transformation source text → syntax tree, but on a
different level of abstraction.
Thus, basically only the mapping phase in Fig. 2 is concerned with the se-
mantic correctness of compilation. The remaining phases deal with decoding and
encoding the structures which carry semantic meaning. Especially the transla-
tion process seen as a mapping between source and target semantics is confined
to the mapping phase only. This insight has been ignored for a long time in the
discussion about correct compilation.
For lexical analysis, parsers, code selectors and other phases routinely gene-
rators are used for deriving the corresponding compiler phase. Verifying such
phases would require to verify the correctness of the corresponding generators,
a very heavy and burdensome task. Previous work on correct compilation thus
relied on hand-writing these phases and verifying the hand-written code. In the
next section we show that there is an easier way of dealing with this problem
when we relax the requirements and only request that (for certain source pro-
grams) the output of a phase is correct instead of requesting that the code of
the compiler phase is producing correct results under all circumstances. Under
this condition we can use unverified generators and other tools as in traditional
compilers and nevertheless trust the resulting code.
Verifying Compilers and ASMs 183
Program checking was originally introduced for algorithms by Blum and Kannan,
[1]. We present it here as in [26] for verifying the outputs of systems such as com-
pilers or parts thereof.
Let C be a program implementing a function f : I → O with precondition
P (x) and postcondition Q(x, C(x)) on input x. Let checker (x, y) : Bool be a
function that returns the value of Q(x, y). Consider the program
function C 0 (x : I) : O
y := C(x);
if checker (x, y) then return y
else abort;
end;
and assume that C 0 (x) does not abort. Then the result Q(x, C 0 (x)) holds if
checker is correctly implemented. Thus, if C 0 does not abort then the result
y = C(x) fulfills its postcondition no matter how it was computed. In particular,
the partial correctness of C 0 does not depend on the partial correctness of C.
Therefore we only need to verify checker but not C for getting a verified result.
In practice the verification of checker is often much simpler than the verifi-
cation of C. This is particularly true if C contains a lot of optimizations which,
from the correctness point of view, are irrelevant as long as they maintain some
simple correctness conditions. E. g. the register allocator within a compiler is
correct as long as the values assigned to the same register have non-overlapping
lifetimes; an optimizing register allocator must maintain this condition while it
is devoting most of its effort to avoiding spill code as far as possible; its quality
in this respect does not influence its correctness.
Generated C Code
22.000 600 KB 300 KB
Impl. IS-Frontend
500 (Parser) 14 KB
Checker (Sather-K) +100 (Compare) 3 KB 200 KB
+700 (AST) 30 KB
184 G. Goos and W. Zimmermann
Generated C Code
18.000 400 KB 500 KB
Impl. MIS Code-Selection
500 (Parser)
Checker (Sather-K) +300 (Checker) 200 KB
+400 (MIS)
q q
i i+1
ρ ρ
q'
q'
i i+1
we must look into the details of the representation of states and how they are
dynamically transformed by executing statements and instructions respectively.
According to our experience abstract state machines, [29, ], provide adequate
means to formally handle this problem.
An Abstract State Machine (ASM) is a tuple (Σ, ΦInit , ΦInv , Trans) where Σ
is a signature, ΦInit and ΦInv are sets of Σ-formulas (the initial and invariant
conditions), and Trans is a finite set of transition rules. A transition rule is a
pair (ϕ, Updates), denoted by
if ϕ then Updates
where ϕ is a Σ-formula and Updates is a set of pairs t1 := t2 of Σ-terms, called
updates. The set of states is the set Alg(Σ) of Σ-algebras that satisfy the Σ-
formulas in ΦInv , i. e. q is a model for ΦInv in the sense of logic, q |= ΦInv ; [[·]]q
denotes the interpretation function of symbols of Σ in Σ-algebra q. A state is
initial iff q |= ΦInit .
Remark 1. This definition of ASMs slightly differs from the usual definition.
Instead of defining the states to be Σ-algebras, we define them as sets of Σ-
algebras that satisfy a set of formulas. By this modification we can prove desired
properties of ASMs by logical calculi more easily.
The state transition relation → is based on transition rules. Intuitively, a
transition rule (ϕ, Updates) fires if its condition ϕ is satisfied. Formally speaking
the condition is true in state q iff q |= ϕ. The updates t1 := t2 of the rule are
then executed and change the interpretation of some symbols in the state. A
state q is final iff no rule fires in q.
Let Exec(q) be the set of updates executed in state q. Exec(q) is consistent iff
. . .
for all2 f (t1 , . . . , tk ) := t, f (t01 , . . . , t0k ) := t0 , q |= t1 = t01 ∧ · · · ∧ tk = t0k ⇒ t = t0
holds. Then the state transition relation q → q̄ is defined by
[[t]]q ∃ f (t1 , . . . , tn ) := t ∈ Exec(q)
[[f ]]q̄ (a1 , . . . , an ) = with [[ti ]]q = ai , i = 1, . . . , n
[[f ]]q (a1 , . . . , an ) otherwise
for each k-ary function symbol f of Σ iff Exec(q) is consistent, q̄ |= ΦInv and
[[T ]]q = [[T ]]q̄ for all sorts of Σ (i.e. the interpretation of sorts is not changed by
the state transition).
– Q = {q ∈ Alg(Σ) | q |= ΦInv }
– I = {q ∈ Alg(Σ) | q |= ΦInv ∪ ΦInit }, and
– → is the transition relation of A.
(i) I ⊆ DOM(φ),
(ii) φ(I) ⊆ I 0 ,
(iii) φ(q0 ) →0 φ(qn ) for every sub-run hq0 , . . . , qn i of S with q0 , qn ∈ DOM(φ),
φ(q0 ) 6= φ(qn ), and q1 , . . . , qn−1 6∈ DOM(φ), cf. Fig. 4.
188 G. Goos and W. Zimmermann
qi0 qj0
φ φ
q0 q1 q2 q3 ···
φ φ φ φ
Remark 2. For final states q of S it is not ensured that q ∈ DOM(φ) and φ(q) is
final in S 0 . It seems desirable that properties analogous to (i) and (ii) hold for
final states. However, for constructing correct compilers the definition above is
exactly what is needed: A final state q 6∈ DOM(φ) represents an abortion of an
execution due to violation of resource limitations.
We are interested in these stepwise refinements since they are used in horizon-
tal composition and by translation steps which act like macro-substitutions. But
compilers based on pure macro-expansion produce inefficient code in particular
for RISC-processors. Stepwise refinement for example would forbid optimizati-
ons that change the order of state transitions such as instruction scheduling and
code motion.
To cope with this problem we replace in Theorem 2 the abstraction function
φ2 by a more general relation ρ ⊆ Q3 ×Q2 but still request that S3 is a refinement
of S1 . Then the relation φ0 = φ1 ◦ ρ must be a partial function. Furthermore
conditions (i)–(iii) for refinements must be ensured by φ0 .
For condition (i) we require I3 ⊆ DOM(ρ) = {q (3) ∈ Q3 | ∃q (2) ∈ Q2 .q (3) ρ
(2)
q } and ρ(I3 ) ⊆ I2 . Since I2 ⊆ DOM(φ1 ) we then have ρ(I3 ) ⊆ DOM(φ1 ) and
thus I3 ⊆ DOM(φ0 ).
ρ(I3 ) ⊆ I2 also implies condition (ii): since S2 φ1 -refines S1 we have φ1 (I2 ) ⊆
I1 . Hence φ0 (I3 ) ⊆ φ1 (I2 ) ⊆ I1 .
(3) (3) (3) (3)
For condition (iii) let hq0 , . . . , qn i be a sub-run of S3 with q0 , qn ∈
(3) (3) (3) (3) (3)
DOM(φ0 ), q1 , . . . , qn−1 6∈ DOM(φ0 ), and φ0 (q0 ) 6= φ0 (qn ). Then φ0 (q0 ) →1
(3) (2) (3)
φ0 (qn ) must hold. Obviously, there must be states q0 ∈ ρ(q0 ) ∩ DOM(φ) and
(2) (3) (2) (2)
qm ∈ ρ(qn ) ∩ DOM(φ) such that φ(q0 ) 6= φ(qm ). Hence, we must require
(2) (2)
q 0 →+ 3
2 qm for at least one pair of these states. Furthermore, there must be at
(2) (2) (2) (2)
least one sub-run hq0 , . . . , qm i with q1 , . . . , qm−1 6∈ DOM(φ); otherwise there
(3) (3)
would be at least two state transitions from φ0 (q0 ) to φ0 (qn ), cf. Fig. 6(c).
This property can be ensured by requiring DOM(φ) ⊆ RAN(ρ) = {q (2) ∈ Q2 |
∃q (3) ∈ Q3 .q (2) ρ q (3) }. Together we have
q0
(1) (1)
q1 q (1)
φ φ φ φ
(2) (2) (2) (2)
q0 ··· qm q0 ··· qm
ρ ρ ρ ρ
φ φ φ
(2) (2) (2)
q0 ··· qj ··· qm
ρ ρ
(2) (3)
q0 ··· qn
Remark 4. Condition (iv) considers all sub-runs beginning and ending with sta-
tes and is more general than argued above. However, every such sub-run can
be decomposed into sub-runs that do not contain intermediate states in the do-
(3) (3)
main of φ0 . If φ0 (q0 ) 6= φ0 (qn ) then condition (i) implies that there are states
(2) (3) (2) (3) (2) (2)
q0 ∈ ρ(q0 ) ∩ DOM(φ) and qm ∈ ρ(qn ) ∩ DOM(φ) such that q0 6= qm .
(2) (2) (2) (2)
Fig. 6(a) and (b) illustrate condition (iv) for the case q0 6= qm and q0 = qm
respectively.
I S T
(ii) Fi,j ⊆ Fi ∩ Ij . (For every edge (Si , Sj ), each state of Fi,j is final in Si and
initial in Sj .)
(iii) Fi,j ∩ Fi,k = ∅ for every j 6= k. (Each final state of Si unambigously
determines its successor.)
(iv) A common state q ∈ Qi ∩ Qj of two components Si , Sj either belongs to
Fi,j ∩ Fj,i , or there is a Sk such that q ∈ Fi,k ∩ Fj,k . (If two components have
common states then either there is an edge between the two components
and the state is in the set associated with the edge, or they have a common
successor Sk and the state is in the sets associated with both edges (Si , Sk )
and (Sj , Sk )).
Stats 2 T
Example 3. Fig. 9 shows the signature of ASMs for a while-language. The signa-
ture contains sorts TASK representing the sort of tasks (or instructions) belon-
ging to various instruction classes. Tasks are represented by constant symbols,
i. e. nullary functions of the algebraic signature Σ, for each instruction occurring
in a program. Similarly, variables are represented by a constant for each occur-
ring variable. Fig. 10 shows state transition rules for instruction classes. These
are used for the basic STSs defined in Fig. 8. For example, the oval vertex if
.
represents an ASM with the transition rule for IF and ΦInit = {CT = if j } for a
j ∈ {1, . . . , k1 }. The function NT is a partial function. A last task is final. The
conditions (i), (ii), (iv) require that each ASM in the composition has different
tasks. Let A = (Σ, ΦInit , ΦInv , Trans) be a non-basic ASM used in the compo-
. .
sition. Then, ΦInv = {CT = task 1 ∨ · · · CT = task k } where task 1 , . . . , task k
are a set of tasks, some of which are initial in successor ASMs. ΦInit and Φfinal
.
have the same form, e. g. ΦInit specify a disjunction of equations CT = task i
that are initial. An edge (A, A0 ) also associated with a disjunction of equations
.
CT = task where each of these equations occur in Φfinal as well as in Φ0Init .
8.3 Montages
During compilation a program is represented by an abstract syntax tree. The
elements of such trees carry semantic meaning and the meaning of a program is
composed from the meaning of these elements. The abstract syntax ‘‘normalizes’’
the concrete syntax in several respects: it no longer cares about representational
details such as use of keywords or rules for operator precedence etc. These details
are encoded either in node names of subtrees or in the structure of an expression
tree. Also selection rules such as
statement ::= assignment | procedure call | loop | ...
which only enumerate the possible alternatives, are avoided: whenever a state-
ment is allowed then one of the alternatives is directly inserted in the abstract
194 G. Goos and W. Zimmermann
Σ≡
sorts INT , VAR, TASK
subsorts DECISION , ASSIGN , EXPR < TASK , DES , PLUS , CONST < EXPR
WHILE , IF < DECISION
syntax tree.4 Thus, the tree only contains leaves and so-called composition pro-
ductions composing a language element from smaller units (subtrees). Every tree
element has a semantic meaning which influences program execution.
Semantic analysis is concerned with evaluating context conditions i. e. name
analysis, type checking/inference, and operator identification, and checking their
consistency; it works on symbolic information and does not care about the dyna-
mic interpretation of a program: whether an operation named ‘‘integer addition’’
really means an addition (not a subtraction) is of no concern. Even the question
which algebraic laws are applicable is uninteresting during this phase. Traditio-
nally semantic analysis is described by an attribute grammar, based on abstract
syntax.
4
Of course, given the non-orthogonal design of many languages the selection may
depend on context conditions which must be checked during semantic analysis.
Verifying Compilers and ASMs 195
.
if CT is IF ∧ ¬value(cond (CT ) = 0 then
CT := TT (CT )
.
if CT is WHILE ∧ ¬value(cond (CT ) = 0 then
CT := TT (CT )
if CT is ASSIGN then
content(id (dest(CT ))) := value(src(CT ))
CT := NT (CT )
if CT is DES then
value(CT ) := content(id (CT ))
CT := NT (CT )
.
if CT is IF ∧ value(cond (CT )) = 0 then
CT := NT (CT )
.
if CT is WHILE ∧ value(cond (CT )) = 0 then
CT := NT (CT )
if CT is PLUS then
value(CT ) := value(lop(CT )) + value(rop(CT ))
CT := NT (CT )
if CT is CONST then
value(CT ) := const(CT ))
CT := NT (CT )
Only when we enter the mapping phase of a compiler we get concerned with
the actual (dynamic) semantics of a program: We need to know the control and
data flow and certain information about the dynamic interpretation of opera-
tions. The required static information can again be represented by attribute
grammars. The dynamic semantics must be correctly transformed by the map-
ping phase; its rôle is that of invariants which must be preserved.
This description can be represented for each production by a variant of Kut-
ter’s montages, [35]. Montages were developed for graphically describing the
dynamic semantics of source languages by help of ASMs. We use it here for
specifying semantic analysis and the mapping phase of a compiler. The main
differences to Kutter’s monateges are that our context-free grammar defines
abstract, not concrete syntax, and the static semantics is described by attribute
grammars (not by ASMs).
A montage in this sense is a tuple consisting of a composition production
p : X0 ::= X1 · · · Xk , a control- and data-flow graph Gp , attribution rules R,
conditions Cp , and a set of transition rules Trans p . The graph Gp consists of
four classes of vertices and edges. A task vertex is graphically represented by
an ellipse. There is at most one task vertex representing the start of execution
of the montage; this vertex must be labeled with X0 . Nonterminal vertices are
labeled with one of the nonterminals of the RHS of p. I and T represent initial
and terminal vertices, respectively.
A control-flow edge is drawn dashed, a data-flow edge is drawn solid. The
origin of named edges must be task vertices. The origin of a data-flow edge must
196 G. Goos and W. Zimmermann
be a task vertex. Each out-edge of a data-flow edge must be named. The out-
edges of a square vertex Xi are labeled with integers. If Xi defines n terminal
sets, then Xi has n out-edges. The destination of named control-flow edges must
be either task vertices or the set of initial tasks has exactly one element. Analo-
gously, the destination of named data-flow edges must be either task vertices or
the set of terminal task has exactly one element.
Fig. 11 shows the montage for the while-loop. The decision whether the loop
ends or is continued rests on the value of the loop condition. This dependence
is indicated by a data-flow edge. There may be additional ways for exiting the
loop if (as in C) the body contains continue or break-statements.
cond
NT
I Expr While T
1 3
TT
Stats 2 T
Stats.loopEnd := choose(While.next)
Stats.loopCont := choose(Expr .initial
Expr .defs := While.defs
Stats.defs := While.defs
if CT is While then
.
if value(cond(CT )) = 0 then
CT := NT (CT )
else
CT := TT (CT )
There are two views on the graph Gp : First it defines a set of attributes and
attribution rules representing the task structure of programs. Together with the
nodes and edges of the graph we have an ASM ASL for the source language.
Additionally we have a parametrized ASM ATL for the target language when
names and data-flow edges are removed from Gp . The mapping is implicitly spe-
cified as a conditional graph rewrite system. A montage thus definea a mapping
from abstract syntax to control- and data-flow graphs.
The operational semantics of a program can be composed from the monta-
ges representing the involved language elements according to the structure of
the syntax tree. Each square vertex represents a different subtree of the AST;
conditions (ii)–(iv) of Section 8.2 are satisfied, since each value of CT specifies
the currently executed ASM.
Verifying Compilers and ASMs 197
The program transformations used by compilers transform the control- and data-
flow graphs. We already discussed that the observable behaviour O of the pro-
gram must be preserved, i.e. the ASM assigned to the transformed program
must be a refinement of O. The program transformations can be described by a
finite set of local graph rewrite rules. Horizontal compositionality implies that
the correctness of such rules can be proven locally. Since O is given by a (hierar-
chical) composition G of STSs, it must be ensured that composing refinements
by the corresponding composition (i.e. using the same graph and replacing each
component by a refinement) is a refinement of O.
A composed STS (ASM) can be refined by refining its components. Let
G = (V, E) be a composition of S1 , . . . , Sk , S10 , . . . , Sk0 be φ1 , . . . , φk -refinements
of S1 , . . . , Sk , respectively, and φ = φ1 ∪ · · · ∪ φk . Furthermore, let G0 = (V 0 , E 0 )
be the graph obtained from G by replacing S1 , . . . , Sk by S10 , . . . , Sk0 , respectively,
0
and associating the sets Fi,j = φ−1 (Fi,j ) with edges (Si0 , Sj0 ). We now investigate
the requirements for G being a composition of S10 , . . . , Sk0 and the STS obtained
0
by G0 being a refinement of G.
For G0 being a composition of S10 , . . . , Sk0 the condition Section 8.2(i) is satis-
fied whereas conditions (ii)–(iv) could be violated. However, this can be cured
by ‘‘renaming’’ states. We leave the formal proof of this to the reader. When
using montages, such a renaming is not necessary since the task pointer CT
unambigously defines the current ASM.
Let S, S 0 be the STS described G, G0 respectively. φ must be a partial function
for S 0 being a φ-refinement of S. This is only the case iff φi (q) = φj (q) for
q ∈ DOM(φi ) ∩ DOM(φj ). It is not hard to see that conditions (i) and (ii)
for S 0 being a φ-refinement of S are satisfied. It remains to prove condition
(iii). Obviously, each sub-run of a component Si0 is also a sub-run of S 0 . Hence,
the only sub-runs that may violate condition (iii) are sub-runs hq00 , . . . , qn0 i with
q00 , qn0 ∈ DOM(φ), and q10 , . . . , qn−1
0
6∈ DOM(φ) where q00 and qn0 strictly belong
to the states of two different STSs. However, there are no such sub-runs because
0
Fi,j ⊆ Ij0 ⊆ DOM(φj ) ⊆ DOM(φ) for all i, j.
Hence, we obtain the
Again, it is not hard to see that these conditions can be ensured by simply
renaming the states. In the case of montages, only φi (q) = φj (q) for q ∈ Qi ∩ Qj
has to be proven. If no data are refined, the condition is always true. Hence, the
compositional refinement of montages is easy to prove whereas data-refinements
(e.g. memory mapping) require additional work.
198 G. Goos and W. Zimmermann
A compilation from one language to another consists of two tasks: data mapping
and operation mapping. In this subsection, we demonstrate the use of the above
theory for showing the correctness of compilations. We use the ASMs ASL and
ATL as given by montages for implicitly describing data and operation map-
ping. Our goal is to independently prove the correctness of data mappings and
conditional graph rewrite rules.
Consider first the data mapping. With STS, we decomposed ASMs in a
‘‘behavioral’’ part and in a data part. A data mapping only maps the data part
and keeps the behavioural part. In particular, it assigns a new semantics (by me-
ans of an ASM A0SL ) to the source language using the concepts of the data part
of the target language (e.g. implementing a runtime stack using an address-based
memory). Since the behavioural part is kept, the correctness of this mapping can
be shown by proving that A0SL 1-1-refines ASL . In order to complete the cor-
rectness proof, we have to show according to Theorem 3 that ATL ρ-simulates
A0SL .
Theorem4 can be used to prove that ATL ρ-simulates A0SL . Observe that
ASMs are STSs and we now use the behavioural part of an ASM, i.e. its view as
a STS. Theorem 4 allows to independently prove the correctness of any graph
rewrite rule. The relation ρ relates the initial and final states of the left-hand
side and the right-hand side of the graph rewrite rule, respectively.
If a graph rewrite rule has an application condition, the correctness proof can
assume that this condition is satisfied. The compiler verifies the satisfaction of
this condition using program checking (in general it checks a stronger condition
because the application condition may be state dependent).
9 Related Work
Correctness of compilers was first considered in [36] but focused on the compi-
lation of arithmetic expressions. Thereafter most people explored the potential
of denotational semantics, e. g. [15,40,41,44,45,48,56], or of refinement calculi,
e. g. [6,4,8,16,17,33,38,43], structural operational semantics, e. g. [18] and alge-
braic models, e. g. [50]. Other approaches use abstract state machines, e. g. [6,
4,8]. Most of these projects did not compile into machine language. Instead,
they designed abstract machines, and compiled for interpreters of these abstract
machines.
These semantics-based approaches lead to monolithic compilers, cf. [24,53].
They do neither allow for reuse of traditional compiler technology nor do they
prepare for program reorganizations, as necessary for global optimization on
the machine code level. E. g., expressions are usually refined into postfix form
and then interpreted on a stack machine. The efficiency of the generated code
is by magnitudes worse than that of other compilers and thus does not meet
practical requirements, cf. [18,44]. Except [38], even projects which really gene-
rated machine language, e. g. [6,4,42,43], and ProCos, [33], chose the transputer,
Verifying Compilers and ASMs 199
10 Conclusions
References
1. M. Blum and S. Kannan. Program correctness checking . . . and the design of
programs that check their work. In Proceedings 21st Symposium on Theory of
Computing, 1989.
2. M. Blum, M. Luby, and R. Rubinfeld. Self–testing/correcting with applications
to numerical problems. In Proceedings 22nd Symposium on Theory of Computing,
1990.
3. Manuel Blum and Sampath Kannan. Designing programs that check their work.
Journal of the Association for Computing Machinery, 42(1):269–291, January 1995.
4. Egon Boerger, Igor Durdanovic, and Dean Rosenzweig. Occam: Specification and
Compiler Correctness.Part I: The Primary Model. In U. Montanari and E.-R. Olde-
rog, editors, Proc. Procomet’94 (IFIP TC2 Working Conference on Programming
Concepts, Methods and Calculi). North-Holland, 1994.
5. E. Börger, G. Del Castillo, P. Glavan, and D. Rosenzweig. Towards a Mathemati-
cal Specification of the APE100 Architecture: the APESE Model. In B. Pehrson
and I. Simon, editors, IFIP 13th World Computer Congress, volume I: Techno-
logy/Foundations, pages 396–401, Elsevier, Amsterdam, the Netherlands, 1994.
6. E. Börger and I. Durdanovic. Correctness of compiling occam to transputer. The
Computer Journal, 39(1):52–92, 1996.
7. E. Börger and S. Mazzanti. A Practical Method for Rigorously Controllable Hard-
ware Design. In J.P. Bowen, M.B. Hinchey, and D. Till, editors, ZUM’97: The
Z Formal Specification Notation, volume 1212 of LNCS, pages 151–187. Springer,
1997.
8. E. Börger and D. Rosenzweig. The WAM-definition and Compiler Correctness.
Technical Report TR-14/92, Dip. di informatica, Univ. Pisa, Italy, 1992.
9. E. Börger and W. Schulte. A Modular Design for the Java VM architecture. In
E. Börger, editor, Architecture Design and Validation Methods. Springer, 1998.
10. E. Börger and W. Schulte. Defining the Java Virtual Machine as Platform for
Provably Correct Java Compilation. In 23rd International Symposium on Mathe-
matical Foundations of Computer Science, LNCS. Springer, 1998. To appear.
11. E. Börger and W. Schulte. Programmer Friendly Modular Definition of the Se-
mantics of Java. In J. Alves-Foss, editor, Formal Syntax and Semantics of Java,
LNCS. Springer, 1998.
12. Borland/Inprise. Official borland/inprise delphi-3 compiler bug list.
http://www.borland.com/devsupport/delphi/fixes/3update/compiler.html,
jul 1999. Delphi3 Compiler Bug List.
13. Borland/Inprise. Official borland/inprise delphi-4 compiler bug list.
http://www.borland.com/devsupport/delphi/fixes/delphi5/compiler.html, jul
1999. Delphi4 Compiler Bug List.
14. Borland/Inprise. Official borland/inprise delphi-5 compiler bug list.
http://www.borland.com/devsupport/delphi/fixes/delphi5/compiler.html, jan
2000. Delphi5 Compiler Bug List.
15. D. F. Brown, H. Moura, and D. A. Watt. Actress: an action semantics directed
compiler generator. In Compiler Compilers 92, volume 641 of Lecture Notes in
Computer Science, 1992.
16. B. Buth, K.-H. Buth, M. Fränzle, B. v. Karger, Y. Lakhneche, H. Langmaack, and
M. Müller-Olm. Provably correct compiler development and implementation. In
U. Kastens and P. Pfahler, editors, Compiler Construction, volume 641 of LNCS.
Springer-Verlag, 1992.
Verifying Compilers and ASMs 201
17. Bettina Buth and Markus Müller-Olm. Provably Correct Compiler Implementa-
tion. In Tutorial Material – Formal Methods Europe ’93, pages 451–465, Denmark,
April 1993. IFAD Odense Teknikum.
18. Stephan Diehl. Semantics-Directed Generation of Compilers and Abstract Machi-
nes. PhD thesis, University of the Saarland, Germany, 1996.
19. A. Dold, T. Gaul, W. Goerigk, G. Goos, A. Heberle, F. von Henke, U. Hoffmann,
H. Langmaack, H. Pfeifer, H. Ruess, and W. Zimmermann. Definition of the Langu-
age IS. Verifix Working Paper [Verifix/UKA/1], University of Karlsruhe/Kiel/Ulm,
1995.
20. A. Dold, T. Gaul, and W. Zimmermann. Mechanized verification of compiler
back-ends. In B. Steffen and T. Margaria, editors, Proceedings of the Internatio-
nal Workshop on Software Tools for Technology Transfer STTT ’98, pages 13–24,
Aalborg, Denmark, 1998.
21. H. Emmelmann. Code selection by regularly controlled term rewriting. In R. Gie-
gerich and S.L. Graham, editors, Code Generation - Concepts, Tools, Techniques,
Workshops in Computing. Springer-Verlag, 1992.
22. H. Emmelmann, F.-W. Schröer, and R. Landwehr. Beg - a generator for efficient
back ends. In ACM Proceedings of the Sigplan Conference on Programming Lan-
guage Design and Implementation, June 1989.
23. Albert Endres. An analysis of errors and their causes in system programs. SIG-
PLAN Notices, 10(6):327–336, 1975.
24. David A. Espinosa. Semantic Lego. PhD thesis, Columbia University, 1995.
25. T.S. Gaul. An Abstract State Machine Specification of the DEC-Alpha Processor
Family. Verifix Working Paper [Verifix/UKA/4], Universität Karlsruhe, 1995.
26. W. Goerigk, T.S. Gaul, and W. Zimmermann. Correct Programs without Proof?
On Checker-Based Program Verification. In Proceedings ATOOLS’98 Workshop on
“Tool Support for System Specification, Development, and Verification”, Advances
in Computing Science, Malente, 1998. Springer Verlag.
27. Wolfgang Goerigk. Towards Rigorous Compiler Implementation Verification. In
Proc. of the 1997 Workshop on Programming Languages and Fundamentals of Pro-
gramming, Avendorf, Germany, November 1997.
28. Gerhard Goos. Sather-K — The Language. Software — Concepts and Tools,
18:91–109, 1997.
29. Y. Gurevich. Evolving Algebras: Lipari Guide. In E. Börger, editor, Specification
and Validation Methods. Oxford University Press, 1995.
30. Y. Gurevich and J. Huggins. The Semantics of the C Programming Language. In
CSL ’92, volume 702 of LNCS, pages 274–308. Springer-Verlag, 1993.
31. A. Heberle, T. Gaul, W. Goerigk, G. Goos, and W. Zimmermann. Construction of
Verified Compiler Front-Ends with Program-Checking. In Proceedings of PSI ’99:
Andrei Ershov Third International Conference on Perspectives Of System Infor-
matics, volume 1755 of Lecture Notes in Computer Science, Novosibirsk, Russia,
1999. Springer Verlag.
32. Andreas Heberle and Dirk Heuzeroth. The formal specification of IS. Technical
Report [Verifix/UKA/2 revised], IPD, Universität Karlsruhe, January 1998.
33. C.A.R. Hoare, He Jifeng, and A. Sampaio. Normal Form Approach to Compiler
Design. Acta Informatica, 30:701–739, 1993.
34. J. Huggins and D. Van Campenhout. Specification and Verification of Pipelining
in the ARM2 RISC Microprocessor. ACM Transactions on Design Automation of
Electronic Systems, 3(4):563–580, October 1998.
35. P. W. Kutter and A. Pierantonio. Montages specifications of realistic programming
languages. Journal of Universal Computer Science, 3(5):416–442, 1997.
202 G. Goos and W. Zimmermann
36. John McCarthy and J. Painter. Correctness of a compiler for arithmetic expressi-
ons. In Schwartz [51], pages 33–41.
37. Sun Microsystems. Sun official java compiler bug database.
http://java.sun.com/products/jdk/1.2/bugs.html, mar 2000.
38. J S. Moore. Piton, A Mechanically Verified Assembly-Level Language. Kluwer
Academic Publishers, 1996.
39. C. Robert Morgan. Building an Optimizing Compiler. Digital Press, Februar 1998.
ISBN 155558179X.
40. P. D. Mosses. Abstract semantic algebras. In D. Bjørner, editor, Formal description
of programming concepts II, pages 63–88. IFIP IC-2 Working Conference, North
Holland, 1982.
41. P. D. Mosses. Action Semantics. Cambridge University Press, 1992.
42. Markus Müller-Olm. An Exercise in Compiler Verification. Internal report, CS
Department, University of Kiel, 1995.
43. Markus Müller-Olm. Modular Compiler Verification. PhD thesis, Techn. Fakultät
der Christian-Albrechts-Universität, Kiel, June 1996.
44. Jens Palsberg. Provably Correct Compiler Generation. PhD thesis, Department of
Computer Science, University of Aarhus, 1992. xii+224 pages.
45. L. Paulson. A compiler generator for semantic grammars. PhD thesis, Stanford
University, 1981.
46. A. Pnueli, M. Siegel, and E. Singermann. Translation validation. In Tools and
Algorithms for the Construction and Analysis of Systems, volume 1384 of Lecture
Notes in Computer Science, pages 151–166. Springer-Verlag, 1998.
47. Amir Pnueli, O. Shtrichman, and M. Siegel. The code validation tool (cvt). Int.
J. on Software Tools for Technology Transfer, 2(2):192–201, 1998.
48. W. Polak. Compiler specification and verification. In Lecture Notes in Computer
Science, number 124 in LNCS. Springer-Verlag, 1981.
49. Robert M. Poston. Preventing software requirements specification errors with ieee
830. IEEE Software, 2(1):83–86, January 1985.
50. T. Rus. Algebraic processing of programming languages. Theoretical Computer
Science, 199:105–143, 1998.
51. J. T. Schwartz, editor. Mathematical Aspects of Computer Science, Proc. Symp.
in Appl. Math., RI, 1967. Am. Math. Soc.
52. Reinier Sterkenburg. Borland pascal compiler bug list.
http://www.dataweb.nl/ r.p.sterkenburg/bugsall.htm, feb 2000.
53. M. Tofte. Compiler Generators. Springer-Verlag, 1990.
54. William M. Waite and Gerhard Goos. Compiler Construction. Springer-Verlag,
1984.
55. C. Wallace. The Semantics of the C++–Programming Language. In E. Börger,
editor, Specification and Validation Methods. Oxford University Press, 1995.
56. M. Wand. A semantic prototyping system. SIGPLAN Notices, 19(6):213–221, June
1984. SIGPLAN 84 Symp. On Compiler Construction.
57. Hal Wasserman and Manuel Blum. Software reliability via run-time result-
checking. Journal of the ACM, 44(6):826–849, November 1997.
58. W. Zimmermann and T. Gaul. An Abstract State Machine for Java Byte Code.
Verifix Working Paper [Verifix/UKA/12], University of Karlsruhe, 1997.
An ASM Dynamic Semantics for Standard ML
Kettering University
Computer Science Program
1700 W. Third Avenue
Flint, MI 48504-4898 USA
{scater,jhuggins}@kettering.edu
1 Introduction
The Abstract State Machines (ASM) methodology [11] is a methodology for
formally specifying computing systems (software, hardware, or mixed). First
introduced by Gurevich [9] (under their former name, “evolving algebras”) the
ASM methodology is mathematically precise, yet general enough to be applicable
to a wide variety of problem domains [3,7,15]. The ASM Thesis [12] asserts that
any computing system can be described at its natural level of abstraction by an
appropriate ASM.
Standard ML (SML) is a functional programming language. It has been de-
scribed as a “safe, modular, strict, functional, polymorphic programming lan-
guage with compile-time type checking and type inference, garbage collection,
exception handling, immutable data types and updatable references, abstract
data types, and parametric modules.” [18]
In this paper, we describe the dynamic semantics of SML using the ASM
methodology, using Milner [19] as our definition of SML. We describe the dyna-
mic semantics of SML by describing an ASM which acts as an SML interpreter,
executing an (appropriately pre-processed) SML program. This provides an ope-
rational semantics for SML; the effect of a given SML instruction can be seen in
terms of the corresponding actions performed by the ASM.
We separate concerns in this work and restrict our attention to the dynamic
semantics of SML, assuming that all static semantic analysis and checking has
been performed in a pre-processing stage. We also follow Milner [19] in restric-
ting our attention to the so-called “bare” language of SML: a subset of SML
from which all other language constructs in SML can be derived. (For example,
arbitrary tuples of the form (t1 , t2 , . . . , tn ) are translated into records of the form
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 203–222, 2000.
c Springer-Verlag Berlin Heidelberg 2000
204 S.C. Cater and J.K. Huggins
We now describe an ASM which acts as an intepreter for SML programs. Since
we are solely concerned with the dynamic semantics of SML rather than the
static semantics, we assume that all static analysis has already been performed
on the program to be interpreted. The input program will thus be represented
in an abstract form (to be described). The ASM operates by walking through
this form of the program, performing the required calculations and changes to
the system state.
In this section, we focus on the evaluation of arbitrary SML expressions
written in the “bare” SML language. In succeeding sections we expand our focus
to include other features of SML.
2.3 Identifiers
In SML, all expressions are evaluated with respect to an environment: a finite
collection of name bindings. Evaluating an identifier in SML results in the cor-
responding value stored in the current environment. Thus, to give the rules for
identifier evaluation, we need to define how environments are represented in our
ASM.
We use universes of Envs (for environments) and EnvEntries (for entries in
an environment) to represent a given environment. An environment contains a
206 S.C. Cater and J.K. Huggins
Fig. 2. Identifiers.
2.4 Records
In SML, a record is represented as a sequence of label-expression pairs; the result
of evaluating a record is a finite mapping of the labels in the sequence to the
corresponding values (as evaluated in the current environment).
Thus, we need a universe of Maps corresponding to these finite pairs of labels
and expressions. (Note that Maps is not the same universe as Envs; environments
carry additional information which record maps do not.) We use functions Crea-
teMap: Identifiers × Values → Maps and AddToMap: Maps × Identifiers ×
Values → Maps to construct these maps.
Record expressions are represented in our ASM as a labeled expression optio-
nally followed by a record expression (following the natural recursive definition
of a sequence). Our ASM rules for evaluating records (shown in Fig. 3) move
through this sequence of record expressions, constructing the corresponding map.
(The rule below uses functions Expr: Terms → Terms and NextRecord: Terms
→ Terms to indicate the two components of a record expression; we implicitly
introduce component functions such as these in future rules without comment.)
Fig. 3. Records.
vironment and expression evaluation mechanism; they thus break the “pure”
functional programming paradigm.
Evaluating reference term of the form “ref t” causes a new memory location
to be allocated and returned as the value of the expression. Additionally, the
new memory location is initialized to the value of the argument expression t, as
evaluated in the current environment.
Thus, we need a new universe of Addresses to represent references, and a
unary function Store: Addresses → values to represent the current memory store.
Evaluating a reference term thus requires allocating a new memory address and
initializing the store appropriately. The resulting rule is shown in Fig. 4.
208 S.C. Cater and J.K. Huggins
2.6 Assignment
Assignment terms change the values stored in system memory. Syntactically,
an assignment appears in the “bare” language as the application of the special
function “:=” to a single value: a record in which the label “1” is bound to the
memory address to be changed and the label “2” is bound to the new value.
Recall that the value of a record is a map. In order to extract the neces-
sary values from the argument record, we use a binary function MapLookup:
Maps × Identifiers → Values. The rule (shown in Fig. 5) extracts the necessary
information and performs the assignment.
All expressions in SML evaluate to a value; however, assignment expressions
are evaluated for their side-effects rather than any value they might return. SML
uses the special value “unit” to represent the value of an assignment expression;
we use a corresponding distinguished element Unit: Values.
use the function Pair: Values × Values → Values to construct such pairs. The
rule for constructor application is shown in Fig. 9.
A second special case occurs when the function to be applied is a built-in
operator. We use a universe PrimitiveOps to represent such primitive operations,
as well as a function Apply: PrimitiveOps × Values → Values to represent the
definition of such primitive operators. Thus, our rule simply uses the Apply
function to generate the appropriate value.
Applying a primitive operator may also generate an exception; thus, if the
value returned by Apply is an exception, we need to initiate exception handling
(as in the case of a raise expression). Thus, the rule shown in Fig. 10 checks
the resulting value before passing it along.
KEEPGOING ::=
if CurTerm = Target then CurTerm := ReturnTerm
else CurTerm := CurTerm.Next
endif
The semantics for evaluating match rules were explained in the introduction to
this section; the corresponding rule is shown in Fig. 19.
Here we begin to present rules for handling various types of patterns. Recall that
CurMatchVal is used to hold the value against which the current pattern is to
be matched; the result of a pattern match is either an environment or the special
value Fail.
The simplest patterns to match are the underscore pattern “_” and the ellipsis
pattern “. . .”. Either pattern matches against any value and creates no bindings;
the empty environment is returned. We use the distinguished element EmptyEnv:
Envs to represent the environment containing no bindings. The corresponding
simple rule is shown in Fig. 20.
One can match values against special patterns representing constants (e.g., nu-
meric literals). The match succeeds if and only if the constant pattern corre-
sponds to the current match value; no bindings are generated. Using the function
ConstValue: Terms → Values to indicate the true value of this constant term,
the rule shown in Fig. 21 performs this operation.
An ASM Dynamic Semantics for Standard ML 217
5 Discussion
ASMs were first proposed as a methodology for specifying the semantics of pro-
gramming languages [8]. ASMs have been applied to a wide variety of program-
ming languages: imperative languages such as C/C++ [13,20], logic program-
ming languages such as Prolog [4] and its variants, object-oriented languages
such as Java [5,21] and Oberon [17], and hardware languages such as VHDL [2].
To the best of our knowledge, this case study in Standard ML is the first appli-
cation of ASMs to provide the semantics of a functional programming language.
The official semantics of Standard ML is given by Milner [19], using an axio-
matic semantics called Natural Semantics. The rules given in Milner, while defi-
nitive, rely heavily on axiomatic notation and proof rules which can be difficult
to read. With the presence of an official semantics, there appear to be few other
treatments of SML using other semantic techniques. A recent paper by Watt [22]
announces the use of Action Semantics to give semantics to SML; another work
by Harper and Stone [14] translates SML into a typed variant of the lambda
calculus to which an operational semantics is given. All of these other works
give both static and dynamic semantics for SML.
220 S.C. Cater and J.K. Huggins
One interesting feature of our ASM semantics for SML is our use of the
EVALUATE macro, used to evaluate a term in an environment differing from
the current environment. The EVALUATE macro appears in roughly one-third
of the rules given above (not counting rules omitted for brevity), suggesting that
term-environment evaluation plays an important role in the dynamic semantics
of SML. This is natural; SML is, after all, a functional programming language,
which draws its roots from the lambda calculus. Since function application (i.e.,
term evaluation in a different environment) is at the heart of the lambda calculus,
it seems natural that this feature should exhibit itself so prominently in our ASM
semantics.
We would argue that the ASM dynamic semantics given above are as precise
and accurate as conventional semantic treatments such as the official definition
[19]. The rules and explanations above are somewhat longer than in an axiomatic
An ASM Dynamic Semantics for Standard ML 221
approach, as the ASM notation lends itself to natural language notations rather
than axiomatic proof rules. However, one may find the readability of these rules is
substantially better. Such explanations can provide a better basis for explaining
and understanding the language with a minimal amount of notational overhead.
Several possible extensions of this work are being contemplated. One clear
gap in the descriptions above is any treatment of the static semantics of SML,
including the extensive type system. The official definition of SML [19] spends
extensive time defining the static aspects of the language, which can also be
confusing at times. Recently Montages [16] have been used to describe both static
and dynamic semantics of programming languages using ASMs; we consider
extending this work using Montages to provide static semantics for SML as well.
Extending this work to the Montages framework has another important be-
nefit: executability. At this time, the ASM description given here has not been
tested by any executable tools. The Gem-Mex tool [1] allows one to execute Mon-
tage descriptions directly; we expect that doing so will give us further evidence
of the correctness of these descriptions.
Alternatively, ASMs have been used for various proofs of correctness, such as
compilation techniques; we consider specifying a compilation technique for SML
to some intermediate language and proving its correctness.
References
1. M. Anlauff, P. Kutter, and A. Pierantonio. Formal Aspects of and Development
Environments for Montages. In M. Sellink, editor, 2nd International Workshop
on the Theory and Practice of Algebraic Specifications, Workshops in Computing,
Amsterdam, 1997. Springer.
2. E. Börger, U. Glässer, and W. Müller. The Semantics of Behavioral VHDL’93
Descriptions. In EURO-DAC’94. European Design Automation Conference with
EURO-VHDL’94, pages 500–505, Los Alamitos, California, 1994. IEEE CS Press.
3. E. Börger and J. Huggins. Abstract State Machines 1988-1998: Commented ASM
Bibliography. Bulletin of EATCS, 64:105–127, February 1998. (An updated version
is available from [15].).
4. E. Börger and D. Rosenzweig. A Mathematical Definition of Full Prolog. In Science
of Computer Programming, volume 24, pages 249–286. North-Holland, 1994.
5. E. Börger and W. Schulte. Programmer Friendly Modular Definition of the Se-
mantics of Java. In J. Alves-Foss, editor, Formal Syntax and Semantics of Java,
LNCS. Springer, 1998.
6. S. Cater and J. Huggins. An ASM Dynamic Semantics for Standard ML. Technical
Report CPSC-1999-2, Kettering University, 1999.
7. U. Glässer. Abstract State Machines Europe Home Page. http://www.uni-
paderborn.de/cs/asm/.
8. Y. Gurevich. Reconsidering Turing’s Thesis: Toward More Realistic Semantics
of Programs. Technical Report CRL-TR-38-84, EECS Department, University of
Michigan, 1984.
9. Y. Gurevich. Logic and the Challenge of Computer Science. In E. Börger, editor,
Current Trends in Theoretical Computer Science, pages 1–57. Computer Science
Press, 1988.
222 S.C. Cater and J.K. Huggins
1 Introduction
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 223–241, 2000.
c Springer-Verlag Berlin Heidelberg 2000
224 E. Börger, A. Cavarra, and E. Riccobene
2 Basic Concepts
In this section we sketch the basic concepts underlying UML state machines and
ASMs and review the notation.
Statechart diagrams are one of the five diagrams in the UML for modeling the
dynamic aspects of systems. Statecharts were invented by David Harel [15,16],
the semantics and the notation of UML statecharts are substantially those of
Harel’s statecharts with adaptations to the object-oriented context [3].
Modeling the Dynamics of UML State Machines 225
ASMs are transition systems, their states are multi-sorted first-order structures,
i.e. sets with relations and functions, where for technical convenience relations
are considered as characteristic boolean-valued functions. The transition relation
is specified by rules describing the modification of the functions from one state
to the next, namely in the form of guarded updates (“rules”)
if Condition then Updates
where U pdates is a set of function updates f (t1 , . . . , tn ) := t, which are simul-
taneously executed when Condition is true.
We use multi-agent ASMs [12,1] to model the concurrent substates and the
internal activities which may appear in a UML statechart diagram. A multi-
agent ASM is given by a set of (sequential) agents, each executing a program
consisting of ASM rules. Their distributed runs are defined in [12].
Since ASMs offer the most general notion of state, namely structures of ar-
bitrary data and operations which can be tailored to any desired level of abstrac-
tion, this allows us on the one side to reflect in a simple and coherent way the
integration of control and data structures, resulting from mapping statecharts to
the UML object model. In fact, machine transitions are described by ASM rules
where the actions become updates of data (function values for given arguments).
226 E. Börger, A. Cavarra, and E. Riccobene
On the other side also the interaction between objects is naturally reflected by
the notion of state of multi-agent (distributed) ASMs.
For the constructs of sequentialization, iteration and submachine of sequen-
tial ASMs we use the definitions which have been given in [8]. They provide the
concept of “stable” state needed to guarantee that the event triggered sequen-
tial exit from and entry into nested diagrams is not interrupted by a too early
occurrence of a next event.
In this section we model the event governed run to completion step in statechart
diagrams. We first introduce the signature of UML statecharts and then define
the execution rules. Statechart diagrams are made up of (control) states1 and
transitions, belonging to the abstract sets STATE and TRANSITION.
The set STATE is partitioned into simple, composite (with substructure), and
pseudo states. Composite states are partitioned into sequential and concurrent
ones. Pseudostates are partitioned into initial and history states.
Simple states (1) are of the form state(entry,exit,do(A),defer)2 , where the pa-
rameters entry/exit denote actions that have to be performed as soon as the
state is entered/exited, do(A) denotes the internal ongoing activity A that must
be executed as long as the state is active, and defer is a set of events that are
candidate to be retained in that state.
Sequential composite states (2) are of the form state(entry,exit,do(A),defer,i-
nit,final,history), where entry/exit, do(A) and defer have the same meaning as
for simple states, init denotes the initial state of the submachine state associated
to this composite state, final denotes its final state, and history its associated
history state (see below for details on initial, final and history states). Sequential
composite states contain one or more substates, exactly one of which is required
to be active when the composite state is active.
Concurrent composite states (3) are of the form state(entry,exit,do(A),defer,
concurrentComp), where entry/exit, do(A) and defer are as above, and concur-
rentComp yields the set of the concurrent (sequential) substates3 composing the
state. When a concurrent state is active, all of its subcomponents are active.
1
This notion of control state, deriving from the finite state machine notion of “inter-
nal” state, is only a tiny fraction of the overall system state which is reflected by the
ASM notion of state as structure, i.e. domains (of to be instantiated objects) with
operations and relations.
2
Simple and composite states may have a name, i.e. a string denoting uniquely the
state. We omit the name parameter in the signature of such states as it is not relevant
for our model.
3
Each substate is sequential because it must enclose an initial and a final state.
Modeling the Dynamics of UML State Machines 227
Initial states • indicate where to start by default when the enclosing (com-
posite sequential) state is invoked. A History state, associated to a sequential
composite state say S, is a pseudostate that can be of two types: shallow history
H and deep history
H∗ . The shallow history state records, upon exiting S,
only the most recent active state directly contained in S and restores the recor-
ded state when the history state is invoked. The deep history state records the
most recent active hierarchical configuration of S, and restores this configuration
when the history state is invoked. To keep track of the configuration, we use a
dynamic function
memory : STATE −→ STATE∗
that is initialized to the empty sequence for each state which has never been
accessed. To guarantee the correct entering order, we handle memory as a LIFO
list. In case ofJshallow history state memory contains at most one state.
Final states • are special states whose activation indicates that the enclosing
state is complete.
We denote by SimpleState, SequentialState, ConcurrentState, PseudoState,
FinalState the characteristic functions of the corresponding subsets of STATE.
Any state which is enclosed within a composite state is called a substate
of that state. In particular, it is called direct substate when it is not contained
in any other state; otherwise it is referred to as a transitively nested substate.
The nesting structure of statechart diagrams is encoded by the following static
functions:
Upchain and DownChain yield empty sequences on each pair of not nested
states. We write Up/DownChain(s1 , sb2 ) to indicate the right open sequence
U p/DownChain(s1 , sb2 ) = [T1 , . . . , Tn [, if it exists. Notice that Up/DownCh-
ain(s, sb) = [].
3.2 Transitions
3.3 Agents
Let AGENT be the set of agents which move through the statechart diagram,
each executing what is required for its currently active state. A state becomes
active when it is entered as result of some transition, and becomes inactive if it
is exited as result of a transition. “When dealing with composite and concurrent
states, the simple term current state can be quite confusing. In a hierarchical
state machine more then one state can be active at once. If the control is on a
simple state that is contained in a composite state, then all the composite states
that either directly or transitively contain the simple state are also active” [4].
Therefore, to maintain what in UML is called the current configuration of active
states, we introduce a dynamic function
currState : AGENT → P(STATE)
Modeling the Dynamics of UML State Machines 229
whose updates follow the control flow of the given statechart diagram. The fun-
ction deepest : AGENT −→ STATE yields the last (innermost) state reached by
an agent.
The agents execute UML statechart diagrams, i.e. they all use the same
program (or ASM Rule). As a consequence, in the formulation of these rules
below, we use the 0-ary function Self which is interpreted by each agent a as a.
When a new agent is created to perform a concurrent subcomputation (defined
by one of the substates in a concurrent composite state), it is linked to the parent
agent by the dynamic function
parent : AGENT → AGENT ∪ {undef}
We assume that this function yields undef for the main agent who is not part
of any concurrent flow. The active subagents of an agent a are collected in the
set SubAgent(a) = {a0 ∈AGENT | parent(a0 ) = a}
At the beginning of the computation, we require that there is a unique agent,
positioned on the initial state of the top state, and whose program consists of
the rules Transition Selection and Generate Completion Event described
below.
In UML it is assumed that a state machine processes one event at a time and
finishes all the consequences of that event before processing another event [5,
20]. “An event is received when it is placed on the event queue of its target. An
event is dispatched when it is dequeued from the event queue and delivered to
the state machine for processing. At this point, it is referred as the current event.
Finally, it is consumed when event processing is complete. A consumed event is
no longer available for processing” [4].
We therefore assume that one event is processed at a time. Since the par-
ticular event enqueuing and dispatching mechanisms are deliberately not furt-
hermore specified in UML, we model them here explicitly as semantic variation
points and therefore use a monitored predicate dispatched indicating which event
is dequeued to be processed. At any moment, the only transitions that are eli-
gible to fire when an event e occurs are the ones departing from an active state
(i.e. whose source state belongs to currState) whose associated guard evaluates
to true4 . This is expressed by the following condition
enabled(t, e) ≡ event(t) = e & guard(t) & source(t) ∈ currState
It is possible for more than one transition to be enabled by the same event,
but UML allows only those transitions to be fired simultaneously which occur
in concurrent substates [4]. In all the other cases, the enabled transitions are
said to be in conflict with each other. One can distinguish three types of conflict
situations: (1) an internal transition in an active state conflicts with a transition
outgoing from that state, (2) two or more transitions originating from the same
source in an active state are enabled by e, and (3) two or more transitions with
different source states but belonging to the same active state are enabled by the
4
If no guard is associated to a transition t, we assume guard(t) = true.
230 E. Börger, A. Cavarra, and E. Riccobene
In this subsection we define the ASM rules for the execution of statecharts, i.e.
we specify the sequences of states that an object goes through, and of the actions
it takes, in response to events which occur during its lifetime [20].
Apparently, UML leaves it unspecified how to choose between dispatched
and releasable events. We reflect this by using a selection function which, at any
moment, chooses either a dispatched event triggering a transition, or an event
that has been deferred. A dispatched event, if deferrable, has to be inserted
into the deferQueue. A releasable event, when chosen for execution, has to be
5
Observe that ≤ is total since all the source states of the transitions in enabled belong
to currState and therefore they are nested.
6
Apparently, this list is meant to be a set, leaving the exact ordering of elements open
as a semantic variation point. A similar remark applies also to other lists occurring
in the UML texts.
Modeling the Dynamics of UML State Machines 231
deleted from deferQueue7 . This implies that when choosing an event which is
simultaneously dispatched and releasable, that event will be deleted from the
deferred events.8
We define in the next section the exact meaning of the state machine execu-
tion of a transition, namely by a parameterized macro stateMachineExecution.
This leads us to the following main rule for selecting the machine transition to
be executed next.
Rule Transition Selection
choose e : dispatched(e) ∨ releasable(e)
choose trans in FirableTrans(e)
stateMachineExecution(trans)
if deferrable(e) then insert(e,deferQueue)
if releasable(e) then delete(e,deferQueue)
The rule for selecting and executing a transition fires simultaneously, at each
“run to completion step”, with a rule to generate completion events.
Completion events are generated when an active state satisfies the completion
condition [4]. They trigger a transition outgoing such states. An active state is
considered completed if one of the following cases occurs: (1) it is an active
pseudostate, (2) it is a sequential composite state with active final state, (3)
the state internal activity terminates while the state is still active, or (4) it is a
concurrent composite state and all its direct substates have reached their final
state. We formalize this by the predicate
completed(S) = true ⇐⇒ PseudoState(S) or
(SequentialState(S) & final(S) ∈ currState) or
terminated(A(S)) or
(ConcurrentState(S) &
∀Si ∈ concurrentComp(S) ∀ai ∈ SubAgent(Self)
final(Si ) ∈ currState(ai ))
where terminated(A(S)) is a derived predicate that holds if and only if the run
of the ASM A(S), which formalizes the internal activity of S, reaches a final
state.
Each time the completion condition evaluates to true for an active state
S that is not a direct substate of a concurrent state9 a completion event is
generated. This is expressed by the rule Generate Completion Event that is
executed simultaneously for each state S ∈ currState.
7
If upon execution of transition trans, a deferred event e ∈ defer(source(trans)) does
not belong to defer(target(trans)), then it must be deleted from deferQueue, as spe-
cified as part of the enterState macro below.
8
Should another interpretation be intended, we would probably change the guard
“if releasable(e)” in the Transition selection rule to e.g. “if releasable(e) & not
dispatched(e)”.
9
This restriction reflects that in UML no direct substate of a concurrent state can
generate a transition event. Such substates are required to be sequential composite
states.
232 E. Börger, A. Cavarra, and E. Riccobene
stateMachineExecution(trans) ≡
if internal(trans) then action(trans)
else seq
exitState(source(trans),ToS)
action(trans)
enterState(FromS,target(trans))
case target(trans)
SequentialState: enterInitialState(target(trans))
ConcurrentState: startConcurrComput(target(trans))
HistoryState: restoreConfig(target(trans))
endcase
where anc = lca(source(trans),target(trans))
ToS = directSubState(anc,UpChain(source(trans),anc))
FromS = directSubState(anc,DownChain(anc,target(trans)))
1. The agent is not inside a concurrent flow (i.e. parent(Self) = undef). If the
agent is (1) not parent of concurrent subagents or (2) it is parent of concur-
rent subagents but each subagent already performed its exit actions, then
for each state from the source state up to, but excluding, the source/target
least common ancestor state (see the stateMachineExecution rule above),
innermost first, it sequentially (a) stops the internal ongoing activities, (b)
performs the exit actions , and (c) removes those states from the agent’s
current state. Moreover, it (d) updates the history (if defined and provided
that the final state has not been reached), memorizing in it all the states it
is exiting in case of deep history, or only its direct active substate in case
of shallow history, and (e) updates deferQueue by deleting all those events
which are no more deferred (see macro sequentialExit which uses a macro
abortInternalActivity defined below). In case (2) the agent must furthermore
update its deferQueue to retain all deferred events of its own but none of
those processed by its subagents. Finally it disconnects all its deactivated
subagents (see the corresponding macro defined below).
2. The agent is inside a concurrent flow (i.e. parent(Self) 6= undef). We have
to consider the two cases, whether the trigger event is relevant for all the
subagents running in parallel within the concurrent state or not. To this
purpose we check that the transition source state belongs to the active state
of both the agent and its parent (when a subagent is created, it inherits
234 E. Börger, A. Cavarra, and E. Riccobene
its parent’s current state, therefore at any time the currState of the parent
agent is a subset of its subagents’ currState). In this case, each subagent
performs the same sequentialExit macro as in the first case, i.e. starting
from its deepest state up to, but excluding, its parent’s deepest state, it
sequentially (a) stops the internal ongoing activities, (b) performs the exit
actions and (c) removes those states from the agent’s current state. Moreover,
it (d) updates the history (if defined and provided that the final state has
not been reached) memorizing in it all the states it is exiting in case of deep
history, or only its direct active substate in case of shallow history, and (e)
updates deferQueue by deleting all those events which are no more deferred
(see macro sequentialExit). Finally, the agent is deactivated, meaning that
its rule is set to undef and its current state to the empty set (see macro
deactivate).
Now consider the case that the transition source state belongs to the active
state of at least one but not to all subagents of an agent. Then the event is
relevant only for this subagent, and this agent performs the sequential exit
as in case 1.
where S = deepest(Self)
S 0 = deepest(parent(Self))
noActiveSubAgents = ∀ai ∈ SubAgent(Self) :
currState(ai ) = ∅
For the definition of the macro sequentialExit we use the macro abortInternalAc-
tivity which will be defined below. In defining sequentialExit we use a function
hist(s, S) whose value depends on whether S is a deep history state or not.
b for deep history, directSubState(S,UpChain(s,S))
hist(s, S) yields UpChain(s,S)
for shallow history.
Modeling the Dynamics of UML State Machines 235
Entering States. A transition may have a target state nested at any depth
in a composite state. Therefore, any state enclosing the target one up to, but
excluding, the least common ancestor will be entered in sequence, outermost first.
Entering a state means that (a) the state is activated, i.e. inserted in currState,
(b) its entry action is performed, and (c) the state internal activity (if any) is
started. This is realized by the macro enterState for which we use the macro
startActivity defined below. The agent’s deferQueue is updated by deleting all
those events which are no more deferred in the target state.
Internal Activities. When a state is active, its internal activity (if any) is re-
quired to be executed. Apparently, internal activities are intended as concurrent
and [4] imposes no particular scheduling conditions for them. We model this by
creating a new worker agent whose job is to execute the activity of its associated
state. The worker agent is created when the state is entered and after its entry
action has been executed. It receives as program the ASM A(S) formalizing the
state activity.
Using an ASM as rigorous replacement for the intuitive notion of “internal UML
activity”, we obtain a mathematically rigorous definition without loosing gene-
rality10 . In addition we make the UML notion of “ongoing” activity precise by
defining it as steps of an ASM in a multi-agent distributed ASM run.
If an activity is aborted prior to its termination as result of the firing of an
outgoing transition, then before leaving the state its associated worker agent is
deactivated since its job is terminated. This is performed by the following macro
which is used for defining sequentialExit.
its currState configuration and the parent’s list of active deferred events. As a
transition drawn to the boundary of a concurrent composite state is equivalent
to a transition to any of its concurrent components and consequently to the com-
ponent initial state, each agent ai activates the component Si and its associated
initial state.
startConcurrComput(S) ≡
let S1 , . . . , Sn = concurrentComp(S)
extend AGENT with a1 , . . . , an
do forall 1 ≤ i ≤ n
parent(ai ) := Self
deferQueue(ai ) := deferQueue(Self)
Rule(ai ) := Rule(Self)
currState(ai ) := insert({Si ,init(Si )},currState)
The parent agent will stand idle waiting for the termination of its subagents’
computation. This is enforced by the definition of when a concurrent state is com-
pleted to trigger the completion event which may enable the transition exiting
the concurrent state. The running subagents can finish their job either because
of a completion event generated by their parent11 or by the firing of an explicit
event labeling a transition outgoing their enclosing state. In our model the sub-
states’ exit action and internal activity abortion are performed by the exitState
macro, in a synchronized fashion. Other choices are easily defined modeling our
rules appropriately. The UML documents seem not to mention this semantically
relevant issue at all.
Remark. In a concurrent compound state S 0 , a transition trans(e,g,a) out-
going from a state S in a concurrent component and incoming to a state S 00 sib-
ling of S 0 (see Fig. 1.a), can be viewed as split into two transitions (see Fig. 1.b):
a transition trans(e,g,Send exitEvent(S)) from S to S 0 , where Send exitEvent(S)
is an event generation action12 , and a transition trans(exitEvent(S),true,a) from
S 0 to S 00 . To guarantee the expected semantics of UML statecharts, we impose,
S’ S’
S S
e/[g]Send exitEvent
e/[g]a exitEvent/[true]a
S’’ S’’
(a) (b)
Fig. 1.
11
In this case they all must have reached their final state.
12
According to [4] an action labeling a transition may consist in sending a signal.
238 E. Börger, A. Cavarra, and E. Riccobene
Fork-Join Pseudostates. Fork and join pseudostates split and merge transiti-
ons arriving at, emanating from, concurrent states. A transition to the boundary
of a concurrent compound state is equivalent to a transition to each of its direct
substates (and therefore to their initial states), and a transition from the bound-
ary of a concurrent compound state is equivalent to a transition from the final
states of each of its substates. Therefore, the fork (resp. join) semantics can be
obtained by allowing only incoming (resp. outgoing) transitions that terminate
on (resp. depart from) the boundary of concurrent states, and imposing that
each concurrent substate must enclose an initial and a final state.
Junction Pseudostate. Junction states are used only to chain together mul-
tiple transitions – this is known as merge –, or to split an incoming transition
into multiple outgoing transitions labeled with different guard conditions – this
is known as conditional branch [4].
Stub States. A stub state is nothing else then an alias for an entry point to or an
exit point from a state s in S 0 of a submachine state state(entry,exit,do(A),inclu-
de(S 0 )).
In this section we discuss some ambiguities in the official semantics of UML [4,5,
20] which are resolved in the ASM model. We also show how UML requirements
for state machines are satisfied by our model.
The state machine execution is formalized through the macro stateMachine-
Execution (invoked by the rule Transition Selection) that reflects the scheme of
a generic control machine. These ASM statecharts generalize the Mealy ASMs
defined in [6].
Our model reflects all the characteristics of the state machines metamodel in
[4] and adds to its structural, static definition the underlying control flow seman-
tics. A subtle question regards the execution of ongoing state activities. What
does happen when an internal transition occurs? Does the activity interrupt and
then restart from the same computation point, or does it never interrupt? The
way we model internal activities guarantees the second, to our understanding
reasonable, alternative. However, our model can be easily adapted to formalize
other behaviors.
By replacing the undefined UML terms of “action” and “activity” with (possi-
bly structured, in the sense of [8]) “ASM rule”, we provide a precise mathematical
content to these terms without loosing the generality intended by the designers
of UML (see in this connection Gurevich’s proof of the ASM thesis [13]). Our
model also provides a precise meaning of the vague UML term “ongoing internal
activity”, namely as execution of an ASM in a multi-agent distributed run as
defined in [12]. The sequentialization, iteration and submachine constructs de-
fined for ASMs in [8] clarify in what sense sequences of nested exit and entry
actions can be guaranteed to be executed in one “run to completion step”, as
postulated by the UML documents, namely before the next event may trigger
the next “step”. Our model also makes some semantically relevant features14
explicit which seem not to have been considered in the official UML documents.
Several semantics for statecharts have been proposed in the literature [21].
Most of these are concerned with modeling Harel’s statecharts, whose semantics
is rather different from UML state machines (e.g. in the event handling policy).
Although our model can be adapted to grasp such differences, our intent is
to define the UML state machine semantics up to the degree of precision one
can reach without compromising the desired freedom of the so called “semantic
variation points”.
Differently from the formalization of UML state machines in [11,19], our
model reflects the original structure of machines as described in the UML do-
cuments, without imposing any graphical transformation or flattening of dia-
grams. [11] uses graph rewriting techniques to transform UML state machines
into a “normal form” machine, without considering the execution of actions
and activities. The model in [19] leaves out some state machines features, and
some are covered by means of semantical equivalences which, however, do not
14
E.g. whether abortion of internal activities and exit actions of concurrent agents
should be synchronized or not.
240 E. Börger, A. Cavarra, and E. Riccobene
always respect the UML metamodel constraints (see [4], pp. 2-126). For instance,
entry/exit actions in a state are replaced by attaching such actions respectively
to the state incoming/outgoing transitions, whereas the metamodel asserts that
the multiplicity of Action in Transition is 0..1, that is no or exactly one action
may label a transition.
In [17] the operational behavior of UML state machine constructs is described
using pseudo-code in a way which in many places includes specific implementa-
tion decisions (mostly without stating them), whereas we tried to let the intended
semantic variation points of UML stand out explicitly as such.
References
1. Abstract State Machines. http://www.eecs.umich.edu/gasm/.
2. Rational Software Corporation, Unified Modeling Language UML, version 1.3,
1999.
3. UML 1.3 Notation, 1999. (Published as part of [2]).
4. UML 1.3 Semantics, 1999. (Published as part of [2]).
5. G. Booch, J. Rumbaugh, and I. Jacobson. The Unified Modeling Language User
Guide. Addison Wesley, 1999.
6. E. Börger. High Level System Design and Analysis using Abstract State Machines.
In D. Hutter and W. Stephan and P. Traverso and M. Ullmann, editor, Current
Trends in Applied Formal Methods (FM-Trends 98), number 1641 in LNCS, pages
1–43. Springer-Verlag, 1999.
7. E. Börger, A. Cavarra, and E. Riccobene. An ASM Semantics for UML Activity
Diagrams. In T. Rus, editor, AMAST2000, volume 1816 of LNCS, pages 293–308.
Springer Verlag, May 2000.
8. E. Börger and J. Schmid. Composition and Submachine Concepts for Sequential
ASMs. In Gurevich Festschrift CSL 2000, 2000. (To appear).
9. A. S. Evans, J-M. Bruel, K. Lano R. France, and B. Rumpe. Making UML Precise.
In In OOPSLA’98 Workshop on Formalizing UML. Why and How?, October 1998.
10. R. B. France, A. S. Evans, K. C. Lano, and B. Rumpe. Developing the UML as a
formal modeling notation. Computer Standards and Interfaces: Special Issues on
Formal Development Techniques, Accepted for publication, 1998.
11. Martin Gogolla and Francesco Parisi-Presicce. State diagrams in UML: A formal
semantics using graph transformations. In Manfred Broy, Derek Coleman, Tom
S. E. Maibaum, and Bernhard Rumpe, editors, Proceedings PSMT’98 Workshop
on Precise Semantics for Modeling Techniques. Technische Universität München,
TUM-I9803, 1998.
12. Y. Gurevich. Evolving Algebras 1993: Lipari Guide. In E. Börger, editor, Specifi-
cation and Validation Methods, pages 9–36. Oxford University Press, 1995.
13. Y. Gurevich. Sequential Abstract State Machines capture Sequential Algorithms.
ACM Transactions on Computational Logic, 1, 2000. (To appear).
14. D. Harel and E. Gery. Executable Object Modeling with Statecharts. Computer,
IEEE Computer Society, 30(7):31–42, 1997.
15. D. Harel and A. Naamad. The STATEMATE Semantics of Statecharts. ACM
Trans. Soft. Eng. method, 5(4):293–333, 1996.
16. D. Harel and M. Politi. Modeling Reactive Systems with Statecharts. McGraw-Hill,
1998.
Modeling the Dynamics of UML State Machines 241
17. Ivan Paltor and Johan Lilius. Formalising UML state machines for model checking.
In Robert France and Bernhard Rumpe, editors, UML’99 - The Unified Modeling
Language. Beyond the Standard. Second International Conference, Fort Collins,
CO, USA, October 28-30. 1999, Proceedings, volume 1723 of LNCS. Springer, 1999.
18. The precise UML group. http://www.cs.york.ac.uk/puml/.
19. G. Reggio, E. Astesiano, C. Choppy, and H. Hussmann. Analysing UML Active
Classes and Associated State Machines – A Lightweight Formal Approach. In
FASE 2000 - Fundamental Approaches to Software Engineering. Lecture Notes in
Computer Science, 2000. (To appear).
20. J. Rumbaugh, I. Jacobson, and G. Booch. The Unified Modeling Language Refe-
rence Manual. Addison Wesley, 1999.
21. M. von der Beek. A Comparison of Statechart Variants. In Formal Techniques in
Real-Time and Fault-Tolerant Systems, volume 526. Lecture Notes in Computer
Science, 1994.
On the Formal Semantics of SDL-2000:
A Compilation Approach Based on an Abstract
SDL Machine
1 Introduction
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 242–265, 2000.
c Springer-Verlag Berlin Heidelberg 2000
On the Formal Semantics of SDL-2000 243
other development languages, SDL satisfies the primary needs of system deve-
lopers. Today, SDL is being broadly applied in industry, and is being supported
by powerful commercial tool environments.
In 1988, the semantics of SDL has been defined formally, upgrading the langu-
age to a formal description technique (FDT). This formal definition has been
updated and maintained for subsequent versions of the SDL language in 1992
and 1996. Building on a combination of the VDM meta language Meta-IV with
a CSP-like communication mechanism, the formal semantic model provides a
comprehensive formalization of all static and dynamic aspects of SDL, though,
it is hardly manageable because of its size2 . Essentially, the formal semantics
is given by a set of Meta-IV programs that take an SDL specification as input,
determine the correctness of its static semantics, perform a number of transfor-
mations to replace non-basic language constructs, and interpret the specification.
It has been argued that this style of defining the formal semantics is particularly
suitable for tool builders.
In November 1999, a new version of the language, called SDL-2000, has been
approved as international standard. In addition to the usual language mainte-
nance, SDL-2000 offers important new features, including object-oriented data
type definitions, a unified concept for blocks, processes, and composite states,
and exception handling. Based on the assessment that the existing Meta-IV pro-
grams would be too difficult to update and maintain, it was decided to conceive
a new formal semantics for SDL-2000 from scratch. For this purpose, a special
task force, the SDL semantics group, consisting of experts from Germany and
China including the authors of this paper, was formed. The draft formal seman-
tics defined by this group (see [24]) has been determined to become the official
formal SDL semantics after its approval, which is expected for November 2000.
Among the primary design objectives of this approach is intelligibility and con-
ciseness of the formal description as a prerequisite for acceptance, correctness
and maintainability. Additionally, executability has become a key issue. Exe-
cutability calls for an operational formalism with readily available tool support.
For this and other reasons (cf. [11]), Abstract State Machines (ASMs) have fi-
nally been selected as the underlying formalism. To support executability, the
formal semantics defines, for each SDL specification, corresponding ASM code.
This differs substantially from the interpreter view taken in previous work, and
enables SDL-to-ASM-compilers. Thus, given the availability of an SDL-to-ASM-
compiler, it will be straightforward to generate reference implementations.
The approach taken by the SDL semantics group to formalize the dynamic se-
mantics of SDL-2000, though basically operational, differs in several aspects from
the approach taken to define the semantics of SDL-88 through SDL-96, and also
from the style of other ASM approaches (cf. [12,10]):
Section 5 introduces the language SSL. Section 6 defines the abstract compi-
lation of SSL specifications to SSLM programs. Section 7 defines the SSL ab-
stract machine (SSLM). Section 8 describes the initialization. Section 9 presents
conclusions. Throughout this paper we assume the reader to be familiar with
Gurevich’s notion of ASM (cf. [14,15,16]).
2 Related Work
Regarding the importance of SDL as a practical tool for industrial systems en-
gineering, it is certainly not astonishing that in addition to activities within
ITU, there is a considerable variety of attempts to formalize the semantics of
SDL using various formal methods. According to the principal objectives be-
hind these attempts, one can distinguish two basically different aspects, namely:
(1) approaches that are mainly concerned with analysis and verification of SDL
system specifications; (2) approaches that are mainly concerned with formal
documentation, maintenance and validation of the SDL language definition.
Fischer, Dimitrov and Tauber propose an extended Petri Net model, so-called
SDL Time Nets, as a formal basis to verify SDL protocol specifications [8], [9].
The transformation of SDL specifications into corresponding net models as well
as the transformation of results back to the SDL level is done automatically
within the SDL Integrated Tools Environment SITE. Though these transfor-
mations, in principle, remain invisible for the user, the authors concede that
certain knowledge is necessary about the internal data structures of the analy-
sis tools. Moreover, the integration of further net analysis algorithms is difficult
and requires detailed knowledge about the internals (like interfaces, structure
and representation formats) of the tools.
3 Pragmatic Foundation
- The SDL view of distributed systems with real-time constraints and the
semantic modeling concept of distributed real-time ASM clearly coincide.
That is, essential properties of the underlying computation models – namely,
the notions of concurrency, reactivity and time as well as the notion of state –
are so tightly related that the common understanding of SDL can directly be
converted into a formal semantic model avoiding any formalization overhead.
- Even without direct support of object-oriented features in ASMs, the resul-
ting ASM model of the dynamic semantics is particularly concise, readable
and understandable. Furthermore, this model can easily be extended and
modified as required for an evolving technical standard. Beyond the purpose
addressed here, the model may as well be utilized for defining a bridging se-
mantics in order to combine SDL with other modeling languages (e.g., UML
and VHDL).
model that reflects the dynamic properties of the language so closely that cor-
rectness can be established by observation and experimentation. In ASM terms,
such a model is called a ground model, cf. [2]. The quality of ground models
considerably depends on the underlying abstractions for dealing with real-world
phenomena, i.e. on the way in which basic objects and operations of a ground
model are related to basic entities and actions as observed in the real world.
Investigating the construction of ground models (as part of the analysis and
design process), one can identify general principles for justifying their appro-
priateness and validity. In [2], Egon Börger convincingly argues that this justifi-
cation process has three basic dimensions, namely: a conceptual justification, an
experimental justification, and a mathematical justification. According to this
understanding, the best we can achieve is a solid pragmatic foundation.
The definition of the formal semantics of SDL-2000 is structured into the follo-
wing major parts:
– grammar,
– well-formedness conditions (static semantics),
– transformation rules, and
– dynamic semantics.
The grammar defines the set of syntactically correct SDL specifications. In the
SDL standard, a concrete textual, a concrete graphical, and an abstract gram-
mar are defined using BNF with some extensions to capture graphical language
elements. The abstract grammar is obtained from the concrete grammars by
removing irrelevant details such as separators and lexical rules.
The well-formedness conditions impose additional context constraints on syntac-
tically correct specifications, such as which names it is allowed to use at a given
place, which kind of values to assign to variables, etc.
For some language constructs, the formal semantics is not provided directly, but
through transformation rules. These rules define how a given specification is to
be transformed in order to replace the language constructs by basic language
elements, and are formally represented as rewrite rules.
Finally, the dynamic semantics is given to syntactically correct SDL specificati-
ons satisfying the well-formedness conditions, after application of the transfor-
mation rules. The dynamic semantics consists of the following parts as illustrated
by figure 1.
– The SDL Abstract Machine (SAM) defines basic signal flow concepts of SDL
such as signals, timers, exceptions, and gates in terms of the ASM model.
Furthermore, ASM agents are specialized to model agents in the context of
SDL. Finally, several signal processing and behavior primitives - in a sense
the abstract machine instructions of the SAM - are predefined.
250 R. Eschbach et al.
Abstract Syntax
Structure Behaviour Data
Interface
Compilation
Initialization
ASM
As in the past, the new formal semantics is defined starting from the abstract
syntax of SDL, which is documented in the SDL standard. From this abstract
syntax, a behavior model that can be understood as abstract code generated
On the Formal Semantics of SDL-2000 251
Regarding layers (2),(1), it is not a priori fixed which aspects of behavior are
subject to modeling at which one of these two layers. Though, in principle,
specific aspects depending on a given SDL specification typically ought to be
encoded into the compilation function, whereas invariant dynamic properties
are more naturally expressed in terms of the SAM model. Finally, to keep the
formalization as concise and elegant as possible, all aspects that both computa-
tion models (SDL and ASM) have in common are preferably given implicitly by
mapping SDL behavior primitives directly onto corresponding primitives of the
underlying ASM model at layer (0).
By splitting the definition of the SDL behavior model into the above layers, one
obtains a flexible framework for semantic modeling that allows for natural and
adequate abstractions, transparent and well defined formalization, and flexible
rearrangements on the basis of an executable semantics.
5.1 Basics
The syntax of SSL is given below. We use Name as a predefined lexical unit and
describe the syntax using a variant of BNF (Backus-Naur-Form). More precisely
we will just give an abstract syntax of the language. We assume the concrete
syntax to be handled by an ordinary parser that provides us with the abstract
representation in terms of the abstract syntax.
Please note, that we use prefixes in the syntax to make the role of the items
clear. Prefixes are separated from the nonterminal name by a ”-”. Prefixes do
not have a semantics.
252 R. Eschbach et al.
specification Hanoi
procedure hanoi (ref x,y,z, val n)
if n = 1 then
x := x - 1; z := z + 1; return;
else
call hanoi(x,z,y,n-1); call hanoi(x,y,z,1);
call hanoi(y,x,z,n-1); return;
end hanoi
var x,y,z,n
begin Hanoi
x := 4; y := 0; z := 0; n := 4; call hanoi(x,y,z,n);
end Hanoi
A procedure is given as a list of its parameters and an expression for the body.
5.3 Expressions
Expressions in the scope of SSL include also the usual behavior of statements. In
this case, their return value is simply ignored. The nonterminal function denotes
constants (like 0 and 1) as well as functions (as + and -). The meaning of
these constructs is supposed to be part of the language. The semantics of ifExpr,
variable, assignment, procCall, procReturn and equality is as usual. For sequence,
On the Formal Semantics of SDL-2000 253
Please note, that the syntax domain names are also used as the constructors for
the syntax tree. A sample SSL specification can be found in figure 2.
6 Compilation
The compilation defines a mapping from the behavioral part of the abstract
syntax into ASM behavior. In fact, an abstract code is defined as the result of
this mapping. The behavior belonging to the behavior primitives in the abstract
code is defined within section 7.
6.1 Basics
In this section, we define a compilation function for SSL specifications and for
SSL procedures.
compileSpec : sslSpecification → AbstractCode
compileProc : procedureDef → AbstractCode
For the representation of code, we use a special labeling. Each instruction has
a label attached to it. The specification keeps the information of the current
label and selects the instruction to process according to this label. Therefore, we
introduce a static domain Label. The abstract code is then a set of instructions
and an instruction a tuple consisting of a label and a primitive.
AbstractCode = Instruction-set
Instruction = Label × Primitive.
The compiled code of all specifications and procedures is merged together in the
initial state. Specifications and procedures are characterized by the functions
entrySpec : sslSpecification → Label,
entryProc : Name → Label,
254 R. Eschbach et al.
which return the entry labels. For SSL we introduce the following primitives.
Again, the prefixes are without semantic relevance.
elsif e ∈ ifExpr // first evaluate the test, then switch to then or to else
then compileExpr(e.test-expression, unique(e)) ∪
{ mk-Instruction(unique(e),
mk-Pif(unique(e.test-expression),
entryLabel(e.then-expression), elseLabel)} ∪
compileExpr(e.then-expression, next) ∪
compileExpr(e.else-expression, next)
else . . .
where
elseLabel ≡
if e.else-expression = undef then next
else entryLabel(e.else-expression)
7.1 Basics
In this section we describe how states are realized in SSLM. Usually, a state is
a mapping of variable names to values. In SSLM locality and scope of variables
are given by a model in which variable names are mapped to (memory) locati-
ons containing values. This model allows for call-by-value and call-by-reference
semantics, i.e. procedures can have reference and value parameters. To this aim
we introduce a dynamic domain Location for locations and dynamic, controlled
functions
loc : VarName → Location
cont : Location → Value.
We use here and in the following VarName, FuncName, and ProcName as syno-
nyms for Name. We use in the following a static, controlled function
splmInstruction : Label → Primitive,
which treats the sets of pairs returned by the compilation functions as a function.
This is possible since the compilation functions always compute a graph of a
function. Furthermore, we introduce a kind of program counter, modeled by
currentLabel :→ Label,
which returns the label associated with the instruction to be executed next.
During the dynamic execution of a program intermediate values can be stored
at labels. More precisely, we introduce the dynamic, controlled function
labelValue : Label → Value,
for this purpose. In case of a sequence seq = (l1 , . . . ln ) of labels we simply write
labelValues(seq) for the sequence (labelValue(l1 ), . . . , labelValue(ln )).
Function names are evaluated by a static function that is provided by the data
semantics of the language. This would again be part of the data interface in the
case of SDL.
evalFunc : FuncName × Value∗ → Value
On the Formal Semantics of SDL-2000 257
7.3 Procedures
Before we can define macros for procedure call and return we must have some
means for storing multiple values of several incarnations of a function. To this aim
we introduce frames which are organized as stacks. The universe Frame comprises
at each point of time a finite set of frames which initially contains one element.
A dynamic, controlled function topFrame :→ Frame indicates the current top
of the stack. The function prev : Frame → Frame returns for each frame its
predecessor (if there is any). The following dynamic, controlled functions are
introduced for the procedure call and return mechanism. The former function
corresponds to a kind of return address, the latter gives the information at which
label the result of a procedure must be stored.
returnLabel : Frame → Label
resultLabel : Frame → Label
258 R. Eschbach et al.
Macro PushNewFrame pushes frame on the stack of frames, and updates returnLabel
and resultLabel.
Macro PassRefParameters has two formal arguments: the procedure name procName
and the sequence of actual reference parameters varNames. This macro realizes
the parameter passing mechanism for reference parameters. This means referen-
ces of variable names to locations in current state are changed appropriately.
We use a function refPar : ProcName × Nat → VarName to determine reference
parameters. Function |.| denotes the length function for sequences.
On the Formal Semantics of SDL-2000 259
Macro PassValParameters creates for each value parameter a new location. Each
label in the sequence labels has a value in frame. We write simply labels(i)
to project on the i-th component of this sequence. The value of a new loca-
tion location is set to the value stored in labels and frame where i ranges over
{1, . . . , |labels|}. The location for the i-th value parameter given by a function
valPar : ProcName × Nat → VarName is set to a new location. We assume that
the size of labels equals the number of value parameters.
Macro StaticBinding saves procName in frame creates a variable binding for those
variables which are not defined in the body of procName.
Macro CreateLocalVars creates new location for local variables associated with a
procedure or a program. Local variables are determined by a means of a static
function locVars : Name → Name-set.
260 R. Eschbach et al.
CreateLocalVars(name, f rame) ≡
forall varN ame ∈ locVars(name) do
extend Location with location
loc(varN ame, f rame) := location
Macro EvalReturn essentially removes the top frame of the stack, saves the result
according to the result label, and jumps to the return label.
EvalReturn(label) ≡
topFrame := prev(topFrame),
labelValue(resultLabel(topFrame), prev(topFrame)) :=
labelValue(label, topFrame),
currentLabel := returnLabel(topFrame)
Label
VarName
L7
returnLabel
L6 x y
Frame
L5
resultLabel
L4 F2
L3 loc
L2 F1
L1
L0 labelValue F0 l0 l1 Location
cont
10 14 14 Value
7.4 Distribution
In this section we refine the SSLM defined so far to a distributed abstract ma-
chine. The underlying distribution model consists of several agents which execute
their programs and communicate by means of shared variables. The refinement
step is done in a similar way as in the preceding section, namely by changing the
declaration of some existing functions. We provide each agent with its own pro-
gram counter. To this aim we change the declaration of functions currentLabel,
and topFrame to
currentLabel : Agent → Label,
topFrame : Agent → Frame.
Every previous occurrence of f inside macros should be replaced by f (Self),
where f is one of the functions {currentLabel, topFrame}.
On the Formal Semantics of SDL-2000 261
8.1 Initialization
In the ASM model, the set of initial states is usually defined by constraints
imposed on domains, functions, and predicates. These constraints are required
to hold in the first state of each run of the abstract state machine. Initial con-
straints are stated by formulae that are preceded by the keyword initially, i.e. in
a property-oriented style.
As it turned out during work on the formal SDL semantics, a property-oriented
definition of the set of initial states, given by the set of SDL agents, SDL agent
sets, and link agents together with the initial values of all functions and predi-
cates defined on these agents is rather complex and hard to follow. Therefore, a
different approach has been chosen, which may be characterized as a combination
of constraints and initial computations: several (simple) constraints determine
the so-called set of pre-initial states. The actual set of initial states is determined
by the initialization phase that starts in a pre-initial state, and creates a set of
agents as well as the system structure.
To give a flavor of the approach taken in the formal SDL semantics, we adapt
it here to define the set of initial states associated with an SSL specification. A
nullary function
main :→ Agent
is introduced to refer to the pre-initial system agent. Furthermore, we define
topLevelSpec :→ sslSpec
mySpec : Agent → sslSpec
The set of pre-initial states is defined by the following set of constraints (strictly
speaking, the set of pre-initial states is the set of initial states of the ASM model):
InitModule ≡
forall p ∈ Self.mySpec.sslSpec-seq do
extend Agent with a
extend Frame with f rame
a.mySpec := p, a.topFrame := f rame
a.currentLabel := p.entrySpec, a.Mod := InitModule,
CreateLocalVars(Self.mySpec.spec-Name, Self.topFrame)
Self.Mod := ExecModule
Firing of InitModule creates, for each SSL specification defined in the specifi-
cation of the current agent, one agent executing the InitModule. Furthermore
local variables of the corresponding SSL specification are created. This leads to
a recursive unfolding of the tree of SSL specifications defined by the top level
specification. Macro CreateLocalVars is defined in section 7.3.
In the SDL semantics, it is important that the initialization phase is comple-
ted before the execution phase starts. This is achieved by a mechanism that
blocks agents after their initialization until the initialization phase has ended.
For simplicity, we have not modeled this mechanism in the solution presented
here.
ExecModule ≡ EvalInstruction(Self.currentLabel.sslmInstruction)
EvalInstruction(p) ≡
if . . .
elsif p ∈ Passign
then EvalAssign(p.Name, pvalue-Label, p.next-Label)
elsif p ∈ Pcall
then EvalCall(p.Name, p.value-Label-seq, p.ref-Name-seq, p.next-Label)
...
On the Formal Semantics of SDL-2000 263
9 Conclusions
In this paper, we have presented the transformational/operational approach
which has been successfully applied to define the semantics of SDL-2000 for-
mally. We have illustrated some basic techniques of the approach using SSL, a
simple specification language.
The following topics have been covered:
– Abstract Machine: The semantics is based on an abstract machine. This
machine is an extension of the ASM model and is specifically tailored towards
the needs of the specification technique. It introduces an abstract code for the
FDT and could therefore also be used for its implementation. The abstract
machine comprises a set a machine instructions and their semantics as well
as some domains and functions. An important part of the abstract machine
is the built-in procedure model that allows for call-by-value and call-by-
reference parameter passing semantics. Furthermore, the concept of static
binding is built into the abstract machine.
– Compilation: Any program/specification of the specification technique is gi-
ven a semantics by transforming it into the abstract machine. This is es-
sentially an abstract compilation function. The compilation transforms the
domain of the abstract syntax trees into the domain of abstract machine
instruction sets. The execution of these sets is then covered within the de-
scription of the abstract machine.
– Initialization: In order to run a specification on the abstract machine, a spe-
cial initialization has to be performed. To this end, the abstract syntax tree
is used to determine the internal parts of the specification to be initialized.
These parts are then initialized as well. This way, the initialization takes
care of structural information, while the behavioral information is handled
by the compilation function. The same initialization behavior could also be
used for dynamic creation of specifications.
The subdivision of the semantics into the parts presented above has proved very
useful in the context of SDL, as it is essential here to be able to adjust the
semantics to the development of the FDT. This is achieved by using two layers
of abstraction: (1) abstract the concrete syntax into an abstract syntax, (2)
transform the abstract syntax to an abstract machine.
It should be noted that the formal SDL semantics has been conceived in parallel
to the language definition itself. During this process, several substantial changes
to the language definition, which turned out to be a ”moving target”, were made.
These changes of course affected the formal semantics definition, but usually did
not touch the SAM.
Finally, it should again be stressed that the work reported here is not an academic
exercise, but takes place in a real-life, industrial setting. In our opinion, it is
this kind of work academic efforts should eventually lead to. The successful
application of mathematical formalism to real-world problems and their approval
by industry is a strong selling point for having formalisms at all. In this sense, the
work in progress reported in this paper is one important step in this direction.
264 R. Eschbach et al.
References
1. J. A. Bergstra and C. A. Middleburg. Process Algebra Semantics of ϕSDL. Techni-
cal Report UNU/IIST Report No. 68, UNU/IIST, The United Nations University,
April 1996.
2. E. Börger. High Level System Design and Analysis using Abstract State Machines”.
In Traverso Ullman Hutter, Stephan, editor, Current Trends in Applied Formal
Methods (FM-Trends 98), LNCS. Springer, 1999.
3. E. Börger and W. Schulte. A Programmer Friendly Modular Definition of the
Semantics of Java. In J. Alves-Foss, editor, Formal Syntax and Semantics of Java,
LNCS. Springer, 1998.
4. M. Broy. Towards a Formal Foundation of the Specification and Description Lan-
guage SDL. Formal Aspects of Computing 3, (3):21–57, 1991.
5. M. Broy, F. Dederichs, C. Dendorfer, M. Fuchs, T. F. Gritzner, and R. Weber.
The design of distributed systems – an introduction to FOCUS. Technical Report
TUM-19202-2 (SFB-Bericht Nr. 342/2-2/92/A), Institut für Informatik, Techni-
sche Universität München, January 1993.
6. J. Ellsberger, D. Hogrefe, and A. Sarma. SDL: Formal Object-oriented Language
for Communicating Systems. Prentice Hall Europe, 1997.
7. R. Eschbach, R. Gotzhein, and A. Prinz. On the Formal Semantics of SDL-2000: A
Compilation Approach using Abstract State Machines. In Local Proc. of ASM2000,
Monte Verità, Switzerland, 2000.
8. J. Fischer and E. Dimitrov. Verification of SDL protocol specifications using ex-
tended Petri Nets. In Proc. of the Workshop on Petri Nets and Protocols of the
16th Intern. Conf. on Application and Theory of Petri Nets, pages 1–12. Torino,
Italy, 1995.
9. J. Fischer, E. Dimitrov, and U. Taubert. Analysis and formal verification of SDL‘92
specifications using extended Petri Nets, 1995. internal report.
10. U. Glässer. ASM semantics of SDL: Concepts, methods, tools. In Y. Lahav,
A. Wolisz, J. Fischer, and E. Holz, editors, Proc. of the 1st Workshop of the SDL
Forum Society on SDL and MSC (Berlin, June 29 - July 1, 1998), volume 2, pages
271–280, 1998.
11. U. Glässer, R. Gotzhein, and A. Prinz. Towards a New Formal SDL Semantics
Based on Abstract State Machines. In SDL’99 - The Next Millennium, Proc. of
the 9th SDL Forum. Elsevier Science B.V., 1999.
12. U. Glässer and R. Karges. Abstract State Machine semantics of SDL. Journal of
Universal Computer Science, 3(12):1382–1414, 1997.
13. R. Gotzhein, B. Geppert, F. Rößler, and P. Schaible. Towards a new formal SDL
semantics. In Y. Lahav, A. Wolisz, J. Fischer, and E. Holz, editors, Proc. of the
1st Workshop of the SDL Forum Society on SDL and MSC (Berlin, June 29 - July
1, 1998), volume 1, pages 55–64, 1998.
14. Y. Gurevich. Evolving Algebras 1993: Lipari Guide. In E. Börger, editor, Specifi-
cation and Validation Methods. Oxford Univ. Press, 1995.
15. Y. Gurevich. May 1997 draft of the ASM guide. Technical Report CSE-TR-336-97,
University of Michigan, 1997.
On the Formal Semantics of SDL-2000 265
16. Y. Gurevich. The sequential ASM thesis. Bulletin of the European Association for
Theoretical Computer Science, 67:93–124, February 1999.
17. Y. Gurevich and J. K. Huggins. The Semantics of the C Programming Language.
In Selected papers from CSL’92 (Computer Science Logic), LNCS, pages 274–308.
Springer, 1993.
18. U. Hinkel. Formale, semantische Fundierung und eine darauf abgestützte Verifika-
tionsmethode für SDL. Ph.D., Techn. Univ. München, 1998.
19. E. Holz and K. Stølen. An Attempt to Embed a Restricted Version of SDL as a
Target Language in Focus. In St. Leue D. Hogrefe, editor, Proc. of Forte ’94, pages
324–339. Chapmann & Hall, 1994.
20. ITU-T. Recommendation Z.100: Specification and Description Language (SDL),
International Telecommunication Union (ITU). Geneva, 1999.
21. St. Lau and A. Prinz. BSDL: The Language – Version 0.2. Department of Computer
Science, Humboldt University Berlin, August 1995.
22. A. Mitschele-Thiel. Performance evaluation of SDL systems. In Y. Lahav, A. Wo-
lisz, J. Fischer, and E. Holz, editors, Proc. of the 1st Workshop of the SDL Forum
Society on SDL and MSC (Berlin, June 29 - July 1, 1998), volume 1, pages 35–44,
1998.
23. A. Olsen, O. Færgemand, B. Møller-Pedersen, R. Reed, and J. R. W. Smith. Sy-
stems Engineering Using SDL-92. Elsevier Science B. V., 1994.
24. A. Prinz, R. Gotzhein, U. Glässer, and R. Eschbach. SDL For-
mal Semantics, Z.100.F, ITU-T, International Telecommunication Union.
www.informatik.hu-berlin.de/˜prinz/Semantics/Z100F_Geneva.doc.
Description and Simulation of Microprocessor
Instruction Sets Using ASMs
1 Motivation
Areas such as digital signal processing and process control have brought up new
kinds of processor architectures which are dedicated to their specific needs and
frequently called ASIPs (application-specific instruction set processors). Exam-
ples are digital signal processors (DSPs) and micro-controllers. Especially in the
area of embedded systems, efficiency of both the architecture and the compiler
for code generation is of utmost importance.
Hence, a new era dealing with the problem of simultaneously developing
processor architecture and compiler has just emerged – architecture and compiler
co-design.
Here, we describe a method that starts with an extraction method following
the approach of Leupers [26] that enables an efficient description and extrac-
tion of the instruction set of a given register transfer description of the target
architecture. The instruction set of a processor may be described by a set of gu-
arded register transfer patterns. Here, these patterns are translated directly into
a set of Gurevich’s ASM [17] (abstract state machine) rules, a model for parallel
computation that has already been widely used for describing the semantics of
as well problem-oriented languages (like C [18], Prolog [11], Oberon [22], Java
[31]) as hardware description languages (e.g., behavioral VHDL [8]). Several case
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 266–286, 2000.
c Springer-Verlag Berlin Heidelberg 2000
Description and Simulation of Microprocessor Instruction Sets Using ASMs 267
1. Instruction set extraction: Using ideas of the work of Leupers [26], the in-
struction set of a processor may be automatically extracted from a processor
description resulting in a set of so called guarded register transfer patterns
that describe the cycle-accurate behavior of the instruction set architecture.
2. ASM generation: From this set of patterns, we show that an ASM descrip-
tion that also reflects the cycle-accurate behavior of the processor may be
automatically generated. In particular, we present a code translation metho-
dology that generates an ASM description into the language XASM [1].
3. Simulator/Debugger Generation: In the last step, the existing ASM simula-
tion and prototyping environment Gem-Mex [23] is used in order to generate
a simulator/debugger for this ASM. Major advantage of using this metho-
dology is its efficiency in the number of simulation cycles per second that
may be obtained for simulating the execution of a microprocessor.
4. Generic Simulation Core: Another highlight of our approach is that the
functional behavior of instructions of many different number representations
and word-lengths may be considered using an arbitrary precision integer
engine.
Description and Simulation of Microprocessor Instruction Sets Using ASMs 269
I.(12:5)
I.(4)
RAM
inp
PC +1 I.(20:13)
IM MUX I.(3:2)
I.(20:0) I.(1:0)
ALU
REG
control-path data-path
5. Library based computing engine: Based on the above simulation core, a li-
brary of generic instructions including arithmetic, transport operations, and
number conversions encountered in almost any microprocessor is supported.
During the execution of a machine instruction, several register transfers may take
place. In [26], the set of these register transfers is represented by so called regi-
ster transfer patterns (RTP) which are templates of register transfers. A register
transfer (RT) in turn reads input values from registers, memory cells, and/or
input ports, performs a computation, and assigns the result to a destination.
That way, a RTP reflects the capabilities of the data-path of a microprocessor
under question.
Definition 3 (RT Pattern). A register transfer pattern (RTP) is a pair (d, e),
where d is a register transfer destination and e is a register transfer expression.
with width(µ) denoting the width of the mode register µ, the index k deno-
ting the k-th bit position of the mode register µ.
Instruction bit variables are denoted by:
IBV = {Ik | Ik ∈ {0, 1}; k ∈ {0, · · · , Lw − 1}}
with Lw denoting the processor’s instruction word length and k indicating
a specified bit position.
Description and Simulation of Microprocessor Instruction Sets Using ASMs 271
databus
I.(2) I.(3)
D1 D2
R1 R2
0 1
MR1 MUX1 R3 R4
0 1 0 1
I.(0)
MUX2 MUX3 MR2
ADD
I.(1)
R5
Fig. 2. Simple data-path from which RTPs may be extracted. I.(0) − I.(3) denote
corresponding bits of the instruction word I of the control-path
– dynamic (run-time) conditions, e.g., the equality of two register contents. Let
COM P denote the set of all comparisons permissible on a processor, i.e.,
the set of RT expressions of the form op(e, e0 ) where op ∈ {=, 6=, <, >, ≤, ≥}
is a comparison operator. The dynamic control variables are defined as:
Now, instruction set extraction is the task of computing all GRTPs for a
given processor. For a computational procedure for instruction set extraction,
Leupers uses an internal graph data structure that is generated from a MIMOLA
netlist of the processor, see [26] for details. By recursively traversing this graph,
the complete set of RTPs may be extracted efficiently.
GRTPs capture all information required for code generation. RTPs represent
the RPs that a processor can execute, while RTCs represent the required machine
state in terms of partial instructions and mode register states, as well as possible
dynamic conditions.
Example 3. Consider again the simple data-path shown in Fig. 2. I stands for
instruction word. MR1 and MR2 denote mode registers. Suppose we want to
analyze the RTC for RTP ”R5:= R2 + R3”.1 Routing the value of R2 to the left
input of module ADD via multiplexers MUX1, MUX2 can be realized by control
codes MM R1 = 1 and I.(0) = 0. By setting MM R2 = 0, the content of R3 is
switched to the right input of ADD. Furthermore, loading R5 with the result of
the functional unit ADD can be enabled by setting I.(1) = 1. The corresponding
RTC is
F1 = I.(0) · I.(1) · MM R1 · M M R2
There exists also an alternative route for transportation of R2 to ADD: If bus
driver D1 is activated by setting I.(2) = 1, and MUX2 passes its right input
(I.(0)=1), then the value of R2 is routed via databus. A potential bus conflict
needs to be avoided by setting driver D2 to a tristate mode (I.(3) = 0). The
control codes I.(1) = 1 and MM R2 = 0 remain unchanged. The resulting RTC is
Since F1 and F2 are alternative conditions for the same operation, we obtain the
GRTP
((F1 ∨ F2 ), R5 := R2 + R3)
Note that this formula already looks very much like a GRTP in case Cond is
an RTC and Updates is an RTP. In general, Cond may be an arbitrary boolean
1
Short expression for the RTP (writer(R5), +(readr(R2), readr(R3)).
2
in the following, also called universes [17]
Description and Simulation of Microprocessor Instruction Sets Using ASMs 273
valued expression (first-order logic formula) and Updates is a finite set of updates
to be described later.
The operational semantics of an ASM may be described as follows [17]: The
effect of a transition rule R when applied to an algebra A is to produce another
algebra A0 which differs from A by the new values for those functions at those
arguments where the values are updated by the rule R. If Cond is true, the rule
can be executed by simultaneously executing each update in the set of updates.
In order to understand this, we have to look at the definition of (function)
updates because these are the mechanism by which the dynamics of arbitrary
systems can be explicitly described.
f (t1 , · · · , tn ) := t
((F1 ∨ F2 ), R5 := R2 + R3)
F1 = I.(0) · I.(1) · MM R1 · M M R2
compute (these are RT expressions). At the end (or beginning) of each clock cy-
cle, the state of the system, as represented by the contents of the registers and
memories, is updated. This is represented by guarded updates in which the fun-
ctions correspond to RT destinations. An ASM description of an instruction set
simultaneously defines an interpreter for the instruction set.
The same notation is used for registers from the universe REG and memories
from the universe M EM in case they have to be distinguished by different
word-lengths. For example, let REG(16) denote the subset of registers having a
word-length of 16 bits.3
For the reason we would like to present processor architectures independent
from the number representation used (e.g., signed, one’s or two’s complement,
etc.), we introduce a function4
that returns the value of a word in the corresponding number system and its
inverse5
val−1 : IN T EGER → W ORD(t)
F : i1 × · · · × in → o1 × · · · × om
The model for registers (universe REG) and memories (universe M EM ) has
already been shown. The contents of registers are modeled by unary functions
for each word-length n ∈ IN T EGER. For memories, the indexed access func-
tions
contents : M EM (n) × IN DEX → W ORD(n)
are defined for each memory m ∈ M EM (n) where IN DEX is defined over the
universe {0, · · · , size(m) − 1} and the function
where
e0 = extract(e, hi, lo)
returns a word e0 of word-length m = hi − lo + 1 in case lo ≤ hi < n that satisfies
∀i ∈ {0, hi − lo} : val(e0 .(i)) = val(e.(i + lo)). Else it is undefined.
Moreover, the function
a b
5 8 6
zero
3
12 4
16
c
Fig. 3. Example of extract and merge of signals
level (i.e., RT-level) of abstraction, fast and independent from the number system
of the architectures’ arithmetic-logical units.
Therefore, we implement a simulation engine that computes functions based
on (arbitrary long) integer representations for implementation of highly complex
arithmetic functions, and use the two functions
to convert numbers from different number systems. Only these two functions
have to be implemented to retarget the simulator to a different arithmetic num-
ber system, see Fig. 4.6 The functions val, respectively val−1 are the interfaces
of the integer engine to a processor with a different number representation and
word-length. Note that in some cases, it may be more convenient to perform ope-
rations on the values of extracted subwords (e.g. single bits) of operands using
extract and not on the values of the complete operands.
In [30], details are given for the definition of the functions val and val−1 for
typical number systems used in computer systems.
In the following example, it is shown how a typical operation of two operands
is simulated generically using the above retargetable simulation engine.
x (WORD(n)) y (WORD(m))
val(x) val(y)
integer
engine
F F
int
val(z)
z (WORD(q)) -1
val
z (WORD(q))
A[31:0]
control
address register
PC
incrementer
PC
register
bank instruction
R14
R15
decode
R0
ALU Bus
&
multiply control
A Bus
register
barrel
shifter
B Bus
ALU
D[31:0]
code and binary code was written using the existing parser support of XASM.
Some details are given in Section 4.2. Given the XASM specification, the Gem-
Mex tool [2] generates a graphical debugger and simulation tool, see Section 4.3.
tion is decoded and assigned to the respective execution unit. During the phase
execute, the instruction will be executed and the result will be stored.
One can think of these phases as being executed sequentially, but in fact the
architecture has parallel logic for all three phases, so in a stable pipeline state,
the ARM processor is able to execute three operations of a machine program in
parallel. In [19], a simple ASM-model of the ARM microprocessor for sequential
execution mode is given and stepwise refined to a model executing the stages in
parallel. The correctness of the refinements is proven.
We started by entering the specifications of the different refinements, and
debugged them. The disadvantage of considering several refinements simulta-
neously is that it is almost impossible to maintain all versions if changes must
be made. Thus, we decided to integrate the different models into one model that
can be transformed into any of the refinements by exchanging some macro defi-
nitions. This version of the description is given in [21]. Depending on the boolean
value of the macro PIPELINED, either the stages are executed sequentially or in
pipelined mode. In sequential mode, a nullary function
function Stage
is used. The values of Stage are fetch, decode, or execute. In XASM syntax, the
three constants can be introduced by enumeration:
universe STAGES = {fetch, decode, execute}
The control structure simulating both the sequential and the parallel execution of
stages is structured as follows. Assume, code[Contents(PC)] fetches the current
instruction from memory.
if PIPELINED or Stage = fetch then
Instr := code[Contents(PC)]
if PIPELINED then
DecodeInstr := Instr
else
Stage := decode
endif
R_FETCH
endif
if PIPELINED or Stage = decode then
if PIPELINED then
ExecuteInstr := DecodeInstr
else
Stage := execute
endif
R_DECODE
endif
if PIPELINED or Stage = execute then
if PIPELINED then
else
Description and Simulation of Microprocessor Instruction Sets Using ASMs 281
Stage := fetch
endif
R_EXECUTE
endif
In pipelined mode, all three parts of the rule are executed in parallel, otherwise
the parts are executed sequentially in cyclic mode.
In the three remaining rules R FETCH, R DECODE, and R EXECUTE of the com-
plete ARM description not given here, the active instructions must be accessed
by three macros defined as follows:
If these macros are used to refer to the instruction, almost the complete functio-
nality of the rules R FETCH, R DECODE, and R EXECUTE can be defined independent
of whether the stages are executed in parallel or sequentially.
In order to make the description retargetable to other architectures, we used
the ASM-library of Section 3. The adaption to our methodology involved mainly
the adaption of the signature. The structure of the rules remained unchanged.
nonterm ARMcode[Instruction];
base Instruction
cont ARMcode ‘‘;’’ Instruction
endnonterm
Finally, the syntax of single instructions is given, for instance for a branch:
A[31:0]
control
ARM.xasm address register
testprg.ass
PC
incrementer
PC
register
bank instruction
decode
&
multiply control
A Bus
register
barrel
shifter
Architecture Instruction
B Bus
Description Description
XASM Assembler
Retargetable code code
Simulation instr.c
Engine
C code
Gem-Mex
Function
Library
C code Tcl/Tk
Debugging
and
Simulation
Environment
where in the rule R one can access the components of the branch instruction
as link, cond, and adr. In the rule R certain informations about the instruction
are precomputed.
If a further analysis of the code is desired, e.g. an abstract verification of
certain properties, the Montages [3] support of Gem-Mex can be used. We re-
stricted the use of Gem-Mex to the generation of graphics for simulation and
debugging.
the architecture description into C-code and calls the gcc-compiler to compile
these C-files. Thereby, the C-library of arithmetic functions described in Section
3 is linked. As a result, an executable is generated and which interacts with
generic Tcl/Tk scripts provided by the Gem-Mex simulation and debugging en-
vironment. The visual aspects of the simulation and debugging environment are
shown at the bottom of the figure.
Figure 7 displays a snapshot of a simulation run both of the pipelined (top)
and the sequential (bottom) implementation. The algorithmic load respectively
consists of a small assembler program, see Figure 8.
Here, after nine steps, the sequential implementation has reached the end of
an execution phase and sets the Stage flag to fetch indicating that the next
operation has to be a fetch operation. The parallel implementation only needs
three cycles to reach the same machine state due to pipelining. The right hand
picture shows the contents of the respective register file.
5 Conclusions
In this paper, we present foundations for the simulation of a processor archi-
tecture and its instruction set given the syntax of the instruction and either a
netlist or graphical description of the architecture’s data-path.
A major advantage of our approach with respect to other approaches is that
the ASM-formalism itself is suited for architecture description. The generated
ASMs can thus serve as additional documentation. Changes may be made di-
rectly at the ASM level and the corresponding changes in the netlist/graphical
description can be regenerated.
284 J. Teich, P.W. Kutter, and R. Weper
References
1. M. Anlauff. Xasm - an extensible, component-based abstract state machines lan-
guage. In This Volume.
2. M. Anlauff, P.W. Kutter, and A. Pierantonio. Formal aspects of and development
environments for Montages. In Second Int. Workshop on the Theory and Practice
of Algebraic Specifications, Workshops in Computing. Springer-Verlag, 1997.
3. M. Anlauff, P.W. Kutter, and A. Pierantonio. Enhanced control flow graphs in
Montages. In Perspective of System Informatics, number 1726 in LNCS, 1999.
4. S. Bashford, U. Bieker, B. Harking, R. Leupers, P. Marwedel, A. Neumann, and
D. Voggenauer. The MIMOLA language - version 4.1. Technical report, University
of Dortmund, 1994.
5. E. Börger. Why use evolving algebras for hardware and software engineering? In
M. Bartosek, J. Staudek, and J. Wiederman, editors, SOFSEM’95, 22nd Seminar
on Current Trends in Theory and Practice of Informatics, volume 1012 of Springer
Lecture Notes on Computer Science (LNCS), pages 236–271, 1995.
Description and Simulation of Microprocessor Instruction Sets Using ASMs 285
6. E. Börger and G. Del Castillo. A formal method for provably correct composition
of a real-life processor out of basic components (the APE100 reverse engineering
study). In B. Werner, editor, Proc. of the First Int. Conf. on Engineering of
Complex Computer Systems (ICECCS’95), pages 145–148, November 1995.
7. E. Börger, G. Del Castillo, P. Glavan, and D. Rosenzweig. Towards a mathema-
tical specification of the APE100 architecture: the APESE model. In B. Pehrson
and I. Simon, editors, IFIP 13th World Computer Congress, volume I : Techno-
logy/Foundations, pages 396–401, Elsevier, Amsterdam, The Netherlands, 1994.
8. E. Börger, U. Glässer, and W. Müller. The semantics of behavioral VHDL’93
descriptions. In European Design Automation Conference (EURO-DAC’94), pages
500–505, IEEE CS Press, Los Alamitos, CA, U.S.A., 1994.
9. E. Börger and J. K. Huggins. Abstract state machines 1988-1998: Commented
ASM bibliography. In H. Ehrig, editor, Formal Specification Column, Bulletin of
the EATCS64. February 1998.
10. E. Börger and S. Mazzanti. A practical method for rigorously controllable hardware
design. In J. P. Bowen, M. B. Hinchey, and D. Till, editors, ZUM’97: The Z Formal
Specification Notation, volume 1212 of Springer Lecture Notes on Computer Science
(LNCS), pages 151–187, 1996.
11. E. Börger and D. Rosenzweig. A mathematical definition of full Prolog. Science
of Computer Programming, 24:249–286, 1995. North-Holland.
12. G. Del Castillo and W. Hardt. Fast dynamic analysis of complex hw/sw-systems
based on abstract state machine models. In Proc. of the 6th Int. Workshop on
Hardware/Software Co-Design (Code/Cashe98), Seattle, pages 77–81, March 1998.
13. A. Fauth. Beyond tool-specific machine descriptions. In P. Marwedel and G. Goos-
sens, editors, Code Generation for Embedded Processors, pages 138–152. Kluwer
Academic Press, 1995.
14. A. Fauth, J. Van Praet, and M. Freericks. Describing instruction set processors
using nML. In Proceedings on the European Design and Test Conference, Paris,
France, pages 503–507, March 1995.
15. S . Furber. ARM System Architecture. Addison-Wesley Longman, 1996.
16. G. Goossens, J. Van Praet, D. Lanneer, W. Geurts, and F. Thoen. Programma-
ble chips in consumer electronics and telecommunications. In G. de Micheli and
M. Sami, editors, Hardware/Software Co-Design, volume 310 of NATO ASI Series
E: Applied Sciences, pages 135–164. Kluwer Academic Press, 1996.
17. Y. Gurevich. Evolving algebras 1993: Lipari guide. In E. Börger, editor, Specifica-
tion and Validation Methods, pages 9–36, Oxford University Press, 1995.
18. Y. Gurevich and J. Huggins. The semantics of the C programming language.
In E. Börger, H. Kleine Büning, G. Jäger, S. Martini, and M. M. Richter, edi-
tors, Computer Science Logic, volume 702 of Springer Lecture Notes on Computer
Science (LNCS), pages 274–309, 1993.
19. J. Huggins and D. Van Campenhout. Specification and verification of pipelining in
the ARM2 RISC microprocessor. Technical report, CSE-TR-321-96, EECS Dept.,
Univ. of Michigan, 1996.
20. J. Huggins and D. Van Campenhout. Specification and verification of pipelining
in the ARM2 RISC microprocessor. ACM Transactions on Design Automation of
Electronic Systems, 3(4):563–580, 1998.
21. S. Küng. Simulation mit Abstrakten Zustandsmaschinen, Studienarbeit, 1998.
22. P.W. Kutter and A. Pierantonio. The formal specification of Oberon. Journal of
Universal Computer Science, 3(5):443–503, 1997.
23. P.W. Kutter and A. Pierantonio. Montages: Specification of realistic programming
languages. Journal of Universal Computer Science, 3(5):416–442, 1997.
286 J. Teich, P.W. Kutter, and R. Weper
24. M. Langevin, E. Cerny, J. Wilberg, and H.-T. Vierhaus. Local microcode genera-
tion in system design. In P. Marwedel and G. Goossens, editors, Code Generation
for Embedded Processors, pages 171–187. Kluwer Academic Press, 1995.
25. D. Lanneer, J. Van Praet, A. Kifli, K. Schoofs, W. Geurts, F. Thoen, and G. Goos-
sens. Chess: Retargetable code generation for embedded dsp processors. In P. Mar-
wedel and G. Goossens, editors, Code Generation for Embedded Processors, pages
85–102. Kluwer Academic Press, 1995.
26. R . Leupers. Retargetable Code Generation for Digital Signal Processors. Kluwer
Academic Publishers, Dordrecht, The Netherlands, 1997.
27. R. Leupers and P. Marwedel. Retargetable code compilation based on structu-
ral processor descriptions. Design Automation for Embedded Systems, 3(1):1–36,
January 1998.
28. L. Nowak. Graph based retargetable microcode compilation in the MIMOLA design
system. In 20th Annual Workshop on Microprogramming (MICRO-20), pages 126–
132, 1987.
29. P. Paulin, C. Liem, T. May, and S. Sutarwala. Flexware: A flexible firmware
development environment for embedded systems. In P. Marwedel and G. Goossens,
editors, Code Generation for Embedded Processors, pages 67–84. Kluwer Academic
Press, 1995.
30. J. Teich, P.W. Kutter, and R. Weper. A retargatable engine for the simulation of
microprocessors using asms; DATE-report 04/00. 2000.
31. C. Wallace. The Semantics of the Java Programming Language: Preliminary Ver-
sion. Technical Report CSE-TR-355-97, EECS Dept., University of Michigan, De-
cember 1997.
Symbolic Analysis of Transition Systems?
Natarajan Shankar
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 287–302, 2000.
c Springer-Verlag Berlin Heidelberg 2000
288 N. Shankar
properties. It takes a fair amount of effort and ingenuity to come up with suitable
invariant strengthenings and progress measures.
Methods like model checking [13] that are based on state-space exploration
have the advantage that they are largely automatic and seldom require the fine-
grain interaction seen with deductive methods. Since these methods typically
explore the reachable state space (i.e., the strongest invariant), there is no need
for invariant strengthening. Progress measures are also irrelevant since the size of
the whole state space is bounded. However, model checking methods apply only
to a limited class of systems that possess small, essentially finite state spaces.
Theorem proving or model checking are not by themselves adequate for effec-
tive verification. It is necessary to combine the expressiveness of the deductive
methods with the automation given by model checking. This way, small, finite-
state systems can be directly verified using model checking. For larger, possi-
bly infinite-state systems, theorem proving can be used to construct property-
preserving abstractions over a smaller state space. Such abstractions convert
data-specific characteristics of a computation into control-specific ones. The
finite-state model constructed by means of abstraction can be analyzed using
model checking. It is easy to actually compute the properties of a system from a
finite-state approximation and map these properties back to the original system.
We give an overview of an ongoing effort aimed at constructing a general
framework for the integration of theorem proving, model checking, and program
analysis. We use the term symbolic analysis to refer to the integration of these
analysis techniques since they all employ representations based on symbolic logic
to carry out a symbolic interpretation of program behavior. The framework also
emphasizes analysis, i.e., the extraction of a large number of useful properties,
over correctness which is the demonstration of a small number of important
properties. The framework is called the Symbolic Analysis Laboratory (SAL).
We motivate the need for symbolic analysis and describe the architecture and
intermediate language of SAL.
1 A Motivating Example
We use a very simple and artificial example to illustrate how symbolic analysis
can bring about a synergistic combination of theorem proving, model checking,
and program analysis. The example consists of a transition system with a state
contain a (control) variable PC ranging over the scalar type {inc, dec}, and two
integer variables B and C. Initially, control is in state inc and the variables B
and C are set to zero. There are three transition rules shown below as guarded
commands:
PC = inc −→ B 0 = B + 2; C 0 = 0; PC 0 = dec;
Symbolic Analysis of Transition Systems 289
B:=B+2; C:= 0;
B:=0;
C:=0;
inc dec
PC = dec ∧ B > 0 −→ B 0 = B − 2; C 0 = C + 1;
There is also an implicit stuttering transition from state dec to itself when none
of the guards of the other transitions holds, i.e., when B ≤ 0. Since the inc state
has a transition with a guard that is always true, there is no need for a stuttering
transition on inc. The transition system is shown diagrammatically in Figure 1.
B =0∨B =2 (1)
290 N. Shankar
Note that the transition system Twos is a potentially infinite state system since
variables B and C range over the integers.
Some mathematical preliminaries are in order. A transition system P is given
by a pair hIP , NP i consisting of an initialization predicate on states IP , and a
binary next-state relation on states NP . We constrain the next-state relation N
to be total so that ∀s : ∃s0 : N (s, s0 ). The metavariables s, s0 range over states.
We treat a set of states as equivalent to its characteristic predicate. The boolean
connectives ∧, ∨, ⊃, are lifted from the booleans to the level of predicates and
correspond to the set-theoretic operations ∩, ∪, and ⊆, respectively. An assertion
is a predicate on states. The metavariables φ, ψ range over assertions. A predi-
cate transformer is a map from predicates to predicates. A monotone predicate
transformer τ preserves the subset or implication ordering on predicates so that
if φ ⊃ ψ, then τ (φ) ⊃ τ (ψ). The fixed point of a monotone predicate transformer
τ is an assertion φ such that φ ≡ τ (φ). As a consequence of the Tarski–Knaster
theorem, every monotone predicate transformer has a least fixed point lf p(τ )
and a greatest fixed point gf p(τ ) such that
Let ⊥ represent the empty set of states, > the set of all states, and ω the set
of natural numbers. If the state space is finite, then the least fixed point lf p(τ )
can be calculated as
for some n. W W
If τ is ∨-continuous (i.e., τ ( i∈ω φi ) = i∈ω τ (φi ) for φi such that whenever
i < j, φi ⊃ φj ), then _
lf p(τ ) = τ i (⊥) (2)
i∈ω
V V
Similarly, if τ is ∧-continuous (i.e., τ ( i∈ω φi ) = i∈ω τ (φi ) for φi such that
whenever i < j, φj ⊃ φi ), then
^
gf p(τ ) = τ i (>) (3)
i∈ω
Equations (2) and (3) provide an iterative way of computing the least and grea-
test fixed points but these on infinite-state spaces, the computations might not
converge in a bounded number of steps.
Typical examples of monotone predicate transformers include
1. Strongest postcondition of a transition relation N , sp(N ), which is defined
as
sp(N )(φ) ≡ (∃s0 : φ(s0 ) ∧ N (s0 , s)).
Symbolic Analysis of Transition Systems 291
` ψ(s) ⊃ φ(s)
` IP (s) ⊃ ψ(s)
` ψ(s0 ) ∧ NP (s0 , s1 ) ⊃ ψ(s1 )
P |= invariant φ
The invariant has to be strengthened with the observation that when PC = inc,
B is always 0 so that it now reads
The strengthened invariant (4) is inductive. The need for invariant strengt-
hening in program proofs is the key disadvantage of the deductive methods with
respect to model checking. Quite a lot of effort is needed to turn a putative
invariant into an inductive one. Once an invariant has been strengthened in this
manner, it can contain a large number of conjuncts that generate a case explosion
in the proof. Much of the focus of symbolic analysis is on supplementing deduc-
tive verification with the means of automatically obtaining useful invariants and
invariant strengthenings.
finite-states can be computed by starting from the set of initial states and explo-
ring the states reachable in n consecutive transitions. Any property that holds
on all the reachable states is a valid invariant. There are many variations on
this basic theme. Many modern enumerative model checkers such as Murphi [18]
and SPIN [24] carry out a depth-first search exploration of the transition graph
while maintaing a hash-table to record states that have already been visited. In
SPIN, the LTL model checking problem is transformed into one of emptiness for
ω-automata, i.e., automata that recognize infinite strings [38,19].
In enumerative model checking, properties written in a branching-time tem-
poral logic CTL can be verified in time proportional to N × F where N is
the size of the transition graph and F the size of the temporal formula. Model
checking linear-time temporal logic formulas is more expensive and takes time
proportional to N × 2F where N is the size of the model and F is of the formula.
The Twos example succumbs rather fortuitously to enumerative model
checking. Even though the potential state space of Twos is unbounded, only
a bounded part of the state space is reachable since B is either 0 or 2, and C is
either 0 or 1. The success of enumerative model checking is somewhat anomalous
since this method is unlikely to terminate on typical infinite-state systems. Even
on finite-state systems, an enumerative check is unlikely to succeed because the
size of the searchable state space can be exponential in the size of the program
state. Still, enumerative model checking is an effective debugging technique that
can often detect and display simple counterexamples when a property fails.
Automatic invariant generation techniques have been studied since the 1970s [14,
21,27,36], and more recently in the work of Bjørner, Browne, and Manna [3], and
Bensalem, Lakhnech, and Saı̈di [9,34,7].
As in model checking, the basic operation in invariant generation is that
of taking the strongest postcondition or weakest precondition of a state set X
with respect to the transition relation N . Some of the techniques for computing
invariants are described briefly below.
Least Fixed Point of the Strongest Postcondition. The invariant computed here
corresponds to the reachability state set. It is computed by starting with an
initial symbolic representation of the initial state set given by the program. This
set is successively enlarged by taking its image under the strongest postcondition
operation until a fixed point is reached, i.e., no new elements are added to the set.
We term this method LFP-SP. It yields a symbolic representation of the set of
reachable states which is the strongest invariant. However, LFP-SP computation
often does not terminate since the computation might not converge to a fixed
point in a finite number of steps. Take, for example, a program that successively
increments by one, a variable x that is initially zero. This program has a least
fixed point, i.e., x is in the set of natural numbers, but the iterative computation
does not converge.
For the Twos example, the LFP-SP computation does terminate with the
desired invariant as seen in the calculation below.
Inv 0 = (P C = inc ∧ B = 0 ∧ C = 0)
Inv 1 = Inv 0 ∨ (P C = dec ∧ B = 2 ∧ C = 0)
Inv 2 = Inv 1 ∨ (B = 0 ∧ C = 1)
Inv 3 = (B = 0 ∧ C = 1) ∨ (P C = dec ∧ B = 2 ∧ C = 0)
= Inv 2
Greatest Fixed Point of the Strongest Postcondition. The greatest fixed point
iteration starts with the entire state space and strengthens it in each iteration
294 N. Shankar
by excluding states that are definitely unreachable. This approach, which we call
GFP-SP, yields a weaker invariant than the least fixed point computation. The
GFP-SP computation also need not terminate. Even when it does terminate, the
resulting invariant might not be strong enough. In the case of the program with
single integer variable x that is initially zero and incremented by one in each
transition, the GFP-SP computation returns the trivial invariant true. However
the GFP-SP method has the advantage that it can be made to converge more
easily than the LFP-SP method, and any intermediate step in the computation
already yields a valid invariant.
The greatest fixed point invariant computation for Twos (ignoring the varia-
ble C) can be carried out as follows. Here Inv i (pc) represents the i iteration of
the invariant for control state pc.
The invariant B ≥ −1 is not all that useful since this information contributes
nothing to the invariants that we wish to establish. Still, the GFP-SP method
is not without value. It is especially useful for propagating known invariants.
For example, if we start the iteration with invariant (1), then we can use the
GFP-SP method to deduce that the strengthened invariant (4).
Greatest Fixed Point of the Weakest Precondition. Both LFP-SP and GFP-
SP compute inductive invariants that are valid, whereas the method GFP-WP
takes a putative invariant and strengthens it in order to make it inductive. The
computation starts with a putative invariant S, and successively applies the
weakest precondition operation wp(P)(S) to it. If this computation terminates,
then either the resulting assertion is a strengthening of the original invariant
that is also inductive, or the given invariant is shown to be invalid.
With the Twos example, the weakest precondition with respect to the puta-
tive invariant (1) yields the strengthened invariant (4).
inc dec
c, -d -c, d
dec
c, -d
SVC
Esterel
SAL
Murphi
ASM
Mocha
JAVA
PVS
SMV
Programs
Verification
conditions
Abstractions
Properties
systems. The individual analyzers (theorem provers, model checkers, static ana-
lyzers) are driven from this intermediate language and the analysis results are
fed back to this intermediate level. In order to analyze systems that are written
in a conventional source language, the transition system model of the source pro-
gram has to be extracted and cast in the SAL intermediate language.1 The model
extracted in the SAL intermediate language essentially captures the transition
system semantics of the original source program.
The SAL architecture is shown in Figure 3 The SAL architecture is constrai-
ned so that the different analysis tools do not communicate directly with each
other, but do so through the SAL intermediate language. The interaction bet-
ween the tools must therefore be at a coarse level of granularity, namely in terms
of transition systems, their properties, and property-preserving transformations
between transition systems. Allowing the tools to communicate directly to each
other would require a quadratic number of different maps (for a given number
of tools) between these analysis tools.
– An input variable to a module can be read but not written by the module.
– An output variable to a module can be read and written by the module, and
only read by an external module.
– A local variable to a module can be read and written by the module, but is
not read or written by the module.
– A global variable to a module can be read and written by the module as well
as by an external module
A basic module also specifies the initialization and transition steps. These can
be given by a combination of definitions or guarded commands. A definition is of
the form x = expression or x0 = expression, where x0 refers to the new value of
variable x in a transition. A definition can also be given as a selection of the form
x0 ∈ set which means that the new value of x is nondeterministically selected
from the value of of set. A guarded command is of the form g −→ S, where g
is a boolean guard and S is a list of definitions of the form x0 = expression or
x0 ∈ set.
As in synchronous language such as Esterel [5] and Lustre [22], SAL allows
synchronous, i.e., Mealy machine, interaction so that the new value of a local or
output variable can be determined by the new value of a variable. Such inter-
action introduces the possibility of a causal cycle where each variable is defined
to react synchronously to the other. Such causal cycles are ruled out by using
static analysis to generate proof obligations demonstrating that such cycles are
not reachable. The UNITY and ASM models do not admit such synchronous
interaction since the new values of a variable in a transition are completely de-
termined by the old values of the variables. SMV allows such interaction but the
semantics is not clearly specified, particularly when causal cycles are possible.
The Reactive Modules [2] language uses a static partial ordering on the variables
that breaks causal loops by allowing synchronous interaction in one direction of
the ordering but not the other. In TLA [28], two modules are composed by conjoi-
ning their transition relations. TLA allows synchronous interaction where causal
loops can be resolved in any manner that is compatible with the conjunction of
the transition relations is satisfied.
SAL modules can be composed
There are rules that govern the usage of variables within a composition.
Two modules engaged in a composition must not share output variables and nor
should the output variables of one module overlap with the global variables of
another. The modules can can share input and global variables, and the input
variables of one module can be the output or global variables of the other. Two
modules that share a global variable cannot be composed synchronously, since
this might create a conflict when both modules attempt to write the variable
synchronously. The rules governing composition allow systems to be analyzed
modularly so that system properties can be composed from module properties [2].
The N-fold synchronous and asynchronous compositions of modules are also
expressible in SAL. Module operations include those for hiding and renaming
of variables. Any module defined by means of composition and other module
operations can always be written as a single basic module, but with a significant
loss of succinctness.
SAL does not contain features other than the rudimentary ones described
above. There are no constructs for synchronization, synchronous message pas-
sing, or dynamic process creation. These have to explicitly implemented by me-
ans of the transition system mechanisms available in SAL. While these features
are useful, their introduction into the language would place a greater burden on
the analysis tools.
The SAL language is thus similar in spirit to Abstract State Machines [20]
in that both serve as basic conceptual models for transition systems. However,
machines described in SAL are not abstract compared with those in ASM nota-
tion since SAL is intended as a front-end to various popular model checking and
program analysis tools.
4 Conclusions
References
1. Rajeev Alur and Thomas A. Henzinger, editors. Computer-Aided Verifi-
cation, CAV ’96, volume 1102 of Lecture Notes in Computer Science, New
Brunswick, NJ, July/August 1996. Springer-Verlag.
2. R. Alur and T.A. Henzinger. Reactive modules. Formal Methods in System
Design, 15(1):7–48, 1999.
3. Nikolaj Bjørner, I. Anca Browne, and Zohar Manna. Automatic generation
of invariants and intermediate assertions. Theoretical Computer Science,
173(1):49–87, 1997.
4. J. R. Burch, E. M. Clarke, K. L. McMillan, D. L. Dill, and L. J. Hwang.
Symbolic model checking: 1020 states and beyond. Information and Com-
putation, 98(2):142–170, June 1992.
5. G. Berry and G. Gonthier. The Esterel synchronous programming lan-
guage: Design, semantics, and implementation. Science of Computer Pro-
gramming, 19(2):87–152, 1992.
6. Saddek Bensalem, Vijay Ganesh, Yassine Lakhnech, César Muñoz, Sam
Owre, Harald Rueß, John Rushby, Vlad Rusu, Hassen Saı̈di, N. Shankar,
Eli Singerman, and Ashish Tiwari. An overview of SAL. In C. Michael Hol-
loway, editor, LFM 2000: Fifth NASA Langley Formal Methods Workshop,
Hampton, VA, June 2000. NASA Langley Research Center. To appear.
7. Saddek Bensalem and Yassine Lakhnech. Automatic generation of invari-
ants. Formal Methods in Systems Design, 15(1):75–92, July 1999.
8. Saddek Bensalem, Yassine Lakhnech, and Sam Owre. Computing abstrac-
tions of infinite state systems compositionally and automatically. In Hu
and Vardi [26], pages 319–331.
9. Saddek Bensalem, Yassine Lakhnech, and Hassen Saı̈di. Powerful techni-
ques for the automatic generation of invariants. In Alur and Henzinger [1],
pages 323–335.
10. R. E. Bryant. Graph-based algorithms for Boolean function manipulation.
IEEE Transactions on Computers, C-35(8):677–691, August 1986.
11. Edmund Clarke, Orna Grumberg, Somesh Jha, Yuan Lu, and Helmut Veith.
Counterexample-guided abstraction refinement. In E. A. Emerson and A. P.
Sistla, editors, Computer-Aided Verification, Lecture Notes in Computer
Science. Springer-Verlag, 2000. To appear.
Symbolic Analysis of Transition Systems 301
12. Edmund M. Clarke, Orna Grumberg, and David E. Long. Model checking
and abstraction. ACM Transactions on Programming Languages and Sy-
stems, 16(5):1512–1542, September 1994.
13. E. M. Clarke, Orna Grumberg, and Doron Peled. Model Checking. MIT
Press, 1999.
14. P. Cousot and N. Halbwachs. Automatic discovery of linear restraints
among variables. In 5th ACM Symposium on Principles of Programming
Languages. Association for Computing Machinery, January 1978.
15. K. Mani Chandy and Jayadev Misra. Parallel Program Design: A Founda-
tion. Addison-Wesley, Reading, MA, 1988.
16. M. A. Col on and T. E. Uribe. Generating finite-state abstractions of re-
active systems using decidion procedures. In Hu and Vardi [26], pages
293–304.
17. Satyaki Das, David L. Dill, and Seungjoon Park. Experience with predicate
abstraction. In Halbwachs and Peled [25], pages 160–171.
18. David L. Dill. The Murφ verification system. In Alur and Henzinger [1],
pages 390–393.
19. R. Gerth, D. Peled, M. Y. Vardi, and P. Wolper. Simple on-the-fly auto-
matic verification of linear temporal logic. In Proc. 15th Work. Protocol
Specification, Testing, and Verification, Warsaw, June 1995. North-Holland.
20. Yuri Gurevich. Evolving algebras 1993: Lipari guide. In Egon Börger, edi-
tor, Specification and Validation Methods, International Schools for Com-
puter Scientists, pages 9–36. Oxford University Press, Oxford, UK, 1995.
21. S. M. German and B. Wegbreit. A synthesizer for inductive assertions.
IEEE Transactions on Software Engineering, 1(1):68–75, March 1975.
22. N. Halbwachs, P. Caspi, P. Raymond, and D. Pilaud. The synchronous da-
taflow programming language Lustre. Proceedings of the IEEE, 79(9):1305–
1320, September 1991.
23. C. A. R. Hoare. An axiomatic basis of computer programming. Commu-
nications of the ACM, 12(10):576–580, October 1969.
24. G. J. Holzmann. Design and Validation of Computer Protocols. Prentice
Hall, 1991.
25. Nicolas Halbwachs and Doron Peled, editors. Computer-Aided Verification,
CAV ’99, volume 1633 of Lecture Notes in Computer Science, Trento, Italy,
July 1999. Springer-Verlag.
26. Alan J. Hu and Moshe Y. Vardi, editors. Computer-Aided Verification,
CAV ’98, volume 1427 of Lecture Notes in Computer Science, Vancouver,
Canada, June 1998. Springer-Verlag.
27. S. Katz and Z. Manna. Logical analysis of programs. Communications of
the ACM, 19(4):188–206, April 1976.
28. Leslie Lamport. The temporal logic of actions. ACM TOPLAS, 16(3):872–
923, May 1994.
29. C. Loiseaux, S. Graf, J. Sifakis, A. Bouajjani, and S. Bensalem. Property
preserving abstractions for the verification of concurrent systems. Formal
Methods in System Design, 6:11–44, 1995.
30. Kenneth L. McMillan. Symbolic Model Checking. Kluwer Academic Publis-
hers, Boston, MA, 1993.
31. Zohar Manna and Amir Pnueli. The Temporal Logic of Reactive and Con-
current Systems, Volume 1: Specification. Springer-Verlag, New York, NY,
1992.
302 N. Shankar
32. Zohar Manna and The STeP Group. STeP: Deductive-algorithmic verifi-
cation of reactive and real-time systems. In Alur and Henzinger [1], pages
415–418.
33. A. Pnueli. The temporal logic of programs. In Proc. 18th Symposium on
Foundations of Computer Science, pages 46–57, Providence, RI, November
1977. ACM.
34. Hassen Saı̈di. A tool for proving invariance properties of concurrent systems
automatically. In Tools and Algorithms for the Construction and Analysis
of Systems TACAS ’96, volume 1055 of Lecture Notes in Computer Science,
pages 412–416, Passau, Germany, March 1996. Springer-Verlag.
35. Hassen Saı̈di and Susanne Graf. Construction of abstract state graphs with
PVS. In Orna Grumberg, editor, Computer-Aided Verification, CAV ’97,
volume 1254 of Lecture Notes in Computer Science, pages 72–83, Haifa,
Israel, June 1997. Springer-Verlag.
36. N. Suzuki and K. Ishihata. Implementation of an array bound checker.
In 4th ACM Symposium on Principles of Programming Languages, pages
132–143, January 1977.
37. Hassen Saı̈di and N. Shankar. Abstract and model check while you prove.
In Halbwachs and Peled [25], pages 443–454.
38. Moshe Y. Vardi and Pierre Wolper. An automata-theoretic approach to
automatic program verification (preliminary report). In Proceedings 1st
Annual IEEE Symp. on Logic in Computer Science, pages 332–344. IEEE
Computer Society Press, 1986.
Encoding Abstract State Machines in PVS
1 Introduction
The Gurevich’s Abstract State Machines (ASMs) [9] have been successfully used
for design and analysis of complex hardware/software systems [3,2]. Through
real case studies, ASMs have shown to be a practical method for rigorous sy-
stem development and to meet the requirements, addressed by Heitmeyer in [10],
that formal methods need to have “to be useful to practitioners”: a user-friendly
notation, useful, easy to understand feedback, integration into standard develop-
ment process. ASMs use only the standard language and standard methods of
programming and mathematics. They are models easy to read, understand and
inspectable by the customer, but they are flexible to change, and precise and
complete to match the designer’s requirements. However, we believe that the
success of a formal method in industrial field also depends on the availability of
related automated tools helping the user during the process of specification and
subsequent verification and validation. Therefore, machine support for the use
of ASMs is extremely useful.
During the last years various investigations have been started in order to
verify standard mathematical reasoning about ASMs by interactive or fully au-
tomated proof tools. Some encouraging results are already reported in literature
(see discussion in [3]). Among theorem provers, PVS (Prototype Verification
System) has been used in [7] to show the correctness of bottom-up rewriting
specification for back-end compilers from an intermediate language into binary
RISC processor code. Dold et al. state that “erroneous rules have been found
using PVS and, through failed proof attempts, errors were corrected by ins-
pection of the proof state”. Mechanical checking of formal specifications and
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 303–322, 2000.
c Springer-Verlag Berlin Heidelberg 2000
304 A. Gargantini and E. Riccobene
properties is useful both as a final confirmation of their correctness and for sim-
plifying the process of discovering inconsistencies and proving properties. In fact,
experience shows that even the most carefully crafted formal specifications and
proofs, when done by hand, can still contain inconsistencies and other errors,
and that such errors are discovered only using tools capable to perform some
sort of analysis (also simply name checking, type consistency checking or other
similar simple analysis). Proofs can also be so long and tedious that machine
support can reduce the human effort.
In this paper, we show how PVS can provide tool support for ASMs, es-
pecially oriented towards automatic proof checking and mechanized proving of
properties. PVS [13], developed by SRI, is a higher order logic specification and
verification environment with a Gentzen-like deduction system. PVS provides
both a highly expressive specification language and automation of proof steps.
Starting from the work of Dold et al. in [8,7] we develop suitable PVS theories
to encode ASM models in PVS and we prove that our transformation preserves
the semantics of ASMs. Our approach differs from that of Dold et al. in the way
of encoding the ASM transition system. Our goal is to find a transformation from
ASMs to PVS that preserves the layout of the original rule transition system. To
achieve this goal we do not take any strong assumption as “only one rule enabled
at a time”, indeed we allow more rules to be simultaneously executed. Therefore,
we do not demand that the user should transform the whole system of rules in
one meta rule, transformation which can require skill and ingenuity, especially
in case of multi-agent models. Instead, we propose an algorithmic approach of
transformation which keeps the set of transition rules as a set of different rules.
We provide useful templates for helping the user during the encoding phase,
and useful proof schemes for requirements verification. The suggested encoding
scheme is mechanizable and we provide a framework for a tool, that we pro-
totypically implemented, supporting automatic transformation in PVS of ASM
models given in the ASM-SL language [5].
Our PVS encoding of ASMs has been tested for multi agent models. We
report here the PVS specification of the Production Cell given in [4], and we
discuss the results of the mechanized proofs of safety and liveness properties.
Several proofs are obtained with assumptions, usually regarding the physical
environment behavior, that are implicit in the formal description but need to
be added in the machine supported approach. Thanks to the PVS features, we
formulate such assumptions in a declarative way instead of using the operational
way required by the model checking approach used by Winter in [14] to show
the correctness of the Production Cell specification.
The article is organized as follows. The problem of encoding ASMs in PVS
is discussed in Sections 2.1-2.3. Section 2.4 shows how to validate specifications
using PVS. Sections 2.5 and 2.6 present proof schemes to support verification of
invariant properties and trace properties. Section 3 contains the PVS specifica-
tion of the Production Cell case study and the results of the verification task.
In Section 4 we discuss our work in comparison with related results, and we
conclude outlining future research directions.
Encoding Abstract State Machines in PVS 305
Functions and states. ASM basic functions are classified in static functions
which remain constant, and dynamic functions which may change interpretation
during computation. Dynamic functions are furthermore classified in controlled,
monitored and shared. Controlled functions occur inside function updates and
306 A. Gargantini and E. Riccobene
How to serialize the parallel rule application. In this section we tackle the
problem of finding an algorithm to serialize firing of ASM rules which guarantees
the semantics of their parallel application as stated in [3]:
Encoding Abstract State Machines in PVS 307
Let {R1 , ..., Rn } be a set of rules. The naive approach of computing the next
state A0 of the current state A by applying rules Ri , i = 1 . . . n, sequentially,1
does not guarantee the ASM semantics for two reasons: (a) guards condi of Ri
are not evaluated in the same state A forall i; (b) terms occurring in the left and
right sides of function updates of Ri are not all evaluated in A. The following
example may help to convince that this approach is wrong.
Example. Let A = (x → 0, y → 1, z → 2) be the current state consisting of
three integer variables x, y, z, and {R1 , R2 , R3 } be the transition system with
rules defined as follows:
R1 : if x = 0 then y := 5 R2 : if y = 5 then x := 2 R3 : z := x + y
By sequential rules application, it results:
x→0 x→0 x→ 2 x→2
R1 R2 R3
A : y → 1 → y → 5 → y → 5 → y → 5 : A0
z→2 z→2 z→2 z→ 7
1
I.e., first evaluate the guard of R1 and, if it is true, perform the updates defined in
R1 to obtain A1 ; then apply R2 to A1 and obtain A2 ; and so on till computing the
final state An to be taken as A0 .
308 A. Gargantini and E. Riccobene
x→0 x→0 x→0 x→0
R R R
A : y → 1 →1 y → 5 →2 y → 5 →3 y → 5 : A0
z→2 z→2 z→2 z→ 1
Rule R2 does not affect the state A because not enabled, and R3 correctly
computes the new value of z using the values of x and y in A.
How to deal with extend. The proposed algorithm does not give the expected
result when dealing with combination of rules which simultaneously add elements
to a same set by the construct extend. This problem arose trying to encode
in PVS the ASM model of crypto-protocol given in [1]. In this model, different
agents can send at the same time messages into the TRAFFIC (a set of messages)
representing the network channel common to all agents.
To be convinced why extend must be dealt with particular attention, con-
sider the following example. Let A be a state where a set ∆ has value ∅. The
following rules are enabled in A:
R1 : if ∆ = ∅ then extend ∆ with a
R2 : if ∆ = ∅ then extend ∆ with b
Encoding “extend ∆ with u” as a function update of the form ∆ := ∆ ∪{u},
by our algorithm, we get the following transformation:
R R
A : (∆ → {}) →1 (∆ → {a}) →2 (∆ → {b}) : A0
may infer that (a) at every step only one rule is enabled, and therefore (b) all
rules “may be combined into a single state transition function, defined by a case
construct or nested conditionals”. Such a single transition function is called “one-
step interpreter” by Dold et al. This assumption of disjoint cases is restrictive,
since in many applications more then one rule might fire at a time. In case
of possible simultaneous firing of many rules, a unique transformation function
might still model the ASM dynamics as well, however we prefer keeping the set
of transition rules as set of distinct rules, without forcing the user to define an
equivalent transformation function from the current state to the next one. The
advantages are two: first stylistic, because we preserve the ASMs structure, and
second practice, because writing one equivalent global transformation function
requires skill and ingenuity. Furthermore, due to the assumption that only one
rule can be applied in one step, the one-step interpreter is absolutely not suitable
in case of multi-agent ASMs.
Remark 4. Another possible approach for rule encoding would be to define
the value of every variable and function (location) in the next step by means of
axioms. For example, the update f (x) := t would be translated into an axiom
of the form update : AXIOM f(next(s))(x) = t, where next(s) stands for
the next state. In this way it would not be necessary to introduce the state
as a record and we could consider the state as simple uninterpreted type. This
approach is similar to that taken by [14] to translate ASMs in SMV. However,
this translation does not preserve the original form of the rules and problems
rise when a rule consists of more than one function update, or when more rules
update the same function (location). In both cases, a complex transformation is
needed as described in detail in [14].
1. cs0 = cs
2. for i = 1, . . . , n : csi = Ri (s, csi−1 )
3. cs’ = csn
R1 R2 Rn
s cs 1 cs 2 cs n-1 cs’=cs n
This algorithm defines only how to compute the controlled part of s0 since
the rules do not change the monitored part (i.e. the environment).
The set of all ASM rules is encoded as a list rules of functions from state
and controlled state to a controlled state:
rules: list[[STATE,CTRLSTATE->CTRLSTATE]]
The application of the list rules using as current state s0 and as intermediate
controlled state cs i, is given by the following recursive definition2 :
The controlled part of the next state of s is defined applying the list rules of
all ASM rules, and taking s as initial current state and its controlled part as
initial intermediate controlled state:
nextEnv(s:STATE) : ENV
The next state of s is the composition of the two next (controlled and mo-
nitored) parts:
next(s:STATE): STATE =
(#env:= nextEnv(s), ctrl:= nextCtrlState(s) #)
2
car and cdr are built-in PVS functions yielding head and tail of a list, respectively.
312 A. Gargantini and E. Riccobene
Templates for rules encoding. We report here the two templates for rules
encoding. They distinguish between rules with and without extending updates.
Let Ri ≡ if condi then updatei be a transition rule.
1 If update i is a sequence of function updates of the form f (t1 , . . . , tn ) := t, then
Ri is translated in PVS as follows:
Ri(current, intCtrl) : CTRLSTATE =
IF cond i(current) THEN intCtrl
WITH [f:=f(intCtrl) WITH [(t1,...,tn):=t]]
ELSE intCtrl ENDIF
If the guard cond i is true in the current state then the updated interme-
diate controlled state intCtrl is returned; otherwise the state intCtrl is
returned unchanged (all terms ti and t are computed in the current state).
2 If update i has the form extend Delta with alpha, then Ri is translated in
PVS as follows:
Ri(current, intCtrl) : CTRLSTATE =
IF cond i(current) THEN intCtrl
WITH [Delta := add(alpha,Delta(intCtrl))]
ELSE intCtrl ENDIF
If the guard cond i is true, the element alpha is added to Delta evaluated
in intCtrl; otherwise intCtrl is returned unchanged.
The following examples may help to better understand rules encoding.
Example 1. Let the ASM rules be
R1 : if x = 0 then y := 5 R2 : if y = 5 then x := 2 R3 : z := x + y
The controlled state is defined as a record of three variables:
CTRLSTATE: TYPE = [# x: int, y: int, z: int #]
The three rules are defined as follows:
R1(current, intCtrl) : CTRLSTATE =
IF x(current) = 0 THEN intCtrl WITH [y := 5]
ELSE intCtrl ENDIF
R2(current, intCtrl) : CTRLSTATE =
IF y(current) = 5 THEN intCtrl WITH [x := 2]
ELSE intCtrl ENDIF
R3(current, intCtrl) : CTRLSTATE =
intCtrl WITH [z := x(current) + y(current)]
Example 2 (with sets).
R1 : if ∆ = ∅ then extend ∆ with a
R2 : if ∆ = ∅ then extend ∆ with b
Assuming that elements of the set ∆ belong to a certain type elementType,
and a and b are two constants of that type:
elementType : TYPE
a,b : elementType
the controlled state is defined as a record containing only the set ∆:
Encoding Abstract State Machines in PVS 313
After having specified system universes, functions and rules, the designer should
check whether the specification is correct or not, i.e. if it meets users’ needs and
if it satisfies all the desired properties (requirements).
The first step in this direction is to check some possible behaviors of the
system as specified and compare them with the desired behaviors. This approach
is followed by ASM simulators (like ASM Workbench and ASM-Gofer). A similar
approach might be followed in our encoding, probing the specification by means
of “formal challenges”. With this term we mean putative theorems, i.e. properties
that should be true if the specification is correct. The designer should start
from formally specifying and proving very simple statements and then gradually
prove more complex properties. Only at the end he/she should try to prove the
complete requirements.
In our Example 1 a very simple property is: “if in state s x is 0, y is 1 and z
is 2, then in the next state x should be equal to 0, y to 5 and z to 1”. It can be
encoded as the following lemma:
prop1: lemma
s‘ctrl= (#x:=0,y:=1,z:=2#) => next(s) = (#x:=0,y:=5,z:=1#)
314 A. Gargantini and E. Riccobene
We have proved the lemma prop1 simply using the PVS decision procedures
of rewriting and symbolic execution. More complex and complete properties
might contain quantification on system quantities and, therefore, to be proven
they might need the use of more advanced PVS strategies.
The main goal of these short proofs is to gain a deeper understanding of
the system, to become confident in the correctness of the proposed formal de-
scription, and discover possible errors or faults as early as possible, since it is
widely acknowledged that the cost of correcting specification errors is order of
magnitudes higher in the later stages of the life cycle of the system (like during
testing or even during its normal functioning). At the end the designer should
be able to formally state and prove the actual requirements. The verification of
system requirements is the subject of the following sections.
We can express in PVS that a property stateProp holds in every state of the
trace t by the following predicate:
always(t,stateProp): bool = FORALL n: stateProp(nth(t,n))
We have proved the equivalence between this approach based on traces and
that based on invariants by proving the following lemma
Lemma 1. “stateProp” is an invariant iff it always holds in every trace of the
system.
In PVS: equivalence: LEMMA
INV(stateProp) <=> forall t: always(t,stateProp)
methods for critical software systems and to prove their applicability to real-
world examples” [11]. Börger and Mearelli propose a solution of the production
cell control problem in [4], and show how to integrate the use of ASMs into a
complete software development life cycle.
. . . the production cell is composed of two conveyor belts, a positioning
table, a two-armed robot, a press, and a traveling crane. Metal plates
inserted in the cell via the feed belt are moved to the press. There, they
are forged and then brought out of the cell via the other belt and the
crane. [11]
The system is specified “as a distributed ASM with six modules, one for the
agents” – the Feed Belt, the Robot, the Press, the Deposit Belt, the Traveling
Crane, the Elevating Rotary Table – “composing the production cell, and working
together concurrently where each of the component ASMs follows its own clock.
Each of the agents represents a sequential process which can execute its rules as
soon as they become enabled. The sequential control of each agent is formalized
using a function currPhase: Agent → Phase which yields at each moment the
current phase of the agent”[4]. We refer the reader to [4] for further details.
FB CRITICAL.
if currPhase = CriticalRun and PieceInFeedBeltLightBarrier
then currPhase:= NormalRun
TableLoaded:= True
Initialization: currPhase = NormalRun, FeedBeltFree = True,
PieceInFeedBeltLightBarrier = False
Encoding Abstract State Machines in PVS 317
We now report the PVS encoding of the Feed Belt specification. To describe
the Feed Belt we define a type containing all the possible values of the feed belt
phase:
FBPhase : TYPE = {NormalRun,Stopped,CriticalRun}
The controlled function FeedBeltPhase is then included as component of the
record CTRLSTATE which represents the controlled part (controlled and shared
functions) of the (global) state:
CTRLSTATE : TYPE =
[# FeedBeltPhase : FBPhase,
FeedBeltFree : bool, % controlled by the FB
TableLoaded : bool, % controlled by the FB and ERT
...#]
The dots are replaced by the controlled part of the other five agents, that we
skip for the sake of conciseness. Monitored variable is defined as function from
the environment to its domain as:
PieceInFeedBeltLightBarrier : [ENV->bool]
Starting from definitions of monitored and controlled functions, derived functions
are defined as:
TableInLoadPosition(s:CTRLSTATE) : bool =
ERTPhase(s) = StoppedInLoadPosition
TableReadyForLoading(s:CTRLSTATE) : bool =
TableInLoadPosition(s) and not TableLoaded(s)
The initial state is modeled by a predicate over the states (we report only the
part concerning FB):
init(s:STATE) : bool =
FeedBeltPhase(s)= NormalRun and FeedBeltFree(s)
and not PieceInFeedBeltLightBarrier(s) ...
For the rules we report only the example of the FB NORMAL rule:
FB NORMAL(current,intCtrl): CTRLSTATE =
if FeedBeltPhase(current) = NormalRun and
PieceInFeedBeltLightBarrier(current)
then intCtrl with
[FeedBeltFree := true,
FeedBeltPhase := if TableReadyForLoading(current)
then CriticalRun
else Stopped
endif]
else intCtrl
endif
318 A. Gargantini and E. Riccobene
Using PVS we have proved all the safety properties (for the Feed Belt, the Robot,
the Press, the Deposit Belt, and the Traveling Crane) of the Production Cell
as given in [4]. For some of these properties, the encoding in PVS and the
proof are straightforward. Others require some user effort and skill. In order to
discuss the degree of interaction necessary for proving in PVS the requirements
of the Production Cell case study, we present some selected examples of proved
properties having different degree of complexity.
The Feed Belt Safety Property: the feed belt does not put metal blanks
on the table if the latter is already loaded or not stopped in loading position. It
has been quickly encoded considering that the feed belt puts metal blanks only
when it is in the CriticalRun phase3 :
FeedBeltSafety: theorem
FeedBeltPhase(s) = CriticalRun
=> ElevatingRotaryTablePhase(s) = StoppedInLoadPosition
and not TableLoaded(s)
The proof of this property, reported below, is immediate (as also its hand proof
in [4]):
(”” (ASM-INDUCT)
((”1” (COMPUTE-NEXT) (GRIND))
(”2” (TYPEPRED ”is!1”) (GRIND))))
PressSafety1a: theorem
PressBottomPosition(s) =>
not PressMotorDown(Press(next(s)))
To prove this property we have to introduce an assumption about the moni-
tored function BottomPosition asserting that if the press is closed for forging,
then it is not already in the bottom position:
notBottom: axiom
PressPhase(s)= ClosedForForging => not BottomPosition(s)
This is an obvious implication considering how the system works, but we have
to explicitly state that by means of an axiom. Upon introducing the notBottom
axiom, the proof of the Press Safety Property 1 is obtained applying induction,
expanding the definitions and applying the PVS decision procedures. For other
properties we introduce similar assumptions by means of axioms, and recall these
axioms during proofs. These assumptions often concern the correct behavior of
the sensors, and sometimes are missing in the original description because im-
plicitly assumed. This again shows that automatic support may help to uncover
errors forcing the designer to precisely introduce every assumption. We also note
that these assumptions are introduced by means of logical statements, similar
to those given in [4], while a model checker would require us to express them in
an operational way.
Another example we like to report here is the Press Safety Property 2:
The press does only close when no robot arm is positioned inside it. In order
to encode it in a concise form, we introduce two (derived) boolean functions:
PressIsClosing and ArmInPress, defined as
PressIsClosing(s): bool = PressMot(PressPhase(s)) = up
i.e. the press is closing only when its motor is going up, and
ArmInPress(s): bool =
Arm1Ext(s) > 0 and Angle(s) = Arm1ToPress or
Arm2Ext(s) > 0 and Angle(s) = Arm2ToPress
Using these definitions the Press Safety property becomes:
PressSafety2: theorem
PressIsClosing(s) => not ArmInPress(s)
To prove this property we model the angle of the robot arm by the monitored
variable Angle encoded as function on the environment:
Angle :[ENV-> real]
Then we formalize all the assumptions about the movement of the robot: how
the robot rotates, how the angle changes, and how the sensors communicate to
the robot when to stop. We prove the Press Safety Property 2 using induction,
case analysis (considering all the possible robot phases in the current and in the
next state), recalling the assumptions about the robot movement, and applying
the automatic decision procedures of PVS.
320 A. Gargantini and E. Riccobene
translation of ASMs in PVS keeping the set of transition rules as a set of different
rules, instead of forcing the user to define an equivalent transformation function
in terms of one meta rule. We also provide the user with useful templates to guide
his/her formalization of ASMs in PVS. These templates have also allowed us to
provide a framework for a tool to automatically translate ASM specifications
in PVS. This tool is under development and we plan to integrate it into the
ASM-Workbench system. In addition we present proof schemes to encode and
prove invariants and properties on traces.
Although the approach based on the model checker SMV allows properties
verification in a completely automatic manner (unless the well known state ex-
plosion problem), the advantages of using our approach regard both the speci-
fication and the verification phase. PVS can easily manage specifications with
infinite sets and an unbounded number of agents, has a powerful language (to
represent functions, sets, lists and so on), has a strong type system, and can
use the usual logical constructs (like universal and existential quantifications).
Proof can be performed almost automatically in the simplest cases (as shown in
the Production Cell case study). For more complex properties, in any case, our
encoding can be used to check proofs done by hand or to support the user during
the proof in an interactive way. In connection with the results presented in [14],
we like to remark that the model checking approach can deal only with a finite
set of agents and each agent having a finite number of possible states. This is the
case of the Production Cell, under the assumption that continuous intervals (for
example, the robot angle values) can be treated as finite sets of discrete values.
This assumption was indeed used by Winter in [14], while it is not necessary in
our approach, since we are able to deal with infinite sets (for example, we treat
the robot angle as a real number). The correctness proof (with its results) of
the Production Cell specification as it is shown in [14] has to be related to the
added formalization of the environmental behavior. It is a mayor benefit of our
approach that the assumptions regarding the interaction of the system and the
environment can be formalized in a logical than in an operational way (i.e. in
terms of transition rules) as required in [14].
Concluding, we like to stress our confidence that the proposed PVS encoding
also works well for multi-agent ASMs with an unlimited number of agents, which
are very complex to treat. It is not so hard to imagine how difficult can be
performing mechanized proof verification of properties regarding interleaving
computations of agents. The case study we present here is an example of a
multi-agent system, but with a limited number of agents. However, the method
has been successfully applied to analyze properties of crypto-protocols, where
an unlimited number of agents run simultaneously. Security and authentication
properties of the ASM specification presented in [1] have been proved in PVS
using the technique of invariants. We have voluntarily left the presentation of
these results out because they would require a specific treatment.
Acknowledgments. We kindly like to thank Egon Börger for his useful advice.
We also thank anonymous referees for their helpful suggestions.
322 A. Gargantini and E. Riccobene
References
1. G. Bella and E. Riccobene. A Realistic Environment for Crypto-Protocol Analyses
by ASMs. In Proceedings of the 28th Annual Conference of the German Society of
Computer Science. Technical Report, Magdeburg University, 1998.
2. E. Börger. Why Use Evolving Algebras for Hardware and Software Engineering? In
M. Bartosek, J. Staudek, and J. Wiederman, editors, Proceedings of SOFSEM’95,
22nd Seminar on Current Trends in Theory and Practice of Informatics, volume
1012 of LNCS, pages 236–271. Springer, 1995.
3. E. Börger. High level system design and analysis using abstract state machines.
In D. Hutter, W. Stephan, P. Traverso, and M. Ullmann, editors, Current Trends
in Applied Formal Methods (FM-Trends 98), number 1641 in LNCS, pages 1–43.
Springer-Verlag, 1999.
4. E. Börger and L. Mearelli. Integrating ASMs into the Software Development Life
Cycle. Journal of Universal Computer Science, 3(5):603–665, 1997.
5. G. Del Castillo. The ASM Workbench: an Open and Extensible Tool Environment
for Abstract State Machines. In Proceedings of the 28th Annual Conference of the
German Society of Computer Science. Technical Report, Magdeburg University,
1998.
6. G. Del Castillo and K. Winter. Model Checking Support for the ASM High-Level
Language. Technical Report TR-RI-99-209, Universität-GH Paderborn, June 1999.
7. A. Dold, T. Gaul, V. Vialard, and W. Zimmerman. ASM-Based Mechanized Veri-
fication of Compiler Back-Ends. In Proceedings of the 28th Annual Conference of
the German Society of Computer Science. Technical Report, Magdeburg Univer-
sity, 1998.
8. Axel Dold. A formal representation of abstract state machines using pvs. Technical
Report Verifix Report Ulm/6.2, Universitat Ulm, July 1998.
9. Y. Gurevich. Evolving Algebras 1993: Lipari Guide. In E. Börger, editor, Specifi-
cation and Validation Methods, pages 9–36. Oxford University Press, 1995.
10. C. Heitmeyer. On the Need for Parctical Formal Methods. In Proceedings of
FTRTFT’98, 5th Intern. Symposium Real-Time Fault-Tolerant Systems, volume
1486 of LNCS, pages 18–26. Springer, 1998.
11. C. Lewerentz and T. Linder, editors. Formal Development of Reactive Systems. A
Case Study “Production Cell”. Number 891 in LNCS. Springer, 1995.
12. G. Schellhorn and W. Ahrendt. Reasoning about Abstract State Machines: The
WAM Case Study. Journal of Universal Computer Science, 3(4):377–413, 1997.
13. N. Shankar, S. Owre, and J. Rushby. The PVS proof checker: A reference manual.
Technical report, Computer Science Lab., SRI Intl., Menlo Park, CA, 1993.
14. K. Winter. Model Checking for Abstract State Machines. Journal of Universal
Computer Science, 3(5):689–701, 1997.
Model Checking Abstract State Machines
and Beyond
Marc Spielmann
1 Introduction
Abstract state machines (ASMs) [14,15,16] have become the formal foundation of
a successful methodology for specification and verification of complex hardware
and software systems. This is particularly witnessed by numerous publications
using the ASM formalism for rigorous mathematical correctness proofs of large-
scale applications. (See the recent bibliography [5] and the web site [24]. For
an introduction to the ASM verification method the reader is referred to [7].)
Interestingly, most of these contributions focus on manual verification, while the
number of publications where all or part of the verification process is automated
is rather small. (For exceptions see [25,26,12,6] and consult [24].) In a nutshell,
computer-aided verification of ASMs, i.e., (semi-) automatic verification of dy-
namic systems expressed in terms of ASMs, has not yet been well developed.
In view of the success of the ASM verification method in manual verification
we think there is need for a systematic investigation of the (semi-) automatic
verifiability of ASMs. The present paper can be viewed as an attempt to initiate
such an investigation.
As a first step toward a systematic investigation of the verifiability of ASMs,
we have to make precise what we actually mean by “verifying ASMs”. In its full
generality, the problem of verifying ASMs can be seen as a decision problem of
the following kind:
Given an ASM M (i.e., a formal description of some dynamic system)
and a specification ϕ (i.e., a formal description of a desirable property of
the system), decide whether M ‘satisfies’ ϕ.
One of our main goals in this paper is to identify decision problems of the above
kind such that solving these problems coincides with proving properties of ASMs.
We put forward two such problems, which we call the model-checking problem
and the verification problem for ASMs.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 323–340, 2000.
c Springer-Verlag Berlin Heidelberg 2000
324 M. Spielmann
The model-checking problem for ASMs, denoted mc, can be stated informally
as follows:
mc: Given an ASM M , a specification ϕ, and an input I appropriate for
M , decide whether ϕ holds during all possible computations of M on I.
Note that, in general, mc cannot be solved by means of testing, i.e., by simply
running a given ASM M on a given input I and observing whether a given
specification ϕ is satisfied. There are two reasons for this. Firstly, M may be
non-deterministic or may access external functions or relations. For instance, if
M chooses a natural number n in the first step and then proceeds depending on
the choice of n, one would have to run M for each of the infinitely many possible
choices for n. Secondly, even if M is deterministic and does not interact with its
environment, it may not halt on I.
The above formulation of mc immediately raises the question of a specifica-
tion language for ASMs, i.e., a formal language suitable to express properties
of ASMs. Since in the literature there is no consensus on the choice of such a
language, we advocate here first-order branching temporal logic (FBTL) as spe-
cification language for ASMs. (FBTL is a straightforward extension of the well-
known propositional branching-time logic CTL∗ by first-order reasoning [11].
For details see the next section.) Another open issue is the notion of input. The
question is which type of input is suitable for ASMs and how does the initial
state of an ASM on some input look like? Concerning this question we follow
[2,4,13,3] and consider ASMs whose inputs are finite structures. To not impose
unnecessary restrictions on the initial states of ASMs, we associate with every
ASM M a function that maps every input I appropriate for M to an initial state
of M on I. In principle, this function can be any mapping from inputs to states.
An investigation of mc is motivated mainly by applications where it suffices
to ensure correctness of an ASM for only a small number of inputs. As an ex-
ample, consider an ASM M running on the notebook computer of a salesperson.
M receives as input a database D, say, the catalog of a supplier stored on a
CD-ROM, and interacts with the salesperson via some external relations. The
salesperson may inquire about the availability and prices of certain products
or may store customer orders in a dynamic relation of M . Since in this scena-
rio M runs on the given database D only, we can check whether M satisfies a
specification ϕ by deciding whether (M, ϕ, D) is a positive instance of mc.
Although a solution of mc is interesting for certain applications, there are
many applications where correctness of an ASM must be guaranteed for all ad-
missible inputs, of which there are often infinitely many. For instance, a compiler
is usually supposed to correctly translate an input program of arbitrary length.
In such cases one has to to check whether for a given ASM M and a given spe-
cification ϕ, (M, ϕ, I) is a positive instance of mc for every admissible input I.
This problem, which we call the verification problem for ASMs and denote by
verify, can be stated informally as follows:
verify: Given an ASM M and a specification ϕ, decide whether for every
input I appropriate for M , ϕ holds during all possible computations of
M on I.
Model Checking Abstract State Machines and Beyond 325
Outline. In the next section, we recall first-order branching temporal logic which
will serve here as specification language for ASMs. In Section 3, we formally
define the model-checking problem for ASMs and present some results concerning
its decidability and complexity (with respect to classes of restricted ASMs). In
Section 4, we define the verification problem for ASMs, show that it subsumes
the model checking problem, and study its decidability and complexity.
(S, T, s0 , P1 , . . . , Pk ) (1)
program Π:
choose x : T (pebble, x)
pebble := x
Observe that the transition graph K− of any Kripke structure K is a finite
structure over Υin , and thus an input over Υ . The initialization mapping of M
− −
is defined by initial (K− ) := (K− , 0K ), where (K− , 0K ) denotes the state over
Υ in which T and 0 are interpreted as in K− , and the nullary dynamic function
−
symbol pebble is interpreted as 0K . One can now verify that S 7→ pebble S is an
isomorphism between CM (K− ) and K− . t
u
C |= ϕ :⇔ (C, S0 ) |= ϕ.
We are now in the position to define the model-checking problem for ASMs
as a computational problem. Let C be a uniformly initialized class of ASMs
and F a fragment of FBTL. The model checking problem for C-ASMs and F -
specifications is the following decision problem:
Remark 8. Note the subtlety in the above definition of mc, namely that by
viewing an ASM M ∈ C as a finitely representable pair (Υ, Π) the problem of
actually representing the initialization mapping of M has not been solved, but
rather has been made part of the model-checking problem itself. t
u
program Π:
choose y1 , . . . , yn ∈ {true, false} : γT (x̄, ȳ)
x̄ := ȳ
if γP1 (ȳ) then p1 := true else p1 := false
...
if γPk (ȳ) then pk := true else pk := false
program Π 0 :
if ¬running then
pebble := pebbleInitial
running := true
if running then
Π
Now, while model checking M , consider only input structures in which the input
symbol pebbleInitial is interpreted as 42.
(b) Suppose that the second condition in Definition 11 is relaxed as follows:
there is a distinguished symbol S ∈ Υ − Υin whose initial interpretation depends
on Υ but is not restricted otherwise. We display a class C of simple ASMs
finitely initialized in the relaxed sense and a simple fragment F of FBTL such
that mc(C, F ) is undecidable.
W.l.o.g., we may assume that S is a boolean symbol and that every ASM
vocabulary contains S. Choose some undecidable problem
P ⊆ {Υ : Υ is an ASM vocabulary}.
(For instance, let TM Υ be the Turing machine whose encoding—in some fixed
standard binary encoding—equals the binary representation of the number of
symbols in Υ . Then P := {Υ : TM Υ halts on the empty word} is undecidable,
as a reduction of the halting problem for Turing machines shows.) For every ASM
vocabulary Υ , let MΥ := (skip, initial Υ , Υ ) be a finitely initialized ASM, except
that now for every input I appropriate for M , initial Υ (I) |= S iff Υ ∈ P . Let
C denote the class of all MΥ , and set F = {S}. For any input I appropriate for
M , Υ 7→ (MΥ , S, I) is a reduction of P to mc(C, F ). This implies that mc(C, F )
is undecidable. t
u
In favor of a succinct formulation of the next theorem we introduce some
additional notation.
Definition 13. Let T denote the closure of the set of first-order formulas under
the rules for negation and disjunction, and the following rule:
(T) If ϕ and ψ are formulas, then Xϕ, ϕUψ, and ϕBψ are formulas.
The universal closure of T, denoted UT, is the set of formulas of the form A∀x̄ϕ
with ϕ ∈ T and free(ϕ) ⊆ {x̄}, where free(ϕ) is defined in the obvious way. u t
We define a restriction of the model-checking problem for ASMs. An inve-
stigation of this restriction is motivated by the observation that the arities of
relations and functions used in practice tend to be rather small. Indeed, for
practical purposes it often suffices to solve mc for ASMs whose vocabulary con-
tains only symbols of arity ≤ m, for some a priori fixed natural number m. Let
mcm (C, F ) denote the restriction of mc(C, F ) to instances where only symbols
of arity ≤ m occur.
The following theorem generalizes a result in [22]. Note that UT can be
viewed as a fragment of FBTL.
334 M. Spielmann
Theorem 14. Let C be the class of finitely initialized ASMs whose program
does not contain import or choose. For any natural number m, mcm (C, UT) is
Pspace-complete.
We already encounter Pspace-hardness of mcm (C, UT) if m = 0 and C is the
class of finitely initialized ASMs of the form (skip, initial Υ , Υ ). This follows by
a reduction of the Pspace-complete model-checking problem for propositional
temporal logic [20,11].
As already pointed out in the introduction, a decision procedure for mc(C, F )
can be useful for the verification of ASMs that are supposed to run correctly on
a small number of inputs only. However, for applications where correctness of an
ASM has to be ensured for a large number of inputs—or even infinitely many
inputs—a solution of mc(C, F ) does not suffice. For such applications we have
to solve the model-checking problem for all admissible inputs. This will be our
main concern in the next section.
Remark 15. One may object that the above formulation of verify does not
adequately reflect real-life verification of ASMs, as in applications one is often
interested in verifying an ASM only for inputs that satisfy certain conditions.
Note however that these conditions can be viewed as part of a specification. For
example, suppose that the FBTL sentence ψ describes all admissible inputs of
an ASM M . That is, an input I is considered to be admissible for M iff I |= ψ.
Then (M, ψ → ϕ) is a positive instance of verify iff for every admissible input
I appropriate for M , CM (I) |= ϕ. t
u
Not surprising, verify(C, F ) is in general undecidable, even for classes C of
simple ASMs and simple fragments F of FBTL. For instance, recall the reduction
of the undecidable problem P to mc(C, F ) in Remark 12 (b) and observe that
the same reduction (with I now removed from the image of every Υ ) also reduces
P to verify(C, F ).
Model Checking Abstract State Machines and Beyond 335
Lemma 18. Let C0 be the class of finitely initialized ASMs whose program does
not contain import and whose input (resp. external) vocabulary does not con-
tain function symbols of arity ≥ 1 (resp. ≥ 0). Furthermore, let F be a frag-
ment of FBTL containing for every boolean symbol b the formula EXb. For any
class C 0 ⊇ C0W and any fragment F 0 ⊇ F W closed under the boolean operators,
mc(C0 , F ) is polynomial-time reducible to verify(C 0 , F 0 ).
Let C0 be as in the above lemma. The proof of the lemma shows that, if
C ⊆ C0 and F ⊆ FBTL have reasonable closure properties and C W ⊆ C and
F W ⊆ F , then mc(C, F ) is polynomial-time reducible to verify(C, F ).
The next proposition provides a sufficient condition for the decidability of the
verification problem. Let us first recall two decision problems from logic.
Finite satisfiability and finite validity. Let L be a logic, Υ a vocabulary,
and ϕ an L-sentence over Υ . ϕ is called finitely satisfiable if there exists a finite
structure A over Υ with A |= ϕ. ϕ is called finitely valid if for every finite struc-
ture A over Υ , A |= ϕ. By fin-sat(L) (resp. fin-val(L)) we denote the problem
of deciding finite satisfiability (resp. finite validity) of a given L-sentence.
1. fin-val(L) is decidable.
2. There exists a computable function which maps every instance (M, ϕ) of
verify(C, F ) to an L-sentence χM,ϕ over the input vocabulary of M , such
that for every input I appropriate for M ,
CM (I) |= ϕ ⇔ I |= χM,ϕ .
Model Checking Abstract State Machines and Beyond 337
The next definition introduces two fragments of FBTL, denoted ETE and
UTU. Informally speaking, the formulas in ETE (resp. UTU) are built from ato-
mic formulas by means of disjunction, conjunction, existential (resp. universal)
quantification (applicable only to state formulas), the temporal operators X, U,
and B, and the path quantifier E (resp. A).
Definition 21. ETE denotes the set of FBTL formulas derivable by means of
rules (S1), (S2), (P1), (P2) in Definition 4, and the following two formula-for-
mation rules:
(SP1)’ If ϕ and ψ are state (resp. path) formulas, then ϕ ∨ ψ and ϕ ∧ ψ are
state (resp. path) formulas.
(SP2)’ If x is a variable and ϕ a state formula, then ∃xϕ is a state formula.
UTU denotes the set of negated ETE formulas. t
u
The proof of the theorem closely follows a construction due to Immerman and
Vardi that was first presented in [17] as a translation of CTL∗ into (FO+TC).
Using this construction one can show that for C = SN-ASMrel , F = ETE, and
L = (E+TC) the second condition in Proposition 19 is satisfied. The containment
assertion of the theorem is then implied by the following two observations:
1. Finite validity and finite satisfiability of (E+TC) sentences over relational
vocabularies is decidable in Pspace if one imposes an upper bound m on
the arities of the occurring relation symbols [21,23].
2. There exists a polynomial-time computable function as in the second condi-
tion of Proposition 19.
verifym (C, F ) is already Pspace-hard if m = 0, F contains EXaccept, and
C includes all ASMs in SN-ASMrel whose external vocabulary is empty and
whose dynamic vocabulary contains only boolean symbols. This follows by a
reduction of the Pspace-complete satisfiability problem for quantified boolean
formulas.
Unfortunately, Theorem 22 does not hold for sequential nullary ASMs whose
inputs contain functions, as the following theorem shows. For the sake of bre-
vity we write liveness(C) instead of verify(C, {EFaccept}) and safety(C)
instead of verify(C, {AG¬error }).
Model Checking Abstract State Machines and Beyond 339
The proof of this theorem is by reduction of the halting problem for Turing
machines. It is based on the following two observations:
1. Inputs that contain non-nullary functions suffice to encode bit-strings.
2. Two nullary dynamic functions suffice to check whether a bit-string encodes
an accepting computation of a Turing machine.
An analysis of the proof indicates that automatic verification of even very simple
ASMs whose inputs can be used for encoding bit-strings is not feasible. Nevert-
heless, Theorem 22 and results in [22] show that restricted variants of ASMs
with relational inputs can be verified automatically.
References
1. A. Blass and Y. Gurevich. Existential fixed-point logic. In E. Börger, editor,
Computation Theory and Logic, volume 270 of Lecture Notes in Computer
Science, pages 20–36. Springer-Verlag, 1987.
2. A. Blass and Y. Gurevich. The Linear Time Hierarchy Theorems for Abstract
State Machines. Journal of Universal Computer Science, 3(4):247–278, 1997.
3. A. Blass, Y. Gurevich, and J. Van den Bussche. Abstract State Machines
and Computationally Complete Query Languages. This volume.
4. A. Blass, Y. Gurevich, and S. Shelah. Choiceless Polynomial Time. Technical
Report MSR-TR-99-08, Mircosoft Research, 1999.
5. E. Börger and J. Huggins. Abstract State Machines 1988–1998: Commented
ASM Bibliography. Bulletin of the EATCS, 64:105–127, February 1998.
6. D. Beauquier and A. Slissenko. Verification of Timed Algorithms: Gurevich
Abstract State Machines versus First Order Timed Logic. Technical Report
TIK-Report 87, ETH Zürich, March 2000.
7. E. Börger. Why Use Evolving Algebras for Hardware and Software Enginee-
ring? In M. Bartosek, J. Staudek, and J. Wiederman, editors, Proceedings
of 22nd Seminar on Current Trends in Theory and Practice of Informatics
(SOFSEM ‘95), volume 1012 of Lecture Notes in Computer Science, pages
236–271. Springer Verlag, 1995.
8. E.M. Clarke, E.A. Emerson, and A.P. Sistla. Automatic Verification of
Finite State Concurrent Systems Using Temporal Logic. ACM Trans. on
Prog. Lang. and Sys., 8(2):244–263, April 1986.
9. G. Del Castillo and K. Winter. Model Checking Support for the ASM High-
Level Language. Technical Report TR-RI-99-209, Universität-GH Pader-
born, June 1999.
10. H. D. Ebbinghaus and J. Flum. Finite Model Theory. Springer-Verlag, 1995.
340 M. Spielmann
11. E.A. Emerson. Temporal and Modal Logic. In J. van Leeuwen, editor, Hand-
book of Theoretical Computer Science, volume B, pages 995–11072. Elsevier
Science Publishers B.V., 1990.
12. A. Gargantini and E. Riccobene. Encoding Abstract State Machines in PVS.
This volume.
13. E. Grädel and M. Spielmann. Logspace Reducibility via Abstract State
Machines. In J. Wing, J. Woodcock, and J. Davies, editors, World Congress
on Formal Methods (FM ‘99), volume 1709 of Lecture Notes in Computer
Science, pages 1738–1757. Springer-Verlag, 1999.
14. Y. Gurevich. Evolving Algebras 1993: Lipari Guide. In E. Börger, editor,
Specification and Validation Methods, pages 9–36. Oxford University Press,
1995.
15. Y. Gurevich. May 1997 Draft of the ASM Guide. Technical Report CSE-
TR-336-97, University of Michigan, May 1997.
16. Y. Gurevich. The Sequential ASM Thesis. Bulletin of the EATCS, 67:93–124,
1999.
17. N. Immerman and M.Y. Vardi. Model Checking and Transitive Closure
Logic. In Proceedings of 9th International Conference on Computer-Aided
Verification (CAV ‘97), volume 1254 of Lecture Notes in Computer Science,
pages 291–302. Springer-Verlag, 1997.
18. A. Levy, I. Mumick, Y. Sagiv, and O. Shmueli. Equivalence, Query-
Reachability, and Satisfiability in Datalog Extensions. In Proceedings of
12th ACM Symposium on Principles of Database Systems (PODS ‘93), pa-
ges 109–122, 1993.
19. E. Rosen. An existential fragment of second order logic. Archive for Mathe-
matical Logic, 38:217–234, 1999.
20. A.P. Sistla and E.M. Clarke. The Complexity of Propositional Linear Tempo-
ral Logics. Journal of the Association for Computing Machinery, 32(3):733–
749, July 1985.
21. M. Spielmann. Automatic Verification of Abstract State Machines. In Pro-
ceedings of 11th International Conference on Computer-Aided Verification
(CAV ‘99), volume 1633 of Lecture Notes in Computer Science, pages 431–
442. Springer-Verlag, 1999.
22. M. Spielmann. Verification of Relational Transducers for Electronic Com-
merce. In Proceedings of 19th ACM Symposium on Principles of Database
Systems (PODS 2000). ACM Press, 2000. To appear.
23. M. Spielmann. Abstract State Machines: Verification Problems and Comple-
xity. PhD thesis, RWTH Aachen. In preparation.
24. ASM Web Site. http://www.eecs.umich.edu/gasm. Maintained by J. Hug-
gins.
25. K. Winter. Model Checking for Abstract State Machines. Journal of Uni-
versal Computer Science, 3(5):689–701, 1997.
26. K. Winter. Methodology for Model Checking ASM: Lessons learned from
the FLASH Case Study. This volume.
Towards a Methodology for Model Checking
ASM:
Lessons Learned from the FLASH Case Study
Kirsten Winter1
1 Introduction
An ASM model comprises the specification of the state space of the system and
its behaviour specified by state transitions. The state space is given by means
of universes and functions over these universes. If the domains and ranges of all
contributing functions are finite (and not too large) and fix an ASM model can
be transformed into a model checker language, e.g. the language of the SMV
model checker ([7]). (We call a universe fix if it may not be extended during a
run by firing some transition rules.)
A first schematic approach is published in [13]. In [2] the schema is extended
for coping with ASM with n-ary functions (n > 0). All n-ary functions will be
unfolded to get 0-ary functions that can be mapped to simple state variables
in the SMV model. Of course, model checking as a fully automatic approach is
limited with respect to the computational effort and thus not feasible for every
ASM model. However, our extended transformation approach can tackle a much
broader set of applications and yields a real improvement of the former simple
transformation.
Once a model is transformed into the model checker language we benefit
from checking properties concerning safety and liveness. If counterexamples can
be detected they may yield a good insight in the model under development.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 341–360, 2000.
c Springer-Verlag Berlin Heidelberg 2000
342 K. Winter
On the other hand, our transformation tool supplies SMV with a high level
modelling language. The modelling task is facilitated by allowing the use of more
complex data types and n-ary functions for parametrisation. In general, we found
that ASM models, in comparison to similar SMV models (cf. section 6), are more
concise but yet more general by means of using parameters. Also, an ASM model
can be scaled up more easily than an SMV model, which have to come along
with simple state variables rather than n-ary functions.
This process of transforming the ASM model into SMV language, and check-
ing the resulting SMV model, is running automatically, i.e. without user interac-
tion, once safety and liveness properties are formalised in a temporal logic (for
SMV this is CTL). This is why the model checking approach is so attractive
for industry. No expertise seems to be necessary for using a model checker, it is
simply a “press button functionality”.
This works not only in principle, it works in practice too, as examples show.
In most cases, however, we have to adapt the model at hand in order to fit it to
the model checking process.
assumptions ASM 0
assump. ASM 1
ass. ASM 2
.. ..
.. ..
interface to
environment Implementation
Often these assumptions are necessary for the mathematical proof in order
to cope with parts that are abstract or external for the ASM model. They are
given informally or formalised by some logic expression in the text that surrounds
the specification of the transition rules. Assumptions related to system inherent
behaviour will be specified in terms of ASM transition rules at some lower level
of abstraction, whereas assumptions over the environment of the system are
dedicated to external behaviour that should not be specified further by rules
Methodology for Model Checking ASM 343
– In the ASM model of the bakery algorithm ([1]) the ticket function T is
external and its behaviour is abstract in the most abstract model. But we
find that some logical properties are assumed that have to be satisfied by T .
This logical properties are then specified in the appropriately refined ASM.
– Also, for the specification of the bakery algorithm, we find that a fairness
assumption is necessary for proving correctness of the algorithm ([1]). At the
moment there is no feature in ASM language to express fairness assumptions.
It should be discussed to extend the language in this direction.
– For embedded systems like the production cell, the specification of the envi-
ronment is abstract ([8]) and not part of the ordinary transition system. The
behaviour of sensors is formalised by means of oracle functions. However, it
is necessary to assume that the behaviour of the environment is “reasonable”
in order to guarantee correctness of the ASM model. In [8] most assumptions
are given in terms of logical formulae that remain external for the refined
ASM, too.
– For the specification of protocols we might abstract from the underlying
communication model governing the transfer of messages, like in the ASM
model of the FLASH cache coherence protocol in [3]. But for the proofs we
have to assume that the messages are transfered according to a particular
strategy (e.g. the FIFO-strategy, such that the ordering of messages is pre-
served). At some lower level in the ASM hierarchy a proper message passing
behaviour has to be specified that implements the assumption made on the
order of transfered messages (cf. section 3).
Obviously, only states, state transitions, and assumptions on the model together
give the complete specification of the problem at hand. This view is sketched
in fig. 1: the dashed box comprises all parts of a model at a particular level of
abstraction.
In ASM, external or system inherent but (on a certain level) abstract be-
haviour is specified by oracle functions. Oracle functions – in case of finite
domains – can be simply transformed into corresponding non restricted state
variables within the SMV model (neither their initialisation nor their updating
is specified), i.e. we give a loose specification and a behaviour may be chosen
non-deterministically. Since model checking is complete testing over the whole
state space, every loose specification leads to a complete case distinction over all
possibilities. The problems arising with this are not only a matter of the known
state explosion problem, but if we fail to meet the additional assumptions on
oracle functions (those which are not expressed by means of transition rules),
the SMV model comprises behaviour that is excluded in the complete ordinary
ASM model.
344 K. Winter
– Fairness constraints can simply be added in the SMV code, since the SMV-
language offers the corresponding feature.
– For embedded systems, since the current ASM tools do not support the spec-
ification of assumptions in terms of some temporal logic, we have to specify
assumptions on the environment by means of ASM transition rules to get a
suitable input for the transformation. It is a difficult task, however, to model
behaviour accurately in a constructive way, because abstract properties have
to be encoded operationally. This process easily leads to under-specification
or over-specification, i.e. the specification is too open or too restrictive. It
must be carefully inspected if the specification meets the assumptions. Oth-
erwise errors may remain undetected or a “wrong counterexample” may be
generated (see below). We have to be aware that the results of model check-
ing will hold only with respect to the specified environment.
– Assumptions made on abstract parts of the internal model can simply be
added by means of refining the model appropriately. As an example the
reader is referred to the case study presented in the following sections. (In
particular, the matters of refinement are discussed in section 3).
Let MASM the set of ASM models, MSMV the set of SMV models, and RUN
the set of runs of an ordinary temporal structure over branching time, which
provides a semantics that both kinds of model have in common. To give a more
precise notion of missing assumptions, we introduce mod and mod be two func-
tions that yields the semantics of a model such that mod : MASM → RUN
and mod : MSMV → RUN . Problems may arise in two cases:
mod(MASM ) ⊂ mod(MSMV )
mod(MASM ) ⊃ mod(MSMV )
In the first case — the transformed SMV-model is strict greater than
the ASM-model because of a loose specification of assumptions —
mod(MSMV ) \ mod(MASM ) may contain runs, that violate the property to be
checked. One of these may give a counterexample that is, however, not a proper
run of the ASM model. No proposition on the model under investigation is
possible then since only one counterexample will be given. We call it wrong
counterexample, it obstructs the overall debugging process.
In the second case — when specifying assumptions too restrictive and the
SMV-model is strict smaller than the ordinary ASM-model — we may fail to
detect errors that occurs only in those runs of the ASM-model that are excluded
for the SMV-model. The runs in mod(MASM ) \ mod(MSMV ) will not be checked
by the model checker.
In the following we give an example for fitting an ASM-model to our model
checking approach. We show how the ASM model of the FLASH cache coherence
protocol that is given in [3] can be refined in order to add the assumption that
Methodology for Model Checking ASM 345
are stated in the proof part. Also, the necessary fairness assumption are added in
the SMV code. Assumptions on environmental behaviour are not needed in this
particular case, we can keep the “environment” by means of oracles functions
without any restricting specification.
In section 2 we present the original abstract model of the FLASH protocol
from [3]. Section 3 introduces our refinements that are necessary to complete
the model to be transformed. The results that show the benefits from model
checking are presented in section 4. In section 5 the transformation and its
possible optimisations is discussed. We give remarks on related work in section 6
and conclude with section 7.
The Stanford FLASH multiprocessor (cf. [6]) integrates support for cache co-
herent shared memory for a large number of interconnected processing nodes.
That is, each node (or processor) holds some part of memory, which is accessible
for other nodes too, in order to read or write data from this part. One of the
problems to be investigated for this architecture is the coherence of the data,
since access to data is realized by working with a copy of it. It may happen that
one node is going to process a copy of the data when the data have changed in
the meanwhile (because of some writing access of another node).
The parts of the distributed memory are given as sets of lines, i.e. small pieces
of memory content. Each line is associated with a home node hosting the part
of the physical memory where the line resides. Whenever a process (or a node)
needs to have access to a particular line, we say that a read or write miss occurs.
Every read or write miss concerning a remote memory line triggers a line request
to its home node.
Being interconnected the nodes are able to communicate with each other.
They can send (and deliver) messages in order to request for a remote line or
to respond on a request by means of sending a copy of the needed data. To
provide coherence of the data additional book-keeping is necessary to prevent
from simultaneous reading and writing on the same line. That is, writing needs
exclusive access to a line whereas, reading is allowed in shared access. Message
passing and book-keeping of shared and exclusive access is the matter of the
protocol specification we consider in our ASM model.
The ASM model of the protocol in [3] is based on agents. Each agent models
one (processor) node that holds a certain block of the distributed memory. A
set of transition rules describes the behaviour of a single agent. Its behaviour is
determined by the incoming message that is to be processed, in fact the type
of a message is suitable for determination. This notion yields the clear model
structure shown in figures 2 and 3.
Incoming messages are requests from remote nodes (or from the receiving
node itself in order to simplify the model, this special case is called intra-node
communication further on). In figure 2 (lower part) the reader may find transi-
tion rules to be triggered when a request for shared access is received. In figure 3
346 K. Winter
(upper part) transition rules for handling requests for exclusive access are de-
picted. For readability the message types to be distinguished are surrounded by
boxes.
A read or write miss that causes a request to be sent is arbitrarily generated
by means of oracles. These oracles come along as messages as well, their types
have the form cc. (cf. figure 2, upper part). Whenever the agent receives such
kind of message it generates the corresponding request and enters a waiting
mode. Releasing an access is handled in the same way. If the agent sends a
request for releasing a line the state of the line is invalidated, that is, the node
has no reliable copy of the line any more.
In our adaptation of the model the parts related to data (to be sent within
the messages) are discarded. Since the data do neither influence the control flow
of the protocol behaviour (data do not control any of the guards in the transition
rules) nor determine the properties to be checked, we do not lose expressiveness
of the model and our checking results.
State Functions. Beside the message type the agent’s behaviour depends on
several state variables: curPhase(line) (phase of the current request), State(line)
(state of the local line copy in use), and pending(line) (flag for currently processed
request). Owner(line) and the set of Sharers of a line are also taken into account.
These functions are local for each agent, the additional parameter self is omitted
in figures 2 and 3.
Message Structure. A message is modelled as a quintuple consisting of the
type of the message, the addressed agent, the sender agent, the agent initiating
the request and the requested line.
The message types related to shared access are:
get: requesting a line from its home
put: granting a line to the requester (source of the request)
fwdget: forwarding the request to an exclusive owner of the line
swb: requesting a write-back of an owned line that is to be shared
nack, nackc: negatively acknowledging the request or forwarded request
(nackc), if it cannot be performed now.
The transition rules that are related to requests for shared access are given
in figure 2: the circulation of a “get”-request. A get-request may either be (a)
negatively acknowledged if another request on the line is already processing
(pending(line) is true), or (b) it is forwarded to the current owner if there is
already an exclusive access (owner(line) not undefined), or (c) it is granted to
the requester if no owner is noted (else case). If there is an owner (case (b)
above), the grant for shared access is given by the agent which “believes” to
be an owner, and, moreover, the owner has to release its exclusive copy. If an
agent gets a forward get-request and does not agree to be the owner of the line
(State(l) is not exclusive) then the request has to be negatively acknowledged
as well.
In analogy, message types related to exclusive access are:
getx: requesting a line for exclusive access from its home
putx: granting a line for exclusive access to the requester
Methodology for Model Checking ASM 347
fwdgetx: forwarding the request for exclusive access the owner of the line
inv: requesting a current sharer of the line to invalidate its local copy
invAck: acknowledging the invalidation of the line
fwdAck: owner’s granting according to a forwarded shared request.
The corresponding behaviour is shown in figure 3: the circulation of a “getx”-
request. Again, the exclusive request may be negatively acknowledged if another
request is in process already, or it is forwarded to the owner if there is one
noted. In addition to the get-request we have to take care for possible sharers
if there is no owner. Each of the sharers of the line has to be informed about
the request for exclusive access (by sending an inv-message). When getting an
inv-message, sharer has to invalidate its copy of the line and response with an
acknowledgement of invalidation (sending invAck-message). When each of the
sharers has sent its invalidation acknowledgement to home a grant for exclusive
access is send to the requester (i.e. sending putx-message is delayed until all
invAck-messages are received).
For releasing a shared or exclusive copy from its cache an agent sends a
write back (wb) or a replace message (rpl) to home. The agent has to be deleted
from the list of shares or not being owner any more.
The universes QLength, Agent, and Line are sets that are finitely restricted by
a maximal index: maxQ, maxAgent, or maxLine. These constants can easily be
adjusted in order to scale up the model under investigation.
Sending a message (SendMsg) is now specified as appending the message
components to the corresponding functions: If ni is the smallest index for which
the queue for messages in transit is empty, we update all message-component-
functions at this index. We indicate emptiness of queue(ni ) by means of message
type noMess for the message type component, i.e. if (MessInTr(ni , ai )=noMess)
is satisfied and for all nj < ni it is not, then ni marks the index for writing the
message to be append. This is specified by means of the following macro:
transition AppendToTransit(agent_,(sender_,mess_,source_,line_)) ==
if MessInTr(1,agent_)=noMess
then SenderInTr(1,agent_) := sender_
MessInTr(1,agent_) := mess_
SourceInTr(1,agent_):= source_
LineInTr(1,agent_) := line_
else do forall i in { 2..maxQ }
if MessInTr((i-1),agent_)!=noMess and MessInTr(i,agent_)=noMess
then SenderInTr(i,agent_) := sender_
MessInTr(i,agent_) := mess_
SourceInTr(i,agent_):= source_
LineInTr(i,agent_) := line_
endif
enddo
endif
For requests (i.e. message type is cc.get, cc.getx, cc.wb, cc.rpl) we in-
troduce an extra queue for request in transit MessInTrR. Analogously, we define
dynamic functions that hold one of the components of a request. Sending of a
request message is specified by means of the macro AppendRequestToTransit
that looks similar to the appending macro shown above.
specify how the messages in transit are read by the agents to be addressed, that
is how messages are passing through (cf. Fig. 4).
Each agent holds a current message (which is seen to be empty if its type
equals to noMess). Again we model the current message by means of its com-
ponents: InMess, InSender, InSource, and InLine are dynamic functions over
domain Agent. The current message is delivered by passing through the first
element of the transit-queues MessInTr and MessInTrR (see Fig. 4). Note, that
requests have lower priority than messages, i.e. a request is passed through only
if there is no message in transit left.
MessInTr
message_passing
... SendMsg
InMess 1 2 3 maxQ
MessInTrR
SendRequest
message_passing ...
1 2 maxR
Processing the current message and delivering a new message operates on the
same dynamic functions of the model. In order to avoid racing we have to inter-
leave both steps: message processing of the agents and message passing through.
We extend the overall behaviour by means of a sub-step for synchronisation. In
the synchronisation step the messages are passed through to the addressed agent
in the proper order.
In an ASM model, introducing a sub-step (to be processed after each state
transition) is structure preserving: in addition to the ASM for message processing
we specify an ASM for the message passing through. That is, we do not have to
interfere the ordinary transition rules (cf. figures 2 and 3) with transition rules
for message passing through. An overall ASM main invokes both “sub-ASM”
agent behaviour and message passing in turn:
transition main ==
if toggle = behave
then agent_behaviour
toggle := sync
else (* if toggle = sync *)
message_passing
toggle := behave
endif
Taking this, we benefit from the clear and understandable structure of the
abstract model that is given by [3].
352 K. Winter
2
The corresponding listings of the counterexamples can be found in the appendix of
the full version of this paper to be find on http://www.first.gmd.de/˜kirsten.
Methodology for Model Checking ASM 353
the comparing results when scaling up the parameters for the number of agents
and lines.4 The variable ordering is determined by the automatic reordering
facility that is given by the SMV.
resources used: 2 agents, 1 line 3 agents, 1 line 2 agents, 2 lines
user time/system time: 4.69 s/0.13 s 5687.52 s/0.6 s 17263.2 s/0.86 s
BDD nodes allocated: 70587 1612740 2975127
Bytes allocated: 4849664 37748736 54657024
BDD nodes repr. transition relation: 19261 + 78 288986 + 82 78365 + 96
Although checking our model of the FLASH protocol is only feasible for a
small number of agents and lines, the results show that the counterexamples
yield extremely helpful scenarios for locating errors.
5 Transformation Issues
The general approach for the transformation of ASM transition rules into SMV
code is already described in [13]. The extended transformation algorithm that
includes the transformation of n-ary dynamic functions (with n > 0) can be
found in [2]. Based on this foundation we want to add some more general remarks
here concerning extensibility and optimisation. In order to make this paper self
containing we recall our main ideas of transformation.
– non-static functions (i.e., dynamic and external functions) are identified with
locations and thus mapped one-to-one to SMV state variables;
– values of the ASM data types are mapped one-to-one to SMV constants;
– applications of static functions are translated to applications of the corre-
sponding built-in operators of SMV.
What remains to be done is to restructure the ASM program into a form where
updates of the same location, together with their guards, are collected together.
This is done in two steps. First, we transform an ASM program P into an
equivalent ASM program P 0 consisting only of a block of guarded updates (i.e.,
rules of the form if G then f (t) := t) by means of a “flattening” transformation:
4
The experiments were carried out on an UltraSPARC-II station with 296MHz and
2048 Mb memory, the operating system is Solaris 2.6.
Methodology for Model Checking ASM 355
(
if G1T then RT1 if G ∧ G1T then RT1
[[RT ]] = ...
...
if Gn n
T then RT
n n
if G ∧ GT then RT
( ⇒ [[if G then RT else RF ]] =
if G1Fthen 1
RF
if ¬G ∧ G1F then RF
1
[[RF ]] = ... ...
if Gm m
F then RF
if ¬G ∧ Gm m
F then RF
Second, we collect all guarded updates of the same location, thus obtaining,
for each location loc occurring on the left-hand side of an update in P 0 , a pair
(loc, {(G1 , t1 ), . . . , (Gn , tn )}) which maps loc to a set of pairs (guard, right-hand
side). Such a pair is translated into the following SMV assignment:
ASSIGN next(C [[loc ]]) :=
case C [[G1 ]] : C [[t1 ]] ; ... C [[Gn ]] : C [[tn ]] ; 1 : C [[loc ]] esac;
where C [[.]] denotes here the ASM → SMV compiling function for terms, which
is straightforward for ASM0 .
The ordinary transformation shown above is extended in order to tackle the
ASM language as complete as possible. We have to exclude, however, infinite
domains as well as the notion of import or export transition rules because
the domains of a model have to be fix and may not grow during a run. But
any other rule constructor as well as arbitrary data types and operations (in
particular, lists, finite sets, finite maps and user-definable freely generated types,
as provided by ASM-SL) can be used without restriction. Finite quantifications
are also supported.
In our approach we reduce an arbitrary (finite and fix) ASM to ASM0 . The
main problem here is that in general we do not know which location is updated
by f (t1 , . . . , tn ) := t (if n > 0) because the parameters ti may be dynamic
functions and thus can change their current value. Evaluation of a location is
possible if and only if all ti are evaluated, i.e. either they are static or have to be
unfold to cover all possible values. Thus, the basic idea of the transformation is
to iteratively unfold and simplify rules until all terms can be reduced to values
or locations.
Terms can be simplified by means of the transformation [[.]]ρ defined in Ta-
ble 1, which is then extended to rules in a canonical way.
The rule-unfolding transformation E, which operates on closed rules such as
the program P , is formally defined in Table 2. It works as follows:
– if the rule R consists of a block of update rules of the form location := value,
it terminates and yields R as result (there is nothing left to unfold);
– otherwise, it looks for the first location l occurring in R (but not as left-hand
side of some update rule) and unfolds R according to the possible values of l.
In turn, the unfolding has to be applied to the sub-rules [[R[ l/xi ]]] obtained
by substituting the values xi for l in R and simplifying.
Term Simplification
[[x]]ρ = x [[ l ]]ρ = l
x = ρ(v) if v ∈ dom(ρ)
[[ v ]]ρ =
v otherwise
Rule Simplification
[[skip]]ρ = skip
[[tL := tR ]]ρ = [[tL ]]ρ := [[tR ]]ρ
[[R1 . . . Rn ]]ρ = [[R1 ]]ρ . . . [[Rn ]]ρ
(
[[RT ]]ρ if [[G]]ρ = true
[[if G then RT else RF ]]ρ = [[RF ]]ρ if [[G]]ρ = false
if [[G]]ρ then [[RT ]]ρ else [[RF ]]ρ otherwise.
forall v in A with G R0 ]]ρ =
[[do
0 0
[[if G then R ]]ρ[v7→x1 ] . . . [[if G then R ]]ρ[v7→xn ]
if [[A]]ρ = x = {x1 , . . . , xn } (i.e., if [[A]]ρ is a value)
= 0
do forall v in [[ A ]] ρ with [[ G ]] (ρ\v) [[ R ]] (ρ\v)
otherwise.
Rule Unfolding
If R has the form l1 := x1 . . . ln := xn , then E(R) = R.
Otherwise:
E(R) = if l = x1 then E([[R[l/x1 ]]]∅ )
else if l = x2 then E([[R[l/x1 ]]]∅ )
...
else if l = xn then E([[R[l/xn ]]]∅ )
where l is the first location occurring in R (but not as lhs of an update rule)
and {x1 , . . . , xn } is the range of location l.
Methodology for Model Checking ASM 357
At least for our example of the FLASH protocol we found that the size of
BDDs that represent the corresponding models are comparable. Therefore we
keep the transformation without exploiting the notion of SMV modules in order
to keep the implementation as simple as possible.
6 Related Work
In [11] a different approach for automated verification for ASM is elaborated.
Spielmann represents an ASM model independently of its possible input by
means of a logic for computation graphs (called CGL*). The resulting formula
is combined with a CTL*-like formula which specifies properties and both are
checked by means of deciding their finite validity. This addresses the problem of
checking systems with arbitrary inputs. However, Spielmann concludes that this
approach is restricted to ASM with only 0-ary dynamic functions and relational
input. For many problems, however, we found that the input, or the environment
respectively, proper to the ASM model at hand is restricted by certain assump-
tions (as described in Sec. 1.1). The restriction to 0-ary functions outweighs this
Methodology for Model Checking ASM 359
potential benefit. In [10] some helpful theoretical results of the model checking
and the verification problem for ASM are published. This investigation tends to
define the limit of both problems in general in terms of their decidability and
complexity.
In some cases the formalisation of an environment behaviour that is restricted
by assumptions is a complex task. Operational semantics (like ASM) may yield
more complex solutions than a logical approach (like the temporal logic LTL). It
might be useful to facilitate the user with LTL for the specification of additional
assumptions on the external behaviour. This approach is supported by the model
checker SVE ([4]). Thus, adapting our transformation to the SVE tool might be
promising.
The case study of FLASH protocol that is used here in order to give an
example for applying our approach is investigated within other approaches as
well. The most elaborated work is published in [9]. Park and Dill introduced
a method relying on so called aggregation function that yields the refinement
relation between specification and implementation. Their approach for proving
correctness is supported by means of the interactive theorem prover PVS. Giving
up on fully automated support they can tackle a broader range of requirements,
of course.
Wing and Vaziri-Farahani investigated in [12] a slightly similar kind protocol
for cache coherence (but not exactly the FLASH protocol) by means of model
checking, namely using SMV. As we do they claim that “Without loss of gen-
erality, we can analyse this cache coherence protocol by considering one client,
C, one server, S, and one file, f”. But the introduced SMV model, in contrast
to the ASM model we used, lacks the possibility to scale up easily by using pa-
rameters. We found the ASM model, being more general but yet concise at the
same time, leads to a more clear and understandable structure than modelling
in plain SMV language only.
Also McMillan in [7] applied the SMV to a distributed cache protocol. But
his SMV model of the protocol being spread over several modules is not fully
introduced and thus difficult to understand. Since the application is not exactly
the same as the FLASH protocol we cannot relate the effort for checking (BDD
size and checking time) properly.
7 Conclusions
In previous work ([13] and [2]) an interface from the ASM Workbench to SMV
was presented. The transformation from ASM to the SMV language comprises
the treatment of dynamic functions of arity n > 0, which is an crucial improve-
ment, as most ASM specifications benefit from the abundant use of parametric
dynamic functions.
In this paper we gave an overview of our experience with model checking
ASM so far. Although, our transformation can treat a great subset of the ASM
language we found that in practice most ASM models have to be refined or ex-
tended to fit for the model checker: Since assumptions on the ASM model that
360 K. Winter
are stated besides the ordinary transition rules are not covered by the trans-
formation we have to add a proper specification of these assumptions into the
resulting SMV-model. This can be done by simply exploiting language features
of SMV (e.g. the fairness construct), by further refinement of the ASM model
at hand, or by specifying environment behaviour by means of ASM transition
rules such that the given assumptions are covered by the transformation.
The practicability of the approach is demonstrated by a non-trivial case
study: the ASM model of the FLASH protocol. In order to fit the model for
the model checking process we have to refine the model in that we specify the
message passing behaviour. We gave some examples for errors found by the model
checker that can hardly be detected by pure mathematical proofs, and deduce
more general constraints for the model at hand from the counterexamples.
We conclude with some remarks on the implementation of the transformation
algorithm concerning extensibility and optimisation issues.
References
1. E. Börger, Y. Gurevich, and D. Rosenzweig. The Bakery Algorithm: Yet Another
Specification and Verification. In E. Börger, editor, Specification and Validation
Methods. Oxford University Press, 1994.
2. G. Del Castillo and K. Winter. Model checking support for the ASM high-level
language. In S. Graf and M. Schwartzbach, editors, Proc. on 6th Int. Conf. TACAS
2000, volume 1785 of LNCS, pages 331–346, 2000.
3. A. Durand. Modeling cache coherence protocol - a case study with FLASH. In
U. Glässer and P. Schmitt, editors, Procs. of the 28th Conf. of German Society of
Computer Science, TR, Magdeburg University, 1998.
4. T. Filkorn et al. SVE Users’ Guide. Siemens AG, München, 1996.
5. The VIS Group. Vis: A system for verification and synthesis. In T. Henzinger
R. Alur, editor, 8th Int. Conf. CAV’96, volume 1102 of LNCS, 1996.
6. J. Kuskin, D. Ofelt, and M. Heinrich et. al. The stanford FLASH multiprocessor.
In 21th Int. Symp. on Computer Architecture, 1994.
7. K. McMillan. Symbolic Model Checking. Kluwer Academic Publishers, 1993.
8. L. Mearelli. An Evolving Algebra Model Of The Production Cell. Master’s thesis,
Universita di Pisa, 1996.
9. S. Park and D. Dill. Verification of cache coherence protocols by aggregatiuon of
distributed transactions. Theory of Computing Systems, 31:355–376, 1998.
10. M. Spielmann. Model Checking Abstract State Machines and Beyond. In This
Volume.
11. M. Spielmann. Automatic verification of abstract state machines. In N. Halbwachs
and D. Peled, editors, Computer Aided Verification, CAV ’99, number 1633 in
LNCS, pages 431–442, Trento, Italy, 1999.
12. J. M. Wing and M. Vaziri-Farahani. A case study in model checking software
systems. Science of Computer Programming, 28:273–299, 1997.
13. K. Winter. Model checking for abstract state machines. J.UCS Journal for Uni-
versal Computer Science (special issue), 3(5):689–702, 1997.
Report on a Practical Application of ASMs in
Software Design
1 FALKO
Abstract State Machines (ASMs) [3,1] have been used at Siemens Corporate
Technology to design a component in a software package called FALKO. This
name is the German acronym for “Timetable Validation and Timetable Con-
struction” describing concisely the main functionality of this tool. Detailed ope-
rational timetables including e.g. vehicle roster plan can be automatically calcu-
lated from raw data like train frequency and infrastructure of a railway system.
For these calculations estimations of trip times are used. Timetables – whether
constructed with FALKO or other tools – have to be validated for operability
and robustness. Conventionally this is done by (physically) driving trial runs.
With FALKO this costly form of validation can be replaced by dynamic simula-
tion of operational timetables modelling quantitative aspects like velocities and
trip times as accurately as needed.
To perform dynamic simulation of timetables, the whole closed-loop traffic
control system is modelled within FALKO. The model is designed in a mo-
dular way with three main components: train supervision/train tracking, in-
terlocking system, and railway process model. Software components for train
supervision/train tracking and for the interlocking system are nowadays part
of real train operation. The railway process model on the other hand serves to
replace the real physical system (trains, signals, switches etc.) in the simulation.
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 361–366, 2000.
c Springer-Verlag Berlin Heidelberg 2000
362 E. Börger, P. Päppinghaus, and J. Schmid
In the design phase of the project the ASM workbench was also used for
early tests of the ASM model(s). At the end of the design phase it was decided
to attempt code generation rather than hand coding the already debugged ASM
model.
In the implementation phase a code generator has been developed, automa-
tically generating C++ code from type correct ASM-SL code. In addition some
wrapper code for interfacing the generated code to the remaining components,
and some low-level “library code” was hand coded.
Formal verification of the ASM model has not been attempted, this was not
a goal of the project.
Design Phase
Implementation Phase
Summing up that part of the above listed effort, which was spent on behalf of
the railway process model component of FALKO, this yields a total effort of 66
person weeks.
It is not possible, of course, to compare this effort reliably to the correspon-
ding effort in a conventional software design process. But a rough estimation
done by an experienced programmer intimately familiar with FALKO says that
the ASM design (including development of the C++ code generator) has excee-
ded a conventional effort by about 10%.
Report on a Practical Application of ASMs 365
C++ Code
– ca. 9 000 lines of generated C++ code
– ca. 2 900 additional lines of handwritten C++ code, consisting of
– ca. 400 lines wrapper code for interfacing to other components of FALKO
– ca. 2 500 lines low-level library code
In the prototypical predecessor system of FALKO the railway process model
consisted of ca. 20 000 lines of (handwritten) C++ code. To be fair one has to
take into account, however, that this component – having been experimented
with – had grown over time (and become difficult to maintain, which was the
reason for redesigning it completely).
3 Experiences
It turned out that one of the main advantages of ASMs for this design was the
parallel update view. This made it possible to model the railway process in such
a way that each state of the ASM model corresponds to a “snapshot” of the
virtual physical process taken at the occurrence of a “relevant discrete event”.
Due to this, one can always have a clear picture of the physical state snapshot,
on which auxiliary computations (specified by static and derived functions) are
based.
FALKO developers and reviewers not concerned with formal methods had no
problems to understand the ASM model. The possibility of early tests by execu-
ting the ASM model with the ASM workbench was very helpful and uncovered
bugs also in other components of FALKO at an early stage.
No serious attempt has been made to measure the potential performance loss
due to the use of generated C++ code. Comparison to the predecessor system of
FALKO has been possible only on one example, for which the data happened to
be available in both systems. In this small example performance of the previous
system was about 30% better than that of the current FALKO system. For the
time being the performance problem has been left aside, since it turned out that
FALKO’s performance is good enough for the purpose the product is used for.
FALKO is used in four installations at the Vienna Subway Operator since
March 1999, one of these installations being in daily use. Up to now (i.e. March
366 E. Börger, P. Päppinghaus, and J. Schmid
2000) the customer reported no bugs. After finishing the first version of FALKO,
two bugs in the railway process model have, however, been discovered in tests
during development of the second version. The FALKO developers have not yet
familiarized themselves with the ASM specific tools. The generated C++ code
being readable enough they chose to implement temporary fixes of the two bugs
by handhacking the generated C++ code, and to postpone correction of the
ASM model until the faults are analyzed more thoroughly.
References
1. E. Börger and J. Huggins. Abstract State Machines 1988-1998: Commented ASM
Bibliography. Bulletin of EATCS, 64:105–127, February 1998. Updated bibliography
available at http://www.eecs.umich.edu/gasm.
2. Giuseppe Del Castillo. The ASM Workbench – A tool environment for computer
aided analysis and validation of ASM models. PhD thesis, University of Paderborn,
to appear.
3. Yuri Gurevich. Evolving Algebras 1993: Lipari Guide. In E. Börger, editor, Speci-
fication and Validation Methods, pages 9–36. Oxford University Press, 1995.
Using Abstract State Machines at Microsoft:
A Case Study
1 Introduction
This paper describes a case study on the use of ASMs as support for design
and analysis of software at Microsoft. In order to use ASMs we needed a tool
for executing ASMs integrated with the Microsoft programming environment,
in particular with the Component Object Model or COM [5]. We developed a
prototype called AsmHugs [8] by extending the Hugs system [2] which is an
implementation of the lazy functional programming language Haskell. AsmHugs
is in many ways similar to AsmGofer [1] but Hugs enabled us to use H/Direct [6,
7] for integration with COM. A detailed technical report of this case study is in
preparation [3].
Y. Gurevich et al. (Eds.): ASM 2000, LNCS 1912, pp. 367–379, 2000.
c Springer-Verlag Berlin Heidelberg 2000
368 M. Barnett et al.
1.3 COM
1. Control model. The abstraction level of this model is at the control level.
The user can enter a command if the debugger is in a mode where the user
has a prompt. The runtime environment can issue a callback to the debugger
only if the latter is expecting a callback. At this level, the only effect of a
callback or user command is to change the control state.
370 M. Barnett et al.
Debugger IDL
Debug
Services
Runtime
2. Object model. This model reflects the compile time or static structure of
the underlying architecture, namely that modules contain classes, classes
contain functions, functions contain code, etc. Furthermore, it provides a
restricted view of the run time structure, namely that processes contain
threads, threads contain frames, etc. At this level exactly those commands
are modeled that are intimately connected to the compile time structure and
to the restricted view of the run time structure, such as execution control,
breakpoint control, and stepping commands.
3. Ground Model. This model has the same core functionality as the debugger.
It provides a more detailed view of the run time structure than the object
model. User commands dealing with inspection of the run time stack, and
contents of individual frames, like inspection of specific object fields, are
modeled here.
AsmHugs
Control Object Ground
Model Model Model H/Direct
Runtime
Debug Services
In principal, all models are executable, either without the runtime or through
the refinements using the actual runtime (see Figure 2). However, in the former
case, a proper simulation harness has to be provided. An example of a test
harness by using a “wizard” is illustrated in Section 3.2. In a related project, we
constructed an ASM model that approximates the runtime. This model could
be used here to embody the runtime in the harness.
When executing the control model using the runtime, each action is interpre-
ted by AsmHugs via the indicated refinements; COM connectivity is provided
by H/Direct.
In this model we consider the main control modes of the debugger. These modes
reflect who has the control: the user, the runtime, or the debugger itself. When
the user has control, the debugger presents a prompt and waits for input. The
debugger waits for a response from the runtime when the runtime has the control.
Once the runtime has responded with an event, the debugger has control and
has to decide what mode to transit to. As we will see, this decision is based on
particular properties of the event and further input from the runtime.
Although the communication between the real debugger and the runtime is
asynchronous, the documentation of the debug API specifies that the runtime
issues at most one asynchronous event at a time; before issuing the next event
the runtime must receive an acknowledgement from the debugger. Furthermore,
the communication protocol between the debugger and the runtime ensures that
at most one of them may have control. Therefore, sequential ASMs suffice for
our modeling purposes.
The remainder of the section is structured as follows. First, we will lay out
the control model ASM in full detail, introducing the necessary state components
and the rules as needed. Second, we will run a particular user scenario that will
indicate a possible discrepancy between the runtime and the model. Finally, we
will remedy the model.
This example shows a typical use of a high level model during the design
phase in a software development cycle.
The debugger can be in one of four modes. In Init mode the debugger either
hasn’t been started yet, or it has been terminated. In Break mode the user
has the control. In Run mode the runtime has the control. In Break? mode the
debugger has the control. The dynamic ASM function, dbgMode, records the
mode of the debugger in the current state; it has the initial value Init. The top
level rule of the debugger is dbg. Below, do is short for do in-parallel.
There are two monitored functions, command and response, that are updated
by the user and the runtime, respectively. Initially, both monitored functions
have a value that indicates that no input has been entered by either.
User commands. We can partition user commands into three groups: com-
mands for starting and quitting the debugger, commands that hand the control
over to the runtime (e.g. execution control and stepping commands), and com-
mands that do not affect the control mode (e.g. state inspection and breakpoint
setting).
The user can issue commands only if the debugger is either in Init mode
or in Break mode. In the first case, the only meaningful action is to start the
debugger.
handleCommands = do onStart
onExit
onBreakingCommand
onRunningCommand
command := "nothing"
isBreakingCommand(x) =
not(isRunningCommand(x)) and x != "exit" and x != "start"
doCommand(x) = skip
Callbacks. The debugger can handle callbacks or responses from the runtime
only in the Run mode. The value of the monitored function response is a callback
message from the runtime notifying the debugger about a runtime event. In the
debugger, each event is classified either as a stopping event or as a non-stopping
event.
handleResponses = do onStoppingEvent
onNonStoppingEvent
response := "nothing"
onStoppingEvent =
if isStoppingEvent(response) then do dbgMode:= Break?
doCallback(response)
onNonStoppingEvent =
if not(isStoppingEvent(response)) then doCallback(response)
Breakpoint hit events, step complete events, and process exit events are al-
ways stopping events. Initially isStoppingEvent is the following unary relation
(unary Boolean function).
However, the relation is dynamic and may evolve during a run as a result of
a specific user command or a specific runtime event.
In the control model the actual handling of callbacks is a skip operation. This
rule is refined in the object model.
doCallback(x) = skip
Pending Events. In Break? mode a stopping event has happened and the
debugger should hand the control over to the user. This happens only if there
are no pending events in the runtime. Otherwise the control goes back to the
runtime and the debugger continues the current process
374 M. Barnett et al.
handlePendingEvent = do onPendingEvent
onNoPendingEvent
onBreakingCommand onNonStoppingEvent
onRunningCommand
Break Run
onPendingEvent
onNoPendingEvent
onStoppingEvent
onStart onExit Break?
Init
The run shows that after we hit a breakpoint there is a pending event in
the runtime. According to the model, the current process is continued and the
control is passed to the runtime. It turns out that the pending event was a
non-stopping event (class was loaded). Obviously, this behaviour contradicts the
expected consequence of reaching a breakpoint, namely that the user should
get the control. At this point we have two options to solve the problem: if we
can constrain the runtime to meet the restriction that only stopping events can
cause the isEventPending flag to become true, then the current model is correct;
otherwise we have to modify the model. We choose the latter, see Figure 4.
onBreakingCommand onNonStoppingEvent
onRunningCommand
Break Run
onNoPendingEvent
onStoppingEvent
onStart onExit Break?
onPendingEvent’
onAnyEvent
Init Run’
4 Object Model
The static and the dynamic structures of the runtime architecture are reflected
in the object model with just enough detail so that one can model features
that deal with execution control, breakpoints, and stepping, and explore their
interaction.
Let us consider some examples. The first example is the refinement of the
user command that starts the debugger. The next two examples specify what
happens in the object model when a module is loaded or unloaded in the runtime.
Recall that all those rules are just skip rules in the control model.
doCommand("start") = do in-sequence
coInitialize
shell := newShell(services = newServicesInterface(...),
callback = newCallbackInterface(...),
modules = {},
breakpoints = {},
debugee = undef)
shell.services.setCallback(shell.callback)
The first rule initializes the COM environment. The second rule creates a new
debugger shell with a pointer to the services and a new callback. The third rule
invokes the setCallback method of the debug services with the new callback
interface pointer as argument, thus providing the services access to the client’s
callback methods.
4.2 Callbacks
Loading of modules
doCallback(<"load module",mod>) = do
shell.modules := shell.modules ++ {mod}
do in-sequence
forall bp in shell.breakpoints do bp.bind(mod)
shell.debugee.continue()
The new module is recorded in the shell. The shell attempts to bind each of
its set of breakpoints to that module. Once all new bindings have been made (if
any), the debugee is continued through an external call.
We have omitted the bind rule that checks if the location that the breakpoint
refers to indeed exists in the module. If the location exists, a real breakpoint
is created at that location through an external call to the services; otherwise,
nothing is done.
Using Abstract State Machines at Microsoft 377
Unloading of modules
The effect of unloading a module is to update the shell and to remove all the
breakpoints from that module.
There are a couple of things worth noticing about the above rules. First, the
design decision to bind a breakpoint that may already be bound implies that if
there is a breakpoint referring to a location that exists in two or more distinct
modules, then the breakpoint is associated to all of them. Second, all breakpoints
are handled simultaneously; there are no ordering constraints between them.
This is a typical situation: in the actual (C++) code there is a linearly ordered
structure maintaining the elements that are then processed sequentially in the
order determined by the structure.
When constructing the object model we detected a mismatch between the
way loading and unloading of module callbacks was implemented. Namely, alt-
hough loading followed the specification above, during unloading of a given mo-
dule, if a breakpoint was bound to this module, then that breakpoint was not
only removed from this module but from all other modules as well. We did not
discover this when studying the code, but only when constructing the object
model. In fact, it is hard to see it from the code, but readily apparent from the
model.
5 Ground Model
The ground model has the same core functionality as the debugger and can be
executed as an AsmHugs program that communicates with the runtime by using
the H/Direct generated glue code. It gives a more detailed view of the run time
structure than the object model. All the user commands, such as inspection of
specific object fields, are modeled here. The ground model is fully described in
the technical report [3].
6 Conclusion
The case study illustrates some particular ways that ASMs can help the system
designer:
378 M. Barnett et al.
To repeat, our goal is to provide a rigorous method, clear notation and con-
venient tool support for high-level system design and analysis. Our main tool
will execute ASMs (and will help you to write ASMs). Several issues that arose
during the case study have influenced the design of that tool.
The first and the third point are illustrated by the examples in Section 4.2.
References
1. AsmGofer. http://www.tydo.de/AsmGofer/.
2. Hugs98. http://www.haskell.org/hugs/.
3. Mike Barnett, Egon Börger, Yuri Gurevich, Wolfram Schulte, and Margus Veanes.
Using ASMs at Microsoft: A case study. Technical report, Microsoft Research,
Redmond, USA, 2000.
4. Egon Börger, Peter Päppinghaus, and Joachim Schmid. Report on a practical ap-
plication of ASMs in software design. In This Volume.
Using Abstract State Machines at Microsoft 379